summaryrefslogtreecommitdiff
path: root/chromium/mojo
diff options
context:
space:
mode:
authorJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-08 14:30:41 +0200
committerJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-12 13:49:54 +0200
commitab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch)
tree498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/mojo
parent4ce69f7403811819800e7c5ae1318b2647e778d1 (diff)
downloadqtwebengine-chromium-ab0a50979b9eb4dfa3320eff7e187e41efedf7a9.tar.gz
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/mojo')
-rw-r--r--chromium/mojo/BUILD.gn12
-rw-r--r--chromium/mojo/DEPS7
-rw-r--r--chromium/mojo/OWNERS10
-rw-r--r--chromium/mojo/README.md6
-rw-r--r--chromium/mojo/android/DEPS3
-rw-r--r--chromium/mojo/android/javatests/AndroidManifest.xml21
-rw-r--r--chromium/mojo/android/javatests/DEPS4
-rw-r--r--chromium/mojo/android/javatests/apk/.empty0
-rw-r--r--chromium/mojo/android/javatests/init_library.cc40
-rw-r--r--chromium/mojo/android/javatests/mojo_test_case.cc62
-rw-r--r--chromium/mojo/android/javatests/mojo_test_case.h20
-rw-r--r--chromium/mojo/android/system/core_impl.cc368
-rw-r--r--chromium/mojo/android/system/core_impl.h20
-rw-r--r--chromium/mojo/apps/js/DEPS4
-rw-r--r--chromium/mojo/apps/js/bindings/connection_unittests.js256
-rw-r--r--chromium/mojo/apps/js/bindings/gl/context.cc187
-rw-r--r--chromium/mojo/apps/js/bindings/gl/context.h77
-rw-r--r--chromium/mojo/apps/js/bindings/gl/module.cc50
-rw-r--r--chromium/mojo/apps/js/bindings/gl/module.h22
-rw-r--r--chromium/mojo/apps/js/bindings/monotonic_clock.cc42
-rw-r--r--chromium/mojo/apps/js/bindings/monotonic_clock.h22
-rw-r--r--chromium/mojo/apps/js/bindings/monotonic_clock_unittests.js20
-rw-r--r--chromium/mojo/apps/js/bindings/sample_service_unittests.js169
-rw-r--r--chromium/mojo/apps/js/bindings/threading.cc47
-rw-r--r--chromium/mojo/apps/js/bindings/threading.h25
-rw-r--r--chromium/mojo/apps/js/main.cc43
-rw-r--r--chromium/mojo/apps/js/main.js398
-rw-r--r--chromium/mojo/apps/js/mojo_runner_delegate.cc82
-rw-r--r--chromium/mojo/apps/js/mojo_runner_delegate.h33
-rw-r--r--chromium/mojo/aura/DEPS10
-rw-r--r--chromium/mojo/aura/aura_init.cc26
-rw-r--r--chromium/mojo/aura/aura_init.h33
-rw-r--r--chromium/mojo/aura/context_factory_mojo.cc136
-rw-r--r--chromium/mojo/aura/context_factory_mojo.h40
-rw-r--r--chromium/mojo/aura/screen_mojo.cc76
-rw-r--r--chromium/mojo/aura/screen_mojo.h54
-rw-r--r--chromium/mojo/aura/window_tree_host_mojo.cc179
-rw-r--r--chromium/mojo/aura/window_tree_host_mojo.h82
-rw-r--r--chromium/mojo/aura/window_tree_host_mojo_delegate.h24
-rw-r--r--chromium/mojo/bindings/js/DEPS4
-rw-r--r--chromium/mojo/bindings/js/codec_unittests.js222
-rw-r--r--chromium/mojo/bindings/js/core.cc278
-rw-r--r--chromium/mojo/bindings/js/core.h22
-rw-r--r--chromium/mojo/bindings/js/core_unittests.js91
-rw-r--r--chromium/mojo/bindings/js/handle.cc41
-rw-r--r--chromium/mojo/bindings/js/handle.h75
-rw-r--r--chromium/mojo/bindings/js/run_js_tests.cc64
-rw-r--r--chromium/mojo/bindings/js/support.cc59
-rw-r--r--chromium/mojo/bindings/js/support.h22
-rw-r--r--chromium/mojo/bindings/js/waiting_callback.cc84
-rw-r--r--chromium/mojo/bindings/js/waiting_callback.h52
-rw-r--r--chromium/mojo/cc/DEPS3
-rw-r--r--chromium/mojo/cc/context_provider_mojo.cc60
-rw-r--r--chromium/mojo/cc/context_provider_mojo.h51
-rw-r--r--chromium/mojo/common/BUILD.gn30
-rw-r--r--chromium/mojo/common/DEPS6
-rw-r--r--chromium/mojo/common/common_type_converters.cc41
-rw-r--r--chromium/mojo/common/common_type_converters.h32
-rw-r--r--chromium/mojo/common/common_type_converters_unittest.cc72
-rw-r--r--chromium/mojo/common/data_pipe_utils.cc72
-rw-r--r--chromium/mojo/common/data_pipe_utils.h32
-rw-r--r--chromium/mojo/common/handle_watcher.cc312
-rw-r--r--chromium/mojo/common/handle_watcher.h59
-rw-r--r--chromium/mojo/common/handle_watcher_unittest.cc341
-rw-r--r--chromium/mojo/common/message_pump_mojo.cc243
-rw-r--r--chromium/mojo/common/message_pump_mojo.h107
-rw-r--r--chromium/mojo/common/message_pump_mojo_handler.h29
-rw-r--r--chromium/mojo/common/message_pump_mojo_unittest.cc22
-rw-r--r--chromium/mojo/common/mojo_common_export.h32
-rw-r--r--chromium/mojo/common/time_helper.cc33
-rw-r--r--chromium/mojo/common/time_helper.h34
-rw-r--r--chromium/mojo/dbus/DEPS4
-rw-r--r--chromium/mojo/dbus/dbus_external_service.cc90
-rw-r--r--chromium/mojo/dbus/dbus_external_service.h88
-rw-r--r--chromium/mojo/embedder/DEPS11
-rw-r--r--chromium/mojo/embedder/README.md13
-rw-r--r--chromium/mojo/embedder/channel_init.cc59
-rw-r--r--chromium/mojo/embedder/channel_init.h59
-rw-r--r--chromium/mojo/embedder/embedder.cc179
-rw-r--r--chromium/mojo/embedder/embedder.h77
-rw-r--r--chromium/mojo/embedder/embedder_unittest.cc518
-rw-r--r--chromium/mojo/embedder/platform_channel_pair.cc32
-rw-r--r--chromium/mojo/embedder/platform_channel_pair.h94
-rw-r--r--chromium/mojo/embedder/platform_channel_pair_posix.cc111
-rw-r--r--chromium/mojo/embedder/platform_channel_pair_posix_unittest.cc243
-rw-r--r--chromium/mojo/embedder/platform_channel_pair_win.cc117
-rw-r--r--chromium/mojo/embedder/platform_channel_utils_posix.cc187
-rw-r--r--chromium/mojo/embedder/platform_channel_utils_posix.h78
-rw-r--r--chromium/mojo/embedder/platform_handle.cc40
-rw-r--r--chromium/mojo/embedder/platform_handle.h47
-rw-r--r--chromium/mojo/embedder/platform_handle_utils.h34
-rw-r--r--chromium/mojo/embedder/platform_handle_utils_posix.cc22
-rw-r--r--chromium/mojo/embedder/platform_handle_utils_win.cc31
-rw-r--r--chromium/mojo/embedder/platform_handle_vector.h35
-rw-r--r--chromium/mojo/embedder/scoped_platform_handle.h61
-rw-r--r--chromium/mojo/embedder/test_embedder.cc54
-rw-r--r--chromium/mojo/embedder/test_embedder.h25
-rw-r--r--chromium/mojo/environment/BUILD.gn47
-rw-r--r--chromium/mojo/environment/default_async_waiter_impl.cc50
-rw-r--r--chromium/mojo/environment/default_async_waiter_impl.h19
-rw-r--r--chromium/mojo/environment/default_logger_impl.cc71
-rw-r--r--chromium/mojo/environment/default_logger_impl.h19
-rw-r--r--chromium/mojo/environment/environment.cc36
-rw-r--r--chromium/mojo/environment/mojo_environment_impl_export.h32
-rw-r--r--chromium/mojo/examples/aura_demo/DEPS9
-rw-r--r--chromium/mojo/examples/aura_demo/aura_demo.cc198
-rw-r--r--chromium/mojo/examples/aura_demo/view_manager_init.cc45
-rw-r--r--chromium/mojo/examples/browser/DEPS10
-rw-r--r--chromium/mojo/examples/browser/browser.cc128
-rw-r--r--chromium/mojo/examples/compositor_app/DEPS5
-rw-r--r--chromium/mojo/examples/compositor_app/compositor_app.cc69
-rw-r--r--chromium/mojo/examples/compositor_app/compositor_host.cc89
-rw-r--r--chromium/mojo/examples/compositor_app/compositor_host.h58
-rw-r--r--chromium/mojo/examples/dbus_echo/dbus_echo_app.cc51
-rw-r--r--chromium/mojo/examples/demo_launcher/demo_launcher.cc43
-rw-r--r--chromium/mojo/examples/embedded_app/DEPS3
-rw-r--r--chromium/mojo/examples/embedded_app/embedded_app.cc155
-rw-r--r--chromium/mojo/examples/html_viewer/DEPS6
-rw-r--r--chromium/mojo/examples/html_viewer/blink_platform_impl.cc213
-rw-r--r--chromium/mojo/examples/html_viewer/blink_platform_impl.h78
-rw-r--r--chromium/mojo/examples/html_viewer/html_document_view.cc140
-rw-r--r--chromium/mojo/examples/html_viewer/html_document_view.h62
-rw-r--r--chromium/mojo/examples/html_viewer/html_viewer.cc105
-rw-r--r--chromium/mojo/examples/html_viewer/webmimeregistry_impl.cc97
-rw-r--r--chromium/mojo/examples/html_viewer/webmimeregistry_impl.h50
-rw-r--r--chromium/mojo/examples/html_viewer/webthread_impl.cc133
-rw-r--r--chromium/mojo/examples/html_viewer/webthread_impl.h75
-rw-r--r--chromium/mojo/examples/html_viewer/weburlloader_impl.cc139
-rw-r--r--chromium/mojo/examples/html_viewer/weburlloader_impl.h60
-rw-r--r--chromium/mojo/examples/image_viewer/DEPS4
-rw-r--r--chromium/mojo/examples/image_viewer/image_viewer.cc138
-rw-r--r--chromium/mojo/examples/nesting_app/DEPS3
-rw-r--r--chromium/mojo/examples/nesting_app/nesting_app.cc136
-rw-r--r--chromium/mojo/examples/pepper_container_app/DEPS6
-rw-r--r--chromium/mojo/examples/pepper_container_app/OWNERS6
-rw-r--r--chromium/mojo/examples/pepper_container_app/graphics_3d_resource.cc160
-rw-r--r--chromium/mojo/examples/pepper_container_app/graphics_3d_resource.h72
-rw-r--r--chromium/mojo/examples/pepper_container_app/interface_list.cc50
-rw-r--r--chromium/mojo/examples/pepper_container_app/interface_list.h37
-rw-r--r--chromium/mojo/examples/pepper_container_app/mojo_ppapi_globals.cc184
-rw-r--r--chromium/mojo/examples/pepper_container_app/mojo_ppapi_globals.h90
-rw-r--r--chromium/mojo/examples/pepper_container_app/pepper_container_app.cc110
-rw-r--r--chromium/mojo/examples/pepper_container_app/plugin_instance.cc416
-rw-r--r--chromium/mojo/examples/pepper_container_app/plugin_instance.h185
-rw-r--r--chromium/mojo/examples/pepper_container_app/plugin_module.cc110
-rw-r--r--chromium/mojo/examples/pepper_container_app/plugin_module.h61
-rw-r--r--chromium/mojo/examples/pepper_container_app/ppb_core_thunk.cc64
-rw-r--r--chromium/mojo/examples/pepper_container_app/ppb_opengles2_thunk.cc1454
-rw-r--r--chromium/mojo/examples/pepper_container_app/resource_creation_impl.cc409
-rw-r--r--chromium/mojo/examples/pepper_container_app/resource_creation_impl.h178
-rw-r--r--chromium/mojo/examples/pepper_container_app/thunk.h20
-rw-r--r--chromium/mojo/examples/pepper_container_app/type_converters.h74
-rw-r--r--chromium/mojo/examples/sample_app/DEPS4
-rw-r--r--chromium/mojo/examples/sample_app/gles2_client_impl.cc134
-rw-r--r--chromium/mojo/examples/sample_app/gles2_client_impl.h50
-rw-r--r--chromium/mojo/examples/sample_app/sample_app.cc84
-rw-r--r--chromium/mojo/examples/sample_app/spinning_cube.cc472
-rw-r--r--chromium/mojo/examples/sample_app/spinning_cube.h51
-rw-r--r--chromium/mojo/examples/wget/wget.cc116
-rw-r--r--chromium/mojo/examples/window_manager/DEPS3
-rw-r--r--chromium/mojo/examples/window_manager/window_manager.cc226
-rw-r--r--chromium/mojo/examples/window_manager/window_manager.mojom11
-rw-r--r--chromium/mojo/gles2/DEPS4
-rw-r--r--chromium/mojo/gles2/README.md5
-rw-r--r--chromium/mojo/gles2/command_buffer_client_impl.cc269
-rw-r--r--chromium/mojo/gles2/command_buffer_client_impl.h114
-rw-r--r--chromium/mojo/gles2/gles2_context.cc73
-rw-r--r--chromium/mojo/gles2/gles2_context.h63
-rw-r--r--chromium/mojo/gles2/gles2_impl_export.h31
-rw-r--r--chromium/mojo/gles2/gles2_support_impl.cc116
-rw-r--r--chromium/mojo/gles2/gles2_support_impl.h46
-rw-r--r--chromium/mojo/mojo.gyp747
-rw-r--r--chromium/mojo/mojo_apps.gypi62
-rw-r--r--chromium/mojo/mojo_apps_js_unittests.isolate54
-rw-r--r--chromium/mojo/mojo_examples.gypi446
-rw-r--r--chromium/mojo/mojo_js_unittests.isolate52
-rw-r--r--chromium/mojo/mojo_public.gypi436
-rw-r--r--chromium/mojo/mojo_services.gypi503
-rw-r--r--chromium/mojo/public/DEPS6
-rw-r--r--chromium/mojo/public/README.md43
-rw-r--r--chromium/mojo/public/bindings/mojom_bindings_generator.gypi82
-rw-r--r--chromium/mojo/public/c/DEPS16
-rw-r--r--chromium/mojo/public/c/README.md45
-rw-r--r--chromium/mojo/public/c/environment/async_waiter.h30
-rw-r--r--chromium/mojo/public/c/environment/logger.h54
-rw-r--r--chromium/mojo/public/c/gles2/DEPS3
-rw-r--r--chromium/mojo/public/c/gles2/gles2.h51
-rw-r--r--chromium/mojo/public/c/gles2/gles2_call_visitor_autogen.h544
-rw-r--r--chromium/mojo/public/c/gles2/gles2_export.h26
-rw-r--r--chromium/mojo/public/c/gles2/gles2_types.h26
-rw-r--r--chromium/mojo/public/c/system/buffer.h187
-rw-r--r--chromium/mojo/public/c/system/core.h20
-rw-r--r--chromium/mojo/public/c/system/data_pipe.h367
-rw-r--r--chromium/mojo/public/c/system/functions.h87
-rw-r--r--chromium/mojo/public/c/system/macros.h72
-rw-r--r--chromium/mojo/public/c/system/message_pipe.h178
-rw-r--r--chromium/mojo/public/c/system/system_export.h33
-rw-r--r--chromium/mojo/public/c/system/types.h176
-rw-r--r--chromium/mojo/public/c/test_support/test_support.h48
-rw-r--r--chromium/mojo/public/c/test_support/test_support_export.h26
-rw-r--r--chromium/mojo/public/cpp/DEPS18
-rw-r--r--chromium/mojo/public/cpp/README.md71
-rw-r--r--chromium/mojo/public/cpp/application/DEPS14
-rw-r--r--chromium/mojo/public/cpp/application/application.h121
-rw-r--r--chromium/mojo/public/cpp/application/connect.h24
-rw-r--r--chromium/mojo/public/cpp/application/lib/application.cc34
-rw-r--r--chromium/mojo/public/cpp/application/lib/mojo_main_chromium.cc23
-rw-r--r--chromium/mojo/public/cpp/application/lib/mojo_main_standalone.cc22
-rw-r--r--chromium/mojo/public/cpp/application/lib/service_connector.cc18
-rw-r--r--chromium/mojo/public/cpp/application/lib/service_connector.h133
-rw-r--r--chromium/mojo/public/cpp/application/lib/service_registry.cc78
-rw-r--r--chromium/mojo/public/cpp/application/lib/service_registry.h55
-rw-r--r--chromium/mojo/public/cpp/bindings/BUILD.gn53
-rw-r--r--chromium/mojo/public/cpp/bindings/DEPS3
-rw-r--r--chromium/mojo/public/cpp/bindings/array.h143
-rw-r--r--chromium/mojo/public/cpp/bindings/callback.h462
-rw-r--r--chromium/mojo/public/cpp/bindings/callback.h.pump80
-rw-r--r--chromium/mojo/public/cpp/bindings/error_handler.h19
-rw-r--r--chromium/mojo/public/cpp/bindings/interface_impl.h109
-rw-r--r--chromium/mojo/public/cpp/bindings/interface_ptr.h124
-rw-r--r--chromium/mojo/public/cpp/bindings/interface_request.h76
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/DEPS5
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/TODO6
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/array_internal.cc70
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/array_internal.h384
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/array_serialization.h179
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/bindings_internal.h86
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/bindings_serialization.cc118
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/bindings_serialization.h83
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/bounds_checker.cc77
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/bounds_checker.h64
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/buffer.h24
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/callback_internal.h26
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/connector.cc157
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/connector.h100
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/filter_chain.cc49
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/filter_chain.h66
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/fixed_buffer.cc53
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/fixed_buffer.h67
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/interface_impl_internal.h94
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/interface_ptr_internal.h84
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/message.cc81
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/message_builder.cc52
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/message_builder.h63
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/message_filter.cc23
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/message_header_validator.cc81
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/message_header_validator.h24
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/message_internal.h44
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/message_queue.cc50
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/message_queue.h47
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/no_interface.cc20
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/router.cc140
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/router.h91
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/shared_data.h84
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/shared_ptr.h63
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/string_serialization.cc38
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/string_serialization.h20
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/sync_dispatcher.cc27
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/template_util.h89
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/validation_errors.cc64
-rw-r--r--chromium/mojo/public/cpp/bindings/lib/validation_errors.h66
-rw-r--r--chromium/mojo/public/cpp/bindings/message.h123
-rw-r--r--chromium/mojo/public/cpp/bindings/message_filter.h39
-rw-r--r--chromium/mojo/public/cpp/bindings/no_interface.h56
-rw-r--r--chromium/mojo/public/cpp/bindings/string.h154
-rw-r--r--chromium/mojo/public/cpp/bindings/struct_ptr.h154
-rw-r--r--chromium/mojo/public/cpp/bindings/sync_dispatcher.h47
-rw-r--r--chromium/mojo/public/cpp/bindings/type_converter.h82
-rw-r--r--chromium/mojo/public/cpp/environment/environment.h41
-rw-r--r--chromium/mojo/public/cpp/environment/lib/DEPS4
-rw-r--r--chromium/mojo/public/cpp/environment/lib/default_async_waiter.cc94
-rw-r--r--chromium/mojo/public/cpp/environment/lib/default_async_waiter.h18
-rw-r--r--chromium/mojo/public/cpp/environment/lib/default_logger.cc71
-rw-r--r--chromium/mojo/public/cpp/environment/lib/default_logger.h18
-rw-r--r--chromium/mojo/public/cpp/environment/lib/environment.cc60
-rw-r--r--chromium/mojo/public/cpp/environment/lib/logging.cc45
-rw-r--r--chromium/mojo/public/cpp/environment/logging.h87
-rw-r--r--chromium/mojo/public/cpp/gles2/DEPS4
-rw-r--r--chromium/mojo/public/cpp/gles2/gles2.h24
-rw-r--r--chromium/mojo/public/cpp/system/core.h555
-rw-r--r--chromium/mojo/public/cpp/system/macros.h53
-rw-r--r--chromium/mojo/public/cpp/test_support/DEPS3
-rw-r--r--chromium/mojo/public/cpp/test_support/lib/test_support.cc26
-rw-r--r--chromium/mojo/public/cpp/test_support/lib/test_utils.cc91
-rw-r--r--chromium/mojo/public/cpp/test_support/test_support.h34
-rw-r--r--chromium/mojo/public/cpp/test_support/test_utils.h39
-rw-r--r--chromium/mojo/public/cpp/utility/lib/mutex.cc52
-rw-r--r--chromium/mojo/public/cpp/utility/lib/run_loop.cc221
-rw-r--r--chromium/mojo/public/cpp/utility/lib/thread.cc69
-rw-r--r--chromium/mojo/public/cpp/utility/lib/thread_local.h61
-rw-r--r--chromium/mojo/public/cpp/utility/lib/thread_local_posix.cc39
-rw-r--r--chromium/mojo/public/cpp/utility/lib/thread_local_win.cc39
-rw-r--r--chromium/mojo/public/cpp/utility/mutex.h70
-rw-r--r--chromium/mojo/public/cpp/utility/run_loop.h108
-rw-r--r--chromium/mojo/public/cpp/utility/run_loop_handler.h25
-rw-r--r--chromium/mojo/public/cpp/utility/thread.h62
-rw-r--r--chromium/mojo/public/gles2/gles2_interface.h23
-rw-r--r--chromium/mojo/public/gles2/gles2_private.cc92
-rw-r--r--chromium/mojo/public/gles2/gles2_private.h46
-rw-r--r--chromium/mojo/public/interfaces/interface_provider/BUILD.gn11
-rw-r--r--chromium/mojo/public/interfaces/interface_provider/interface_provider.mojom12
-rw-r--r--chromium/mojo/public/interfaces/service_provider/BUILD.gn11
-rw-r--r--chromium/mojo/public/interfaces/service_provider/service_provider.mojom19
-rw-r--r--chromium/mojo/public/interfaces/shell/BUILD.gn11
-rw-r--r--chromium/mojo/public/js/bindings/BUILD.gn10
-rw-r--r--chromium/mojo/public/js/bindings/codec.js739
-rw-r--r--chromium/mojo/public/js/bindings/connection.js30
-rw-r--r--chromium/mojo/public/js/bindings/connector.js109
-rw-r--r--chromium/mojo/public/js/bindings/constants.cc15
-rw-r--r--chromium/mojo/public/js/bindings/constants.h19
-rw-r--r--chromium/mojo/public/js/bindings/core.js215
-rw-r--r--chromium/mojo/public/js/bindings/router.js93
-rw-r--r--chromium/mojo/public/js/bindings/support.js30
-rw-r--r--chromium/mojo/public/js/bindings/unicode.js51
-rw-r--r--chromium/mojo/public/platform/native/system_thunks.cc170
-rw-r--r--chromium/mojo/public/platform/native/system_thunks.h137
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/enum_declaration.tmpl9
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl49
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl315
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl23
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl14
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl6
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl6
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl14
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl49
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl86
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl108
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/params_definition.tmpl33
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl22
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl28
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl91
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl5
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl51
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl31
-rw-r--r--chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl15
-rw-r--r--chromium/mojo/public/tools/bindings/generators/java_templates/constant_definition.tmpl5
-rw-r--r--chromium/mojo/public/tools/bindings/generators/java_templates/constants.java.tmpl12
-rw-r--r--chromium/mojo/public/tools/bindings/generators/java_templates/enum.java.tmpl4
-rw-r--r--chromium/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl21
-rw-r--r--chromium/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl11
-rw-r--r--chromium/mojo/public/tools/bindings/generators/java_templates/java_macros.tmpl3
-rw-r--r--chromium/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl14
-rw-r--r--chromium/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl122
-rw-r--r--chromium/mojo/public/tools/bindings/generators/js_templates/module.js.tmpl52
-rw-r--r--chromium/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl72
-rw-r--r--chromium/mojo/public/tools/bindings/generators/mojom_cpp_generator.py286
-rw-r--r--chromium/mojo/public/tools/bindings/generators/mojom_java_generator.py187
-rw-r--r--chromium/mojo/public/tools/bindings/generators/mojom_js_generator.py186
-rwxr-xr-xchromium/mojo/public/tools/bindings/generators/run_cpp_generator.py28
-rw-r--r--chromium/mojo/public/tools/bindings/mojom.gni90
-rw-r--r--chromium/mojo/public/tools/bindings/mojom_bindings_generator.gypi99
-rwxr-xr-xchromium/mojo/public/tools/bindings/mojom_bindings_generator.py179
-rw-r--r--chromium/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py23
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/__init__.py0
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/error.py27
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py0
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/generate/data.py348
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py86
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/generate/generator.py90
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/generate/module.py217
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/generate/module_tests.py34
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/generate/pack.py150
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py176
-rwxr-xr-xchromium/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py35
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py54
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py193
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py0
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/parse/ast.py25
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py277
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/parse/parser.py323
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/parse/translate.py144
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom_tests/__init__.py0
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/__init__.py0
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py187
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py493
-rwxr-xr-xchromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py39
-rwxr-xr-xchromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py37
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom_tests/support/__init__.py0
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py32
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py47
-rw-r--r--chromium/mojo/service_manager/BUILD.gn33
-rw-r--r--chromium/mojo/service_manager/background_service_loader.cc104
-rw-r--r--chromium/mojo/service_manager/background_service_loader.h61
-rw-r--r--chromium/mojo/service_manager/service_loader.h32
-rw-r--r--chromium/mojo/service_manager/service_manager.cc206
-rw-r--r--chromium/mojo/service_manager/service_manager.h117
-rw-r--r--chromium/mojo/service_manager/service_manager_export.h32
-rw-r--r--chromium/mojo/service_manager/service_manager_unittest.cc406
-rw-r--r--chromium/mojo/service_manager/test.mojom24
-rw-r--r--chromium/mojo/services/DEPS10
-rw-r--r--chromium/mojo/services/dbus_echo/DEPS5
-rw-r--r--chromium/mojo/services/dbus_echo/dbus_echo_service.cc58
-rw-r--r--chromium/mojo/services/dbus_echo/echo.mojom11
-rw-r--r--chromium/mojo/services/gles2/DEPS5
-rw-r--r--chromium/mojo/services/gles2/command_buffer.mojom49
-rw-r--r--chromium/mojo/services/gles2/command_buffer_impl.cc198
-rw-r--r--chromium/mojo/services/gles2/command_buffer_impl.h72
-rw-r--r--chromium/mojo/services/gles2/command_buffer_type_conversions.cc40
-rw-r--r--chromium/mojo/services/gles2/command_buffer_type_conversions.h27
-rw-r--r--chromium/mojo/services/gles2/mojo_buffer_backing.cc36
-rw-r--r--chromium/mojo/services/gles2/mojo_buffer_backing.h40
-rw-r--r--chromium/mojo/services/launcher/DEPS3
-rw-r--r--chromium/mojo/services/launcher/launcher.cc176
-rw-r--r--chromium/mojo/services/native_viewport/DEPS8
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport.h62
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_android.cc161
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_android.h71
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_export.h32
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_mac.mm88
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_service.cc165
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_service.h18
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_stub.cc50
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_win.cc167
-rw-r--r--chromium/mojo/services/native_viewport/native_viewport_x11.cc163
-rw-r--r--chromium/mojo/services/network/DEPS4
-rw-r--r--chromium/mojo/services/network/main.cc30
-rw-r--r--chromium/mojo/services/network/network_context.cc123
-rw-r--r--chromium/mojo/services/network/network_context.h42
-rw-r--r--chromium/mojo/services/network/network_service_impl.cc23
-rw-r--r--chromium/mojo/services/network/network_service_impl.h29
-rw-r--r--chromium/mojo/services/network/url_loader_impl.cc253
-rw-r--r--chromium/mojo/services/network/url_loader_impl.h61
-rw-r--r--chromium/mojo/services/public/cpp/geometry/DEPS3
-rw-r--r--chromium/mojo/services/public/cpp/geometry/geometry_type_converters.h37
-rw-r--r--chromium/mojo/services/public/cpp/geometry/lib/geometry_type_converters.cc58
-rw-r--r--chromium/mojo/services/public/cpp/geometry/mojo_geometry_export.h32
-rw-r--r--chromium/mojo/services/public/cpp/input_events/DEPS3
-rw-r--r--chromium/mojo/services/public/cpp/input_events/input_events_type_converters.h30
-rw-r--r--chromium/mojo/services/public/cpp/input_events/lib/input_events_type_converters.cc68
-rw-r--r--chromium/mojo/services/public/cpp/input_events/mojo_input_events_export.h32
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/DEPS4
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/DEPS11
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/node.cc432
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/node_observer.cc23
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/node_private.cc23
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/node_private.h62
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/view.cc97
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc841
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h162
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.cc24
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.h28
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/view_manager_unittests.cc14
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/view_private.cc23
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/lib/view_private.h44
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/node.h108
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/node_observer.h66
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/types.h27
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/util.h32
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/view.h60
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/view_manager.h44
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/view_manager_delegate.h25
-rw-r--r--chromium/mojo/services/public/cpp/view_manager/view_observer.h37
-rw-r--r--chromium/mojo/services/public/interfaces/geometry/geometry.mojom34
-rw-r--r--chromium/mojo/services/public/interfaces/input_events/input_events.mojom27
-rw-r--r--chromium/mojo/services/public/interfaces/launcher/launcher.mojom36
-rw-r--r--chromium/mojo/services/public/interfaces/native_viewport/native_viewport.mojom28
-rw-r--r--chromium/mojo/services/public/interfaces/navigation/navigation.mojom36
-rw-r--r--chromium/mojo/services/public/interfaces/network/network_error.mojom12
-rw-r--r--chromium/mojo/services/public/interfaces/network/network_service.mojom15
-rw-r--r--chromium/mojo/services/public/interfaces/network/url_loader.mojom94
-rw-r--r--chromium/mojo/services/public/interfaces/surfaces/quads.mojom165
-rw-r--r--chromium/mojo/services/public/interfaces/surfaces/surface_id.mojom11
-rw-r--r--chromium/mojo/services/public/interfaces/surfaces/surfaces.mojom57
-rw-r--r--chromium/mojo/services/public/interfaces/view_manager/view_manager.mojom193
-rw-r--r--chromium/mojo/services/public/interfaces/view_manager/view_manager_constants.mojom12
-rw-r--r--chromium/mojo/services/test_service/test_service.mojom11
-rw-r--r--chromium/mojo/services/test_service/test_service_application.cc44
-rw-r--r--chromium/mojo/services/test_service/test_service_application.h33
-rw-r--r--chromium/mojo/services/test_service/test_service_impl.cc32
-rw-r--r--chromium/mojo/services/test_service/test_service_impl.h35
-rw-r--r--chromium/mojo/services/view_manager/DEPS22
-rw-r--r--chromium/mojo/services/view_manager/context_factory_impl.cc75
-rw-r--r--chromium/mojo/services/view_manager/context_factory_impl.h56
-rw-r--r--chromium/mojo/services/view_manager/ids.h94
-rw-r--r--chromium/mojo/services/view_manager/main.cc35
-rw-r--r--chromium/mojo/services/view_manager/node.cc189
-rw-r--r--chromium/mojo/services/view_manager/node.h112
-rw-r--r--chromium/mojo/services/view_manager/node_delegate.h45
-rw-r--r--chromium/mojo/services/view_manager/root_node_manager.cc265
-rw-r--r--chromium/mojo/services/view_manager/root_node_manager.h224
-rw-r--r--chromium/mojo/services/view_manager/root_view_manager.cc159
-rw-r--r--chromium/mojo/services/view_manager/root_view_manager.h72
-rw-r--r--chromium/mojo/services/view_manager/root_view_manager_delegate.h27
-rw-r--r--chromium/mojo/services/view_manager/screen_impl.cc80
-rw-r--r--chromium/mojo/services/view_manager/screen_impl.h58
-rw-r--r--chromium/mojo/services/view_manager/test_change_tracker.cc263
-rw-r--r--chromium/mojo/services/view_manager/test_change_tracker.h134
-rw-r--r--chromium/mojo/services/view_manager/view.cc28
-rw-r--r--chromium/mojo/services/view_manager/view.h50
-rw-r--r--chromium/mojo/services/view_manager/view_manager_export.h31
-rw-r--r--chromium/mojo/services/view_manager/view_manager_init_service_impl.cc62
-rw-r--r--chromium/mojo/services/view_manager/view_manager_init_service_impl.h79
-rw-r--r--chromium/mojo/services/view_manager/view_manager_service_impl.cc803
-rw-r--r--chromium/mojo/services/view_manager/view_manager_service_impl.h269
-rw-r--r--chromium/mojo/services/view_manager/view_manager_unittest.cc1370
-rw-r--r--chromium/mojo/services/view_manager/window_tree_host_impl.cc177
-rw-r--r--chromium/mojo/services/view_manager/window_tree_host_impl.h76
-rw-r--r--chromium/mojo/shell/DEPS6
-rw-r--r--chromium/mojo/shell/android/DEPS3
-rw-r--r--chromium/mojo/shell/android/apk/AndroidManifest.xml28
-rw-r--r--chromium/mojo/shell/android/apk/res/layout/mojo_shell_activity.xml13
-rw-r--r--chromium/mojo/shell/android/apk/res/values/strings.xml10
-rw-r--r--chromium/mojo/shell/android/library_loader.cc46
-rw-r--r--chromium/mojo/shell/android/mojo_main.cc80
-rw-r--r--chromium/mojo/shell/android/mojo_main.h16
-rw-r--r--chromium/mojo/shell/app_child_process.cc289
-rw-r--r--chromium/mojo/shell/app_child_process.h30
-rw-r--r--chromium/mojo/shell/app_child_process.mojom17
-rw-r--r--chromium/mojo/shell/app_child_process_host.cc63
-rw-r--r--chromium/mojo/shell/app_child_process_host.h55
-rw-r--r--chromium/mojo/shell/child_process.cc58
-rw-r--r--chromium/mojo/shell/child_process.h57
-rw-r--r--chromium/mojo/shell/child_process_host.cc105
-rw-r--r--chromium/mojo/shell/child_process_host.h84
-rw-r--r--chromium/mojo/shell/child_process_host_unittest.cc56
-rw-r--r--chromium/mojo/shell/context.cc141
-rw-r--r--chromium/mojo/shell/context.h62
-rw-r--r--chromium/mojo/shell/dbus_service_loader_linux.cc181
-rw-r--r--chromium/mojo/shell/dbus_service_loader_linux.h89
-rw-r--r--chromium/mojo/shell/desktop/mojo_main.cc56
-rw-r--r--chromium/mojo/shell/dynamic_service_loader.cc186
-rw-r--r--chromium/mojo/shell/dynamic_service_loader.h51
-rw-r--r--chromium/mojo/shell/dynamic_service_runner.h57
-rw-r--r--chromium/mojo/shell/external_service.mojom11
-rw-r--r--chromium/mojo/shell/in_process_dynamic_service_runner.cc102
-rw-r--r--chromium/mojo/shell/in_process_dynamic_service_runner.h52
-rw-r--r--chromium/mojo/shell/init.cc24
-rw-r--r--chromium/mojo/shell/init.h18
-rw-r--r--chromium/mojo/shell/keep_alive.cc34
-rw-r--r--chromium/mojo/shell/keep_alive.h44
-rw-r--r--chromium/mojo/shell/mojo_url_resolver.cc78
-rw-r--r--chromium/mojo/shell/mojo_url_resolver.h49
-rw-r--r--chromium/mojo/shell/out_of_process_dynamic_service_runner.cc64
-rw-r--r--chromium/mojo/shell/out_of_process_dynamic_service_runner.h55
-rw-r--r--chromium/mojo/shell/run.cc33
-rw-r--r--chromium/mojo/shell/run.h22
-rw-r--r--chromium/mojo/shell/shell_test_base.cc63
-rw-r--r--chromium/mojo/shell/shell_test_base.h58
-rw-r--r--chromium/mojo/shell/shell_test_base_unittest.cc110
-rw-r--r--chromium/mojo/shell/shell_test_helper.cc52
-rw-r--r--chromium/mojo/shell/shell_test_helper.h61
-rw-r--r--chromium/mojo/shell/shell_test_main.cc31
-rw-r--r--chromium/mojo/shell/switches.cc30
-rw-r--r--chromium/mojo/shell/switches.h19
-rw-r--r--chromium/mojo/shell/task_runners.cc38
-rw-r--r--chromium/mojo/shell/task_runners.h53
-rw-r--r--chromium/mojo/shell/test_child_process.cc28
-rw-r--r--chromium/mojo/shell/test_child_process.h28
-rw-r--r--chromium/mojo/shell/view_manager_loader.cc36
-rw-r--r--chromium/mojo/shell/view_manager_loader.h41
-rw-r--r--chromium/mojo/spy/DEPS3
-rw-r--r--chromium/mojo/spy/PRESUBMIT.py40
-rwxr-xr-xchromium/mojo/spy/run_ui_dev_server22
-rwxr-xr-xchromium/mojo/spy/run_ui_tests23
-rw-r--r--chromium/mojo/spy/spy.cc215
-rw-r--r--chromium/mojo/spy/spy.h40
-rw-r--r--chromium/mojo/spy/ui/__init__.py5
-rw-r--r--chromium/mojo/spy/ui/dev_server.py23
-rw-r--r--chromium/mojo/spy/ui/spy.html35
-rw-r--r--chromium/mojo/spy/ui/spy.js96
-rw-r--r--chromium/mojo/spy/ui/spy_project.py18
-rw-r--r--chromium/mojo/spy/ui/spy_shell.html38
-rw-r--r--chromium/mojo/spy/ui/spy_shell.js68
-rwxr-xr-xchromium/mojo/spy/ui/spy_shell_to_html13
-rw-r--r--chromium/mojo/spy/ui/spy_shell_to_html.py39
-rw-r--r--chromium/mojo/spy/ui/spy_test.js51
-rw-r--r--chromium/mojo/spy/ui/tvcm_stub.py18
-rw-r--r--chromium/mojo/spy/ui/ui_unittest.py15
-rw-r--r--chromium/mojo/spy/websocket_server.cc70
-rw-r--r--chromium/mojo/spy/websocket_server.h44
-rw-r--r--chromium/mojo/system/BUILD.gn109
-rw-r--r--chromium/mojo/system/channel.cc505
-rw-r--r--chromium/mojo/system/channel.h202
-rw-r--r--chromium/mojo/system/channel_unittest.cc329
-rw-r--r--chromium/mojo/system/constants.h48
-rw-r--r--chromium/mojo/system/core.cc561
-rw-r--r--chromium/mojo/system/core.h131
-rw-r--r--chromium/mojo/system/core_test_base.cc349
-rw-r--r--chromium/mojo/system/core_test_base.h105
-rw-r--r--chromium/mojo/system/core_unittest.cc888
-rw-r--r--chromium/mojo/system/data_pipe.cc403
-rw-r--r--chromium/mojo/system/data_pipe.h205
-rw-r--r--chromium/mojo/system/data_pipe_consumer_dispatcher.cc130
-rw-r--r--chromium/mojo/system/data_pipe_consumer_dispatcher.h62
-rw-r--r--chromium/mojo/system/data_pipe_producer_dispatcher.cc109
-rw-r--r--chromium/mojo/system/data_pipe_producer_dispatcher.h64
-rw-r--r--chromium/mojo/system/data_pipe_unittest.cc360
-rw-r--r--chromium/mojo/system/dispatcher.cc461
-rw-r--r--chromium/mojo/system/dispatcher.h379
-rw-r--r--chromium/mojo/system/dispatcher_unittest.cc274
-rw-r--r--chromium/mojo/system/entrypoints.cc158
-rw-r--r--chromium/mojo/system/entrypoints.h24
-rw-r--r--chromium/mojo/system/handle_signals_state.h50
-rw-r--r--chromium/mojo/system/handle_table.cc237
-rw-r--r--chromium/mojo/system/handle_table.h144
-rw-r--r--chromium/mojo/system/local_data_pipe.cc332
-rw-r--r--chromium/mojo/system/local_data_pipe.h84
-rw-r--r--chromium/mojo/system/local_data_pipe_unittest.cc1644
-rw-r--r--chromium/mojo/system/local_message_pipe_endpoint.cc165
-rw-r--r--chromium/mojo/system/local_message_pipe_endpoint.h63
-rw-r--r--chromium/mojo/system/mapping_table.cc48
-rw-r--r--chromium/mojo/system/mapping_table.h57
-rw-r--r--chromium/mojo/system/memory.cc99
-rw-r--r--chromium/mojo/system/memory.h56
-rw-r--r--chromium/mojo/system/memory_unittest.cc136
-rw-r--r--chromium/mojo/system/message_in_transit.cc206
-rw-r--r--chromium/mojo/system/message_in_transit.h258
-rw-r--r--chromium/mojo/system/message_in_transit_queue.cc33
-rw-r--r--chromium/mojo/system/message_in_transit_queue.h69
-rw-r--r--chromium/mojo/system/message_pipe.cc285
-rw-r--r--chromium/mojo/system/message_pipe.h122
-rw-r--r--chromium/mojo/system/message_pipe_dispatcher.cc281
-rw-r--r--chromium/mojo/system/message_pipe_dispatcher.h131
-rw-r--r--chromium/mojo/system/message_pipe_dispatcher_unittest.cc598
-rw-r--r--chromium/mojo/system/message_pipe_endpoint.cc56
-rw-r--r--chromium/mojo/system/message_pipe_endpoint.h93
-rw-r--r--chromium/mojo/system/message_pipe_unittest.cc525
-rw-r--r--chromium/mojo/system/multiprocess_message_pipe_unittest.cc555
-rw-r--r--chromium/mojo/system/options_validation.h123
-rw-r--r--chromium/mojo/system/options_validation_unittest.cc190
-rw-r--r--chromium/mojo/system/platform_handle_dispatcher.cc123
-rw-r--r--chromium/mojo/system/platform_handle_dispatcher.h64
-rw-r--r--chromium/mojo/system/platform_handle_dispatcher_unittest.cc102
-rw-r--r--chromium/mojo/system/proxy_message_pipe_endpoint.cc148
-rw-r--r--chromium/mojo/system/proxy_message_pipe_endpoint.h104
-rw-r--r--chromium/mojo/system/raw_channel.cc500
-rw-r--r--chromium/mojo/system/raw_channel.h315
-rw-r--r--chromium/mojo/system/raw_channel_posix.cc469
-rw-r--r--chromium/mojo/system/raw_channel_unittest.cc689
-rw-r--r--chromium/mojo/system/raw_channel_win.cc568
-rw-r--r--chromium/mojo/system/raw_shared_buffer.cc86
-rw-r--r--chromium/mojo/system/raw_shared_buffer.h137
-rw-r--r--chromium/mojo/system/raw_shared_buffer_posix.cc151
-rw-r--r--chromium/mojo/system/raw_shared_buffer_unittest.cc181
-rw-r--r--chromium/mojo/system/raw_shared_buffer_win.cc88
-rw-r--r--chromium/mojo/system/remote_message_pipe_unittest.cc843
-rw-r--r--chromium/mojo/system/shared_buffer_dispatcher.cc272
-rw-r--r--chromium/mojo/system/shared_buffer_dispatcher.h98
-rw-r--r--chromium/mojo/system/shared_buffer_dispatcher_unittest.cc268
-rw-r--r--chromium/mojo/system/simple_dispatcher.cc49
-rw-r--r--chromium/mojo/system/simple_dispatcher.h53
-rw-r--r--chromium/mojo/system/simple_dispatcher_unittest.cc541
-rw-r--r--chromium/mojo/system/system_impl_export.h29
-rw-r--r--chromium/mojo/system/test_utils.cc96
-rw-r--r--chromium/mojo/system/test_utils.h100
-rw-r--r--chromium/mojo/system/transport_data.cc345
-rw-r--r--chromium/mojo/system/transport_data.h192
-rw-r--r--chromium/mojo/system/waiter.cc98
-rw-r--r--chromium/mojo/system/waiter.h81
-rw-r--r--chromium/mojo/system/waiter_list.cc57
-rw-r--r--chromium/mojo/system/waiter_list.h58
-rw-r--r--chromium/mojo/system/waiter_list_unittest.cc297
-rw-r--r--chromium/mojo/system/waiter_test_utils.cc66
-rw-r--r--chromium/mojo/system/waiter_test_utils.h101
-rw-r--r--chromium/mojo/system/waiter_unittest.cc303
-rwxr-xr-xchromium/mojo/tools/check_mojom_golden_files.py99
-rw-r--r--chromium/mojo/tools/data/unittests26
-rw-r--r--chromium/mojo/tools/generate_java_callback_interfaces.py59
-rw-r--r--chromium/mojo/tools/message_generator.cc63
-rwxr-xr-xchromium/mojo/tools/mojob.sh186
-rwxr-xr-xchromium/mojo/tools/mojosh.sh71
-rw-r--r--chromium/mojo/tools/pylib/transitive_hash.py89
-rwxr-xr-xchromium/mojo/tools/run_mojo_python_tests.py74
-rwxr-xr-xchromium/mojo/tools/test_runner.py116
-rw-r--r--chromium/mojo/views/DEPS9
-rw-r--r--chromium/mojo/views/native_widget_view_manager.cc131
-rw-r--r--chromium/mojo/views/native_widget_view_manager.h61
-rw-r--r--chromium/mojo/views/views_init.cc27
-rw-r--r--chromium/mojo/views/views_init.h26
669 files changed, 71022 insertions, 318 deletions
diff --git a/chromium/mojo/BUILD.gn b/chromium/mojo/BUILD.gn
new file mode 100644
index 00000000000..da17324bd8f
--- /dev/null
+++ b/chromium/mojo/BUILD.gn
@@ -0,0 +1,12 @@
+# Copyright 2014 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.
+
+group("mojo") {
+ deps = [
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/interfaces/bindings/tests:test_interfaces",
+ "//mojo/public/interfaces/service_provider:service_provider",
+ "//mojo/public/js/bindings",
+ ]
+}
diff --git a/chromium/mojo/DEPS b/chromium/mojo/DEPS
new file mode 100644
index 00000000000..22cb4c1ac91
--- /dev/null
+++ b/chromium/mojo/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+ "+base",
+ "+build",
+ "+mojo",
+ "-mojo/system",
+ "+testing",
+]
diff --git a/chromium/mojo/OWNERS b/chromium/mojo/OWNERS
new file mode 100644
index 00000000000..49426f5ff58
--- /dev/null
+++ b/chromium/mojo/OWNERS
@@ -0,0 +1,10 @@
+aa@chromium.org
+abarth@chromium.org
+ben@chromium.org
+darin@chromium.org
+davemoore@chromium.org
+jamesr@chromium.org
+mpcomplete@chromium.org
+sky@chromium.org
+viettrungluu@chromium.org
+yzshen@chromium.org
diff --git a/chromium/mojo/README.md b/chromium/mojo/README.md
new file mode 100644
index 00000000000..5af1dcb50be
--- /dev/null
+++ b/chromium/mojo/README.md
@@ -0,0 +1,6 @@
+Mojo
+====
+
+Mojo is an effort to extract a common platform out of Chrome's renderer and
+plugin processes that can support multiple types of sandboxed content, such as
+HTML, Pepper, or NaCl.
diff --git a/chromium/mojo/android/DEPS b/chromium/mojo/android/DEPS
new file mode 100644
index 00000000000..c80012b5621
--- /dev/null
+++ b/chromium/mojo/android/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+jni",
+]
diff --git a/chromium/mojo/android/javatests/AndroidManifest.xml b/chromium/mojo/android/javatests/AndroidManifest.xml
new file mode 100644
index 00000000000..8968941f5b5
--- /dev/null
+++ b/chromium/mojo/android/javatests/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+ <!-- Copyright (c) 2012 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. -->
+ <!-- package name must be unique so suffix with "tests" so package loader
+ doesn't ignore this. -->
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.chromium.mojo.tests">
+ <!-- We add an application tag here just so that we can indicate that this
+ package needs to link against the android.test library, which is
+ needed when building test cases. -->
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" />
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="org.chromium.mojo.tests"
+ android:label="Tests for org.chromium.mojo"/>
+ <uses-permission android:name="android.permission.RUN_INSTRUMENTATION" />
+ <uses-permission android:name="android.permission.INJECT_EVENTS" />
+</manifest>
diff --git a/chromium/mojo/android/javatests/DEPS b/chromium/mojo/android/javatests/DEPS
new file mode 100644
index 00000000000..78cf465ca0c
--- /dev/null
+++ b/chromium/mojo/android/javatests/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ # out should be allowed by default, but bots are failing on this.
+ "+out",
+]
diff --git a/chromium/mojo/android/javatests/apk/.empty b/chromium/mojo/android/javatests/apk/.empty
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/chromium/mojo/android/javatests/apk/.empty
diff --git a/chromium/mojo/android/javatests/init_library.cc b/chromium/mojo/android/javatests/init_library.cc
new file mode 100644
index 00000000000..28cee79443a
--- /dev/null
+++ b/chromium/mojo/android/javatests/init_library.cc
@@ -0,0 +1,40 @@
+// Copyright 2014 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 "base/android/base_jni_registrar.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_registrar.h"
+#include "base/android/library_loader/library_loader_hooks.h"
+#include "mojo/android/javatests/mojo_test_case.h"
+#include "mojo/android/system/core_impl.h"
+
+namespace {
+
+base::android::RegistrationMethod kMojoRegisteredMethods[] = {
+ { "CoreImpl", mojo::android::RegisterCoreImpl },
+ { "MojoTestCase", mojo::android::RegisterMojoTestCase },
+};
+
+bool RegisterMojoJni(JNIEnv* env) {
+ return RegisterNativeMethods(env, kMojoRegisteredMethods,
+ arraysize(kMojoRegisteredMethods));
+}
+
+} // namespace
+
+JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+ base::android::InitVM(vm);
+ JNIEnv* env = base::android::AttachCurrentThread();
+
+ if (!base::android::RegisterLibraryLoaderEntryHook(env))
+ return -1;
+
+ if (!base::android::RegisterJni(env))
+ return -1;
+
+ if (!RegisterMojoJni(env))
+ return -1;
+
+ return JNI_VERSION_1_4;
+}
diff --git a/chromium/mojo/android/javatests/mojo_test_case.cc b/chromium/mojo/android/javatests/mojo_test_case.cc
new file mode 100644
index 00000000000..f14dfd8cce4
--- /dev/null
+++ b/chromium/mojo/android/javatests/mojo_test_case.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 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 "mojo/android/javatests/mojo_test_case.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/test_support_android.h"
+#include "jni/MojoTestCase_jni.h"
+#include "mojo/public/cpp/environment/environment.h"
+
+namespace {
+
+struct TestEnvironment {
+ base::ShadowingAtExitManager at_exit;
+ base::MessageLoopForUI message_loop;
+};
+
+} // namespace
+
+namespace mojo {
+namespace android {
+
+static void InitApplicationContext(JNIEnv* env,
+ jobject jcaller,
+ jobject context) {
+ base::android::ScopedJavaLocalRef<jobject> scoped_context(env, context);
+ base::android::InitApplicationContext(env, scoped_context);
+ base::InitAndroidTestMessageLoop();
+}
+
+static jlong SetupTestEnvironment(JNIEnv* env, jobject jcaller) {
+ return reinterpret_cast<intptr_t>(new TestEnvironment());
+}
+
+static void TearDownTestEnvironment(JNIEnv* env,
+ jobject jcaller,
+ jlong test_environment) {
+ delete reinterpret_cast<TestEnvironment*>(test_environment);
+}
+
+static void RunLoop(JNIEnv* env, jobject jcaller, jlong timeout_ms) {
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::MessageLoop::QuitClosure(),
+ base::TimeDelta::FromMilliseconds(timeout_ms));
+ base::RunLoop run_loop;
+ run_loop.Run();
+}
+
+bool RegisterMojoTestCase(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace mojo
diff --git a/chromium/mojo/android/javatests/mojo_test_case.h b/chromium/mojo/android/javatests/mojo_test_case.h
new file mode 100644
index 00000000000..2ce342848e2
--- /dev/null
+++ b/chromium/mojo/android/javatests/mojo_test_case.h
@@ -0,0 +1,20 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_ANDROID_JAVATESTS_CORE_TEST_H_
+#define MOJO_ANDROID_JAVATESTS_CORE_TEST_H_
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+
+namespace mojo {
+namespace android {
+
+JNI_EXPORT bool RegisterMojoTestCase(JNIEnv* env);
+
+} // namespace android
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_ANDROID_JAVATESTS_CORE_TEST_H_
diff --git a/chromium/mojo/android/system/core_impl.cc b/chromium/mojo/android/system/core_impl.cc
new file mode 100644
index 00000000000..142cd0595fe
--- /dev/null
+++ b/chromium/mojo/android/system/core_impl.cc
@@ -0,0 +1,368 @@
+// Copyright 2014 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 "mojo/android/system/core_impl.h"
+
+#include "base/android/base_jni_registrar.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_registrar.h"
+#include "base/android/library_loader/library_loader_hooks.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "jni/CoreImpl_jni.h"
+#include "mojo/embedder/embedder.h"
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/c/system/core.h"
+#include "mojo/public/cpp/environment/environment.h"
+
+namespace {
+
+// |AsyncWait| is guaranteed never to return 0.
+const MojoAsyncWaitID kInvalidHandleCancelID = 0;
+
+struct AsyncWaitCallbackData {
+ base::android::ScopedJavaGlobalRef<jobject> core_impl;
+ base::android::ScopedJavaGlobalRef<jobject> callback;
+ base::android::ScopedJavaGlobalRef<jobject> cancellable;
+
+ AsyncWaitCallbackData(JNIEnv* env, jobject core_impl, jobject callback) {
+ this->core_impl.Reset(env, core_impl);
+ this->callback.Reset(env, callback);
+ }
+};
+
+void AsyncWaitCallback(void* data, MojoResult result) {
+ scoped_ptr<AsyncWaitCallbackData> callback_data(
+ static_cast<AsyncWaitCallbackData*>(data));
+ mojo::android::Java_CoreImpl_onAsyncWaitResult(
+ base::android::AttachCurrentThread(),
+ callback_data->core_impl.obj(),
+ result,
+ callback_data->callback.obj(),
+ callback_data->cancellable.obj());
+}
+
+} // namespace
+
+namespace mojo {
+namespace android {
+
+static void Constructor(JNIEnv* env, jobject jcaller) {
+ mojo::embedder::Init();
+}
+
+static jlong GetTimeTicksNow(JNIEnv* env, jobject jcaller) {
+ return MojoGetTimeTicksNow();
+}
+
+static jint WaitMany(JNIEnv* env,
+ jobject jcaller,
+ jobject buffer,
+ jlong deadline) {
+ // Buffer contains first the list of handles, then the list of flags.
+ const void* buffer_start = env->GetDirectBufferAddress(buffer);
+ DCHECK(buffer_start);
+ const size_t record_size = 8;
+ const size_t buffer_size = env->GetDirectBufferCapacity(buffer);
+ DCHECK_EQ(buffer_size % record_size, 0u);
+
+ const size_t nb_handles = buffer_size / record_size;
+ const MojoHandle* handle_start = static_cast<const MojoHandle*>(buffer_start);
+ const MojoHandleSignals* signals_start =
+ static_cast<const MojoHandleSignals*>(handle_start + nb_handles);
+ return MojoWaitMany(handle_start, signals_start, nb_handles, deadline);
+}
+
+static jobject CreateMessagePipe(JNIEnv* env, jobject jcaller) {
+ MojoHandle handle1;
+ MojoHandle handle2;
+ // TODO(vtl): Add support for the options struct.
+ MojoResult result = MojoCreateMessagePipe(NULL, &handle1, &handle2);
+ return Java_CoreImpl_newNativeCreationResult(env, result, handle1, handle2)
+ .Release();
+}
+
+static jobject CreateDataPipe(JNIEnv* env,
+ jobject jcaller,
+ jobject options_buffer) {
+ const MojoCreateDataPipeOptions* options = NULL;
+ if (options_buffer) {
+ const void* buffer_start = env->GetDirectBufferAddress(options_buffer);
+ DCHECK(buffer_start);
+ const size_t buffer_size = env->GetDirectBufferCapacity(options_buffer);
+ DCHECK_EQ(buffer_size, sizeof(MojoCreateDataPipeOptions));
+ options = static_cast<const MojoCreateDataPipeOptions*>(buffer_start);
+ DCHECK_EQ(options->struct_size, buffer_size);
+ }
+ MojoHandle handle1;
+ MojoHandle handle2;
+ MojoResult result = MojoCreateDataPipe(options, &handle1, &handle2);
+ return Java_CoreImpl_newNativeCreationResult(env, result, handle1, handle2)
+ .Release();
+}
+
+static jobject CreateSharedBuffer(JNIEnv* env,
+ jobject jcaller,
+ jobject options_buffer,
+ jlong num_bytes) {
+ const MojoCreateSharedBufferOptions* options = 0;
+ if (options_buffer) {
+ const void* buffer_start = env->GetDirectBufferAddress(options_buffer);
+ DCHECK(buffer_start);
+ const size_t buffer_size = env->GetDirectBufferCapacity(options_buffer);
+ DCHECK_EQ(buffer_size, sizeof(MojoCreateSharedBufferOptions));
+ options = static_cast<const MojoCreateSharedBufferOptions*>(buffer_start);
+ DCHECK_EQ(options->struct_size, buffer_size);
+ }
+ MojoHandle handle;
+ MojoResult result = MojoCreateSharedBuffer(options, num_bytes, &handle);
+ return Java_CoreImpl_newNativeCreationResult(env, result, handle, 0)
+ .Release();
+}
+
+static jint Close(JNIEnv* env, jobject jcaller, jint mojo_handle) {
+ return MojoClose(mojo_handle);
+}
+
+static jint Wait(JNIEnv* env,
+ jobject jcaller,
+ jint mojo_handle,
+ jint signals,
+ jlong deadline) {
+ return MojoWait(mojo_handle, signals, deadline);
+}
+
+static jint WriteMessage(JNIEnv* env,
+ jobject jcaller,
+ jint mojo_handle,
+ jobject bytes,
+ jint num_bytes,
+ jobject handles_buffer,
+ jint flags) {
+ const void* buffer_start = 0;
+ uint32_t buffer_size = 0;
+ if (bytes) {
+ buffer_start = env->GetDirectBufferAddress(bytes);
+ DCHECK(buffer_start);
+ DCHECK(env->GetDirectBufferCapacity(bytes) >= num_bytes);
+ buffer_size = num_bytes;
+ }
+ const MojoHandle* handles = 0;
+ uint32_t num_handles = 0;
+ if (handles_buffer) {
+ handles =
+ static_cast<MojoHandle*>(env->GetDirectBufferAddress(handles_buffer));
+ num_handles = env->GetDirectBufferCapacity(handles_buffer) / 4;
+ }
+ // Java code will handle invalidating handles if the write succeeded.
+ return MojoWriteMessage(
+ mojo_handle, buffer_start, buffer_size, handles, num_handles, flags);
+}
+
+static jobject ReadMessage(JNIEnv* env,
+ jobject jcaller,
+ jint mojo_handle,
+ jobject bytes,
+ jobject handles_buffer,
+ jint flags) {
+ void* buffer_start = 0;
+ uint32_t buffer_size = 0;
+ if (bytes) {
+ buffer_start = env->GetDirectBufferAddress(bytes);
+ DCHECK(buffer_start);
+ buffer_size = env->GetDirectBufferCapacity(bytes);
+ }
+ MojoHandle* handles = 0;
+ uint32_t num_handles = 0;
+ if (handles_buffer) {
+ handles =
+ static_cast<MojoHandle*>(env->GetDirectBufferAddress(handles_buffer));
+ num_handles = env->GetDirectBufferCapacity(handles_buffer) / 4;
+ }
+ MojoResult result = MojoReadMessage(
+ mojo_handle, buffer_start, &buffer_size, handles, &num_handles, flags);
+ // Jave code will handle taking ownership of any received handle.
+ return Java_CoreImpl_newReadMessageResult(
+ env, result, buffer_size, num_handles).Release();
+}
+
+static jint ReadData(JNIEnv* env,
+ jobject jcaller,
+ jint mojo_handle,
+ jobject elements,
+ jint elements_capacity,
+ jint flags) {
+ void* buffer_start = 0;
+ uint32_t buffer_size = elements_capacity;
+ if (elements) {
+ buffer_start = env->GetDirectBufferAddress(elements);
+ DCHECK(buffer_start);
+ DCHECK(elements_capacity <= env->GetDirectBufferCapacity(elements));
+ }
+ MojoResult result =
+ MojoReadData(mojo_handle, buffer_start, &buffer_size, flags);
+ if (result < 0) {
+ return result;
+ }
+ return buffer_size;
+}
+
+static jobject BeginReadData(JNIEnv* env,
+ jobject jcaller,
+ jint mojo_handle,
+ jint num_bytes,
+ jint flags) {
+ void const* buffer = 0;
+ uint32_t buffer_size = num_bytes;
+ MojoResult result =
+ MojoBeginReadData(mojo_handle, &buffer, &buffer_size, flags);
+ jobject byte_buffer = 0;
+ if (result == MOJO_RESULT_OK) {
+ byte_buffer =
+ env->NewDirectByteBuffer(const_cast<void*>(buffer), buffer_size);
+ }
+ return Java_CoreImpl_newNativeCodeAndBufferResult(env, result, byte_buffer)
+ .Release();
+}
+
+static jint EndReadData(JNIEnv* env,
+ jobject jcaller,
+ jint mojo_handle,
+ jint num_bytes_read) {
+ return MojoEndReadData(mojo_handle, num_bytes_read);
+}
+
+static jint WriteData(JNIEnv* env,
+ jobject jcaller,
+ jint mojo_handle,
+ jobject elements,
+ jint limit,
+ jint flags) {
+ void* buffer_start = env->GetDirectBufferAddress(elements);
+ DCHECK(buffer_start);
+ DCHECK(limit <= env->GetDirectBufferCapacity(elements));
+ uint32_t buffer_size = limit;
+ MojoResult result =
+ MojoWriteData(mojo_handle, buffer_start, &buffer_size, flags);
+ if (result < 0) {
+ return result;
+ }
+ return buffer_size;
+}
+
+static jobject BeginWriteData(JNIEnv* env,
+ jobject jcaller,
+ jint mojo_handle,
+ jint num_bytes,
+ jint flags) {
+ void* buffer = 0;
+ uint32_t buffer_size = num_bytes;
+ MojoResult result =
+ MojoBeginWriteData(mojo_handle, &buffer, &buffer_size, flags);
+ jobject byte_buffer = 0;
+ if (result == MOJO_RESULT_OK) {
+ byte_buffer = env->NewDirectByteBuffer(buffer, buffer_size);
+ }
+ return Java_CoreImpl_newNativeCodeAndBufferResult(env, result, byte_buffer)
+ .Release();
+}
+
+static jint EndWriteData(JNIEnv* env,
+ jobject jcaller,
+ jint mojo_handle,
+ jint num_bytes_written) {
+ return MojoEndWriteData(mojo_handle, num_bytes_written);
+}
+
+static jobject Duplicate(JNIEnv* env,
+ jobject jcaller,
+ jint mojo_handle,
+ jobject options_buffer) {
+ const MojoDuplicateBufferHandleOptions* options = 0;
+ if (options_buffer) {
+ const void* buffer_start = env->GetDirectBufferAddress(options_buffer);
+ DCHECK(buffer_start);
+ const size_t buffer_size = env->GetDirectBufferCapacity(options_buffer);
+ DCHECK_EQ(buffer_size, sizeof(MojoDuplicateBufferHandleOptions));
+ options =
+ static_cast<const MojoDuplicateBufferHandleOptions*>(buffer_start);
+ DCHECK_EQ(options->struct_size, buffer_size);
+ }
+ MojoHandle handle;
+ MojoResult result = MojoDuplicateBufferHandle(mojo_handle, options, &handle);
+ return Java_CoreImpl_newNativeCreationResult(env, result, handle, 0)
+ .Release();
+}
+
+static jobject Map(JNIEnv* env,
+ jobject jcaller,
+ jint mojo_handle,
+ jlong offset,
+ jlong num_bytes,
+ jint flags) {
+ void* buffer = 0;
+ MojoResult result =
+ MojoMapBuffer(mojo_handle, offset, num_bytes, &buffer, flags);
+ jobject byte_buffer = 0;
+ if (result == MOJO_RESULT_OK) {
+ byte_buffer = env->NewDirectByteBuffer(buffer, num_bytes);
+ }
+ return Java_CoreImpl_newNativeCodeAndBufferResult(env, result, byte_buffer)
+ .Release();
+}
+
+static int Unmap(JNIEnv* env, jobject jcaller, jobject buffer) {
+ void* buffer_start = env->GetDirectBufferAddress(buffer);
+ DCHECK(buffer_start);
+ return MojoUnmapBuffer(buffer_start);
+}
+
+static jobject AsyncWait(JNIEnv* env,
+ jobject jcaller,
+ jint mojo_handle,
+ jint signals,
+ jlong deadline,
+ jobject callback) {
+ AsyncWaitCallbackData* callback_data =
+ new AsyncWaitCallbackData(env, jcaller, callback);
+ MojoAsyncWaitID cancel_id;
+ if (static_cast<MojoHandle>(mojo_handle) != MOJO_HANDLE_INVALID) {
+ cancel_id = mojo::Environment::GetDefaultAsyncWaiter()->AsyncWait(
+ mojo_handle, signals, deadline, AsyncWaitCallback, callback_data);
+ } else {
+ cancel_id = kInvalidHandleCancelID;
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &AsyncWaitCallback, callback_data, MOJO_RESULT_INVALID_ARGUMENT));
+ }
+ base::android::ScopedJavaLocalRef<jobject> cancellable =
+ Java_CoreImpl_newAsyncWaiterCancellableImpl(
+ env, jcaller, cancel_id, reinterpret_cast<intptr_t>(callback_data));
+ callback_data->cancellable.Reset(env, cancellable.obj());
+ return cancellable.Release();
+}
+
+static void CancelAsyncWait(JNIEnv* env,
+ jobject jcaller,
+ jlong id,
+ jlong data_ptr) {
+ if (id == 0) {
+ // If |id| is |kInvalidHandleCancelID|, the async wait was done on an
+ // invalid handle, so the AsyncWaitCallback will be called and will clear
+ // the data_ptr.
+ return;
+ }
+ scoped_ptr<AsyncWaitCallbackData> deleter(
+ reinterpret_cast<AsyncWaitCallbackData*>(data_ptr));
+ mojo::Environment::GetDefaultAsyncWaiter()->CancelWait(id);
+}
+
+bool RegisterCoreImpl(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace mojo
diff --git a/chromium/mojo/android/system/core_impl.h b/chromium/mojo/android/system/core_impl.h
new file mode 100644
index 00000000000..c6249994e5c
--- /dev/null
+++ b/chromium/mojo/android/system/core_impl.h
@@ -0,0 +1,20 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_ANDROID_SYSTEM_CORE_IMPL_H_
+#define MOJO_ANDROID_SYSTEM_CORE_IMPL_H_
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+
+namespace mojo {
+namespace android {
+
+JNI_EXPORT bool RegisterCoreImpl(JNIEnv* env);
+
+} // namespace android
+} // namespace mojo
+
+#endif // MOJO_ANDROID_SYSTEM_CORE_IMPL_H_
diff --git a/chromium/mojo/apps/js/DEPS b/chromium/mojo/apps/js/DEPS
new file mode 100644
index 00000000000..d974b6806fc
--- /dev/null
+++ b/chromium/mojo/apps/js/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+gin",
+ "+v8",
+]
diff --git a/chromium/mojo/apps/js/bindings/connection_unittests.js b/chromium/mojo/apps/js/bindings/connection_unittests.js
new file mode 100644
index 00000000000..0b0a71b2d03
--- /dev/null
+++ b/chromium/mojo/apps/js/bindings/connection_unittests.js
@@ -0,0 +1,256 @@
+// Copyright 2013 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.
+
+// Mock out the support module to avoid depending on the message loop.
+define("mojo/public/js/bindings/support", ["timer"], function(timer) {
+ var waitingCallbacks = [];
+
+ function WaitCookie(id) {
+ this.id = id;
+ }
+
+ function asyncWait(handle, flags, callback) {
+ var id = waitingCallbacks.length;
+ waitingCallbacks.push(callback);
+ return new WaitCookie(id);
+ }
+
+ function cancelWait(cookie) {
+ waitingCallbacks[cookie.id] = null;
+ }
+
+ function numberOfWaitingCallbacks() {
+ var count = 0;
+ for (var i = 0; i < waitingCallbacks.length; ++i) {
+ if (waitingCallbacks[i])
+ ++count;
+ }
+ return count;
+ }
+
+ function pumpOnce(result) {
+ var callbacks = waitingCallbacks;
+ waitingCallbacks = [];
+ for (var i = 0; i < callbacks.length; ++i) {
+ if (callbacks[i])
+ callbacks[i](result);
+ }
+ }
+
+ // Queue up a pumpOnce call to execute after the stack unwinds. Use
+ // this to trigger a pump after all Promises are executed.
+ function queuePump(result) {
+ timer.createOneShot(0, pumpOnce.bind(undefined, result));
+ }
+
+ var exports = {};
+ exports.asyncWait = asyncWait;
+ exports.cancelWait = cancelWait;
+ exports.numberOfWaitingCallbacks = numberOfWaitingCallbacks;
+ exports.pumpOnce = pumpOnce;
+ exports.queuePump = queuePump;
+ return exports;
+});
+
+define([
+ "gin/test/expect",
+ "mojo/public/js/bindings/support",
+ "mojo/public/js/bindings/core",
+ "mojo/public/js/bindings/connection",
+ "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom",
+ "mojo/public/interfaces/bindings/tests/sample_service.mojom",
+ "mojo/apps/js/bindings/threading",
+ "gc",
+], function(expect,
+ mockSupport,
+ core,
+ connection,
+ sample_interfaces,
+ sample_service,
+ threading,
+ gc) {
+ testClientServer();
+ testWriteToClosedPipe();
+ testRequestResponse().then(function() {
+ this.result = "PASS";
+ gc.collectGarbage(); // should not crash
+ threading.quit();
+ }.bind(this)).catch(function(e) {
+ this.result = "FAIL: " + (e.stack || e);
+ threading.quit();
+ }.bind(this));
+
+ function testClientServer() {
+ var receivedFrobinate = false;
+ var receivedDidFrobinate = false;
+
+ // ServiceImpl -------------------------------------------------------------
+
+ function ServiceImpl(peer) {
+ this.peer = peer;
+ }
+
+ ServiceImpl.prototype = Object.create(sample_service.ServiceStub.prototype);
+
+ ServiceImpl.prototype.frobinate = function(foo, baz, port) {
+ receivedFrobinate = true;
+
+ expect(foo.name).toBe("Example name");
+ expect(baz).toBeTruthy();
+ expect(core.close(port)).toBe(core.RESULT_OK);
+
+ this.peer.didFrobinate(42);
+ };
+
+ // ServiceImpl -------------------------------------------------------------
+
+ function ServiceClientImpl(peer) {
+ this.peer = peer;
+ }
+
+ ServiceClientImpl.prototype =
+ Object.create(sample_service.ServiceClientStub.prototype);
+
+ ServiceClientImpl.prototype.didFrobinate = function(result) {
+ receivedDidFrobinate = true;
+
+ expect(result).toBe(42);
+ };
+
+ var pipe = core.createMessagePipe();
+ var anotherPipe = core.createMessagePipe();
+ var sourcePipe = core.createMessagePipe();
+
+ var connection0 = new connection.Connection(
+ pipe.handle0, ServiceImpl, sample_service.ServiceClientProxy);
+
+ var connection1 = new connection.Connection(
+ pipe.handle1, ServiceClientImpl, sample_service.ServiceProxy);
+
+ var foo = new sample_service.Foo();
+ foo.bar = new sample_service.Bar();
+ foo.name = "Example name";
+ foo.source = sourcePipe.handle0;
+ connection1.remote.frobinate(foo, true, anotherPipe.handle0);
+
+ mockSupport.pumpOnce(core.RESULT_OK);
+
+ expect(receivedFrobinate).toBeTruthy();
+ expect(receivedDidFrobinate).toBeTruthy();
+
+ connection0.close();
+ connection1.close();
+
+ expect(mockSupport.numberOfWaitingCallbacks()).toBe(0);
+
+ // sourcePipe.handle0 was closed automatically when sent over IPC.
+ expect(core.close(sourcePipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT);
+ // sourcePipe.handle1 hasn't been closed yet.
+ expect(core.close(sourcePipe.handle1)).toBe(core.RESULT_OK);
+
+ // anotherPipe.handle0 was closed automatically when sent over IPC.
+ expect(core.close(anotherPipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT);
+ // anotherPipe.handle1 hasn't been closed yet.
+ expect(core.close(anotherPipe.handle1)).toBe(core.RESULT_OK);
+
+ // The Connection object is responsible for closing these handles.
+ expect(core.close(pipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT);
+ expect(core.close(pipe.handle1)).toBe(core.RESULT_INVALID_ARGUMENT);
+ }
+
+ function testWriteToClosedPipe() {
+ var pipe = core.createMessagePipe();
+
+ var connection1 = new connection.Connection(
+ pipe.handle1, function() {}, sample_service.ServiceProxy);
+
+ // Close the other end of the pipe.
+ core.close(pipe.handle0);
+
+ // Not observed yet because we haven't pumped events yet.
+ expect(connection1.encounteredError()).toBeFalsy();
+
+ var foo = new sample_service.Foo();
+ foo.bar = new sample_service.Bar();
+ // TODO(darin): crbug.com/357043: pass null in place of |foo| here.
+ connection1.remote.frobinate(foo, true, null);
+
+ // Write failures are not reported.
+ expect(connection1.encounteredError()).toBeFalsy();
+
+ // Pump events, and then we should start observing the closed pipe.
+ mockSupport.pumpOnce(core.RESULT_OK);
+
+ expect(connection1.encounteredError()).toBeTruthy();
+
+ connection1.close();
+ }
+
+ function testRequestResponse() {
+
+ // ProviderImpl ------------------------------------------------------------
+
+ function ProviderImpl(peer) {
+ this.peer = peer;
+ }
+
+ ProviderImpl.prototype =
+ Object.create(sample_interfaces.ProviderStub.prototype);
+
+ ProviderImpl.prototype.echoString = function(a) {
+ mockSupport.queuePump(core.RESULT_OK);
+ return Promise.resolve({a: a});
+ };
+
+ ProviderImpl.prototype.echoStrings = function(a, b) {
+ mockSupport.queuePump(core.RESULT_OK);
+ return Promise.resolve({a: a, b: b});
+ };
+
+ // ProviderClientImpl ------------------------------------------------------
+
+ function ProviderClientImpl(peer) {
+ this.peer = peer;
+ }
+
+ ProviderClientImpl.prototype =
+ Object.create(sample_interfaces.ProviderClientStub.prototype);
+
+ var pipe = core.createMessagePipe();
+
+ var connection0 = new connection.Connection(
+ pipe.handle0, ProviderImpl, sample_interfaces.ProviderClientProxy);
+
+ var connection1 = new connection.Connection(
+ pipe.handle1, ProviderClientImpl, sample_interfaces.ProviderProxy);
+
+ var origReadMessage = core.readMessage;
+ // echoString
+ mockSupport.queuePump(core.RESULT_OK);
+ return connection1.remote.echoString("hello").then(function(response) {
+ expect(response.a).toBe("hello");
+ }).then(function() {
+ // echoStrings
+ mockSupport.queuePump(core.RESULT_OK);
+ return connection1.remote.echoStrings("hello", "world");
+ }).then(function(response) {
+ expect(response.a).toBe("hello");
+ expect(response.b).toBe("world");
+ }).then(function() {
+ // Mock a read failure, expect it to fail.
+ core.readMessage = function() {
+ return { result: core.RESULT_UNKNOWN };
+ };
+ mockSupport.queuePump(core.RESULT_OK);
+ return connection1.remote.echoString("goodbye");
+ }).then(function() {
+ throw Error("Expected echoString to fail.");
+ }, function(error) {
+ expect(error.message).toBe("Connection error: " + core.RESULT_UNKNOWN);
+
+ // Clean up.
+ core.readMessage = origReadMessage;
+ });
+ }
+});
diff --git a/chromium/mojo/apps/js/bindings/gl/context.cc b/chromium/mojo/apps/js/bindings/gl/context.cc
new file mode 100644
index 00000000000..18c37e793d2
--- /dev/null
+++ b/chromium/mojo/apps/js/bindings/gl/context.cc
@@ -0,0 +1,187 @@
+// Copyright 2013 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 "mojo/apps/js/bindings/gl/context.h"
+
+#include <GLES2/gl2.h>
+
+#include "gin/arguments.h"
+#include "gin/array_buffer.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_context_data.h"
+#include "mojo/public/c/gles2/gles2.h"
+
+namespace gin {
+template<>
+struct Converter<GLboolean> {
+ static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
+ GLboolean* out) {
+ bool bool_val = false;
+ if (!Converter<bool>::FromV8(isolate, val, &bool_val))
+ return false;
+ *out = static_cast<GLboolean>(bool_val);
+ return true;
+ }
+};
+}
+
+namespace mojo {
+namespace js {
+namespace gl {
+
+gin::WrapperInfo Context::kWrapperInfo = { gin::kEmbedderNativeGin };
+
+gin::Handle<Context> Context::Create(
+ v8::Isolate* isolate,
+ mojo::Handle handle,
+ v8::Handle<v8::Function> context_lost_callback) {
+ return gin::CreateHandle(isolate,
+ new Context(isolate, handle, context_lost_callback));
+}
+
+void Context::BufferData(GLenum target, const gin::ArrayBufferView& buffer,
+ GLenum usage) {
+ glBufferData(target, static_cast<GLsizeiptr>(buffer.num_bytes()),
+ buffer.bytes(), usage);
+}
+
+void Context::CompileShader(const gin::Arguments& args, GLuint shader) {
+ glCompileShader(shader);
+ GLint compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ args.ThrowTypeError(std::string("Could not compile shader: ") +
+ GetShaderInfoLog(shader));
+ }
+}
+
+GLuint Context::CreateBuffer() {
+ GLuint result = 0;
+ glGenBuffers(1, &result);
+ return result;
+}
+
+void Context::DrawElements(GLenum mode, GLsizei count, GLenum type,
+ uint64_t indices) {
+ // This looks scary, but it's what WebGL does too:
+ // http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.1
+ glDrawElements(mode, count, type, reinterpret_cast<void*>(indices));
+}
+
+GLint Context::GetAttribLocation(GLuint program, const std::string& name) {
+ return glGetAttribLocation(program, name.c_str());
+}
+
+std::string Context::GetProgramInfoLog(GLuint program) {
+ GLint info_log_length = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length);
+ std::string info_log(info_log_length, 0);
+ glGetProgramInfoLog(program, info_log_length, NULL, &info_log.at(0));
+ return info_log;
+}
+
+std::string Context::GetShaderInfoLog(GLuint shader) {
+ GLint info_log_length = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length);
+ std::string info_log(info_log_length, 0);
+ glGetShaderInfoLog(shader, info_log_length, NULL, &info_log.at(0));
+ return info_log;
+}
+
+GLint Context::GetUniformLocation(GLuint program, const std::string& name) {
+ return glGetUniformLocation(program, name.c_str());
+}
+
+void Context::ShaderSource(GLuint shader, const std::string& source) {
+ const char* source_chars = source.c_str();
+ glShaderSource(shader, 1, &source_chars, NULL);
+}
+
+void Context::UniformMatrix4fv(GLint location, GLboolean transpose,
+ const gin::ArrayBufferView& buffer) {
+ glUniformMatrix4fv(location, 1, transpose,
+ static_cast<float*>(buffer.bytes()));
+}
+
+void Context::VertexAttribPointer(GLuint index, GLint size, GLenum type,
+ GLboolean normalized, GLsizei stride,
+ uint64_t offset) {
+ glVertexAttribPointer(index, size, type, normalized, stride,
+ reinterpret_cast<void*>(offset));
+}
+
+gin::ObjectTemplateBuilder Context::GetObjectTemplateBuilder(
+ v8::Isolate* isolate) {
+ return gin::ObjectTemplateBuilder(isolate)
+ .SetValue("ARRAY_BUFFER", GL_ARRAY_BUFFER)
+ .SetValue("COLOR_BUFFER_BIT", GL_COLOR_BUFFER_BIT)
+ .SetValue("ELEMENT_ARRAY_BUFFER", GL_ELEMENT_ARRAY_BUFFER)
+ .SetValue("FLOAT", GL_FLOAT)
+ .SetValue("FRAGMENT_SHADER", GL_FRAGMENT_SHADER)
+ .SetValue("STATIC_DRAW", GL_STATIC_DRAW)
+ .SetValue("TRIANGLES", GL_TRIANGLES)
+ .SetValue("UNSIGNED_SHORT", GL_UNSIGNED_SHORT)
+ .SetValue("VERTEX_SHADER", GL_VERTEX_SHADER)
+ .SetMethod("attachShader", glAttachShader)
+ .SetMethod("bindBuffer", glBindBuffer)
+ .SetMethod("bufferData", BufferData)
+ .SetMethod("clear", glClear)
+ .SetMethod("clearColor", glClearColor)
+ .SetMethod("compileShader", CompileShader)
+ .SetMethod("createBuffer", CreateBuffer)
+ .SetMethod("createProgram", glCreateProgram)
+ .SetMethod("createShader", glCreateShader)
+ .SetMethod("deleteShader", glDeleteShader)
+ .SetMethod("drawElements", DrawElements)
+ .SetMethod("enableVertexAttribArray", glEnableVertexAttribArray)
+ .SetMethod("getAttribLocation", GetAttribLocation)
+ .SetMethod("getProgramInfoLog", GetProgramInfoLog)
+ .SetMethod("getShaderInfoLog", GetShaderInfoLog)
+ .SetMethod("getUniformLocation", GetUniformLocation)
+ .SetMethod("linkProgram", glLinkProgram)
+ .SetMethod("shaderSource", ShaderSource)
+ .SetMethod("swapBuffers", MojoGLES2SwapBuffers)
+ .SetMethod("uniformMatrix4fv", UniformMatrix4fv)
+ .SetMethod("useProgram", glUseProgram)
+ .SetMethod("vertexAttribPointer", VertexAttribPointer)
+ .SetMethod("viewport", glViewport);
+}
+
+Context::Context(v8::Isolate* isolate,
+ mojo::Handle handle,
+ v8::Handle<v8::Function> context_lost_callback) {
+ v8::Handle<v8::Context> context = isolate->GetCurrentContext();
+ runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr();
+ context_lost_callback_.Reset(isolate, context_lost_callback);
+ context_ = MojoGLES2CreateContext(
+ handle.value(),
+ &ContextLostThunk,
+ NULL,
+ this);
+ MojoGLES2MakeCurrent(context_);
+}
+
+Context::~Context() {
+ MojoGLES2DestroyContext(context_);
+}
+
+void Context::ContextLost() {
+ if (!runner_)
+ return;
+ gin::Runner::Scope scope(runner_.get());
+ v8::Isolate* isolate = runner_->GetContextHolder()->isolate();
+
+ v8::Handle<v8::Function> callback = v8::Local<v8::Function>::New(
+ isolate, context_lost_callback_);
+
+ runner_->Call(callback, runner_->global(), 0, NULL);
+}
+
+void Context::ContextLostThunk(void* closure) {
+ static_cast<Context*>(closure)->ContextLost();
+}
+
+} // namespace gl
+} // namespace js
+} // namespace mojo
diff --git a/chromium/mojo/apps/js/bindings/gl/context.h b/chromium/mojo/apps/js/bindings/gl/context.h
new file mode 100644
index 00000000000..905f0fd00de
--- /dev/null
+++ b/chromium/mojo/apps/js/bindings/gl/context.h
@@ -0,0 +1,77 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_APPS_JS_BINDINGS_GL_CONTEXT_H_
+#define MOJO_APPS_JS_BINDINGS_GL_CONTEXT_H_
+
+#include <GLES2/gl2.h>
+
+#include "base/memory/weak_ptr.h"
+#include "gin/handle.h"
+#include "gin/public/wrapper_info.h"
+#include "gin/runner.h"
+#include "gin/wrappable.h"
+#include "mojo/bindings/js/handle.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "v8/include/v8.h"
+
+namespace gin {
+class Arguments;
+class ArrayBufferView;
+}
+
+namespace mojo {
+namespace js {
+namespace gl {
+
+// Context implements WebGLRenderingContext.
+class Context : public gin::Wrappable<Context> {
+ public:
+ static gin::WrapperInfo kWrapperInfo;
+
+ // TODO(piman): draw animation frame callback.
+ static gin::Handle<Context> Create(
+ v8::Isolate* isolate,
+ mojo::Handle handle,
+ v8::Handle<v8::Function> context_lost_callback);
+
+ static void BufferData(GLenum target, const gin::ArrayBufferView& buffer,
+ GLenum usage);
+ static void CompileShader(const gin::Arguments& args, GLuint shader);
+ static GLuint CreateBuffer();
+ static void DrawElements(GLenum mode, GLsizei count, GLenum type,
+ uint64_t indices);
+ static GLint GetAttribLocation(GLuint program, const std::string& name);
+ static std::string GetProgramInfoLog(GLuint program);
+ static std::string GetShaderInfoLog(GLuint shader);
+ static GLint GetUniformLocation(GLuint program, const std::string& name);
+ static void ShaderSource(GLuint shader, const std::string& source);
+ static void UniformMatrix4fv(GLint location, GLboolean transpose,
+ const gin::ArrayBufferView& buffer);
+ static void VertexAttribPointer(GLuint index, GLint size, GLenum type,
+ GLboolean normalized, GLsizei stride,
+ uint64_t offset);
+
+ private:
+ virtual gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
+ v8::Isolate* isolate) OVERRIDE;
+
+ explicit Context(v8::Isolate* isolate,
+ mojo::Handle handle,
+ v8::Handle<v8::Function> context_lost_callback);
+ virtual ~Context();
+
+ void ContextLost();
+ static void ContextLostThunk(void* closure);
+
+ base::WeakPtr<gin::Runner> runner_;
+ v8::Persistent<v8::Function> context_lost_callback_;
+ MojoGLES2Context context_;
+};
+
+} // namespace gl
+} // namespace js
+} // namespace mojo
+
+#endif // MOJO_APPS_JS_BINDINGS_GL_CONTEXT_H_
diff --git a/chromium/mojo/apps/js/bindings/gl/module.cc b/chromium/mojo/apps/js/bindings/gl/module.cc
new file mode 100644
index 00000000000..413f22ed3d5
--- /dev/null
+++ b/chromium/mojo/apps/js/bindings/gl/module.cc
@@ -0,0 +1,50 @@
+// Copyright 2013 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 "mojo/apps/js/bindings/gl/module.h"
+
+#include "base/logging.h"
+#include "gin/arguments.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "gin/wrappable.h"
+#include "mojo/apps/js/bindings/gl/context.h"
+#include "mojo/bindings/js/handle.h"
+
+namespace mojo {
+namespace js {
+namespace gl {
+
+const char* kModuleName = "mojo/apps/js/bindings/gl";
+
+namespace {
+
+gin::WrapperInfo kWrapperInfo = { gin::kEmbedderNativeGin };
+
+gin::Handle<Context> CreateContext(
+ const gin::Arguments& args,
+ mojo::Handle handle,
+ v8::Handle<v8::Function> context_lost_callback) {
+ return Context::Create(args.isolate(), handle, context_lost_callback);
+}
+
+} // namespace
+
+v8::Local<v8::Value> GetModule(v8::Isolate* isolate) {
+ gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
+ v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(&kWrapperInfo);
+
+ if (templ.IsEmpty()) {
+ templ = gin::ObjectTemplateBuilder(isolate)
+ .SetMethod("Context", CreateContext)
+ .Build();
+ data->SetObjectTemplate(&kWrapperInfo, templ);
+ }
+
+ return templ->NewInstance();
+}
+
+} // namespace gl
+} // namespace js
+} // namespace mojo
diff --git a/chromium/mojo/apps/js/bindings/gl/module.h b/chromium/mojo/apps/js/bindings/gl/module.h
new file mode 100644
index 00000000000..0ae0d14357b
--- /dev/null
+++ b/chromium/mojo/apps/js/bindings/gl/module.h
@@ -0,0 +1,22 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_APPS_JS_BINDINGS_GL_MODULE_H_
+#define MOJO_APPS_JS_BINDINGS_GL_MODULE_H_
+
+#include "gin/public/wrapper_info.h"
+#include "v8/include/v8.h"
+
+namespace mojo {
+namespace js {
+namespace gl {
+
+extern const char* kModuleName;
+v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+
+} // namespace gl
+} // namespace js
+} // namespace mojo
+
+#endif // MOJO_APPS_JS_BINDINGS_GL_H_
diff --git a/chromium/mojo/apps/js/bindings/monotonic_clock.cc b/chromium/mojo/apps/js/bindings/monotonic_clock.cc
new file mode 100644
index 00000000000..733af8a69e2
--- /dev/null
+++ b/chromium/mojo/apps/js/bindings/monotonic_clock.cc
@@ -0,0 +1,42 @@
+// Copyright 2014 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 "mojo/apps/js/bindings/monotonic_clock.h"
+
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "gin/public/wrapper_info.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace apps {
+
+namespace {
+
+gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
+
+double GetMonotonicSeconds() {
+ const double kMicrosecondsPerSecond = 1000000;
+ return static_cast<double>(mojo::GetTimeTicksNow()) / kMicrosecondsPerSecond;
+}
+
+} // namespace
+
+const char MonotonicClock::kModuleName[] = "monotonic_clock";
+
+v8::Local<v8::Value> MonotonicClock::GetModule(v8::Isolate* isolate) {
+ gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
+ v8::Local<v8::ObjectTemplate> templ =
+ data->GetObjectTemplate(&g_wrapper_info);
+ if (templ.IsEmpty()) {
+ templ = gin::ObjectTemplateBuilder(isolate)
+ .SetMethod("seconds", GetMonotonicSeconds)
+ .Build();
+ data->SetObjectTemplate(&g_wrapper_info, templ);
+ }
+ return templ->NewInstance();
+}
+
+} // namespace apps
+} // namespace mojo
diff --git a/chromium/mojo/apps/js/bindings/monotonic_clock.h b/chromium/mojo/apps/js/bindings/monotonic_clock.h
new file mode 100644
index 00000000000..6278821e8f8
--- /dev/null
+++ b/chromium/mojo/apps/js/bindings/monotonic_clock.h
@@ -0,0 +1,22 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_APPS_JS_BINDING_MONOTONIC_CLOCK_H_
+#define MOJO_APPS_JS_BINDING_MONOTONIC_CLOCK_H_
+
+#include "v8/include/v8.h"
+
+namespace mojo {
+namespace apps {
+
+class MonotonicClock {
+ public:
+ static const char kModuleName[];
+ static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+};
+
+} // namespace apps
+} // namespace mojo
+
+#endif // MOJO_APPS_JS_BINDING_MONOTONIC_CLOCK_H_
diff --git a/chromium/mojo/apps/js/bindings/monotonic_clock_unittests.js b/chromium/mojo/apps/js/bindings/monotonic_clock_unittests.js
new file mode 100644
index 00000000000..a1a253ed68e
--- /dev/null
+++ b/chromium/mojo/apps/js/bindings/monotonic_clock_unittests.js
@@ -0,0 +1,20 @@
+// Copyright 2014 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.
+
+define([
+ "console",
+ "gin/test/expect",
+ "monotonic_clock",
+ "timer",
+ "mojo/apps/js/bindings/threading"
+], function(console, expect, monotonicClock, timer, threading) {
+ var global = this;
+ var then = monotonicClock.seconds();
+ var t = timer.createOneShot(100, function() {
+ var now = monotonicClock.seconds();
+ expect(now).toBeGreaterThan(then);
+ global.result = "PASS";
+ threading.quit();
+ });
+});
diff --git a/chromium/mojo/apps/js/bindings/sample_service_unittests.js b/chromium/mojo/apps/js/bindings/sample_service_unittests.js
new file mode 100644
index 00000000000..c01ddee8bb2
--- /dev/null
+++ b/chromium/mojo/apps/js/bindings/sample_service_unittests.js
@@ -0,0 +1,169 @@
+// Copyright 2013 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.
+
+define([
+ "console",
+ "mojo/apps/js/test/hexdump",
+ "gin/test/expect",
+ "mojo/public/interfaces/bindings/tests/sample_service.mojom",
+ "mojo/public/interfaces/bindings/tests/sample_import.mojom",
+ "mojo/public/interfaces/bindings/tests/sample_import2.mojom",
+ ], function(console, hexdump, expect, sample, imported, imported2) {
+
+ var global = this;
+
+ // Set this variable to true to print the binary message in hex.
+ var dumpMessageAsHex = false;
+
+ function makeFoo() {
+ var bar = new sample.Bar();
+ bar.alpha = 20;
+ bar.beta = 40;
+ bar.gamma = 60;
+ bar.type = sample.Bar.Type.TYPE_VERTICAL;
+
+ var extra_bars = new Array(3);
+ for (var i = 0; i < extra_bars.length; ++i) {
+ var base = i * 100;
+ var type = i % 2 ?
+ sample.Bar.Type.TYPE_VERTICAL : sample.Bar.Type.TYPE_HORIZONTAL;
+ extra_bars[i] = new sample.Bar();
+ extra_bars[i].alpha = base;
+ extra_bars[i].beta = base + 20;
+ extra_bars[i].gamma = base + 40;
+ extra_bars[i].type = type;
+ }
+
+ var data = new Array(10);
+ for (var i = 0; i < data.length; ++i) {
+ data[i] = data.length - i;
+ }
+
+ var source = 0xFFFF; // Invent a dummy handle.
+
+ var foo = new sample.Foo();
+ foo.name = "foopy";
+ foo.x = 1;
+ foo.y = 2;
+ foo.a = false;
+ foo.b = true;
+ foo.c = false;
+ foo.bar = bar;
+ foo.extra_bars = extra_bars;
+ foo.data = data;
+ foo.source = source;
+ return foo;
+ }
+
+ // Check that the given |Foo| is identical to the one made by |MakeFoo()|.
+ function checkFoo(foo) {
+ expect(foo.name).toBe("foopy");
+ expect(foo.x).toBe(1);
+ expect(foo.y).toBe(2);
+ expect(foo.a).toBeFalsy();
+ expect(foo.b).toBeTruthy();
+ expect(foo.c).toBeFalsy();
+ expect(foo.bar.alpha).toBe(20);
+ expect(foo.bar.beta).toBe(40);
+ expect(foo.bar.gamma).toBe(60);
+ expect(foo.bar.type).toBe(sample.Bar.Type.TYPE_VERTICAL);
+
+ expect(foo.extra_bars.length).toBe(3);
+ for (var i = 0; i < foo.extra_bars.length; ++i) {
+ var base = i * 100;
+ var type = i % 2 ?
+ sample.Bar.Type.TYPE_VERTICAL : sample.Bar.Type.TYPE_HORIZONTAL;
+ expect(foo.extra_bars[i].alpha).toBe(base);
+ expect(foo.extra_bars[i].beta).toBe(base + 20);
+ expect(foo.extra_bars[i].gamma).toBe(base + 40);
+ expect(foo.extra_bars[i].type).toBe(type);
+ }
+
+ expect(foo.data.length).toBe(10);
+ for (var i = 0; i < foo.data.length; ++i)
+ expect(foo.data[i]).toBe(foo.data.length - i);
+
+ expect(foo.source).toBe(0xFFFF);
+ }
+
+ // Check that values are set to the defaults if we don't override them.
+ function checkDefaultValues() {
+ var bar = new sample.Bar();
+ expect(bar.alpha).toBe(255);
+ expect(bar.type).toBe(sample.Bar.Type.TYPE_VERTICAL);
+
+ var foo = new sample.Foo();
+ expect(foo.name).toBe("Fooby");
+ expect(foo.a).toBeTruthy();
+ // TODO(vtl): crbug.com/375845
+ // expect(foo.data).toBeNull();
+
+ var defaults = new sample.DefaultsTest();
+ expect(defaults.a0).toBe(-12);
+ expect(defaults.a1).toBe(sample.kTwelve);
+ expect(defaults.a2).toBe(1234);
+ expect(defaults.a3).toBe(34567);
+ expect(defaults.a4).toBe(123456);
+ // TODO(vtl): crbug.com/375522
+ // expect(defaults.a5).toBe(3456789012);
+ expect(defaults.a6).toBe(111111111111);
+ // TODO(vtl): crbug.com/375522 (Also, can we get exact values for large
+ // int64/uint64's in JS?)
+ // expect(defaults.a7).toBe(9999999999999999999);
+ expect(defaults.a8).toBe(0x12345);
+ expect(defaults.a9).toBe(-0x12345);
+ expect(defaults.a10).toBe(1234);
+ expect(defaults.a11).toBe(true);
+ expect(defaults.a12).toBe(false);
+ expect(defaults.a13).toBe(123.25);
+ expect(defaults.a14).toBe(1234567890.123);
+ expect(defaults.a15).toBe(1E10);
+ expect(defaults.a16).toBe(-1.2E+20);
+ expect(defaults.a17).toBe(1.23E-20);
+ expect(defaults.a20).toBe(sample.Bar.Type.TYPE_BOTH);
+ expect(defaults.a21).toBeNull();
+ expect(defaults.a22).toBeTruthy();
+ expect(defaults.a22.shape).toBe(imported.Shape.SHAPE_RECTANGLE);
+ expect(defaults.a22.color).toBe(imported2.Color.COLOR_BLACK);
+ // TODO(vtl): crbug.com/375845
+ // expect(defaults.a21).toBeNull();
+ // expect(defaults.a22).toBeNull();
+ }
+
+ function ServiceImpl() {
+ }
+
+ ServiceImpl.prototype = Object.create(sample.ServiceStub.prototype);
+
+ ServiceImpl.prototype.frobinate = function(foo, baz, port) {
+ checkFoo(foo);
+ expect(baz).toBe(sample.ServiceStub.BazOptions.BAZ_EXTRA);
+ expect(port).toBe(10);
+ global.result = "PASS";
+ };
+
+ function SimpleMessageReceiver() {
+ }
+
+ SimpleMessageReceiver.prototype.accept = function(message) {
+ if (dumpMessageAsHex) {
+ var uint8Array = new Uint8Array(message.buffer.arrayBuffer);
+ console.log(hexdump.dumpArray(uint8Array));
+ }
+ // Imagine some IPC happened here.
+ var serviceImpl = new ServiceImpl();
+ serviceImpl.accept(message);
+ };
+
+ var receiver = new SimpleMessageReceiver();
+ var serviceProxy = new sample.ServiceProxy(receiver);
+
+ checkDefaultValues();
+
+ var foo = makeFoo();
+ checkFoo(foo);
+
+ var port = 10;
+ serviceProxy.frobinate(foo, sample.ServiceProxy.BazOptions.BAZ_EXTRA, port);
+});
diff --git a/chromium/mojo/apps/js/bindings/threading.cc b/chromium/mojo/apps/js/bindings/threading.cc
new file mode 100644
index 00000000000..64e32fc8f21
--- /dev/null
+++ b/chromium/mojo/apps/js/bindings/threading.cc
@@ -0,0 +1,47 @@
+// Copyright 2013 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 "mojo/apps/js/bindings/threading.h"
+
+#include "base/message_loop/message_loop.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "mojo/bindings/js/handle.h"
+
+namespace mojo {
+namespace apps {
+
+namespace {
+
+void Quit() {
+ base::MessageLoop::current()->QuitNow();
+}
+
+gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
+
+} // namespace
+
+const char Threading::kModuleName[] = "mojo/apps/js/bindings/threading";
+
+v8::Local<v8::Value> Threading::GetModule(v8::Isolate* isolate) {
+ gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
+ v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(
+ &g_wrapper_info);
+
+ if (templ.IsEmpty()) {
+ templ = gin::ObjectTemplateBuilder(isolate)
+ .SetMethod("quit", Quit)
+ .Build();
+
+ data->SetObjectTemplate(&g_wrapper_info, templ);
+ }
+
+ return templ->NewInstance();
+}
+
+Threading::Threading() {
+}
+
+} // namespace apps
+} // namespace mojo
diff --git a/chromium/mojo/apps/js/bindings/threading.h b/chromium/mojo/apps/js/bindings/threading.h
new file mode 100644
index 00000000000..ac8e221377a
--- /dev/null
+++ b/chromium/mojo/apps/js/bindings/threading.h
@@ -0,0 +1,25 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_APPS_JS_BINDINGS_THREADING_H_
+#define MOJO_APPS_JS_BINDINGS_THREADING_H_
+
+#include "gin/public/wrapper_info.h"
+#include "v8/include/v8.h"
+
+namespace mojo {
+namespace apps {
+
+class Threading {
+ public:
+ static const char kModuleName[];
+ static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+ private:
+ Threading();
+};
+
+} // namespace apps
+} // namespace mojo
+
+#endif // MOJO_APPS_JS_BINDINGS_THREADING_H_
diff --git a/chromium/mojo/apps/js/main.cc b/chromium/mojo/apps/js/main.cc
new file mode 100644
index 00000000000..84231e5971e
--- /dev/null
+++ b/chromium/mojo/apps/js/main.cc
@@ -0,0 +1,43 @@
+// Copyright 2013 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 "base/message_loop/message_loop.h"
+#include "gin/public/isolate_holder.h"
+#include "mojo/apps/js/mojo_runner_delegate.h"
+#include "mojo/public/cpp/gles2/gles2.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/macros.h"
+
+#if defined(WIN32)
+#if !defined(CDECL)
+#define CDECL __cdecl
+#endif
+#define MOJO_APPS_JS_EXPORT __declspec(dllexport)
+#else
+#define CDECL
+#define MOJO_APPS_JS_EXPORT __attribute__((visibility("default")))
+#endif
+
+namespace mojo {
+namespace apps {
+
+void Start(MojoHandle pipe, const std::string& module) {
+ base::MessageLoop loop;
+
+ gin::IsolateHolder instance(gin::IsolateHolder::kStrictMode);
+ MojoRunnerDelegate delegate;
+ gin::ShellRunner runner(&delegate, instance.isolate());
+ delegate.Start(&runner, pipe, module);
+
+ base::MessageLoop::current()->Run();
+}
+
+} // namespace apps
+} // namespace mojo
+
+extern "C" MOJO_APPS_JS_EXPORT MojoResult CDECL MojoMain(MojoHandle pipe) {
+ mojo::GLES2Initializer gles2;
+ mojo::apps::Start(pipe, "mojo/apps/js/main");
+ return MOJO_RESULT_OK;
+}
diff --git a/chromium/mojo/apps/js/main.js b/chromium/mojo/apps/js/main.js
new file mode 100644
index 00000000000..a216126b35b
--- /dev/null
+++ b/chromium/mojo/apps/js/main.js
@@ -0,0 +1,398 @@
+// Copyright 2013 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.
+
+define([
+ 'console',
+ 'monotonic_clock',
+ 'timer',
+ 'mojo/public/js/bindings/connection',
+ 'mojo/public/js/bindings/core',
+ 'mojo/apps/js/bindings/gl',
+ 'mojo/apps/js/bindings/threading',
+ 'mojo/services/native_viewport/native_viewport.mojom',
+ 'mojo/public/interfaces/service_provider/service_provider.mojom',
+], function(console,
+ monotonicClock,
+ timer,
+ connection,
+ core,
+ gljs,
+ threading,
+ nativeViewport,
+ service_provider) {
+
+ const VERTEX_SHADER_SOURCE = [
+ 'uniform mat4 u_mvpMatrix;',
+ 'attribute vec4 a_position;',
+ 'void main()',
+ '{',
+ ' gl_Position = u_mvpMatrix * a_position;',
+ '}'
+ ].join('\n');
+
+ const FRAGMENT_SHADER_SOURCE = [
+ 'precision mediump float;',
+ 'void main()',
+ '{',
+ ' gl_FragColor = vec4( 0.0, 1.0, 0.0, 1.0 );',
+ '}'
+ ].join('\n');
+
+ function ESMatrix() {
+ this.m = new Float32Array(16);
+ }
+
+ ESMatrix.prototype.getIndex = function(x, y) {
+ return x * 4 + y;
+ }
+
+ ESMatrix.prototype.set = function(x, y, v) {
+ this.m[this.getIndex(x, y)] = v;
+ };
+
+ ESMatrix.prototype.get = function(x, y) {
+ return this.m[this.getIndex(x, y)];
+ };
+
+ ESMatrix.prototype.loadZero = function() {
+ for (var i = 0; i < this.m.length; i++) {
+ this.m[i] = 0;
+ }
+ };
+
+ ESMatrix.prototype.loadIdentity = function() {
+ this.loadZero();
+ for (var i = 0; i < 4; i++) {
+ this.set(i, i, 1);
+ }
+ };
+
+ ESMatrix.prototype.multiply = function(a, b) {
+ var result = new ESMatrix();
+ for (var i = 0; i < 4; i++) {
+ result.set(i, 0,
+ (a.get(i, 0) * b.get(0, 0)) +
+ (a.get(i, 1) * b.get(1, 0)) +
+ (a.get(i, 2) * b.get(2, 0)) +
+ (a.get(i, 3) * b.get(3, 0)));
+
+ result.set(i, 1,
+ (a.get(i, 0) * b.get(0, 1)) +
+ (a.get(i, 1) * b.get(1, 1)) +
+ (a.get(i, 2) * b.get(2, 1)) +
+ (a.get(i, 3) * b.get(3, 1)));
+
+ result.set(i, 2,
+ (a.get(i, 0) * b.get(0, 2)) +
+ (a.get(i, 1) * b.get(1, 2)) +
+ (a.get(i, 2) * b.get(2, 2)) +
+ (a.get(i, 3) * b.get(3, 2)));
+
+ result.set(i, 3,
+ (a.get(i, 0) * b.get(0, 3)) +
+ (a.get(i, 1) * b.get(1, 3)) +
+ (a.get(i, 2) * b.get(2, 3)) +
+ (a.get(i, 3) * b.get(3, 3)));
+ }
+ for (var i = 0; i < result.m.length; i++) {
+ this.m[i] = result.m[i];
+ }
+ };
+
+ ESMatrix.prototype.frustrum = function(left, right, bottom, top, nearZ,
+ farZ) {
+ var deltaX = right - left;
+ var deltaY = top - bottom;
+ var deltaZ = farZ - nearZ;
+
+ if (nearZ < 0 || farZ < 0 || deltaZ < 0 || deltaY < 0 || deltaX < 0) {
+ return;
+ }
+
+ var frust = new ESMatrix();
+ frust.set(0, 0, 2 * nearZ / deltaX);
+
+ frust.set(1, 1, 2 * nearZ / deltaY);
+
+ frust.set(2, 0, (right + left) / deltaX);
+ frust.set(2, 1, (top + bottom) / deltaY);
+ frust.set(2, 2, -(nearZ + farZ) / deltaZ);
+ frust.set(2, 3, -1);
+
+ frust.set(3, 2, -2 * nearZ * farZ / deltaZ);
+
+ this.multiply(frust, this);
+ };
+
+ ESMatrix.prototype.perspective = function(fovY, aspect, nearZ, farZ) {
+ var frustrumH = Math.tan(fovY / 360 * Math.PI) * nearZ;
+ var frustrumW = frustrumH * aspect;
+ this.frustrum(-frustrumW, frustrumW, -frustrumH, frustrumH, nearZ, farZ);
+ };
+
+ ESMatrix.prototype.translate = function(tx, ty, tz) {
+ this.set(3, 0, this.get(3, 0) + this.get(0, 0) *
+ tx + this.get(1, 0) * ty + this.get(2, 0) * tz);
+ this.set(3, 1, this.get(3, 1) + this.get(0, 1) *
+ tx + this.get(1, 1) * ty + this.get(2, 1) * tz);
+ this.set(3, 2, this.get(3, 2) + this.get(0, 2) *
+ tx + this.get(1, 2) * ty + this.get(2, 2) * tz);
+ this.set(3, 3, this.get(3, 3) + this.get(0, 3) *
+ tx + this.get(1, 3) * ty + this.get(2, 3) * tz);
+ };
+
+ ESMatrix.prototype.rotate = function(angle, x, y, z) {
+ var mag = Math.sqrt(x * x + y * y + z * z);
+ var sinAngle = Math.sin(angle * Math.PI / 180);
+ var cosAngle = Math.cos(angle * Math.PI / 180);
+ if (mag <= 0) {
+ return;
+ }
+
+ var xx, yy, zz, xy, yz, zx, xs, ys, zs, oneMinusCos;
+ var rotation = new ESMatrix();
+
+ x /= mag;
+ y /= mag;
+ z /= mag;
+
+ xx = x * x;
+ yy = y * y;
+ zz = z * z;
+ xy = x * y;
+ yz = y * z;
+ zx = z * x;
+ xs = x * sinAngle;
+ ys = y * sinAngle;
+ zs = z * sinAngle;
+ oneMinusCos = 1 - cosAngle;
+
+ rotation.set(0, 0, (oneMinusCos * xx) + cosAngle);
+ rotation.set(0, 1, (oneMinusCos * xy) - zs);
+ rotation.set(0, 2, (oneMinusCos * zx) + ys);
+ rotation.set(0, 3, 0);
+
+ rotation.set(1, 0, (oneMinusCos * xy) + zs);
+ rotation.set(1, 1, (oneMinusCos * yy) + cosAngle);
+ rotation.set(1, 2, (oneMinusCos * yz) - xs);
+ rotation.set(1, 3, 0);
+
+ rotation.set(2, 0, (oneMinusCos * zx) - ys);
+ rotation.set(2, 1, (oneMinusCos * yz) + xs);
+ rotation.set(2, 2, (oneMinusCos * zz) + cosAngle);
+ rotation.set(2, 3, 0);
+
+ rotation.set(3, 0, 0);
+ rotation.set(3, 1, 0);
+ rotation.set(3, 2, 0);
+ rotation.set(3, 3, 1);
+
+ this.multiply(rotation, this);
+ };
+
+ function loadProgram(gl) {
+ var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+ gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE);
+ gl.compileShader(vertexShader);
+
+ var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+ gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE);
+ gl.compileShader(fragmentShader);
+
+ var program = gl.createProgram();
+ gl.attachShader(program, vertexShader);
+ gl.attachShader(program, fragmentShader);
+
+ gl.linkProgram(program);
+ // TODO(aa): Check for errors using getProgramiv and LINK_STATUS.
+
+ gl.deleteShader(vertexShader);
+ gl.deleteShader(fragmentShader);
+
+ return program;
+ }
+
+ var vboVertices;
+ var vboIndices;
+ function generateCube(gl) {
+ var numVertices = 24 * 3;
+ var numIndices = 12 * 3;
+
+ var cubeVertices = new Float32Array([
+ -0.5, -0.5, -0.5,
+ -0.5, -0.5, 0.5,
+ 0.5, -0.5, 0.5,
+ 0.5, -0.5, -0.5,
+ -0.5, 0.5, -0.5,
+ -0.5, 0.5, 0.5,
+ 0.5, 0.5, 0.5,
+ 0.5, 0.5, -0.5,
+ -0.5, -0.5, -0.5,
+ -0.5, 0.5, -0.5,
+ 0.5, 0.5, -0.5,
+ 0.5, -0.5, -0.5,
+ -0.5, -0.5, 0.5,
+ -0.5, 0.5, 0.5,
+ 0.5, 0.5, 0.5,
+ 0.5, -0.5, 0.5,
+ -0.5, -0.5, -0.5,
+ -0.5, -0.5, 0.5,
+ -0.5, 0.5, 0.5,
+ -0.5, 0.5, -0.5,
+ 0.5, -0.5, -0.5,
+ 0.5, -0.5, 0.5,
+ 0.5, 0.5, 0.5,
+ 0.5, 0.5, -0.5
+ ]);
+
+ var cubeIndices = new Uint16Array([
+ 0, 2, 1,
+ 0, 3, 2,
+ 4, 5, 6,
+ 4, 6, 7,
+ 8, 9, 10,
+ 8, 10, 11,
+ 12, 15, 14,
+ 12, 14, 13,
+ 16, 17, 18,
+ 16, 18, 19,
+ 20, 23, 22,
+ 20, 22, 21
+ ]);
+
+ // TODO(aa): The C++ program branches here on whether the pointer is
+ // non-NULL.
+ vboVertices = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, vboVertices);
+ gl.bufferData(gl.ARRAY_BUFFER, cubeVertices, gl.STATIC_DRAW);
+ gl.bindBuffer(gl.ARRAY_BUFFER, 0);
+
+ vboIndices = gl.createBuffer();
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vboIndices);
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, cubeIndices, gl.STATIC_DRAW);
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0);
+
+ return cubeIndices.length;
+ }
+
+ function SampleApp(service_provider) {
+ this.service_provider_ = service_provider;
+
+ var pipe = new core.createMessagePipe();
+ this.service_provider_.connect('mojo:mojo_native_viewport_service',
+ pipe.handle1);
+ new connection.Connection(pipe.handle0, NativeViewportClientImpl,
+ nativeViewport.NativeViewportProxy);
+ }
+ // TODO(aa): It is a bummer to need this stub object in JavaScript. We should
+ // have a 'client' object that contains both the sending and receiving bits of
+ // the client side of the interface. Since JS is loosely typed, we do not need
+ // a separate base class to inherit from to receive callbacks.
+ SampleApp.prototype =
+ Object.create(service_provider.ServiceProviderStub.prototype);
+
+
+ function NativeViewportClientImpl(remote) {
+ this.remote_ = remote;
+
+ var pipe = core.createMessagePipe();
+
+ var rect = new nativeViewport.Rect;
+ rect.position = new nativeViewport.Point;
+ rect.size = new nativeViewport.Size;
+ rect.size.width = 800;
+ rect.size.height = 600;
+ this.remote_.create(rect);
+ this.remote_.show();
+ this.remote_.createGLES2Context(pipe.handle1);
+ this.gles2_ = new GLES2ClientImpl(pipe.handle0);
+ }
+ NativeViewportClientImpl.prototype =
+ Object.create(nativeViewport.NativeViewportClientStub.prototype);
+
+ NativeViewportClientImpl.prototype.onCreated = function() {
+ console.log('NativeViewportClientImpl.prototype.OnCreated');
+ };
+
+ NativeViewportClientImpl.prototype.onBoundsChanged = function(bounds) {
+ console.log('NativeViewportClientImpl.prototype.OnBoundsChanged');
+ this.gles2_.setDimensions(bounds.size);
+ }
+
+ function GLES2ClientImpl(remotePipe) {
+ this.gl_ = new gljs.Context(remotePipe, this.contextLost.bind(this));
+ this.lastTime_ = monotonicClock.seconds();
+ this.angle_ = 45;
+
+ this.program_ = loadProgram(this.gl_);
+ this.positionLocation_ =
+ this.gl_.getAttribLocation(this.program_, 'a_position');
+ this.mvpLocation_ =
+ this.gl_.getUniformLocation(this.program_, 'u_mvpMatrix');
+ this.numIndices_ = generateCube(this.gl_);
+ this.mvpMatrix_ = new ESMatrix();
+ this.mvpMatrix_.loadIdentity();
+
+ this.gl_.clearColor(0, 0, 0, 0);
+ }
+
+ GLES2ClientImpl.prototype.setDimensions = function(size) {
+ this.width_ = size.width;
+ this.height_ = size.height;
+ this.timer_ = timer.createRepeating(16, this.handleTimer.bind(this));
+ }
+
+ GLES2ClientImpl.prototype.drawCube = function() {
+ this.gl_.viewport(0, 0, this.width_, this.height_);
+ this.gl_.clear(this.gl_.COLOR_BUFFER_BIT);
+ this.gl_.useProgram(this.program_);
+ this.gl_.bindBuffer(this.gl_.ARRAY_BUFFER, vboVertices);
+ this.gl_.bindBuffer(this.gl_.ELEMENT_ARRAY_BUFFER, vboIndices);
+ this.gl_.vertexAttribPointer(this.positionLocation_, 3, this.gl_.FLOAT,
+ false, 12, 0);
+ this.gl_.enableVertexAttribArray(this.positionLocation_);
+ this.gl_.uniformMatrix4fv(this.mvpLocation_, false, this.mvpMatrix_.m);
+ this.gl_.drawElements(this.gl_.TRIANGLES, this.numIndices_,
+ this.gl_.UNSIGNED_SHORT, 0);
+ this.gl_.swapBuffers();
+ };
+
+ GLES2ClientImpl.prototype.handleTimer = function() {
+ var now = monotonicClock.seconds();
+ var secondsDelta = now - this.lastTime_;
+ this.lastTime_ = now;
+
+ this.angle_ += this.getRotationForTimeDelta(secondsDelta);
+ this.angle_ = this.angle_ % 360;
+
+ var aspect = this.width_ / this.height_;
+
+ var perspective = new ESMatrix();
+ perspective.loadIdentity();
+ perspective.perspective(60, aspect, 1, 20);
+
+ var modelView = new ESMatrix();
+ modelView.loadIdentity();
+ modelView.translate(0, 0, -2);
+ modelView.rotate(this.angle_, 1, 0, 1);
+
+ this.mvpMatrix_.multiply(modelView, perspective);
+
+ this.drawCube();
+ };
+
+ GLES2ClientImpl.prototype.getRotationForTimeDelta = function(secondsDelta) {
+ return secondsDelta * 40;
+ };
+
+ GLES2ClientImpl.prototype.contextLost = function() {
+ console.log('GLES2ClientImpl.prototype.contextLost');
+ };
+
+
+ return function(handle) {
+ new connection.Connection(
+ handle, SampleApp, service_provider.ServiceProviderProxy);
+ };
+});
diff --git a/chromium/mojo/apps/js/mojo_runner_delegate.cc b/chromium/mojo/apps/js/mojo_runner_delegate.cc
new file mode 100644
index 00000000000..8bec392564d
--- /dev/null
+++ b/chromium/mojo/apps/js/mojo_runner_delegate.cc
@@ -0,0 +1,82 @@
+// Copyright 2013 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 "mojo/apps/js/mojo_runner_delegate.h"
+
+#include "base/bind.h"
+#include "base/path_service.h"
+#include "gin/converter.h"
+#include "gin/modules/console.h"
+#include "gin/modules/module_registry.h"
+#include "gin/modules/timer.h"
+#include "gin/try_catch.h"
+#include "mojo/apps/js/bindings/gl/module.h"
+#include "mojo/apps/js/bindings/monotonic_clock.h"
+#include "mojo/apps/js/bindings/threading.h"
+#include "mojo/bindings/js/core.h"
+#include "mojo/bindings/js/handle.h"
+#include "mojo/bindings/js/support.h"
+
+namespace mojo {
+namespace apps {
+
+namespace {
+
+// TODO(abarth): Rather than loading these modules from the file system, we
+// should load them from the network via Mojo IPC.
+std::vector<base::FilePath> GetModuleSearchPaths() {
+ std::vector<base::FilePath> search_paths(2);
+ PathService::Get(base::DIR_SOURCE_ROOT, &search_paths[0]);
+ PathService::Get(base::DIR_EXE, &search_paths[1]);
+ search_paths[1] = search_paths[1].AppendASCII("gen");
+ return search_paths;
+}
+
+void StartCallback(base::WeakPtr<gin::Runner> runner,
+ MojoHandle pipe,
+ v8::Handle<v8::Value> module) {
+ v8::Isolate* isolate = runner->GetContextHolder()->isolate();
+ v8::Handle<v8::Function> start;
+ CHECK(gin::ConvertFromV8(isolate, module, &start));
+
+ v8::Handle<v8::Value> args[] = {
+ gin::ConvertToV8(isolate, Handle(pipe)) };
+ runner->Call(start, runner->global(), 1, args);
+}
+
+} // namespace
+
+MojoRunnerDelegate::MojoRunnerDelegate()
+ : ModuleRunnerDelegate(GetModuleSearchPaths()) {
+ AddBuiltinModule(gin::Console::kModuleName, gin::Console::GetModule);
+ AddBuiltinModule(gin::TimerModule::kName, gin::TimerModule::GetModule);
+ AddBuiltinModule(js::Core::kModuleName, js::Core::GetModule);
+ AddBuiltinModule(js::Support::kModuleName, js::Support::GetModule);
+ AddBuiltinModule(js::gl::kModuleName, js::gl::GetModule);
+ AddBuiltinModule(MonotonicClock::kModuleName, MonotonicClock::GetModule);
+ AddBuiltinModule(Threading::kModuleName, Threading::GetModule);
+}
+
+MojoRunnerDelegate::~MojoRunnerDelegate() {
+}
+
+void MojoRunnerDelegate::Start(gin::Runner* runner,
+ MojoHandle pipe,
+ const std::string& module) {
+ gin::Runner::Scope scope(runner);
+ gin::ModuleRegistry* registry =
+ gin::ModuleRegistry::From(runner->GetContextHolder()->context());
+ registry->LoadModule(runner->GetContextHolder()->isolate(), module,
+ base::Bind(StartCallback, runner->GetWeakPtr(), pipe));
+ AttemptToLoadMoreModules(runner);
+}
+
+void MojoRunnerDelegate::UnhandledException(gin::ShellRunner* runner,
+ gin::TryCatch& try_catch) {
+ gin::ModuleRunnerDelegate::UnhandledException(runner, try_catch);
+ LOG(ERROR) << try_catch.GetStackTrace();
+}
+
+} // namespace apps
+} // namespace mojo
diff --git a/chromium/mojo/apps/js/mojo_runner_delegate.h b/chromium/mojo/apps/js/mojo_runner_delegate.h
new file mode 100644
index 00000000000..71ee4aff167
--- /dev/null
+++ b/chromium/mojo/apps/js/mojo_runner_delegate.h
@@ -0,0 +1,33 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_APPS_JS_MOJO_RUNNER_DELEGATE_H_
+#define MOJO_APPS_JS_MOJO_RUNNER_DELEGATE_H_
+
+#include "base/compiler_specific.h"
+#include "gin/modules/module_runner_delegate.h"
+#include "mojo/public/c/system/core.h"
+
+namespace mojo {
+namespace apps {
+
+class MojoRunnerDelegate : public gin::ModuleRunnerDelegate {
+ public:
+ MojoRunnerDelegate();
+ virtual ~MojoRunnerDelegate();
+
+ void Start(gin::Runner* runner, MojoHandle pipe, const std::string& module);
+
+ private:
+ // From ModuleRunnerDelegate:
+ virtual void UnhandledException(gin::ShellRunner* runner,
+ gin::TryCatch& try_catch) OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(MojoRunnerDelegate);
+};
+
+} // namespace apps
+} // namespace mojo
+
+#endif // MOJO_APPS_JS_MOJO_RUNNER_DELEGATE_H_
diff --git a/chromium/mojo/aura/DEPS b/chromium/mojo/aura/DEPS
new file mode 100644
index 00000000000..ad60823f193
--- /dev/null
+++ b/chromium/mojo/aura/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+ "+cc",
+ "+skia",
+ "+ui/aura",
+ "+ui/compositor",
+ "+ui/events",
+ "+ui/gfx",
+ "+ui/gl",
+ "+webkit/common/gpu",
+]
diff --git a/chromium/mojo/aura/aura_init.cc b/chromium/mojo/aura/aura_init.cc
new file mode 100644
index 00000000000..a99ce25fc3a
--- /dev/null
+++ b/chromium/mojo/aura/aura_init.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2014 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 "mojo/aura/aura_init.h"
+
+#include "mojo/aura/context_factory_mojo.h"
+#include "mojo/aura/screen_mojo.h"
+#include "ui/aura/env.h"
+
+namespace mojo {
+
+AuraInit::AuraInit() {
+ aura::Env::CreateInstance(true);
+
+ context_factory_.reset(new ContextFactoryMojo);
+ aura::Env::GetInstance()->set_context_factory(context_factory_.get());
+
+ screen_.reset(ScreenMojo::Create());
+ gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, screen_.get());
+}
+
+AuraInit::~AuraInit() {
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/aura/aura_init.h b/chromium/mojo/aura/aura_init.h
new file mode 100644
index 00000000000..dea16843761
--- /dev/null
+++ b/chromium/mojo/aura/aura_init.h
@@ -0,0 +1,33 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_AURA_AURA_INIT_MOJO_H_
+#define MOJO_AURA_AURA_INIT_MOJO_H_
+
+#include "base/memory/scoped_ptr.h"
+
+namespace ui {
+class ContextFactory;
+}
+
+namespace mojo {
+
+class ScreenMojo;
+
+// Sets up necessary state for aura when run with the viewmanager.
+class AuraInit {
+ public:
+ AuraInit();
+ ~AuraInit();
+
+ private:
+ scoped_ptr<ui::ContextFactory> context_factory_;
+ scoped_ptr<ScreenMojo> screen_;
+
+ DISALLOW_COPY_AND_ASSIGN(AuraInit);
+};
+
+} // namespace mojo
+
+#endif // MOJO_AURA_AURA_INIT_MOJO_H_
diff --git a/chromium/mojo/aura/context_factory_mojo.cc b/chromium/mojo/aura/context_factory_mojo.cc
new file mode 100644
index 00000000000..6b2b709cdcd
--- /dev/null
+++ b/chromium/mojo/aura/context_factory_mojo.cc
@@ -0,0 +1,136 @@
+// Copyright 2014 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 "mojo/aura/context_factory_mojo.h"
+
+#include "base/bind.h"
+#include "cc/output/output_surface.h"
+#include "cc/output/software_output_device.h"
+#include "cc/resources/shared_bitmap_manager.h"
+#include "mojo/aura/window_tree_host_mojo.h"
+#include "skia/ext/platform_canvas.h"
+#include "ui/compositor/reflector.h"
+
+namespace mojo {
+namespace {
+
+void FreeSharedBitmap(cc::SharedBitmap* shared_bitmap) {
+ delete shared_bitmap->memory();
+}
+
+void IgnoreSharedBitmap(cc::SharedBitmap* shared_bitmap) {}
+
+class SoftwareOutputDeviceViewManager : public cc::SoftwareOutputDevice {
+ public:
+ explicit SoftwareOutputDeviceViewManager(ui::Compositor* compositor)
+ : compositor_(compositor) {
+ }
+ virtual ~SoftwareOutputDeviceViewManager() {}
+
+ // cc::SoftwareOutputDevice:
+ virtual void EndPaint(cc::SoftwareFrameData* frame_data) OVERRIDE {
+ WindowTreeHostMojo* window_tree_host =
+ WindowTreeHostMojo::ForCompositor(compositor_);
+ DCHECK(window_tree_host);
+ window_tree_host->SetContents(
+ skia::GetTopDevice(*canvas_)->accessBitmap(true));
+
+ SoftwareOutputDevice::EndPaint(frame_data);
+ }
+
+ private:
+ ui::Compositor* compositor_;
+
+ DISALLOW_COPY_AND_ASSIGN(SoftwareOutputDeviceViewManager);
+};
+
+// TODO(sky): this is a copy from cc/test. Copy to a common place.
+class TestSharedBitmapManager : public cc::SharedBitmapManager {
+ public:
+ TestSharedBitmapManager() {}
+ virtual ~TestSharedBitmapManager() {}
+
+ virtual scoped_ptr<cc::SharedBitmap> AllocateSharedBitmap(
+ const gfx::Size& size) OVERRIDE {
+ base::AutoLock lock(lock_);
+ scoped_ptr<base::SharedMemory> memory(new base::SharedMemory);
+ memory->CreateAndMapAnonymous(size.GetArea() * 4);
+ cc::SharedBitmapId id = cc::SharedBitmap::GenerateId();
+ bitmap_map_[id] = memory.get();
+ return scoped_ptr<cc::SharedBitmap>(
+ new cc::SharedBitmap(memory.release(), id,
+ base::Bind(&FreeSharedBitmap)));
+ }
+
+ virtual scoped_ptr<cc::SharedBitmap> GetSharedBitmapFromId(
+ const gfx::Size&,
+ const cc::SharedBitmapId& id) OVERRIDE {
+ base::AutoLock lock(lock_);
+ if (bitmap_map_.find(id) == bitmap_map_.end())
+ return scoped_ptr<cc::SharedBitmap>();
+ return scoped_ptr<cc::SharedBitmap>(
+ new cc::SharedBitmap(bitmap_map_[id], id,
+ base::Bind(&IgnoreSharedBitmap)));
+ }
+
+ virtual scoped_ptr<cc::SharedBitmap> GetBitmapForSharedMemory(
+ base::SharedMemory* memory) OVERRIDE {
+ base::AutoLock lock(lock_);
+ cc::SharedBitmapId id = cc::SharedBitmap::GenerateId();
+ bitmap_map_[id] = memory;
+ return scoped_ptr<cc::SharedBitmap>(
+ new cc::SharedBitmap(memory, id, base::Bind(&IgnoreSharedBitmap)));
+ }
+
+ private:
+ base::Lock lock_;
+ std::map<cc::SharedBitmapId, base::SharedMemory*> bitmap_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestSharedBitmapManager);
+};
+
+} // namespace
+
+ContextFactoryMojo::ContextFactoryMojo()
+ : shared_bitmap_manager_(new TestSharedBitmapManager()) {
+}
+
+ContextFactoryMojo::~ContextFactoryMojo() {}
+
+scoped_ptr<cc::OutputSurface> ContextFactoryMojo::CreateOutputSurface(
+ ui::Compositor* compositor,
+ bool software_fallback) {
+ scoped_ptr<cc::SoftwareOutputDevice> output_device(
+ new SoftwareOutputDeviceViewManager(compositor));
+ return make_scoped_ptr(new cc::OutputSurface(output_device.Pass()));
+}
+
+scoped_refptr<ui::Reflector> ContextFactoryMojo::CreateReflector(
+ ui::Compositor* mirroed_compositor,
+ ui::Layer* mirroring_layer) {
+ return new ui::Reflector();
+}
+
+void ContextFactoryMojo::RemoveReflector(
+ scoped_refptr<ui::Reflector> reflector) {
+}
+
+scoped_refptr<cc::ContextProvider>
+ContextFactoryMojo::SharedMainThreadContextProvider() {
+ return scoped_refptr<cc::ContextProvider>(NULL);
+}
+
+void ContextFactoryMojo::RemoveCompositor(ui::Compositor* compositor) {}
+
+bool ContextFactoryMojo::DoesCreateTestContexts() { return false; }
+
+cc::SharedBitmapManager* ContextFactoryMojo::GetSharedBitmapManager() {
+ return shared_bitmap_manager_.get();
+}
+
+base::MessageLoopProxy* ContextFactoryMojo::GetCompositorMessageLoop() {
+ return NULL;
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/aura/context_factory_mojo.h b/chromium/mojo/aura/context_factory_mojo.h
new file mode 100644
index 00000000000..800afa7bd6d
--- /dev/null
+++ b/chromium/mojo/aura/context_factory_mojo.h
@@ -0,0 +1,40 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_AURA_CONTEXT_FACTORY_MOJO_H_
+#define MOJO_AURA_CONTEXT_FACTORY_MOJO_H_
+
+#include "ui/compositor/compositor.h"
+
+namespace mojo {
+
+class ContextFactoryMojo : public ui::ContextFactory {
+ public:
+ ContextFactoryMojo();
+ virtual ~ContextFactoryMojo();
+
+ private:
+ // ContextFactory:
+ virtual scoped_ptr<cc::OutputSurface> CreateOutputSurface(
+ ui::Compositor* compositor,
+ bool software_fallback) OVERRIDE;
+ virtual scoped_refptr<ui::Reflector> CreateReflector(
+ ui::Compositor* mirrored_compositor,
+ ui::Layer* mirroring_layer) OVERRIDE;
+ virtual void RemoveReflector(scoped_refptr<ui::Reflector> reflector) OVERRIDE;
+ virtual scoped_refptr<cc::ContextProvider> SharedMainThreadContextProvider()
+ OVERRIDE;
+ virtual void RemoveCompositor(ui::Compositor* compositor) OVERRIDE;
+ virtual bool DoesCreateTestContexts() OVERRIDE;
+ virtual cc::SharedBitmapManager* GetSharedBitmapManager() OVERRIDE;
+ virtual base::MessageLoopProxy* GetCompositorMessageLoop() OVERRIDE;
+
+ scoped_ptr<cc::SharedBitmapManager> shared_bitmap_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContextFactoryMojo);
+};
+
+} // namespace mojo
+
+#endif // MOJO_AURA_CONTEXT_FACTORY_MOJO_H_
diff --git a/chromium/mojo/aura/screen_mojo.cc b/chromium/mojo/aura/screen_mojo.cc
new file mode 100644
index 00000000000..b07c320a601
--- /dev/null
+++ b/chromium/mojo/aura/screen_mojo.cc
@@ -0,0 +1,76 @@
+// Copyright (c) 2014 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 "mojo/aura/screen_mojo.h"
+
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/rect_conversions.h"
+
+namespace mojo {
+
+// static
+ScreenMojo* ScreenMojo::Create() {
+ return new ScreenMojo(gfx::Rect(0, 0, 800, 600));
+}
+
+ScreenMojo::~ScreenMojo() {
+}
+
+bool ScreenMojo::IsDIPEnabled() {
+ NOTIMPLEMENTED();
+ return true;
+}
+
+gfx::Point ScreenMojo::GetCursorScreenPoint() {
+ NOTIMPLEMENTED();
+ return gfx::Point();
+}
+
+gfx::NativeWindow ScreenMojo::GetWindowUnderCursor() {
+ return GetWindowAtScreenPoint(GetCursorScreenPoint());
+}
+
+gfx::NativeWindow ScreenMojo::GetWindowAtScreenPoint(const gfx::Point& point) {
+ NOTIMPLEMENTED();
+ return NULL;
+}
+
+int ScreenMojo::GetNumDisplays() const {
+ return 1;
+}
+
+std::vector<gfx::Display> ScreenMojo::GetAllDisplays() const {
+ return std::vector<gfx::Display>(1, display_);
+}
+
+gfx::Display ScreenMojo::GetDisplayNearestWindow(
+ gfx::NativeWindow window) const {
+ return display_;
+}
+
+gfx::Display ScreenMojo::GetDisplayNearestPoint(const gfx::Point& point) const {
+ return display_;
+}
+
+gfx::Display ScreenMojo::GetDisplayMatching(const gfx::Rect& match_rect) const {
+ return display_;
+}
+
+gfx::Display ScreenMojo::GetPrimaryDisplay() const {
+ return display_;
+}
+
+void ScreenMojo::AddObserver(gfx::DisplayObserver* observer) {
+}
+
+void ScreenMojo::RemoveObserver(gfx::DisplayObserver* observer) {
+}
+
+ScreenMojo::ScreenMojo(const gfx::Rect& screen_bounds) {
+ static int64 synthesized_display_id = 2000;
+ display_.set_id(synthesized_display_id++);
+ display_.SetScaleAndBounds(1.0f, screen_bounds);
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/aura/screen_mojo.h b/chromium/mojo/aura/screen_mojo.h
new file mode 100644
index 00000000000..ad50c1cee71
--- /dev/null
+++ b/chromium/mojo/aura/screen_mojo.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2014 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.
+
+#ifndef MOJO_AURA_SCREEN_MOJO_H_
+#define MOJO_AURA_SCREEN_MOJO_H_
+
+#include "base/compiler_specific.h"
+#include "ui/gfx/display.h"
+#include "ui/gfx/screen.h"
+
+namespace gfx {
+class Rect;
+class Transform;
+}
+
+namespace mojo {
+
+// A minimal implementation of gfx::Screen for mojo.
+class ScreenMojo : public gfx::Screen {
+ public:
+ static ScreenMojo* Create();
+ virtual ~ScreenMojo();
+
+ protected:
+ // gfx::Screen overrides:
+ virtual bool IsDIPEnabled() OVERRIDE;
+ virtual gfx::Point GetCursorScreenPoint() OVERRIDE;
+ virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE;
+ virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point)
+ OVERRIDE;
+ virtual int GetNumDisplays() const OVERRIDE;
+ virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE;
+ virtual gfx::Display GetDisplayNearestWindow(
+ gfx::NativeView view) const OVERRIDE;
+ virtual gfx::Display GetDisplayNearestPoint(
+ const gfx::Point& point) const OVERRIDE;
+ virtual gfx::Display GetDisplayMatching(
+ const gfx::Rect& match_rect) const OVERRIDE;
+ virtual gfx::Display GetPrimaryDisplay() const OVERRIDE;
+ virtual void AddObserver(gfx::DisplayObserver* observer) OVERRIDE;
+ virtual void RemoveObserver(gfx::DisplayObserver* observer) OVERRIDE;
+
+ private:
+ explicit ScreenMojo(const gfx::Rect& screen_bounds);
+
+ gfx::Display display_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScreenMojo);
+};
+
+} // namespace mojo
+
+#endif // MOJO_AURA_SCREEN_MOJO_H_
diff --git a/chromium/mojo/aura/window_tree_host_mojo.cc b/chromium/mojo/aura/window_tree_host_mojo.cc
new file mode 100644
index 00000000000..75fdb7dff93
--- /dev/null
+++ b/chromium/mojo/aura/window_tree_host_mojo.cc
@@ -0,0 +1,179 @@
+// Copyright 2014 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 "mojo/aura/window_tree_host_mojo.h"
+
+#include <vector>
+
+#include "mojo/aura/window_tree_host_mojo_delegate.h"
+#include "ui/aura/env.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/events/event.h"
+#include "ui/events/event_constants.h"
+
+namespace mojo {
+namespace {
+
+const char kTreeHostsKey[] = "tree_hosts";
+
+typedef std::vector<WindowTreeHostMojo*> Managers;
+
+class TreeHosts : public base::SupportsUserData::Data {
+ public:
+ TreeHosts() {}
+ virtual ~TreeHosts() {}
+
+ static TreeHosts* Get() {
+ TreeHosts* hosts = static_cast<TreeHosts*>(
+ aura::Env::GetInstance()->GetUserData(kTreeHostsKey));
+ if (!hosts) {
+ hosts = new TreeHosts;
+ aura::Env::GetInstance()->SetUserData(kTreeHostsKey, hosts);
+ }
+ return hosts;
+ }
+
+ void Add(WindowTreeHostMojo* manager) {
+ managers_.push_back(manager);
+ }
+
+ void Remove(WindowTreeHostMojo* manager) {
+ Managers::iterator i = std::find(managers_.begin(), managers_.end(),
+ manager);
+ DCHECK(i != managers_.end());
+ managers_.erase(i);
+ }
+
+ const std::vector<WindowTreeHostMojo*> managers() const {
+ return managers_;
+ }
+
+ private:
+ Managers managers_;
+
+ DISALLOW_COPY_AND_ASSIGN(TreeHosts);
+};
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowTreeHostMojo, public:
+
+WindowTreeHostMojo::WindowTreeHostMojo(view_manager::Node* node,
+ WindowTreeHostMojoDelegate* delegate)
+ : node_(node),
+ bounds_(node->bounds()),
+ delegate_(delegate) {
+ node_->AddObserver(this);
+ CreateCompositor(GetAcceleratedWidget());
+
+ TreeHosts::Get()->Add(this);
+}
+
+WindowTreeHostMojo::~WindowTreeHostMojo() {
+ node_->RemoveObserver(this);
+ TreeHosts::Get()->Remove(this);
+ DestroyCompositor();
+ DestroyDispatcher();
+}
+
+// static
+WindowTreeHostMojo* WindowTreeHostMojo::ForCompositor(
+ ui::Compositor* compositor) {
+ const Managers& managers = TreeHosts::Get()->managers();
+ for (size_t i = 0; i < managers.size(); ++i) {
+ if (managers[i]->compositor() == compositor)
+ return managers[i];
+ }
+ return NULL;
+}
+
+void WindowTreeHostMojo::SetContents(const SkBitmap& contents) {
+ delegate_->CompositorContentsChanged(contents);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowTreeHostMojo, aura::WindowTreeHost implementation:
+
+ui::EventSource* WindowTreeHostMojo::GetEventSource() {
+ return this;
+}
+
+gfx::AcceleratedWidget WindowTreeHostMojo::GetAcceleratedWidget() {
+ return gfx::kNullAcceleratedWidget;
+}
+
+void WindowTreeHostMojo::Show() {
+ window()->Show();
+}
+
+void WindowTreeHostMojo::Hide() {
+}
+
+gfx::Rect WindowTreeHostMojo::GetBounds() const {
+ return bounds_;
+}
+
+void WindowTreeHostMojo::SetBounds(const gfx::Rect& bounds) {
+ window()->SetBounds(gfx::Rect(bounds.size()));
+}
+
+gfx::Point WindowTreeHostMojo::GetLocationOnNativeScreen() const {
+ return gfx::Point(0, 0);
+}
+
+void WindowTreeHostMojo::SetCapture() {
+ NOTIMPLEMENTED();
+}
+
+void WindowTreeHostMojo::ReleaseCapture() {
+ NOTIMPLEMENTED();
+}
+
+void WindowTreeHostMojo::PostNativeEvent(
+ const base::NativeEvent& native_event) {
+ NOTIMPLEMENTED();
+}
+
+void WindowTreeHostMojo::OnDeviceScaleFactorChanged(
+ float device_scale_factor) {
+ NOTIMPLEMENTED();
+}
+
+void WindowTreeHostMojo::SetCursorNative(gfx::NativeCursor cursor) {
+ NOTIMPLEMENTED();
+}
+
+void WindowTreeHostMojo::MoveCursorToNative(const gfx::Point& location) {
+ NOTIMPLEMENTED();
+}
+
+void WindowTreeHostMojo::OnCursorVisibilityChangedNative(bool show) {
+ NOTIMPLEMENTED();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowTreeHostMojo, ui::EventSource implementation:
+
+ui::EventProcessor* WindowTreeHostMojo::GetEventProcessor() {
+ return dispatcher();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowTreeHostMojo, view_manager::NodeObserver implementation:
+
+void WindowTreeHostMojo::OnNodeBoundsChange(
+ view_manager::Node* node,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds,
+ view_manager::NodeObserver::DispositionChangePhase phase) {
+ bounds_ = new_bounds;
+ if (old_bounds.origin() != new_bounds.origin())
+ OnHostMoved(bounds_.origin());
+ if (old_bounds.size() != new_bounds.size())
+ OnHostResized(bounds_.size());
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/aura/window_tree_host_mojo.h b/chromium/mojo/aura/window_tree_host_mojo.h
new file mode 100644
index 00000000000..993c4b1a02c
--- /dev/null
+++ b/chromium/mojo/aura/window_tree_host_mojo.h
@@ -0,0 +1,82 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EXAMPLES_AURA_DEMO_WINDOW_TREE_HOST_VIEW_MANAGER_H_
+#define MOJO_EXAMPLES_AURA_DEMO_WINDOW_TREE_HOST_VIEW_MANAGER_H_
+
+#include "mojo/services/public/cpp/view_manager/node_observer.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/events/event_source.h"
+#include "ui/gfx/geometry/rect.h"
+
+class SkBitmap;
+
+namespace ui {
+class Compositor;
+}
+
+namespace mojo {
+
+class WindowTreeHostMojoDelegate;
+
+class WindowTreeHostMojo : public aura::WindowTreeHost,
+ public ui::EventSource,
+ public view_manager::NodeObserver {
+ public:
+ WindowTreeHostMojo(view_manager::Node* node,
+ WindowTreeHostMojoDelegate* delegate);
+ virtual ~WindowTreeHostMojo();
+
+ // Returns the WindowTreeHostMojo for the specified compositor.
+ static WindowTreeHostMojo* ForCompositor(ui::Compositor* compositor);
+
+ const gfx::Rect& bounds() const { return bounds_; }
+
+ // Sets the contents to show in this WindowTreeHost. This forwards to the
+ // delegate.
+ void SetContents(const SkBitmap& contents);
+
+ ui::EventDispatchDetails SendEventToProcessor(ui::Event* event) {
+ return ui::EventSource::SendEventToProcessor(event);
+ }
+
+ private:
+ // WindowTreeHost:
+ virtual ui::EventSource* GetEventSource() OVERRIDE;
+ virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE;
+ virtual void Show() OVERRIDE;
+ virtual void Hide() OVERRIDE;
+ virtual gfx::Rect GetBounds() const OVERRIDE;
+ virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE;
+ virtual gfx::Point GetLocationOnNativeScreen() const OVERRIDE;
+ virtual void SetCapture() OVERRIDE;
+ virtual void ReleaseCapture() OVERRIDE;
+ virtual void PostNativeEvent(const base::NativeEvent& native_event) OVERRIDE;
+ virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE;
+ virtual void SetCursorNative(gfx::NativeCursor cursor) OVERRIDE;
+ virtual void MoveCursorToNative(const gfx::Point& location) OVERRIDE;
+ virtual void OnCursorVisibilityChangedNative(bool show) OVERRIDE;
+
+ // ui::EventSource:
+ virtual ui::EventProcessor* GetEventProcessor() OVERRIDE;
+
+ // view_manager::NodeObserver:
+ virtual void OnNodeBoundsChange(
+ view_manager::Node* node,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds,
+ view_manager::NodeObserver::DispositionChangePhase phase) OVERRIDE;
+
+ view_manager::Node* node_;
+
+ gfx::Rect bounds_;
+
+ WindowTreeHostMojoDelegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowTreeHostMojo);
+};
+
+} // namespace mojo
+
+#endif // MOJO_EXAMPLES_AURA_DEMO_WINDOW_TREE_HOST_VIEW_MANAGER_H_
diff --git a/chromium/mojo/aura/window_tree_host_mojo_delegate.h b/chromium/mojo/aura/window_tree_host_mojo_delegate.h
new file mode 100644
index 00000000000..9ab13b27d25
--- /dev/null
+++ b/chromium/mojo/aura/window_tree_host_mojo_delegate.h
@@ -0,0 +1,24 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EXAMPLES_AURA_DEMO_WINDOW_TREE_HOST_VIEW_MANAGER_DELEGATE_H_
+#define MOJO_EXAMPLES_AURA_DEMO_WINDOW_TREE_HOST_VIEW_MANAGER_DELEGATE_H_
+
+class SkBitmap;
+
+namespace mojo {
+
+class WindowTreeHostMojoDelegate {
+ public:
+ // Invoked when the contents of the composite associated with the
+ // WindowTreeHostMojo are updated.
+ virtual void CompositorContentsChanged(const SkBitmap& bitmap) = 0;
+
+ protected:
+ virtual ~WindowTreeHostMojoDelegate() {}
+};
+
+} // namespace mojo
+
+#endif // MOJO_EXAMPLES_AURA_DEMO_WINDOW_TREE_HOST_VIEW_MANAGER_DELEGATE_H_
diff --git a/chromium/mojo/bindings/js/DEPS b/chromium/mojo/bindings/js/DEPS
new file mode 100644
index 00000000000..d974b6806fc
--- /dev/null
+++ b/chromium/mojo/bindings/js/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+gin",
+ "+v8",
+]
diff --git a/chromium/mojo/bindings/js/codec_unittests.js b/chromium/mojo/bindings/js/codec_unittests.js
new file mode 100644
index 00000000000..6a223ffe89b
--- /dev/null
+++ b/chromium/mojo/bindings/js/codec_unittests.js
@@ -0,0 +1,222 @@
+// Copyright 2013 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.
+
+define([
+ "gin/test/expect",
+ "mojo/public/js/bindings/codec",
+ "mojo/public/interfaces/bindings/tests/sample_service.mojom",
+ ], function(expect, codec, sample) {
+ testBar();
+ testFoo();
+ testTypes();
+ testAlign();
+ testUtf8();
+ this.result = "PASS";
+
+ function testBar() {
+ var bar = new sample.Bar();
+ bar.alpha = 1;
+ bar.beta = 2;
+ bar.gamma = 3;
+ bar.type = 0x08070605;
+ bar.extraProperty = "banana";
+
+ var messageName = 42;
+ var payloadSize = sample.Bar.encodedSize;
+
+ var builder = new codec.MessageBuilder(messageName, payloadSize);
+ builder.encodeStruct(sample.Bar, bar);
+
+ var message = builder.finish();
+
+ var expectedMemory = new Uint8Array([
+ 16, 0, 0, 0,
+ 2, 0, 0, 0,
+ 42, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ 16, 0, 0, 0,
+ 4, 0, 0, 0,
+
+ 1, 2, 3, 0,
+ 5, 6, 7, 8,
+ ]);
+
+ var actualMemory = new Uint8Array(message.buffer.arrayBuffer);
+ expect(actualMemory).toEqual(expectedMemory);
+
+ var reader = new codec.MessageReader(message);
+
+ expect(reader.payloadSize).toBe(payloadSize);
+ expect(reader.messageName).toBe(messageName);
+
+ var bar2 = reader.decodeStruct(sample.Bar);
+
+ expect(bar2.alpha).toBe(bar.alpha);
+ expect(bar2.beta).toBe(bar.beta);
+ expect(bar2.gamma).toBe(bar.gamma);
+ expect("extraProperty" in bar2).toBeFalsy();
+ }
+
+ function testFoo() {
+ var foo = new sample.Foo();
+ foo.x = 0x212B4D5;
+ foo.y = 0x16E93;
+ foo.a = 1;
+ foo.b = 0;
+ foo.c = 3; // This will get truncated to one bit.
+ foo.bar = new sample.Bar();
+ foo.bar.alpha = 91;
+ foo.bar.beta = 82;
+ foo.bar.gamma = 73;
+ foo.data = [
+ 4, 5, 6, 7, 8,
+ ];
+ foo.extra_bars = [
+ new sample.Bar(), new sample.Bar(), new sample.Bar(),
+ ];
+ for (var i = 0; i < foo.extra_bars.length; ++i) {
+ foo.extra_bars[i].alpha = 1 * i;
+ foo.extra_bars[i].beta = 2 * i;
+ foo.extra_bars[i].gamma = 3 * i;
+ }
+ foo.name = "I am a banana";
+ // This is supposed to be a handle, but we fake it with an integer.
+ foo.source = 23423782;
+ foo.array_of_array_of_bools = [
+ [true], [false, true]
+ ];
+
+ var messageName = 31;
+ var payloadSize = 304;
+
+ var builder = new codec.MessageBuilder(messageName, payloadSize);
+ builder.encodeStruct(sample.Foo, foo);
+
+ var message = builder.finish();
+
+ var expectedMemory = new Uint8Array([
+ /* 0: */ 16, 0, 0, 0, 2, 0, 0, 0,
+ /* 8: */ 31, 0, 0, 0, 0, 0, 0, 0,
+ /* 16: */ 88, 0, 0, 0, 14, 0, 0, 0,
+ /* 24: */ 0xD5, 0xB4, 0x12, 0x02, 0x93, 0x6E, 0x01, 0,
+ /* 32: */ 5, 0, 0, 0, 0, 0, 0, 0,
+ /* 40: */ 64, 0, 0, 0, 0, 0, 0, 0,
+ ]);
+ // TODO(abarth): Test more of the message's raw memory.
+ var actualMemory = new Uint8Array(message.buffer.arrayBuffer,
+ 0, expectedMemory.length);
+ expect(actualMemory).toEqual(expectedMemory);
+
+ var expectedHandles = [
+ 23423782,
+ ];
+
+ expect(message.handles).toEqual(expectedHandles);
+
+ var reader = new codec.MessageReader(message);
+
+ expect(reader.payloadSize).toBe(payloadSize);
+ expect(reader.messageName).toBe(messageName);
+
+ var foo2 = reader.decodeStruct(sample.Foo);
+
+ expect(foo2.x).toBe(foo.x);
+ expect(foo2.y).toBe(foo.y);
+
+ expect(foo2.a).toBe(foo.a & 1 ? true : false);
+ expect(foo2.b).toBe(foo.b & 1 ? true : false);
+ expect(foo2.c).toBe(foo.c & 1 ? true : false);
+
+ expect(foo2.bar).toEqual(foo.bar);
+ expect(foo2.data).toEqual(foo.data);
+
+ expect(foo2.extra_bars).toEqual(foo.extra_bars);
+ expect(foo2.name).toBe(foo.name);
+ expect(foo2.source).toEqual(foo.source);
+ }
+
+ function testTypes() {
+ function encodeDecode(cls, input, expectedResult, encodedSize) {
+ var messageName = 42;
+ var payloadSize = encodedSize || cls.encodedSize;
+
+ var builder = new codec.MessageBuilder(messageName, payloadSize);
+ builder.encodeStruct(cls, input)
+ var message = builder.finish();
+
+ var reader = new codec.MessageReader(message);
+ expect(reader.payloadSize).toBe(payloadSize);
+ expect(reader.messageName).toBe(messageName);
+ var result = reader.decodeStruct(cls);
+ expect(result).toEqual(expectedResult);
+ }
+ encodeDecode(codec.String, "banana", "banana", 24);
+ encodeDecode(codec.Int8, -1, -1);
+ encodeDecode(codec.Int8, 0xff, -1);
+ encodeDecode(codec.Int16, -1, -1);
+ encodeDecode(codec.Int16, 0xff, 0xff);
+ encodeDecode(codec.Int16, 0xffff, -1);
+ encodeDecode(codec.Int32, -1, -1);
+ encodeDecode(codec.Int32, 0xffff, 0xffff);
+ encodeDecode(codec.Int32, 0xffffffff, -1);
+ encodeDecode(codec.Float, 1.0, 1.0);
+ encodeDecode(codec.Double, 1.0, 1.0);
+ }
+
+ function testAlign() {
+ var aligned = [
+ 0, // 0
+ 8, // 1
+ 8, // 2
+ 8, // 3
+ 8, // 4
+ 8, // 5
+ 8, // 6
+ 8, // 7
+ 8, // 8
+ 16, // 9
+ 16, // 10
+ 16, // 11
+ 16, // 12
+ 16, // 13
+ 16, // 14
+ 16, // 15
+ 16, // 16
+ 24, // 17
+ 24, // 18
+ 24, // 19
+ 24, // 20
+ ];
+ for (var i = 0; i < aligned.length; ++i)
+ expect(codec.align(i)).toBe(aligned[i]);
+ }
+
+ function testUtf8() {
+ var str = "B\u03ba\u1f79"; // some UCS-2 codepoints
+ var messageName = 42;
+ var payloadSize = 24;
+
+ var builder = new codec.MessageBuilder(messageName, payloadSize);
+ var encoder = builder.createEncoder(8);
+ encoder.encodeStringPointer(str);
+ var message = builder.finish();
+ var expectedMemory = new Uint8Array([
+ /* 0: */ 16, 0, 0, 0, 2, 0, 0, 0,
+ /* 8: */ 42, 0, 0, 0, 0, 0, 0, 0,
+ /* 16: */ 8, 0, 0, 0, 0, 0, 0, 0,
+ /* 24: */ 14, 0, 0, 0, 6, 0, 0, 0,
+ /* 32: */ 0x42, 0xCE, 0xBA, 0xE1, 0xBD, 0xB9, 0, 0,
+ ]);
+ var actualMemory = new Uint8Array(message.buffer.arrayBuffer);
+ expect(actualMemory.length).toEqual(expectedMemory.length);
+ expect(actualMemory).toEqual(expectedMemory);
+
+ var reader = new codec.MessageReader(message);
+ expect(reader.payloadSize).toBe(payloadSize);
+ expect(reader.messageName).toBe(messageName);
+ var str2 = reader.decoder.decodeStringPointer();
+ expect(str2).toEqual(str);
+ }
+});
diff --git a/chromium/mojo/bindings/js/core.cc b/chromium/mojo/bindings/js/core.cc
new file mode 100644
index 00000000000..d6bd2ea68a8
--- /dev/null
+++ b/chromium/mojo/bindings/js/core.cc
@@ -0,0 +1,278 @@
+// Copyright 2014 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 "mojo/bindings/js/core.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "gin/arguments.h"
+#include "gin/array_buffer.h"
+#include "gin/converter.h"
+#include "gin/dictionary.h"
+#include "gin/function_template.h"
+#include "gin/handle.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "gin/public/wrapper_info.h"
+#include "gin/wrappable.h"
+#include "mojo/bindings/js/handle.h"
+
+namespace mojo {
+namespace js {
+
+namespace {
+
+MojoResult CloseHandle(gin::Handle<gin::HandleWrapper> handle) {
+ if (!handle->get().is_valid())
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ handle->Close();
+ return MOJO_RESULT_OK;
+}
+
+MojoResult WaitHandle(mojo::Handle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline) {
+ return MojoWait(handle.value(), signals, deadline);
+}
+
+MojoResult WaitMany(
+ const std::vector<mojo::Handle>& handles,
+ const std::vector<MojoHandleSignals>& signals,
+ MojoDeadline deadline) {
+ return mojo::WaitMany(handles, signals, deadline);
+}
+
+gin::Dictionary CreateMessagePipe(const gin::Arguments& args) {
+ MojoHandle handle0 = MOJO_HANDLE_INVALID;
+ MojoHandle handle1 = MOJO_HANDLE_INVALID;
+ // TODO(vtl): Add support for the options struct.
+ MojoResult result = MojoCreateMessagePipe(NULL, &handle0, &handle1);
+ CHECK(result == MOJO_RESULT_OK);
+
+ gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+ dictionary.Set("handle0", mojo::Handle(handle0));
+ dictionary.Set("handle1", mojo::Handle(handle1));
+ return dictionary;
+}
+
+MojoResult WriteMessage(
+ mojo::Handle handle,
+ const gin::ArrayBufferView& buffer,
+ const std::vector<gin::Handle<gin::HandleWrapper> >& handles,
+ MojoWriteMessageFlags flags) {
+ std::vector<MojoHandle> raw_handles(handles.size());
+ for (size_t i = 0; i < handles.size(); ++i)
+ raw_handles[i] = handles[i]->get().value();
+ MojoResult rv = MojoWriteMessage(handle.value(),
+ buffer.bytes(),
+ static_cast<uint32_t>(buffer.num_bytes()),
+ raw_handles.empty() ? NULL : &raw_handles[0],
+ static_cast<uint32_t>(raw_handles.size()),
+ flags);
+ // MojoWriteMessage takes ownership of the handles upon success, so
+ // release them here.
+ if (rv == MOJO_RESULT_OK) {
+ for (size_t i = 0; i < handles.size(); ++i)
+ ignore_result(handles[i]->release());
+ }
+ return rv;
+}
+
+gin::Dictionary ReadMessage(const gin::Arguments& args,
+ mojo::Handle handle,
+ MojoReadMessageFlags flags) {
+ uint32_t num_bytes = 0;
+ uint32_t num_handles = 0;
+ MojoResult result = MojoReadMessage(
+ handle.value(), NULL, &num_bytes, NULL, &num_handles, flags);
+ if (result != MOJO_RESULT_RESOURCE_EXHAUSTED) {
+ gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+ dictionary.Set("result", result);
+ return dictionary;
+ }
+
+ v8::Handle<v8::ArrayBuffer> array_buffer =
+ v8::ArrayBuffer::New(args.isolate(), num_bytes);
+ std::vector<mojo::Handle> handles(num_handles);
+
+ gin::ArrayBuffer buffer;
+ ConvertFromV8(args.isolate(), array_buffer, &buffer);
+ CHECK(buffer.num_bytes() == num_bytes);
+
+ result = MojoReadMessage(handle.value(),
+ buffer.bytes(),
+ &num_bytes,
+ handles.empty() ? NULL :
+ reinterpret_cast<MojoHandle*>(&handles[0]),
+ &num_handles,
+ flags);
+
+ CHECK(buffer.num_bytes() == num_bytes);
+ CHECK(handles.size() == num_handles);
+
+ gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+ dictionary.Set("result", result);
+ dictionary.Set("buffer", array_buffer);
+ dictionary.Set("handles", handles);
+ return dictionary;
+}
+
+gin::Dictionary CreateDataPipe(const gin::Arguments& args,
+ v8::Handle<v8::Value> options_value) {
+ gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+ dictionary.Set("result", MOJO_RESULT_INVALID_ARGUMENT);
+
+ MojoHandle producer_handle = MOJO_HANDLE_INVALID;
+ MojoHandle consumer_handle = MOJO_HANDLE_INVALID;
+ MojoResult result = MOJO_RESULT_OK;
+
+ if (options_value->IsObject()) {
+ gin::Dictionary options_dict(args.isolate(), options_value->ToObject());
+ MojoCreateDataPipeOptions options;
+ // For future struct_size, we can probably infer that from the presence of
+ // properties in options_dict. For now, it's always 16.
+ options.struct_size = 16;
+ // Ideally these would be optional. But the interface makes it hard to
+ // typecheck them then.
+ if (!options_dict.Get("flags", &options.flags) ||
+ !options_dict.Get("elementNumBytes", &options.element_num_bytes) ||
+ !options_dict.Get("capacityNumBytes", &options.capacity_num_bytes)) {
+ return dictionary;
+ }
+
+ result = MojoCreateDataPipe(&options, &producer_handle, &consumer_handle);
+ } else if (options_value->IsNull() || options_value->IsUndefined()) {
+ result = MojoCreateDataPipe(NULL, &producer_handle, &consumer_handle);
+ } else {
+ return dictionary;
+ }
+
+ CHECK_EQ(MOJO_RESULT_OK, result);
+
+ dictionary.Set("result", result);
+ dictionary.Set("producerHandle", mojo::Handle(producer_handle));
+ dictionary.Set("consumerHandle", mojo::Handle(consumer_handle));
+ return dictionary;
+}
+
+gin::Dictionary WriteData(const gin::Arguments& args,
+ mojo::Handle handle,
+ const gin::ArrayBufferView& buffer,
+ MojoWriteDataFlags flags) {
+ uint32_t num_bytes = static_cast<uint32_t>(buffer.num_bytes());
+ MojoResult result =
+ MojoWriteData(handle.value(), buffer.bytes(), &num_bytes, flags);
+ gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+ dictionary.Set("result", result);
+ dictionary.Set("numBytes", num_bytes);
+ return dictionary;
+}
+
+gin::Dictionary ReadData(const gin::Arguments& args,
+ mojo::Handle handle,
+ MojoReadDataFlags flags) {
+ uint32_t num_bytes = 0;
+ MojoResult result = MojoReadData(
+ handle.value(), NULL, &num_bytes, MOJO_READ_DATA_FLAG_QUERY);
+ if (result != MOJO_RESULT_OK) {
+ gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+ dictionary.Set("result", result);
+ return dictionary;
+ }
+
+ v8::Handle<v8::ArrayBuffer> array_buffer =
+ v8::ArrayBuffer::New(args.isolate(), num_bytes);
+ gin::ArrayBuffer buffer;
+ ConvertFromV8(args.isolate(), array_buffer, &buffer);
+ CHECK_EQ(num_bytes, buffer.num_bytes());
+
+ result = MojoReadData(handle.value(), buffer.bytes(), &num_bytes, flags);
+ CHECK_EQ(num_bytes, buffer.num_bytes());
+
+ gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+ dictionary.Set("result", result);
+ dictionary.Set("buffer", array_buffer);
+ return dictionary;
+}
+
+gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
+
+} // namespace
+
+const char Core::kModuleName[] = "mojo/public/js/bindings/core";
+
+v8::Local<v8::Value> Core::GetModule(v8::Isolate* isolate) {
+ gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
+ v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(
+ &g_wrapper_info);
+
+ if (templ.IsEmpty()) {
+ templ = gin::ObjectTemplateBuilder(isolate)
+ // TODO(mpcomplete): Should these just be methods on the JS Handle
+ // object?
+ .SetMethod("close", CloseHandle)
+ .SetMethod("wait", WaitHandle)
+ .SetMethod("waitMany", WaitMany)
+ .SetMethod("createMessagePipe", CreateMessagePipe)
+ .SetMethod("writeMessage", WriteMessage)
+ .SetMethod("readMessage", ReadMessage)
+ .SetMethod("createDataPipe", CreateDataPipe)
+ .SetMethod("writeData", WriteData)
+ .SetMethod("readData", ReadData)
+
+ .SetValue("RESULT_OK", MOJO_RESULT_OK)
+ .SetValue("RESULT_CANCELLED", MOJO_RESULT_CANCELLED)
+ .SetValue("RESULT_UNKNOWN", MOJO_RESULT_UNKNOWN)
+ .SetValue("RESULT_INVALID_ARGUMENT", MOJO_RESULT_INVALID_ARGUMENT)
+ .SetValue("RESULT_DEADLINE_EXCEEDED", MOJO_RESULT_DEADLINE_EXCEEDED)
+ .SetValue("RESULT_NOT_FOUND", MOJO_RESULT_NOT_FOUND)
+ .SetValue("RESULT_ALREADY_EXISTS", MOJO_RESULT_ALREADY_EXISTS)
+ .SetValue("RESULT_PERMISSION_DENIED", MOJO_RESULT_PERMISSION_DENIED)
+ .SetValue("RESULT_RESOURCE_EXHAUSTED", MOJO_RESULT_RESOURCE_EXHAUSTED)
+ .SetValue("RESULT_FAILED_PRECONDITION", MOJO_RESULT_FAILED_PRECONDITION)
+ .SetValue("RESULT_ABORTED", MOJO_RESULT_ABORTED)
+ .SetValue("RESULT_OUT_OF_RANGE", MOJO_RESULT_OUT_OF_RANGE)
+ .SetValue("RESULT_UNIMPLEMENTED", MOJO_RESULT_UNIMPLEMENTED)
+ .SetValue("RESULT_INTERNAL", MOJO_RESULT_INTERNAL)
+ .SetValue("RESULT_UNAVAILABLE", MOJO_RESULT_UNAVAILABLE)
+ .SetValue("RESULT_DATA_LOSS", MOJO_RESULT_DATA_LOSS)
+ .SetValue("RESULT_BUSY", MOJO_RESULT_BUSY)
+ .SetValue("RESULT_SHOULD_WAIT", MOJO_RESULT_SHOULD_WAIT)
+
+ .SetValue("DEADLINE_INDEFINITE", MOJO_DEADLINE_INDEFINITE)
+
+ .SetValue("HANDLE_SIGNAL_NONE", MOJO_HANDLE_SIGNAL_NONE)
+ .SetValue("HANDLE_SIGNAL_READABLE", MOJO_HANDLE_SIGNAL_READABLE)
+ .SetValue("HANDLE_SIGNAL_READABLE", MOJO_HANDLE_SIGNAL_READABLE)
+
+ .SetValue("WRITE_MESSAGE_FLAG_NONE", MOJO_WRITE_MESSAGE_FLAG_NONE)
+
+ .SetValue("READ_MESSAGE_FLAG_NONE", MOJO_READ_MESSAGE_FLAG_NONE)
+ .SetValue("READ_MESSAGE_FLAG_MAY_DISCARD",
+ MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)
+
+ .SetValue("CREATE_DATA_PIPE_OPTIONS_FLAG_NONE",
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE)
+ .SetValue("CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD",
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD)
+
+ .SetValue("WRITE_DATA_FLAG_NONE", MOJO_WRITE_DATA_FLAG_NONE)
+ .SetValue("WRITE_DATA_FLAG_ALL_OR_NONE",
+ MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)
+
+ .SetValue("READ_DATA_FLAG_NONE", MOJO_READ_DATA_FLAG_NONE)
+ .SetValue("READ_DATA_FLAG_ALL_OR_NONE",
+ MOJO_READ_DATA_FLAG_ALL_OR_NONE)
+ .SetValue("READ_DATA_FLAG_DISCARD", MOJO_READ_DATA_FLAG_DISCARD)
+ .SetValue("READ_DATA_FLAG_QUERY", MOJO_READ_DATA_FLAG_QUERY)
+ .Build();
+
+ data->SetObjectTemplate(&g_wrapper_info, templ);
+ }
+
+ return templ->NewInstance();
+}
+
+} // namespace js
+} // namespace mojo
diff --git a/chromium/mojo/bindings/js/core.h b/chromium/mojo/bindings/js/core.h
new file mode 100644
index 00000000000..bde327ce5ad
--- /dev/null
+++ b/chromium/mojo/bindings/js/core.h
@@ -0,0 +1,22 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_BINDINGS_JS_CORE_H_
+#define MOJO_BINDINGS_JS_CORE_H_
+
+#include "v8/include/v8.h"
+
+namespace mojo {
+namespace js {
+
+class Core {
+ public:
+ static const char kModuleName[];
+ static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+};
+
+} // namespace js
+} // namespace mojo
+
+#endif // MOJO_BINDINGS_JS_CORE_H_
diff --git a/chromium/mojo/bindings/js/core_unittests.js b/chromium/mojo/bindings/js/core_unittests.js
new file mode 100644
index 00000000000..20446dbc260
--- /dev/null
+++ b/chromium/mojo/bindings/js/core_unittests.js
@@ -0,0 +1,91 @@
+// Copyright 2013 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.
+
+define([
+ "gin/test/expect",
+ "mojo/public/js/bindings/core",
+ "gc",
+ ], function(expect, core, gc) {
+ runWithMessagePipe(testNop);
+ runWithMessagePipe(testReadAndWriteMessage);
+ runWithDataPipe(testNop);
+ runWithDataPipe(testReadAndWriteDataPipe);
+ gc.collectGarbage(); // should not crash
+ this.result = "PASS";
+
+ function runWithMessagePipe(test) {
+ var pipe = core.createMessagePipe();
+
+ test(pipe);
+
+ expect(core.close(pipe.handle0)).toBe(core.RESULT_OK);
+ expect(core.close(pipe.handle1)).toBe(core.RESULT_OK);
+ }
+
+ function runWithDataPipe(test) {
+ var pipe = core.createDataPipe({
+ flags: core.CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
+ elementNumBytes: 1,
+ capacityNumBytes: 64
+ });
+ expect(pipe.result).toBe(core.RESULT_OK);
+
+ test(pipe);
+
+ expect(core.close(pipe.producerHandle)).toBe(core.RESULT_OK);
+ expect(core.close(pipe.consumerHandle)).toBe(core.RESULT_OK);
+ }
+
+ function testNop(pipe) {
+ }
+
+ function testReadAndWriteMessage(pipe) {
+ var senderData = new Uint8Array(42);
+ for (var i = 0; i < senderData.length; ++i) {
+ senderData[i] = i * i;
+ }
+
+ var result = core.writeMessage(
+ pipe.handle0, senderData, [],
+ core.WRITE_MESSAGE_FLAG_NONE);
+
+ expect(result).toBe(core.RESULT_OK);
+
+ var read = core.readMessage(
+ pipe.handle1, core.READ_MESSAGE_FLAG_NONE);
+
+ expect(read.result).toBe(core.RESULT_OK);
+ expect(read.buffer.byteLength).toBe(42);
+ expect(read.handles.length).toBe(0);
+
+ var memory = new Uint8Array(read.buffer);
+ for (var i = 0; i < memory.length; ++i)
+ expect(memory[i]).toBe((i * i) & 0xFF);
+ }
+
+ function testReadAndWriteDataPipe(pipe) {
+ var senderData = new Uint8Array(42);
+ for (var i = 0; i < senderData.length; ++i) {
+ senderData[i] = i * i;
+ }
+
+ var write = core.writeData(
+ pipe.producerHandle, senderData,
+ core.WRITE_DATA_FLAG_ALL_OR_NONE);
+
+ expect(write.result).toBe(core.RESULT_OK);
+ expect(write.numBytes).toBe(42);
+
+ var read = core.readData(
+ pipe.consumerHandle, core.READ_DATA_FLAG_ALL_OR_NONE);
+
+ expect(read.result).toBe(core.RESULT_OK);
+ expect(read.buffer.byteLength).toBe(42);
+
+ var memory = new Uint8Array(read.buffer);
+ for (var i = 0; i < memory.length; ++i)
+ expect(memory[i]).toBe((i * i) & 0xFF);
+ }
+
+});
diff --git a/chromium/mojo/bindings/js/handle.cc b/chromium/mojo/bindings/js/handle.cc
new file mode 100644
index 00000000000..e1d0fb04b60
--- /dev/null
+++ b/chromium/mojo/bindings/js/handle.cc
@@ -0,0 +1,41 @@
+// Copyright 2014 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 "mojo/bindings/js/handle.h"
+
+namespace gin {
+
+gin::WrapperInfo HandleWrapper::kWrapperInfo = { gin::kEmbedderNativeGin };
+
+HandleWrapper::HandleWrapper(MojoHandle handle)
+ : handle_(mojo::Handle(handle)) {
+}
+
+HandleWrapper::~HandleWrapper() {
+}
+
+v8::Handle<v8::Value> Converter<mojo::Handle>::ToV8(v8::Isolate* isolate,
+ const mojo::Handle& val) {
+ if (!val.is_valid())
+ return v8::Null(isolate);
+ return HandleWrapper::Create(isolate, val.value()).ToV8();
+}
+
+bool Converter<mojo::Handle>::FromV8(v8::Isolate* isolate,
+ v8::Handle<v8::Value> val,
+ mojo::Handle* out) {
+ if (val->IsNull()) {
+ *out = mojo::Handle();
+ return true;
+ }
+
+ gin::Handle<HandleWrapper> handle;
+ if (!Converter<gin::Handle<HandleWrapper> >::FromV8(isolate, val, &handle))
+ return false;
+
+ *out = handle->get();
+ return true;
+}
+
+} // namespace gin
diff --git a/chromium/mojo/bindings/js/handle.h b/chromium/mojo/bindings/js/handle.h
new file mode 100644
index 00000000000..35202b07ea6
--- /dev/null
+++ b/chromium/mojo/bindings/js/handle.h
@@ -0,0 +1,75 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_BINDINGS_JS_HANDLE_H_
+#define MOJO_BINDINGS_JS_HANDLE_H_
+
+#include "gin/converter.h"
+#include "gin/handle.h"
+#include "gin/wrappable.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace gin {
+
+// Wrapper for mojo Handles exposed to JavaScript. This ensures the Handle
+// is Closed when its JS object is garbage collected.
+class HandleWrapper : public gin::Wrappable<HandleWrapper> {
+ public:
+ static gin::WrapperInfo kWrapperInfo;
+
+ static gin::Handle<HandleWrapper> Create(v8::Isolate* isolate,
+ MojoHandle handle) {
+ return gin::CreateHandle(isolate, new HandleWrapper(handle));
+ }
+
+ mojo::Handle get() const { return handle_.get(); }
+ mojo::Handle release() { return handle_.release(); }
+ void Close() { handle_.reset(); }
+
+ protected:
+ HandleWrapper(MojoHandle handle);
+ virtual ~HandleWrapper();
+ mojo::ScopedHandle handle_;
+};
+
+// Note: It's important to use this converter rather than the one for
+// MojoHandle, since that will do a simple int32 conversion. It's unfortunate
+// there's no way to prevent against accidental use.
+// TODO(mpcomplete): define converters for all Handle subtypes.
+template<>
+struct Converter<mojo::Handle> {
+ static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
+ const mojo::Handle& val);
+ static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
+ mojo::Handle* out);
+};
+
+// We need to specialize the normal gin::Handle converter in order to handle
+// converting |null| to a wrapper for an empty mojo::Handle.
+template<>
+struct Converter<gin::Handle<gin::HandleWrapper> > {
+ static v8::Handle<v8::Value> ToV8(
+ v8::Isolate* isolate, const gin::Handle<gin::HandleWrapper>& val) {
+ return val.ToV8();
+ }
+
+ static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val,
+ gin::Handle<gin::HandleWrapper>* out) {
+ if (val->IsNull()) {
+ *out = HandleWrapper::Create(isolate, MOJO_HANDLE_INVALID);
+ return true;
+ }
+
+ gin::HandleWrapper* object = NULL;
+ if (!Converter<gin::HandleWrapper*>::FromV8(isolate, val, &object)) {
+ return false;
+ }
+ *out = gin::Handle<gin::HandleWrapper>(val, object);
+ return true;
+ }
+};
+
+} // namespace gin
+
+#endif // MOJO_BINDINGS_JS_HANDLE_H_
diff --git a/chromium/mojo/bindings/js/run_js_tests.cc b/chromium/mojo/bindings/js/run_js_tests.cc
new file mode 100644
index 00000000000..976a0b17591
--- /dev/null
+++ b/chromium/mojo/bindings/js/run_js_tests.cc
@@ -0,0 +1,64 @@
+// Copyright 2014 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 "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "gin/modules/console.h"
+#include "gin/modules/module_registry.h"
+#include "gin/modules/timer.h"
+#include "gin/test/file_runner.h"
+#include "gin/test/gtest.h"
+#include "mojo/bindings/js/core.h"
+#include "mojo/common/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace js {
+namespace {
+
+class TestRunnerDelegate : public gin::FileRunnerDelegate {
+ public:
+ TestRunnerDelegate() {
+ AddBuiltinModule(gin::Console::kModuleName, gin::Console::GetModule);
+ AddBuiltinModule(Core::kModuleName, Core::GetModule);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestRunnerDelegate);
+};
+
+void RunTest(std::string test, bool run_until_idle) {
+ base::FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ path = path.AppendASCII("mojo")
+ .AppendASCII("bindings")
+ .AppendASCII("js")
+ .AppendASCII(test);
+ TestRunnerDelegate delegate;
+ gin::RunTestFromFile(path, &delegate, run_until_idle);
+}
+
+// TODO(abarth): Should we autogenerate these stubs from GYP?
+TEST(JSTest, core) {
+ RunTest("core_unittests.js", true);
+}
+
+TEST(JSTest, codec) {
+ // TODO(yzshen): Remove this check once isolated tests are supported on the
+ // Chromium waterfall. (http://crbug.com/351214)
+ const base::FilePath test_file_path(
+ test::GetFilePathForJSResource(
+ "mojo/public/interfaces/bindings/tests/sample_service.mojom"));
+ if (!base::PathExists(test_file_path)) {
+ LOG(WARNING) << "Mojom binding files don't exist. Skipping the test.";
+ return;
+ }
+
+ RunTest("codec_unittests.js", true);
+}
+
+} // namespace
+} // namespace js
+} // namespace mojo
diff --git a/chromium/mojo/bindings/js/support.cc b/chromium/mojo/bindings/js/support.cc
new file mode 100644
index 00000000000..dc97865cca7
--- /dev/null
+++ b/chromium/mojo/bindings/js/support.cc
@@ -0,0 +1,59 @@
+// Copyright 2014 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 "mojo/bindings/js/support.h"
+
+#include "base/bind.h"
+#include "gin/arguments.h"
+#include "gin/converter.h"
+#include "gin/function_template.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "gin/public/wrapper_info.h"
+#include "gin/wrappable.h"
+#include "mojo/bindings/js/handle.h"
+#include "mojo/bindings/js/waiting_callback.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace js {
+
+namespace {
+
+WaitingCallback* AsyncWait(const gin::Arguments& args, mojo::Handle handle,
+ MojoHandleSignals signals,
+ v8::Handle<v8::Function> callback) {
+ return WaitingCallback::Create(args.isolate(), callback, handle, signals)
+ .get();
+}
+
+void CancelWait(WaitingCallback* waiting_callback) {
+ waiting_callback->Cancel();
+}
+
+gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
+
+} // namespace
+
+const char Support::kModuleName[] = "mojo/public/js/bindings/support";
+
+v8::Local<v8::Value> Support::GetModule(v8::Isolate* isolate) {
+ gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
+ v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(
+ &g_wrapper_info);
+
+ if (templ.IsEmpty()) {
+ templ = gin::ObjectTemplateBuilder(isolate)
+ .SetMethod("asyncWait", AsyncWait)
+ .SetMethod("cancelWait", CancelWait)
+ .Build();
+
+ data->SetObjectTemplate(&g_wrapper_info, templ);
+ }
+
+ return templ->NewInstance();
+}
+
+} // namespace js
+} // namespace mojo
diff --git a/chromium/mojo/bindings/js/support.h b/chromium/mojo/bindings/js/support.h
new file mode 100644
index 00000000000..0f6eb07c2b1
--- /dev/null
+++ b/chromium/mojo/bindings/js/support.h
@@ -0,0 +1,22 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_BINDINGS_JS_SUPPORT_H_
+#define MOJO_BINDINGS_JS_SUPPORT_H_
+
+#include "v8/include/v8.h"
+
+namespace mojo {
+namespace js {
+
+class Support {
+ public:
+ static const char kModuleName[];
+ static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+};
+
+} // namespace js
+} // namespace mojo
+
+#endif // MOJO_BINDINGS_JS_SUPPORT_H_
diff --git a/chromium/mojo/bindings/js/waiting_callback.cc b/chromium/mojo/bindings/js/waiting_callback.cc
new file mode 100644
index 00000000000..692551bb0e7
--- /dev/null
+++ b/chromium/mojo/bindings/js/waiting_callback.cc
@@ -0,0 +1,84 @@
+// Copyright 2014 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 "mojo/bindings/js/waiting_callback.h"
+
+#include "gin/per_context_data.h"
+#include "mojo/public/cpp/environment/environment.h"
+
+namespace mojo {
+namespace js {
+
+namespace {
+
+v8::Handle<v8::String> GetHiddenPropertyName(v8::Isolate* isolate) {
+ return gin::StringToSymbol(isolate, "::mojo::js::WaitingCallback");
+}
+
+} // namespace
+
+gin::WrapperInfo WaitingCallback::kWrapperInfo = { gin::kEmbedderNativeGin };
+
+// static
+gin::Handle<WaitingCallback> WaitingCallback::Create(
+ v8::Isolate* isolate,
+ v8::Handle<v8::Function> callback,
+ mojo::Handle handle,
+ MojoHandleSignals signals) {
+ gin::Handle<WaitingCallback> waiting_callback =
+ gin::CreateHandle(isolate, new WaitingCallback(isolate, callback));
+ waiting_callback->wait_id_ = Environment::GetDefaultAsyncWaiter()->AsyncWait(
+ handle.value(),
+ signals,
+ MOJO_DEADLINE_INDEFINITE,
+ &WaitingCallback::CallOnHandleReady,
+ waiting_callback.get());
+ return waiting_callback;
+}
+
+void WaitingCallback::Cancel() {
+ if (!wait_id_)
+ return;
+
+ Environment::GetDefaultAsyncWaiter()->CancelWait(wait_id_);
+ wait_id_ = 0;
+}
+
+WaitingCallback::WaitingCallback(v8::Isolate* isolate,
+ v8::Handle<v8::Function> callback)
+ : wait_id_() {
+ v8::Handle<v8::Context> context = isolate->GetCurrentContext();
+ runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr();
+ GetWrapper(isolate)->SetHiddenValue(GetHiddenPropertyName(isolate), callback);
+}
+
+WaitingCallback::~WaitingCallback() {
+ Cancel();
+}
+
+// static
+void WaitingCallback::CallOnHandleReady(void* closure, MojoResult result) {
+ static_cast<WaitingCallback*>(closure)->OnHandleReady(result);
+}
+
+void WaitingCallback::OnHandleReady(MojoResult result) {
+ wait_id_ = 0;
+
+ if (!runner_)
+ return;
+
+ gin::Runner::Scope scope(runner_.get());
+ v8::Isolate* isolate = runner_->GetContextHolder()->isolate();
+
+ v8::Handle<v8::Value> hidden_value =
+ GetWrapper(isolate)->GetHiddenValue(GetHiddenPropertyName(isolate));
+ v8::Handle<v8::Function> callback;
+ CHECK(gin::ConvertFromV8(isolate, hidden_value, &callback));
+
+ v8::Handle<v8::Value> args[] = { gin::ConvertToV8(isolate, result) };
+ runner_->Call(callback, runner_->global(), 1, args);
+}
+
+} // namespace js
+} // namespace mojo
diff --git a/chromium/mojo/bindings/js/waiting_callback.h b/chromium/mojo/bindings/js/waiting_callback.h
new file mode 100644
index 00000000000..973a500cdf0
--- /dev/null
+++ b/chromium/mojo/bindings/js/waiting_callback.h
@@ -0,0 +1,52 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_BINDINGS_JS_WAITING_CALLBACK_H_
+#define MOJO_BINDINGS_JS_WAITING_CALLBACK_H_
+
+#include "gin/handle.h"
+#include "gin/runner.h"
+#include "gin/wrappable.h"
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace js {
+
+class WaitingCallback : public gin::Wrappable<WaitingCallback> {
+ public:
+ static gin::WrapperInfo kWrapperInfo;
+
+ // Creates a new WaitingCallback.
+ static gin::Handle<WaitingCallback> Create(
+ v8::Isolate* isolate,
+ v8::Handle<v8::Function> callback,
+ mojo::Handle handle,
+ MojoHandleSignals signals);
+
+ // Cancels the callback. Does nothing if a callback is not pending. This is
+ // implicitly invoked from the destructor but can be explicitly invoked as
+ // necessary.
+ void Cancel();
+
+ private:
+ WaitingCallback(v8::Isolate* isolate, v8::Handle<v8::Function> callback);
+ virtual ~WaitingCallback();
+
+ // Callback from MojoAsyncWaiter. |closure| is the WaitingCallback.
+ static void CallOnHandleReady(void* closure, MojoResult result);
+
+ // Invoked from CallOnHandleReady() (CallOnHandleReady() must be static).
+ void OnHandleReady(MojoResult result);
+
+ base::WeakPtr<gin::Runner> runner_;
+ MojoAsyncWaitID wait_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(WaitingCallback);
+};
+
+} // namespace js
+} // namespace mojo
+
+#endif // MOJO_BINDINGS_JS_WAITING_CALLBACK_H_
diff --git a/chromium/mojo/cc/DEPS b/chromium/mojo/cc/DEPS
new file mode 100644
index 00000000000..1d0b8872407
--- /dev/null
+++ b/chromium/mojo/cc/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+cc",
+]
diff --git a/chromium/mojo/cc/context_provider_mojo.cc b/chromium/mojo/cc/context_provider_mojo.cc
new file mode 100644
index 00000000000..7356b587c40
--- /dev/null
+++ b/chromium/mojo/cc/context_provider_mojo.cc
@@ -0,0 +1,60 @@
+// Copyright 2014 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 "mojo/cc/context_provider_mojo.h"
+
+#include "base/logging.h"
+
+namespace mojo {
+
+ContextProviderMojo::ContextProviderMojo(
+ ScopedMessagePipeHandle command_buffer_handle)
+ : command_buffer_handle_(command_buffer_handle.Pass()) {}
+
+bool ContextProviderMojo::BindToCurrentThread() {
+ DCHECK(command_buffer_handle_.is_valid());
+ context_ = MojoGLES2CreateContext(
+ command_buffer_handle_.release().value(),
+ &ContextLostThunk,
+ NULL,
+ this);
+ return !!context_;
+}
+
+gpu::gles2::GLES2Interface* ContextProviderMojo::ContextGL() {
+ if (!context_)
+ return NULL;
+ return static_cast<gpu::gles2::GLES2Interface*>(
+ MojoGLES2GetGLES2Interface(context_));
+}
+
+gpu::ContextSupport* ContextProviderMojo::ContextSupport() {
+ if (!context_)
+ return NULL;
+ return static_cast<gpu::ContextSupport*>(
+ MojoGLES2GetContextSupport(context_));
+}
+
+class GrContext* ContextProviderMojo::GrContext() { return NULL; }
+
+cc::ContextProvider::Capabilities ContextProviderMojo::ContextCapabilities() {
+ return capabilities_;
+}
+
+bool ContextProviderMojo::IsContextLost() { return !context_; }
+bool ContextProviderMojo::DestroyedOnMainThread() { return !context_; }
+
+ContextProviderMojo::~ContextProviderMojo() {
+ if (context_)
+ MojoGLES2DestroyContext(context_);
+}
+
+void ContextProviderMojo::ContextLost() {
+ if (context_) {
+ MojoGLES2DestroyContext(context_);
+ context_ = NULL;
+ }
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/cc/context_provider_mojo.h b/chromium/mojo/cc/context_provider_mojo.h
new file mode 100644
index 00000000000..eda6e43b2e6
--- /dev/null
+++ b/chromium/mojo/cc/context_provider_mojo.h
@@ -0,0 +1,51 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_CC_CONTEXT_PROVIDER_MOJO_H_
+#define MOJO_CC_CONTEXT_PROVIDER_MOJO_H_
+
+#include "cc/output/context_provider.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+class ContextProviderMojo : public cc::ContextProvider {
+ public:
+ explicit ContextProviderMojo(ScopedMessagePipeHandle command_buffer_handle);
+
+ // cc::ContextProvider implementation.
+ virtual bool BindToCurrentThread() OVERRIDE;
+ virtual gpu::gles2::GLES2Interface* ContextGL() OVERRIDE;
+ virtual gpu::ContextSupport* ContextSupport() OVERRIDE;
+ virtual class GrContext* GrContext() OVERRIDE;
+ virtual Capabilities ContextCapabilities() OVERRIDE;
+ virtual bool IsContextLost() OVERRIDE;
+ virtual void VerifyContexts() OVERRIDE {}
+ virtual void DeleteCachedResources() OVERRIDE {}
+ virtual bool DestroyedOnMainThread() OVERRIDE;
+ virtual void SetLostContextCallback(
+ const LostContextCallback& lost_context_callback) OVERRIDE {}
+ virtual void SetMemoryPolicyChangedCallback(
+ const MemoryPolicyChangedCallback& memory_policy_changed_callback)
+ OVERRIDE {}
+
+ protected:
+ friend class base::RefCountedThreadSafe<ContextProviderMojo>;
+ virtual ~ContextProviderMojo();
+
+ private:
+ static void ContextLostThunk(void* closure) {
+ static_cast<ContextProviderMojo*>(closure)->ContextLost();
+ }
+ void ContextLost();
+
+ cc::ContextProvider::Capabilities capabilities_;
+ ScopedMessagePipeHandle command_buffer_handle_;
+ MojoGLES2Context context_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_CC_CONTEXT_PROVIDER_MOJO_H_
diff --git a/chromium/mojo/common/BUILD.gn b/chromium/mojo/common/BUILD.gn
new file mode 100644
index 00000000000..79610b9d190
--- /dev/null
+++ b/chromium/mojo/common/BUILD.gn
@@ -0,0 +1,30 @@
+# Copyright 2014 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.
+
+# GYP version: mojo/mojo.gyp:mojo_common_lib
+component("common") {
+ output_name = "mojo_common_lib"
+
+ sources = [
+ "common_type_converters.cc",
+ "common_type_converters.h",
+ "data_pipe_utils.cc",
+ "data_pipe_utils.h",
+ "handle_watcher.cc",
+ "handle_watcher.h",
+ "message_pump_mojo.cc",
+ "message_pump_mojo.h",
+ "message_pump_mojo_handler.h",
+ "time_helper.cc",
+ "time_helper.h",
+ ]
+
+ defines = [ "MOJO_COMMON_IMPLEMENTATION" ]
+
+ deps = [
+ "//base",
+ "//base/third_party/dynamic_annotations",
+ "//mojo/system",
+ ]
+}
diff --git a/chromium/mojo/common/DEPS b/chromium/mojo/common/DEPS
new file mode 100644
index 00000000000..e8ac42887d1
--- /dev/null
+++ b/chromium/mojo/common/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ # common must not depend on embedder.
+ "-mojo",
+ "+mojo/common",
+ "+mojo/public",
+]
diff --git a/chromium/mojo/common/common_type_converters.cc b/chromium/mojo/common/common_type_converters.cc
new file mode 100644
index 00000000000..a82aa51cda1
--- /dev/null
+++ b/chromium/mojo/common/common_type_converters.cc
@@ -0,0 +1,41 @@
+// Copyright 2013 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 "mojo/common/common_type_converters.h"
+
+#include <string>
+
+#include "base/strings/utf_string_conversions.h"
+
+namespace mojo {
+
+// static
+String TypeConverter<String, base::StringPiece>::ConvertFrom(
+ const base::StringPiece& input) {
+ if (input.empty()) {
+ char c = 0;
+ return String(&c, 0);
+ }
+ return String(input.data(), input.size());
+}
+// static
+base::StringPiece TypeConverter<String, base::StringPiece>::ConvertTo(
+ const String& input) {
+ return input.get();
+}
+
+// static
+String TypeConverter<String, base::string16>::ConvertFrom(
+ const base::string16& input) {
+ return TypeConverter<String, base::StringPiece>::ConvertFrom(
+ base::UTF16ToUTF8(input));
+}
+// static
+base::string16 TypeConverter<String, base::string16>::ConvertTo(
+ const String& input) {
+ return base::UTF8ToUTF16(TypeConverter<String, base::StringPiece>::ConvertTo(
+ input));
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/common/common_type_converters.h b/chromium/mojo/common/common_type_converters.h
new file mode 100644
index 00000000000..a75e734a9f1
--- /dev/null
+++ b/chromium/mojo/common/common_type_converters.h
@@ -0,0 +1,32 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_COMMON_COMMON_TYPE_CONVERTERS_H_
+#define MOJO_COMMON_COMMON_TYPE_CONVERTERS_H_
+
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "mojo/common/mojo_common_export.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+namespace mojo {
+
+template <>
+class MOJO_COMMON_EXPORT TypeConverter<String, base::StringPiece> {
+ public:
+ static String ConvertFrom(const base::StringPiece& input);
+ static base::StringPiece ConvertTo(const String& input);
+};
+
+template <>
+class MOJO_COMMON_EXPORT TypeConverter<String, base::string16> {
+ public:
+ static String ConvertFrom(const base::string16& input);
+ static base::string16 ConvertTo(const String& input);
+};
+
+} // namespace mojo
+
+#endif // MOJO_COMMON_COMMON_TYPE_CONVERTERS_H_
diff --git a/chromium/mojo/common/common_type_converters_unittest.cc b/chromium/mojo/common/common_type_converters_unittest.cc
new file mode 100644
index 00000000000..47771393260
--- /dev/null
+++ b/chromium/mojo/common/common_type_converters_unittest.cc
@@ -0,0 +1,72 @@
+// Copyright 2013 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 "mojo/common/common_type_converters.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace common {
+namespace test {
+namespace {
+
+void ExpectEqualsStringPiece(const std::string& expected,
+ const base::StringPiece& str) {
+ EXPECT_EQ(expected, str.as_string());
+}
+
+void ExpectEqualsMojoString(const std::string& expected,
+ const String& str) {
+ EXPECT_EQ(expected, str.get());
+}
+
+void ExpectEqualsString16(const base::string16& expected,
+ const base::string16& actual) {
+ EXPECT_EQ(expected, actual);
+}
+
+void ExpectEqualsMojoString(const base::string16& expected,
+ const String& str) {
+ EXPECT_EQ(expected, str.To<base::string16>());
+}
+
+} // namespace
+
+TEST(CommonTypeConvertersTest, StringPiece) {
+ std::string kText("hello world");
+
+ base::StringPiece string_piece(kText);
+ String mojo_string(String::From(string_piece));
+
+ ExpectEqualsMojoString(kText, mojo_string);
+ ExpectEqualsStringPiece(kText, mojo_string.To<base::StringPiece>());
+
+ // Test implicit construction and conversion:
+ ExpectEqualsMojoString(kText, String::From(string_piece));
+ ExpectEqualsStringPiece(kText, mojo_string.To<base::StringPiece>());
+
+ // Test null String:
+ base::StringPiece empty_string_piece = String().To<base::StringPiece>();
+ EXPECT_TRUE(empty_string_piece.empty());
+}
+
+TEST(CommonTypeConvertersTest, String16) {
+ const base::string16 string16(base::ASCIIToUTF16("hello world"));
+ const String mojo_string(String::From(string16));
+
+ ExpectEqualsMojoString(string16, mojo_string);
+ EXPECT_EQ(string16, mojo_string.To<base::string16>());
+
+ // Test implicit construction and conversion:
+ ExpectEqualsMojoString(string16, String::From(string16));
+ ExpectEqualsString16(string16, mojo_string.To<base::string16>());
+
+ // Test empty string conversion.
+ ExpectEqualsMojoString(base::string16(), String::From(base::string16()));
+}
+
+} // namespace test
+} // namespace common
+} // namespace mojo
diff --git a/chromium/mojo/common/data_pipe_utils.cc b/chromium/mojo/common/data_pipe_utils.cc
new file mode 100644
index 00000000000..cf9dc78c30d
--- /dev/null
+++ b/chromium/mojo/common/data_pipe_utils.cc
@@ -0,0 +1,72 @@
+// Copyright 2014 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 "mojo/common/data_pipe_utils.h"
+
+#include <stdio.h>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "base/message_loop/message_loop.h"
+#include "base/task_runner_util.h"
+#include "mojo/common/handle_watcher.h"
+
+namespace mojo {
+namespace common {
+
+bool BlockingCopyToFile(ScopedDataPipeConsumerHandle source,
+ const base::FilePath& destination) {
+ base::ScopedFILE fp(base::OpenFile(destination, "wb"));
+ if (!fp)
+ return false;
+
+ for (;;) {
+ const void* buffer;
+ uint32_t num_bytes;
+ MojoResult result = BeginReadDataRaw(source.get(), &buffer, &num_bytes,
+ MOJO_READ_DATA_FLAG_NONE);
+ if (result == MOJO_RESULT_OK) {
+ size_t bytes_written = fwrite(buffer, 1, num_bytes, fp.get());
+ result = EndReadDataRaw(source.get(), num_bytes);
+ if (bytes_written < num_bytes || result != MOJO_RESULT_OK)
+ return false;
+ } else if (result == MOJO_RESULT_SHOULD_WAIT) {
+ result = Wait(source.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE);
+ if (result != MOJO_RESULT_OK) {
+ // If the producer handle was closed, then treat as EOF.
+ return result == MOJO_RESULT_FAILED_PRECONDITION;
+ }
+ } else if (result == MOJO_RESULT_FAILED_PRECONDITION) {
+ // If the producer handle was closed, then treat as EOF.
+ return true;
+ } else {
+ // Some other error occurred.
+ break;
+ }
+ }
+
+ return false;
+}
+
+void CompleteBlockingCopyToFile(const base::Callback<void(bool)>& callback,
+ bool result) {
+ callback.Run(result);
+}
+
+void CopyToFile(ScopedDataPipeConsumerHandle source,
+ const base::FilePath& destination,
+ base::TaskRunner* task_runner,
+ const base::Callback<void(bool)>& callback) {
+ base::PostTaskAndReplyWithResult(
+ task_runner,
+ FROM_HERE,
+ base::Bind(&BlockingCopyToFile, base::Passed(&source), destination),
+ callback);
+}
+
+} // namespace common
+} // namespace mojo
diff --git a/chromium/mojo/common/data_pipe_utils.h b/chromium/mojo/common/data_pipe_utils.h
new file mode 100644
index 00000000000..dc2f66a0059
--- /dev/null
+++ b/chromium/mojo/common/data_pipe_utils.h
@@ -0,0 +1,32 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SHELL_DATA_PIPE_UTILS_H_
+#define MOJO_SHELL_DATA_PIPE_UTILS_H_
+
+#include "base/callback_forward.h"
+#include "mojo/common/mojo_common_export.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace base {
+class FilePath;
+class TaskRunner;
+}
+
+namespace mojo {
+namespace common {
+
+// Asynchronously copies data from source to the destination file. The given
+// |callback| is run upon completion. File writes will be scheduled to the
+// given |task_runner|.
+void MOJO_COMMON_EXPORT CopyToFile(
+ ScopedDataPipeConsumerHandle source,
+ const base::FilePath& destination,
+ base::TaskRunner* task_runner,
+ const base::Callback<void(bool /*success*/)>& callback);
+
+} // namespace common
+} // namespace mojo
+
+#endif // MOJO_SHELL_DATA_PIPE_UTILS_H_
diff --git a/chromium/mojo/common/handle_watcher.cc b/chromium/mojo/common/handle_watcher.cc
new file mode 100644
index 00000000000..84e8b64fb9a
--- /dev/null
+++ b/chromium/mojo/common/handle_watcher.cc
@@ -0,0 +1,312 @@
+// Copyright 2013 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 "mojo/common/handle_watcher.h"
+
+#include <map>
+
+#include "base/atomic_sequence_num.h"
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/memory/singleton.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "mojo/common/message_pump_mojo.h"
+#include "mojo/common/message_pump_mojo_handler.h"
+#include "mojo/common/time_helper.h"
+
+namespace mojo {
+namespace common {
+
+typedef int WatcherID;
+
+namespace {
+
+const char kWatcherThreadName[] = "handle-watcher-thread";
+
+// TODO(sky): this should be unnecessary once MessageLoop has been refactored.
+MessagePumpMojo* message_pump_mojo = NULL;
+
+scoped_ptr<base::MessagePump> CreateMessagePumpMojo() {
+ message_pump_mojo = new MessagePumpMojo;
+ return scoped_ptr<base::MessagePump>(message_pump_mojo).Pass();
+}
+
+base::TimeTicks MojoDeadlineToTimeTicks(MojoDeadline deadline) {
+ return deadline == MOJO_DEADLINE_INDEFINITE ? base::TimeTicks() :
+ internal::NowTicks() + base::TimeDelta::FromMicroseconds(deadline);
+}
+
+// Tracks the data for a single call to Start().
+struct WatchData {
+ WatchData()
+ : id(0),
+ handle_signals(MOJO_HANDLE_SIGNAL_NONE),
+ message_loop(NULL) {}
+
+ WatcherID id;
+ Handle handle;
+ MojoHandleSignals handle_signals;
+ base::TimeTicks deadline;
+ base::Callback<void(MojoResult)> callback;
+ scoped_refptr<base::MessageLoopProxy> message_loop;
+};
+
+// WatcherBackend --------------------------------------------------------------
+
+// WatcherBackend is responsible for managing the requests and interacting with
+// MessagePumpMojo. All access (outside of creation/destruction) is done on the
+// thread WatcherThreadManager creates.
+class WatcherBackend : public MessagePumpMojoHandler {
+ public:
+ WatcherBackend();
+ virtual ~WatcherBackend();
+
+ void StartWatching(const WatchData& data);
+ void StopWatching(WatcherID watcher_id);
+
+ private:
+ typedef std::map<Handle, WatchData> HandleToWatchDataMap;
+
+ // Invoked when a handle needs to be removed and notified.
+ void RemoveAndNotify(const Handle& handle, MojoResult result);
+
+ // Searches through |handle_to_data_| for |watcher_id|. Returns true if found
+ // and sets |handle| to the Handle. Returns false if not a known id.
+ bool GetMojoHandleByWatcherID(WatcherID watcher_id, Handle* handle) const;
+
+ // MessagePumpMojoHandler overrides:
+ virtual void OnHandleReady(const Handle& handle) OVERRIDE;
+ virtual void OnHandleError(const Handle& handle, MojoResult result) OVERRIDE;
+
+ // Maps from assigned id to WatchData.
+ HandleToWatchDataMap handle_to_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(WatcherBackend);
+};
+
+WatcherBackend::WatcherBackend() {
+}
+
+WatcherBackend::~WatcherBackend() {
+}
+
+void WatcherBackend::StartWatching(const WatchData& data) {
+ RemoveAndNotify(data.handle, MOJO_RESULT_CANCELLED);
+
+ DCHECK_EQ(0u, handle_to_data_.count(data.handle));
+
+ handle_to_data_[data.handle] = data;
+ message_pump_mojo->AddHandler(this, data.handle,
+ data.handle_signals,
+ data.deadline);
+}
+
+void WatcherBackend::StopWatching(WatcherID watcher_id) {
+ // Because of the thread hop it is entirely possible to get here and not
+ // have a valid handle registered for |watcher_id|.
+ Handle handle;
+ if (!GetMojoHandleByWatcherID(watcher_id, &handle))
+ return;
+
+ handle_to_data_.erase(handle);
+ message_pump_mojo->RemoveHandler(handle);
+}
+
+void WatcherBackend::RemoveAndNotify(const Handle& handle,
+ MojoResult result) {
+ if (handle_to_data_.count(handle) == 0)
+ return;
+
+ const WatchData data(handle_to_data_[handle]);
+ handle_to_data_.erase(handle);
+ message_pump_mojo->RemoveHandler(handle);
+ data.message_loop->PostTask(FROM_HERE, base::Bind(data.callback, result));
+}
+
+bool WatcherBackend::GetMojoHandleByWatcherID(WatcherID watcher_id,
+ Handle* handle) const {
+ for (HandleToWatchDataMap::const_iterator i = handle_to_data_.begin();
+ i != handle_to_data_.end(); ++i) {
+ if (i->second.id == watcher_id) {
+ *handle = i->second.handle;
+ return true;
+ }
+ }
+ return false;
+}
+
+void WatcherBackend::OnHandleReady(const Handle& handle) {
+ RemoveAndNotify(handle, MOJO_RESULT_OK);
+}
+
+void WatcherBackend::OnHandleError(const Handle& handle, MojoResult result) {
+ RemoveAndNotify(handle, result);
+}
+
+// WatcherThreadManager --------------------------------------------------------
+
+// WatcherThreadManager manages the background thread that listens for handles
+// to be ready. All requests are handled by WatcherBackend.
+class WatcherThreadManager {
+ public:
+ ~WatcherThreadManager();
+
+ // Returns the shared instance.
+ static WatcherThreadManager* GetInstance();
+
+ // Starts watching the requested handle. Returns a unique ID that is used to
+ // stop watching the handle. When the handle is ready |callback| is notified
+ // on the thread StartWatching() was invoked on.
+ // This may be invoked on any thread.
+ WatcherID StartWatching(const Handle& handle,
+ MojoHandleSignals handle_signals,
+ base::TimeTicks deadline,
+ const base::Callback<void(MojoResult)>& callback);
+
+ // Stops watching a handle.
+ // This may be invoked on any thread.
+ void StopWatching(WatcherID watcher_id);
+
+ private:
+ friend struct DefaultSingletonTraits<WatcherThreadManager>;
+ WatcherThreadManager();
+
+ base::Thread thread_;
+
+ base::AtomicSequenceNumber watcher_id_generator_;
+
+ WatcherBackend backend_;
+
+ DISALLOW_COPY_AND_ASSIGN(WatcherThreadManager);
+};
+
+WatcherThreadManager::~WatcherThreadManager() {
+ thread_.Stop();
+}
+
+WatcherThreadManager* WatcherThreadManager::GetInstance() {
+ return Singleton<WatcherThreadManager>::get();
+}
+
+WatcherID WatcherThreadManager::StartWatching(
+ const Handle& handle,
+ MojoHandleSignals handle_signals,
+ base::TimeTicks deadline,
+ const base::Callback<void(MojoResult)>& callback) {
+ WatchData data;
+ data.id = watcher_id_generator_.GetNext();
+ data.handle = handle;
+ data.callback = callback;
+ data.handle_signals = handle_signals;
+ data.deadline = deadline;
+ data.message_loop = base::MessageLoopProxy::current();
+ DCHECK_NE(static_cast<base::MessageLoopProxy*>(NULL),
+ data.message_loop.get());
+ // We outlive |thread_|, so it's safe to use Unretained() here.
+ thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&WatcherBackend::StartWatching,
+ base::Unretained(&backend_),
+ data));
+ return data.id;
+}
+
+void WatcherThreadManager::StopWatching(WatcherID watcher_id) {
+ // We outlive |thread_|, so it's safe to use Unretained() here.
+ thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&WatcherBackend::StopWatching,
+ base::Unretained(&backend_),
+ watcher_id));
+}
+
+WatcherThreadManager::WatcherThreadManager()
+ : thread_(kWatcherThreadName) {
+ base::Thread::Options thread_options;
+ thread_options.message_pump_factory = base::Bind(&CreateMessagePumpMojo);
+ thread_.StartWithOptions(thread_options);
+}
+
+} // namespace
+
+// HandleWatcher::State --------------------------------------------------------
+
+// Represents the state of the HandleWatcher. Owns the user's callback and
+// monitors the current thread's MessageLoop to know when to force the callback
+// to run (with an error) even though the pipe hasn't been signaled yet.
+class HandleWatcher::State : public base::MessageLoop::DestructionObserver {
+ public:
+ State(HandleWatcher* watcher,
+ const Handle& handle,
+ MojoHandleSignals handle_signals,
+ MojoDeadline deadline,
+ const base::Callback<void(MojoResult)>& callback)
+ : watcher_(watcher),
+ callback_(callback),
+ weak_factory_(this) {
+ base::MessageLoop::current()->AddDestructionObserver(this);
+
+ watcher_id_ = WatcherThreadManager::GetInstance()->StartWatching(
+ handle,
+ handle_signals,
+ MojoDeadlineToTimeTicks(deadline),
+ base::Bind(&State::OnHandleReady, weak_factory_.GetWeakPtr()));
+ }
+
+ virtual ~State() {
+ base::MessageLoop::current()->RemoveDestructionObserver(this);
+
+ WatcherThreadManager::GetInstance()->StopWatching(watcher_id_);
+ }
+
+ private:
+ virtual void WillDestroyCurrentMessageLoop() OVERRIDE {
+ // The current thread is exiting. Simulate a watch error.
+ OnHandleReady(MOJO_RESULT_ABORTED);
+ }
+
+ void OnHandleReady(MojoResult result) {
+ base::Callback<void(MojoResult)> callback = callback_;
+ watcher_->Stop(); // Destroys |this|.
+
+ callback.Run(result);
+ }
+
+ HandleWatcher* watcher_;
+ WatcherID watcher_id_;
+ base::Callback<void(MojoResult)> callback_;
+
+ // Used to weakly bind |this| to the WatcherThreadManager.
+ base::WeakPtrFactory<State> weak_factory_;
+};
+
+// HandleWatcher ---------------------------------------------------------------
+
+HandleWatcher::HandleWatcher() {
+}
+
+HandleWatcher::~HandleWatcher() {
+}
+
+void HandleWatcher::Start(const Handle& handle,
+ MojoHandleSignals handle_signals,
+ MojoDeadline deadline,
+ const base::Callback<void(MojoResult)>& callback) {
+ DCHECK(handle.is_valid());
+ DCHECK_NE(MOJO_HANDLE_SIGNAL_NONE, handle_signals);
+
+ state_.reset(new State(this, handle, handle_signals, deadline, callback));
+}
+
+void HandleWatcher::Stop() {
+ state_.reset();
+}
+
+} // namespace common
+} // namespace mojo
diff --git a/chromium/mojo/common/handle_watcher.h b/chromium/mojo/common/handle_watcher.h
new file mode 100644
index 00000000000..9fac3f51edc
--- /dev/null
+++ b/chromium/mojo/common/handle_watcher.h
@@ -0,0 +1,59 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_COMMON_HANDLE_WATCHER_H_
+#define MOJO_COMMON_HANDLE_WATCHER_H_
+
+#include "base/basictypes.h"
+#include "base/callback_forward.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "mojo/common/mojo_common_export.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace base {
+class Thread;
+}
+
+namespace mojo {
+namespace common {
+namespace test {
+class HandleWatcherTest;
+}
+
+// HandleWatcher is used to asynchronously wait on a handle and notify a Closure
+// when the handle is ready, or the deadline has expired.
+class MOJO_COMMON_EXPORT HandleWatcher {
+ public:
+ HandleWatcher();
+ ~HandleWatcher();
+
+ // Starts listening for |handle|. This implicitly invokes Stop(). In other
+ // words, Start() performs one asynchronous watch at a time. It is ok to call
+ // Start() multiple times, but it cancels any existing watches. |callback| is
+ // notified when the handle is ready, invalid or deadline has passed and is
+ // notified on the thread Start() was invoked on. If the current thread exits
+ // before the handle is ready, then |callback| is invoked with a result of
+ // MOJO_RESULT_ABORTED.
+ void Start(const Handle& handle,
+ MojoHandleSignals handle_signals,
+ MojoDeadline deadline,
+ const base::Callback<void(MojoResult)>& callback);
+
+ // Stops listening. Does nothing if not in the process of listening.
+ void Stop();
+
+ private:
+ class State;
+
+ // If non-NULL Start() has been invoked.
+ scoped_ptr<State> state_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleWatcher);
+};
+
+} // namespace common
+} // namespace mojo
+
+#endif // MOJO_COMMON_HANDLE_WATCHER_H_
diff --git a/chromium/mojo/common/handle_watcher_unittest.cc b/chromium/mojo/common/handle_watcher_unittest.cc
new file mode 100644
index 00000000000..02cc6105d6a
--- /dev/null
+++ b/chromium/mojo/common/handle_watcher_unittest.cc
@@ -0,0 +1,341 @@
+// Copyright 2013 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 "mojo/common/handle_watcher.h"
+
+#include <string>
+
+#include "base/at_exit.h"
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "mojo/common/time_helper.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace common {
+namespace test {
+
+void ObserveCallback(bool* was_signaled,
+ MojoResult* result_observed,
+ MojoResult result) {
+ *was_signaled = true;
+ *result_observed = result;
+}
+
+void RunUntilIdle() {
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+}
+
+void DeleteWatcherAndForwardResult(
+ HandleWatcher* watcher,
+ base::Callback<void(MojoResult)> next_callback,
+ MojoResult result) {
+ delete watcher;
+ next_callback.Run(result);
+}
+
+// Helper class to manage the callback and running the message loop waiting for
+// message to be received. Typical usage is something like:
+// Schedule callback returned from GetCallback().
+// RunUntilGotCallback();
+// EXPECT_TRUE(got_callback());
+// clear_callback();
+class CallbackHelper {
+ public:
+ CallbackHelper()
+ : got_callback_(false),
+ run_loop_(NULL),
+ weak_factory_(this) {}
+ ~CallbackHelper() {}
+
+ // See description above |got_callback_|.
+ bool got_callback() const { return got_callback_; }
+ void clear_callback() { got_callback_ = false; }
+
+ // Runs the current MessageLoop until the callback returned from GetCallback()
+ // is notified.
+ void RunUntilGotCallback() {
+ ASSERT_TRUE(run_loop_ == NULL);
+ base::RunLoop run_loop;
+ base::AutoReset<base::RunLoop*> reseter(&run_loop_, &run_loop);
+ run_loop.Run();
+ }
+
+ base::Callback<void(MojoResult)> GetCallback() {
+ return base::Bind(&CallbackHelper::OnCallback, weak_factory_.GetWeakPtr());
+ }
+
+ void Start(HandleWatcher* watcher, const MessagePipeHandle& handle) {
+ StartWithCallback(watcher, handle, GetCallback());
+ }
+
+ void StartWithCallback(HandleWatcher* watcher,
+ const MessagePipeHandle& handle,
+ const base::Callback<void(MojoResult)>& callback) {
+ watcher->Start(handle, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE, callback);
+ }
+
+ private:
+ void OnCallback(MojoResult result) {
+ got_callback_ = true;
+ if (run_loop_)
+ run_loop_->Quit();
+ }
+
+ // Set to true when the callback is called.
+ bool got_callback_;
+
+ // If non-NULL we're in RunUntilGotCallback().
+ base::RunLoop* run_loop_;
+
+ base::WeakPtrFactory<CallbackHelper> weak_factory_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CallbackHelper);
+};
+
+class HandleWatcherTest : public testing::Test {
+ public:
+ HandleWatcherTest() {}
+ virtual ~HandleWatcherTest() {
+ test::SetTickClockForTest(NULL);
+ }
+
+ protected:
+ void InstallTickClock() {
+ test::SetTickClockForTest(&tick_clock_);
+ }
+
+ base::SimpleTestTickClock tick_clock_;
+
+ private:
+ base::ShadowingAtExitManager at_exit_;
+ base::MessageLoop message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleWatcherTest);
+};
+
+// Trivial test case with a single handle to watch.
+TEST_F(HandleWatcherTest, SingleHandler) {
+ MessagePipe test_pipe;
+ ASSERT_TRUE(test_pipe.handle0.is_valid());
+ CallbackHelper callback_helper;
+ HandleWatcher watcher;
+ callback_helper.Start(&watcher, test_pipe.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper.got_callback());
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe.handle1.get(),
+ std::string()));
+ callback_helper.RunUntilGotCallback();
+ EXPECT_TRUE(callback_helper.got_callback());
+}
+
+// Creates three handles and notfies them in reverse order ensuring each one is
+// notified appropriately.
+TEST_F(HandleWatcherTest, ThreeHandles) {
+ MessagePipe test_pipe1;
+ MessagePipe test_pipe2;
+ MessagePipe test_pipe3;
+ CallbackHelper callback_helper1;
+ CallbackHelper callback_helper2;
+ CallbackHelper callback_helper3;
+ ASSERT_TRUE(test_pipe1.handle0.is_valid());
+ ASSERT_TRUE(test_pipe2.handle0.is_valid());
+ ASSERT_TRUE(test_pipe3.handle0.is_valid());
+
+ HandleWatcher watcher1;
+ callback_helper1.Start(&watcher1, test_pipe1.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+ EXPECT_FALSE(callback_helper3.got_callback());
+
+ HandleWatcher watcher2;
+ callback_helper2.Start(&watcher2, test_pipe2.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+ EXPECT_FALSE(callback_helper3.got_callback());
+
+ HandleWatcher watcher3;
+ callback_helper3.Start(&watcher3, test_pipe3.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+ EXPECT_FALSE(callback_helper3.got_callback());
+
+ // Write to 3 and make sure it's notified.
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe3.handle1.get(),
+ std::string()));
+ callback_helper3.RunUntilGotCallback();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+ EXPECT_TRUE(callback_helper3.got_callback());
+ callback_helper3.clear_callback();
+
+ // Write to 1 and 3. Only 1 should be notified since 3 was is no longer
+ // running.
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe1.handle1.get(),
+ std::string()));
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe3.handle1.get(),
+ std::string()));
+ callback_helper1.RunUntilGotCallback();
+ EXPECT_TRUE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+ EXPECT_FALSE(callback_helper3.got_callback());
+ callback_helper1.clear_callback();
+
+ // Write to 1 and 2. Only 2 should be notified (since 1 was already notified).
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe1.handle1.get(),
+ std::string()));
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe2.handle1.get(),
+ std::string()));
+ callback_helper2.RunUntilGotCallback();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_TRUE(callback_helper2.got_callback());
+ EXPECT_FALSE(callback_helper3.got_callback());
+}
+
+// Verifies Start() invoked a second time works.
+TEST_F(HandleWatcherTest, Restart) {
+ MessagePipe test_pipe1;
+ MessagePipe test_pipe2;
+ CallbackHelper callback_helper1;
+ CallbackHelper callback_helper2;
+ ASSERT_TRUE(test_pipe1.handle0.is_valid());
+ ASSERT_TRUE(test_pipe2.handle0.is_valid());
+
+ HandleWatcher watcher1;
+ callback_helper1.Start(&watcher1, test_pipe1.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+
+ HandleWatcher watcher2;
+ callback_helper2.Start(&watcher2, test_pipe2.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+
+ // Write to 1 and make sure it's notified.
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe1.handle1.get(),
+ std::string()));
+ callback_helper1.RunUntilGotCallback();
+ EXPECT_TRUE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+ callback_helper1.clear_callback();
+ EXPECT_TRUE(mojo::test::DiscardMessage(test_pipe1.handle0.get()));
+
+ // Write to 2 and make sure it's notified.
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe2.handle1.get(),
+ std::string()));
+ callback_helper2.RunUntilGotCallback();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_TRUE(callback_helper2.got_callback());
+ callback_helper2.clear_callback();
+
+ // Listen on 1 again.
+ callback_helper1.Start(&watcher1, test_pipe1.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+
+ // Write to 1 and make sure it's notified.
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe1.handle1.get(),
+ std::string()));
+ callback_helper1.RunUntilGotCallback();
+ EXPECT_TRUE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+}
+
+// Verifies deadline is honored.
+TEST_F(HandleWatcherTest, Deadline) {
+ InstallTickClock();
+
+ MessagePipe test_pipe1;
+ MessagePipe test_pipe2;
+ MessagePipe test_pipe3;
+ CallbackHelper callback_helper1;
+ CallbackHelper callback_helper2;
+ CallbackHelper callback_helper3;
+ ASSERT_TRUE(test_pipe1.handle0.is_valid());
+ ASSERT_TRUE(test_pipe2.handle0.is_valid());
+ ASSERT_TRUE(test_pipe3.handle0.is_valid());
+
+ // Add a watcher with an infinite timeout.
+ HandleWatcher watcher1;
+ callback_helper1.Start(&watcher1, test_pipe1.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+ EXPECT_FALSE(callback_helper3.got_callback());
+
+ // Add another watcher wth a timeout of 500 microseconds.
+ HandleWatcher watcher2;
+ watcher2.Start(test_pipe2.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, 500,
+ callback_helper2.GetCallback());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+ EXPECT_FALSE(callback_helper3.got_callback());
+
+ // Advance the clock passed the deadline. We also have to start another
+ // watcher to wake up the background thread.
+ tick_clock_.Advance(base::TimeDelta::FromMicroseconds(501));
+
+ HandleWatcher watcher3;
+ callback_helper3.Start(&watcher3, test_pipe3.handle0.get());
+
+ callback_helper2.RunUntilGotCallback();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_TRUE(callback_helper2.got_callback());
+ EXPECT_FALSE(callback_helper3.got_callback());
+}
+
+TEST_F(HandleWatcherTest, DeleteInCallback) {
+ MessagePipe test_pipe;
+ CallbackHelper callback_helper;
+
+ HandleWatcher* watcher = new HandleWatcher();
+ callback_helper.StartWithCallback(watcher, test_pipe.handle1.get(),
+ base::Bind(&DeleteWatcherAndForwardResult,
+ watcher,
+ callback_helper.GetCallback()));
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe.handle0.get(),
+ std::string()));
+ callback_helper.RunUntilGotCallback();
+ EXPECT_TRUE(callback_helper.got_callback());
+}
+
+TEST(HandleWatcherCleanEnvironmentTest, AbortedOnMessageLoopDestruction) {
+ bool was_signaled = false;
+ MojoResult result = MOJO_RESULT_OK;
+
+ base::ShadowingAtExitManager at_exit;
+ MessagePipe pipe;
+ HandleWatcher watcher;
+ {
+ base::MessageLoop loop;
+
+ watcher.Start(pipe.handle0.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ base::Bind(&ObserveCallback, &was_signaled, &result));
+
+ // Now, let the MessageLoop get torn down. We expect our callback to run.
+ }
+
+ EXPECT_TRUE(was_signaled);
+ EXPECT_EQ(MOJO_RESULT_ABORTED, result);
+}
+
+} // namespace test
+} // namespace common
+} // namespace mojo
diff --git a/chromium/mojo/common/message_pump_mojo.cc b/chromium/mojo/common/message_pump_mojo.cc
new file mode 100644
index 00000000000..37d636bc8f4
--- /dev/null
+++ b/chromium/mojo/common/message_pump_mojo.cc
@@ -0,0 +1,243 @@
+// Copyright 2013 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 "mojo/common/message_pump_mojo.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/debug/alias.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "mojo/common/message_pump_mojo_handler.h"
+#include "mojo/common/time_helper.h"
+
+namespace mojo {
+namespace common {
+
+// State needed for one iteration of WaitMany. The first handle and flags
+// corresponds to that of the control pipe.
+struct MessagePumpMojo::WaitState {
+ std::vector<Handle> handles;
+ std::vector<MojoHandleSignals> wait_signals;
+};
+
+struct MessagePumpMojo::RunState {
+ RunState() : should_quit(false) {
+ CreateMessagePipe(NULL, &read_handle, &write_handle);
+ }
+
+ base::TimeTicks delayed_work_time;
+
+ // Used to wake up WaitForWork().
+ ScopedMessagePipeHandle read_handle;
+ ScopedMessagePipeHandle write_handle;
+
+ bool should_quit;
+};
+
+MessagePumpMojo::MessagePumpMojo() : run_state_(NULL), next_handler_id_(0) {
+}
+
+MessagePumpMojo::~MessagePumpMojo() {
+}
+
+// static
+scoped_ptr<base::MessagePump> MessagePumpMojo::Create() {
+ return scoped_ptr<MessagePump>(new MessagePumpMojo());
+}
+
+void MessagePumpMojo::AddHandler(MessagePumpMojoHandler* handler,
+ const Handle& handle,
+ MojoHandleSignals wait_signals,
+ base::TimeTicks deadline) {
+ DCHECK(handler);
+ DCHECK(handle.is_valid());
+ // Assume it's an error if someone tries to reregister an existing handle.
+ DCHECK_EQ(0u, handlers_.count(handle));
+ Handler handler_data;
+ handler_data.handler = handler;
+ handler_data.wait_signals = wait_signals;
+ handler_data.deadline = deadline;
+ handler_data.id = next_handler_id_++;
+ handlers_[handle] = handler_data;
+}
+
+void MessagePumpMojo::RemoveHandler(const Handle& handle) {
+ handlers_.erase(handle);
+}
+
+void MessagePumpMojo::Run(Delegate* delegate) {
+ RunState run_state;
+ // TODO: better deal with error handling.
+ CHECK(run_state.read_handle.is_valid());
+ CHECK(run_state.write_handle.is_valid());
+ RunState* old_state = NULL;
+ {
+ base::AutoLock auto_lock(run_state_lock_);
+ old_state = run_state_;
+ run_state_ = &run_state;
+ }
+ DoRunLoop(&run_state, delegate);
+ {
+ base::AutoLock auto_lock(run_state_lock_);
+ run_state_ = old_state;
+ }
+}
+
+void MessagePumpMojo::Quit() {
+ base::AutoLock auto_lock(run_state_lock_);
+ if (run_state_)
+ run_state_->should_quit = true;
+}
+
+void MessagePumpMojo::ScheduleWork() {
+ base::AutoLock auto_lock(run_state_lock_);
+ if (run_state_)
+ SignalControlPipe(*run_state_);
+}
+
+void MessagePumpMojo::ScheduleDelayedWork(
+ const base::TimeTicks& delayed_work_time) {
+ base::AutoLock auto_lock(run_state_lock_);
+ if (!run_state_)
+ return;
+ run_state_->delayed_work_time = delayed_work_time;
+ SignalControlPipe(*run_state_);
+}
+
+void MessagePumpMojo::DoRunLoop(RunState* run_state, Delegate* delegate) {
+ bool more_work_is_plausible = true;
+ for (;;) {
+ const bool block = !more_work_is_plausible;
+ DoInternalWork(*run_state, block);
+
+ // There isn't a good way to know if there are more handles ready, we assume
+ // not.
+ more_work_is_plausible = false;
+
+ if (run_state->should_quit)
+ break;
+
+ more_work_is_plausible |= delegate->DoWork();
+ if (run_state->should_quit)
+ break;
+
+ more_work_is_plausible |= delegate->DoDelayedWork(
+ &run_state->delayed_work_time);
+ if (run_state->should_quit)
+ break;
+
+ if (more_work_is_plausible)
+ continue;
+
+ more_work_is_plausible = delegate->DoIdleWork();
+ if (run_state->should_quit)
+ break;
+ }
+}
+
+void MessagePumpMojo::DoInternalWork(const RunState& run_state, bool block) {
+ const MojoDeadline deadline = block ? GetDeadlineForWait(run_state) : 0;
+ const WaitState wait_state = GetWaitState(run_state);
+ const MojoResult result =
+ WaitMany(wait_state.handles, wait_state.wait_signals, deadline);
+ if (result == 0) {
+ // Control pipe was written to.
+ uint32_t num_bytes = 0;
+ ReadMessageRaw(run_state.read_handle.get(), NULL, &num_bytes, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
+ } else if (result > 0) {
+ const size_t index = static_cast<size_t>(result);
+ DCHECK(handlers_.find(wait_state.handles[index]) != handlers_.end());
+ handlers_[wait_state.handles[index]].handler->OnHandleReady(
+ wait_state.handles[index]);
+ } else {
+ switch (result) {
+ case MOJO_RESULT_CANCELLED:
+ case MOJO_RESULT_FAILED_PRECONDITION:
+ case MOJO_RESULT_INVALID_ARGUMENT:
+ RemoveFirstInvalidHandle(wait_state);
+ break;
+ case MOJO_RESULT_DEADLINE_EXCEEDED:
+ break;
+ default:
+ base::debug::Alias(&result);
+ // Unexpected result is likely fatal, crash so we can determine cause.
+ CHECK(false);
+ }
+ }
+
+ // Notify and remove any handlers whose time has expired. Make a copy in case
+ // someone tries to add/remove new handlers from notification.
+ const HandleToHandler cloned_handlers(handlers_);
+ const base::TimeTicks now(internal::NowTicks());
+ for (HandleToHandler::const_iterator i = cloned_handlers.begin();
+ i != cloned_handlers.end(); ++i) {
+ // Since we're iterating over a clone of the handlers, verify the handler is
+ // still valid before notifying.
+ if (!i->second.deadline.is_null() && i->second.deadline < now &&
+ handlers_.find(i->first) != handlers_.end() &&
+ handlers_[i->first].id == i->second.id) {
+ i->second.handler->OnHandleError(i->first, MOJO_RESULT_DEADLINE_EXCEEDED);
+ }
+ }
+}
+
+void MessagePumpMojo::RemoveFirstInvalidHandle(const WaitState& wait_state) {
+ // TODO(sky): deal with control pipe going bad.
+ for (size_t i = 1; i < wait_state.handles.size(); ++i) {
+ const MojoResult result =
+ Wait(wait_state.handles[i], wait_state.wait_signals[i], 0);
+ if (result == MOJO_RESULT_INVALID_ARGUMENT ||
+ result == MOJO_RESULT_FAILED_PRECONDITION ||
+ result == MOJO_RESULT_CANCELLED) {
+ // Remove the handle first, this way if OnHandleError() tries to remove
+ // the handle our iterator isn't invalidated.
+ DCHECK(handlers_.find(wait_state.handles[i]) != handlers_.end());
+ MessagePumpMojoHandler* handler =
+ handlers_[wait_state.handles[i]].handler;
+ handlers_.erase(wait_state.handles[i]);
+ handler->OnHandleError(wait_state.handles[i], result);
+ return;
+ }
+ }
+}
+
+void MessagePumpMojo::SignalControlPipe(const RunState& run_state) {
+ // TODO(sky): deal with error?
+ WriteMessageRaw(run_state.write_handle.get(), NULL, 0, NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE);
+}
+
+MessagePumpMojo::WaitState MessagePumpMojo::GetWaitState(
+ const RunState& run_state) const {
+ WaitState wait_state;
+ wait_state.handles.push_back(run_state.read_handle.get());
+ wait_state.wait_signals.push_back(MOJO_HANDLE_SIGNAL_READABLE);
+
+ for (HandleToHandler::const_iterator i = handlers_.begin();
+ i != handlers_.end(); ++i) {
+ wait_state.handles.push_back(i->first);
+ wait_state.wait_signals.push_back(i->second.wait_signals);
+ }
+ return wait_state;
+}
+
+MojoDeadline MessagePumpMojo::GetDeadlineForWait(
+ const RunState& run_state) const {
+ base::TimeTicks min_time = run_state.delayed_work_time;
+ for (HandleToHandler::const_iterator i = handlers_.begin();
+ i != handlers_.end(); ++i) {
+ if (min_time.is_null() && i->second.deadline < min_time)
+ min_time = i->second.deadline;
+ }
+ return min_time.is_null() ? MOJO_DEADLINE_INDEFINITE :
+ std::max(static_cast<MojoDeadline>(0),
+ static_cast<MojoDeadline>(
+ (min_time - internal::NowTicks()).InMicroseconds()));
+}
+
+} // namespace common
+} // namespace mojo
diff --git a/chromium/mojo/common/message_pump_mojo.h b/chromium/mojo/common/message_pump_mojo.h
new file mode 100644
index 00000000000..751311850eb
--- /dev/null
+++ b/chromium/mojo/common/message_pump_mojo.h
@@ -0,0 +1,107 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_COMMON_MESSAGE_PUMP_MOJO_H_
+#define MOJO_COMMON_MESSAGE_PUMP_MOJO_H_
+
+#include <map>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_pump.h"
+#include "base/synchronization/lock.h"
+#include "base/time/time.h"
+#include "mojo/common/mojo_common_export.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace common {
+
+class MessagePumpMojoHandler;
+
+// Mojo implementation of MessagePump.
+class MOJO_COMMON_EXPORT MessagePumpMojo : public base::MessagePump {
+ public:
+ MessagePumpMojo();
+ virtual ~MessagePumpMojo();
+
+ // Static factory function (for using with |base::Thread::Options|, wrapped
+ // using |base::Bind()|).
+ static scoped_ptr<base::MessagePump> Create();
+
+ // Registers a MessagePumpMojoHandler for the specified handle. Only one
+ // handler can be registered for a specified handle.
+ void AddHandler(MessagePumpMojoHandler* handler,
+ const Handle& handle,
+ MojoHandleSignals wait_signals,
+ base::TimeTicks deadline);
+
+ void RemoveHandler(const Handle& handle);
+
+ // MessagePump:
+ virtual void Run(Delegate* delegate) OVERRIDE;
+ virtual void Quit() OVERRIDE;
+ virtual void ScheduleWork() OVERRIDE;
+ virtual void ScheduleDelayedWork(
+ const base::TimeTicks& delayed_work_time) OVERRIDE;
+
+ private:
+ struct RunState;
+ struct WaitState;
+
+ // Contains the data needed to track a request to AddHandler().
+ struct Handler {
+ Handler() : handler(NULL), wait_signals(MOJO_HANDLE_SIGNAL_NONE), id(0) {}
+
+ MessagePumpMojoHandler* handler;
+ MojoHandleSignals wait_signals;
+ base::TimeTicks deadline;
+ // See description of |MessagePumpMojo::next_handler_id_| for details.
+ int id;
+ };
+
+ typedef std::map<Handle, Handler> HandleToHandler;
+
+ // Implementation of Run().
+ void DoRunLoop(RunState* run_state, Delegate* delegate);
+
+ // Services the set of handles ready. If |block| is true this waits for a
+ // handle to become ready, otherwise this does not block.
+ void DoInternalWork(const RunState& run_state, bool block);
+
+ // Removes the first invalid handle. This is called if MojoWaitMany finds an
+ // invalid handle.
+ void RemoveFirstInvalidHandle(const WaitState& wait_state);
+
+ void SignalControlPipe(const RunState& run_state);
+
+ WaitState GetWaitState(const RunState& run_state) const;
+
+ // Returns the deadline for the call to MojoWaitMany().
+ MojoDeadline GetDeadlineForWait(const RunState& run_state) const;
+
+ // If non-NULL we're running (inside Run()). Member is reference to value on
+ // stack.
+ RunState* run_state_;
+
+ // Lock for accessing |run_state_|. In general the only method that we have to
+ // worry about is ScheduleWork(). All other methods are invoked on the same
+ // thread.
+ base::Lock run_state_lock_;
+
+ HandleToHandler handlers_;
+
+ // An ever increasing value assigned to each Handler::id. Used to detect
+ // uniqueness while notifying. That is, while notifying expired timers we copy
+ // |handlers_| and only notify handlers whose id match. If the id does not
+ // match it means the handler was removed then added so that we shouldn't
+ // notify it.
+ int next_handler_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpMojo);
+};
+
+} // namespace common
+} // namespace mojo
+
+#endif // MOJO_COMMON_MESSAGE_PUMP_MOJO_H_
diff --git a/chromium/mojo/common/message_pump_mojo_handler.h b/chromium/mojo/common/message_pump_mojo_handler.h
new file mode 100644
index 00000000000..5809051e1d9
--- /dev/null
+++ b/chromium/mojo/common/message_pump_mojo_handler.h
@@ -0,0 +1,29 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_COMMON_MESSAGE_PUMP_MOJO_HANDLER_H_
+#define MOJO_COMMON_MESSAGE_PUMP_MOJO_HANDLER_H_
+
+#include "mojo/common/mojo_common_export.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace common {
+
+// Used by MessagePumpMojo to notify when a handle is either ready or has become
+// invalid.
+class MOJO_COMMON_EXPORT MessagePumpMojoHandler {
+ public:
+ virtual void OnHandleReady(const Handle& handle) = 0;
+
+ virtual void OnHandleError(const Handle& handle, MojoResult result) = 0;
+
+ protected:
+ virtual ~MessagePumpMojoHandler() {}
+};
+
+} // namespace common
+} // namespace mojo
+
+#endif // MOJO_COMMON_MESSAGE_PUMP_MOJO_HANDLER_H_
diff --git a/chromium/mojo/common/message_pump_mojo_unittest.cc b/chromium/mojo/common/message_pump_mojo_unittest.cc
new file mode 100644
index 00000000000..fb5ae2470e1
--- /dev/null
+++ b/chromium/mojo/common/message_pump_mojo_unittest.cc
@@ -0,0 +1,22 @@
+// Copyright 2013 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 "mojo/common/message_pump_mojo.h"
+
+#include "base/message_loop/message_loop_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace common {
+namespace test {
+
+scoped_ptr<base::MessagePump> CreateMojoMessagePump() {
+ return scoped_ptr<base::MessagePump>(new MessagePumpMojo());
+}
+
+RUN_MESSAGE_LOOP_TESTS(Mojo, &CreateMojoMessagePump);
+
+} // namespace test
+} // namespace common
+} // namespace mojo
diff --git a/chromium/mojo/common/mojo_common_export.h b/chromium/mojo/common/mojo_common_export.h
new file mode 100644
index 00000000000..48d21d0d3d2
--- /dev/null
+++ b/chromium/mojo/common/mojo_common_export.h
@@ -0,0 +1,32 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_COMMON_MOJO_COMMON_EXPORT_H_
+#define MOJO_COMMON_MOJO_COMMON_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_COMMON_IMPLEMENTATION)
+#define MOJO_COMMON_EXPORT __declspec(dllexport)
+#else
+#define MOJO_COMMON_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_COMMON_IMPLEMENTATION)
+#define MOJO_COMMON_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_COMMON_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#else // !defined(COMPONENT_BUILD)
+#define MOJO_COMMON_EXPORT
+#endif
+
+#endif // MOJO_COMMON_MOJO_COMMON_EXPORT_H_
diff --git a/chromium/mojo/common/time_helper.cc b/chromium/mojo/common/time_helper.cc
new file mode 100644
index 00000000000..36fd0879e34
--- /dev/null
+++ b/chromium/mojo/common/time_helper.cc
@@ -0,0 +1,33 @@
+// Copyright 2014 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 "mojo/common/time_helper.h"
+
+#include "base/time/tick_clock.h"
+
+namespace mojo {
+namespace common {
+
+namespace {
+
+base::TickClock* tick_clock = NULL;
+
+} // namespace
+
+namespace test {
+
+void SetTickClockForTest(base::TickClock* clock) {
+ tick_clock = clock;
+}
+} // namespace test
+
+namespace internal {
+
+base::TimeTicks NowTicks() {
+ return tick_clock ? tick_clock->NowTicks() : base::TimeTicks::Now();
+}
+
+} // namespace internal
+} // namespace common
+} // namespace mojo
diff --git a/chromium/mojo/common/time_helper.h b/chromium/mojo/common/time_helper.h
new file mode 100644
index 00000000000..365ae04993b
--- /dev/null
+++ b/chromium/mojo/common/time_helper.h
@@ -0,0 +1,34 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_COMMON_TIME_HELPER_H_
+#define MOJO_COMMON_TIME_HELPER_H_
+
+#include "base/time/time.h"
+#include "mojo/common/mojo_common_export.h"
+
+namespace base {
+class TickClock;
+}
+
+namespace mojo {
+namespace common {
+namespace test {
+
+// Sets the TickClock used for getting TimeTicks::Now(). This is currently used
+// by both HandleWatcher and MessagePumpMojo.
+MOJO_COMMON_EXPORT void SetTickClockForTest(base::TickClock* clock);
+
+} // namespace test
+
+namespace internal {
+
+// Returns now. Used internally; generally not useful.
+MOJO_COMMON_EXPORT base::TimeTicks NowTicks();
+
+} // namespace internal
+} // namespace common
+} // namespace mojo
+
+#endif // MOJO_COMMON_TIME_HELPER_H_
diff --git a/chromium/mojo/dbus/DEPS b/chromium/mojo/dbus/DEPS
new file mode 100644
index 00000000000..0abf6831571
--- /dev/null
+++ b/chromium/mojo/dbus/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+base",
+ "+dbus",
+]
diff --git a/chromium/mojo/dbus/dbus_external_service.cc b/chromium/mojo/dbus/dbus_external_service.cc
new file mode 100644
index 00000000000..a0b8981f3f4
--- /dev/null
+++ b/chromium/mojo/dbus/dbus_external_service.cc
@@ -0,0 +1,90 @@
+// Copyright 2014 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 "mojo/dbus/dbus_external_service.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "dbus/bus.h"
+#include "dbus/exported_object.h"
+#include "dbus/file_descriptor.h"
+#include "dbus/message.h"
+#include "dbus/object_path.h"
+#include "mojo/embedder/channel_init.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+#include "mojo/shell/external_service.mojom.h"
+
+namespace mojo {
+
+DBusExternalServiceBase::DBusExternalServiceBase(
+ const std::string& service_name)
+ : service_name_(service_name),
+ exported_object_(NULL) {
+}
+DBusExternalServiceBase::~DBusExternalServiceBase() {}
+
+void DBusExternalServiceBase::Start() {
+ InitializeDBus();
+ ExportMethods();
+ TakeDBusServiceOwnership();
+ DVLOG(1) << "External service started";
+}
+
+void DBusExternalServiceBase::ConnectChannel(
+ dbus::MethodCall* method_call,
+ dbus::ExportedObject::ResponseSender sender) {
+ dbus::MessageReader reader(method_call);
+ dbus::FileDescriptor wrapped_fd;
+ if (!reader.PopFileDescriptor(&wrapped_fd)) {
+ sender.Run(
+ dbus::ErrorResponse::FromMethodCall(
+ method_call,
+ "org.chromium.Mojo.BadHandle",
+ "Invalid FD.").PassAs<dbus::Response>());
+ return;
+ }
+ wrapped_fd.CheckValidity();
+ channel_init_.reset(new mojo::embedder::ChannelInit);
+ mojo::ScopedMessagePipeHandle message_pipe =
+ channel_init_->Init(wrapped_fd.TakeValue(),
+ base::MessageLoopProxy::current());
+ CHECK(message_pipe.is_valid());
+
+ Connect(message_pipe.Pass());
+ sender.Run(dbus::Response::FromMethodCall(method_call));
+}
+
+void DBusExternalServiceBase::ExportMethods() {
+ CHECK(exported_object_);
+ CHECK(exported_object_->ExportMethodAndBlock(
+ kMojoDBusInterface, kMojoDBusConnectMethod,
+ base::Bind(&DBusExternalServiceBase::ConnectChannel,
+ base::Unretained(this))));
+}
+
+void DBusExternalServiceBase::InitializeDBus() {
+ CHECK(!bus_);
+ dbus::Bus::Options options;
+ options.bus_type = dbus::Bus::SESSION;
+ bus_ = new dbus::Bus(options);
+ CHECK(bus_->Connect());
+ CHECK(bus_->SetUpAsyncOperations());
+
+ exported_object_ =
+ bus_->GetExportedObject(dbus::ObjectPath(kMojoDBusImplPath));
+}
+
+void DBusExternalServiceBase::TakeDBusServiceOwnership() {
+ CHECK(bus_->RequestOwnershipAndBlock(
+ service_name_,
+ dbus::Bus::REQUIRE_PRIMARY_ALLOW_REPLACEMENT))
+ << "Unable to take ownership of " << service_name_;
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/dbus/dbus_external_service.h b/chromium/mojo/dbus/dbus_external_service.h
new file mode 100644
index 00000000000..213c074f993
--- /dev/null
+++ b/chromium/mojo/dbus/dbus_external_service.h
@@ -0,0 +1,88 @@
+// Copyright 2014 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 "base/memory/scoped_ptr.h"
+#include "dbus/bus.h"
+#include "dbus/exported_object.h"
+#include "dbus/message.h"
+#include "dbus/object_path.h"
+#include "mojo/embedder/channel_init.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+#include "mojo/shell/external_service.mojom.h"
+
+namespace mojo {
+const char kMojoDBusImplPath[] = "/org/chromium/MojoImpl";
+const char kMojoDBusInterface[] = "org.chromium.Mojo";
+const char kMojoDBusConnectMethod[] = "ConnectChannel";
+
+class DBusExternalServiceBase {
+ public:
+ explicit DBusExternalServiceBase(const std::string& service_name);
+ virtual ~DBusExternalServiceBase();
+
+ void Start();
+
+ protected:
+ // TODO(cmasone): Enable multiple peers to connect/disconnect
+ virtual void Connect(ScopedMessagePipeHandle client_handle) = 0;
+ virtual void Disconnect() = 0;
+
+ private:
+ // Implementation of org.chromium.Mojo.ConnectChannel, exported over DBus.
+ // Takes a file descriptor and uses it to create a MessagePipe that is then
+ // hooked to an implementation of ExternalService.
+ void ConnectChannel(dbus::MethodCall* method_call,
+ dbus::ExportedObject::ResponseSender sender);
+
+ void ExportMethods();
+ void InitializeDBus();
+ void TakeDBusServiceOwnership();
+
+ const std::string service_name_;
+ scoped_refptr<dbus::Bus> bus_;
+ dbus::ExportedObject* exported_object_; // Owned by bus_;
+ scoped_ptr<embedder::ChannelInit> channel_init_;
+ DISALLOW_COPY_AND_ASSIGN(DBusExternalServiceBase);
+};
+
+template <class ServiceImpl>
+class DBusExternalService : public DBusExternalServiceBase {
+ public:
+ explicit DBusExternalService(const std::string& service_name)
+ : DBusExternalServiceBase(service_name) {
+ }
+ virtual ~DBusExternalService() {}
+
+ protected:
+ virtual void Connect(ScopedMessagePipeHandle client_handle) OVERRIDE {
+ external_service_.reset(BindToPipe(new Impl(this), client_handle.Pass()));
+ }
+
+ virtual void Disconnect() OVERRIDE {
+ external_service_.reset();
+ }
+
+ private:
+ class Impl : public InterfaceImpl<ExternalService> {
+ public:
+ explicit Impl(DBusExternalService* service) : service_(service) {
+ }
+ virtual void OnConnectionError() OVERRIDE {
+ service_->Disconnect();
+ }
+ virtual void Activate(ScopedMessagePipeHandle service_provider_handle)
+ OVERRIDE {
+ app_.reset(new Application(service_provider_handle.Pass()));
+ app_->AddService<ServiceImpl>();
+ }
+ private:
+ DBusExternalService* service_;
+ scoped_ptr<Application> app_;
+ };
+
+ scoped_ptr<Impl> external_service_;
+};
+
+} // namespace mojo
diff --git a/chromium/mojo/embedder/DEPS b/chromium/mojo/embedder/DEPS
new file mode 100644
index 00000000000..1c627b70d37
--- /dev/null
+++ b/chromium/mojo/embedder/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+ "+mojo/system/system_impl_export.h",
+]
+
+specific_include_rules = {
+ # Implementation files may freely access mojo/system, but we don't want to
+ # leak implementation details through the headers.
+ ".*\.cc": [
+ "+mojo/system",
+ ]
+}
diff --git a/chromium/mojo/embedder/README.md b/chromium/mojo/embedder/README.md
new file mode 100644
index 00000000000..5510e99f65f
--- /dev/null
+++ b/chromium/mojo/embedder/README.md
@@ -0,0 +1,13 @@
+Mojo Embedder API
+=================
+
+The Mojo Embedder API is an unstable, internal API to the Mojo system
+implementation. It should be used by code running on top of the system-level
+APIs to set up the Mojo environment (instead of directly instantiating things
+from src/mojo/system).
+
+Example uses: Mojo shell, to set up the Mojo environment for Mojo apps; Chromium
+code, to set up the Mojo IPC system for use between processes. Note that most
+code should use the Mojo Public API (under src/mojo/public) instead. The
+Embedder API should only be used to initialize the environment, set up the
+initial MessagePipe between two processes, etc.
diff --git a/chromium/mojo/embedder/channel_init.cc b/chromium/mojo/embedder/channel_init.cc
new file mode 100644
index 00000000000..136a5389817
--- /dev/null
+++ b/chromium/mojo/embedder/channel_init.cc
@@ -0,0 +1,59 @@
+// Copyright 2014 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 "mojo/embedder/channel_init.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/embedder/embedder.h"
+
+namespace mojo {
+namespace embedder {
+
+ChannelInit::ChannelInit()
+ : channel_info_(NULL),
+ weak_factory_(this) {
+}
+
+ChannelInit::~ChannelInit() {
+ if (channel_info_) {
+ io_thread_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&mojo::embedder::DestroyChannelOnIOThread, channel_info_));
+ }
+}
+
+mojo::ScopedMessagePipeHandle ChannelInit::Init(
+ base::PlatformFile file,
+ scoped_refptr<base::TaskRunner> io_thread_task_runner) {
+ DCHECK(!io_thread_task_runner_.get()); // Should only init once.
+ io_thread_task_runner_ = io_thread_task_runner;
+ mojo::ScopedMessagePipeHandle message_pipe = mojo::embedder::CreateChannel(
+ mojo::embedder::ScopedPlatformHandle(
+ mojo::embedder::PlatformHandle(file)),
+ io_thread_task_runner,
+ base::Bind(&ChannelInit::OnCreatedChannel, weak_factory_.GetWeakPtr(),
+ io_thread_task_runner),
+ base::MessageLoop::current()->message_loop_proxy()).Pass();
+ return message_pipe.Pass();
+}
+
+// static
+void ChannelInit::OnCreatedChannel(
+ base::WeakPtr<ChannelInit> host,
+ scoped_refptr<base::TaskRunner> io_thread,
+ embedder::ChannelInfo* channel) {
+ // By the time we get here |host| may have been destroyed. If so, shutdown the
+ // channel.
+ if (!host.get()) {
+ io_thread->PostTask(
+ FROM_HERE,
+ base::Bind(&mojo::embedder::DestroyChannelOnIOThread, channel));
+ return;
+ }
+ host->channel_info_ = channel;
+}
+
+} // namespace embedder
+} // namespace mojo
diff --git a/chromium/mojo/embedder/channel_init.h b/chromium/mojo/embedder/channel_init.h
new file mode 100644
index 00000000000..002bd868aec
--- /dev/null
+++ b/chromium/mojo/embedder/channel_init.h
@@ -0,0 +1,59 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EMBEDDER_CHANNEL_INIT_H_
+#define MOJO_EMBEDDER_CHANNEL_INIT_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/platform_file.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace base {
+class MessageLoopProxy;
+class TaskRunner;
+}
+
+namespace mojo {
+namespace embedder {
+struct ChannelInfo;
+}
+
+namespace embedder {
+
+// ChannelInit handle creation (and destruction) of the mojo channel. It is
+// expected that this class is created and destroyed on the main thread.
+class MOJO_SYSTEM_IMPL_EXPORT ChannelInit {
+ public:
+ ChannelInit();
+ ~ChannelInit();
+
+ // Initializes the channel. This takes ownership of |file|. Returns the
+ // primordial MessagePipe for the channel.
+ mojo::ScopedMessagePipeHandle Init(
+ base::PlatformFile file,
+ scoped_refptr<base::TaskRunner> io_thread_task_runner);
+
+ private:
+ // Invoked on the main thread once the channel has been established.
+ static void OnCreatedChannel(
+ base::WeakPtr<ChannelInit> host,
+ scoped_refptr<base::TaskRunner> io_thread,
+ embedder::ChannelInfo* channel);
+
+ scoped_refptr<base::TaskRunner> io_thread_task_runner_;
+
+ // If non-null the channel has been established.
+ embedder::ChannelInfo* channel_info_;
+
+ base::WeakPtrFactory<ChannelInit> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChannelInit);
+};
+
+} // namespace embedder
+} // namespace mojo
+
+#endif // MOJO_EMBEDDER_CHANNEL_INIT_H_
diff --git a/chromium/mojo/embedder/embedder.cc b/chromium/mojo/embedder/embedder.cc
new file mode 100644
index 00000000000..d4fd3814392
--- /dev/null
+++ b/chromium/mojo/embedder/embedder.cc
@@ -0,0 +1,179 @@
+// Copyright 2014 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 "mojo/embedder/embedder.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/system/channel.h"
+#include "mojo/system/core.h"
+#include "mojo/system/entrypoints.h"
+#include "mojo/system/message_in_transit.h"
+#include "mojo/system/message_pipe.h"
+#include "mojo/system/message_pipe_dispatcher.h"
+#include "mojo/system/platform_handle_dispatcher.h"
+#include "mojo/system/raw_channel.h"
+
+namespace mojo {
+namespace embedder {
+
+// This is defined here (instead of a header file), since it's opaque to the
+// outside world. But we need to define it before our (internal-only) functions
+// that use it.
+struct ChannelInfo {
+ explicit ChannelInfo(scoped_refptr<system::Channel> channel)
+ : channel(channel) {}
+ ~ChannelInfo() {}
+
+ scoped_refptr<system::Channel> channel;
+};
+
+namespace {
+
+// Helper for |CreateChannelOnIOThread()|. (Note: May return null for some
+// failures.)
+scoped_refptr<system::Channel> MakeChannel(
+ ScopedPlatformHandle platform_handle,
+ scoped_refptr<system::MessagePipe> message_pipe) {
+ DCHECK(platform_handle.is_valid());
+
+ // Create and initialize a |system::Channel|.
+ scoped_refptr<system::Channel> channel = new system::Channel();
+ if (!channel->Init(system::RawChannel::Create(platform_handle.Pass()))) {
+ // This is very unusual (e.g., maybe |platform_handle| was invalid or we
+ // reached some system resource limit).
+ LOG(ERROR) << "Channel::Init() failed";
+ // Return null, since |Shutdown()| shouldn't be called in this case.
+ return scoped_refptr<system::Channel>();
+ }
+ // Once |Init()| has succeeded, we have to return |channel| (since
+ // |Shutdown()| will have to be called on it).
+
+ // Attach the message pipe endpoint.
+ system::MessageInTransit::EndpointId endpoint_id =
+ channel->AttachMessagePipeEndpoint(message_pipe, 1);
+ if (endpoint_id == system::MessageInTransit::kInvalidEndpointId) {
+ // This means that, e.g., the other endpoint of the message pipe was closed
+ // first. But it's not necessarily an error per se.
+ DVLOG(2) << "Channel::AttachMessagePipeEndpoint() failed";
+ return channel;
+ }
+ CHECK_EQ(endpoint_id, system::Channel::kBootstrapEndpointId);
+
+ if (!channel->RunMessagePipeEndpoint(system::Channel::kBootstrapEndpointId,
+ system::Channel::kBootstrapEndpointId)) {
+ // Currently, there's no reason for this to fail.
+ NOTREACHED() << "Channel::RunMessagePipeEndpoint() failed";
+ return channel;
+ }
+
+ return channel;
+}
+
+void CreateChannelOnIOThread(
+ ScopedPlatformHandle platform_handle,
+ scoped_refptr<system::MessagePipe> message_pipe,
+ DidCreateChannelCallback callback,
+ scoped_refptr<base::TaskRunner> callback_thread_task_runner) {
+ scoped_ptr<ChannelInfo> channel_info(
+ new ChannelInfo(MakeChannel(platform_handle.Pass(), message_pipe)));
+
+ // Hand the channel back to the embedder.
+ if (callback_thread_task_runner) {
+ callback_thread_task_runner->PostTask(FROM_HERE,
+ base::Bind(callback,
+ channel_info.release()));
+ } else {
+ callback.Run(channel_info.release());
+ }
+}
+
+} // namespace
+
+void Init() {
+ system::entrypoints::SetCore(new system::Core());
+}
+
+ScopedMessagePipeHandle CreateChannel(
+ ScopedPlatformHandle platform_handle,
+ scoped_refptr<base::TaskRunner> io_thread_task_runner,
+ DidCreateChannelCallback callback,
+ scoped_refptr<base::TaskRunner> callback_thread_task_runner) {
+ DCHECK(platform_handle.is_valid());
+
+ std::pair<scoped_refptr<system::MessagePipeDispatcher>,
+ scoped_refptr<system::MessagePipe> > remote_message_pipe =
+ system::MessagePipeDispatcher::CreateRemoteMessagePipe();
+
+ system::Core* core = system::entrypoints::GetCore();
+ DCHECK(core);
+ ScopedMessagePipeHandle rv(
+ MessagePipeHandle(core->AddDispatcher(remote_message_pipe.first)));
+ // TODO(vtl): Do we properly handle the failure case here?
+ if (rv.is_valid()) {
+ io_thread_task_runner->PostTask(FROM_HERE,
+ base::Bind(&CreateChannelOnIOThread,
+ base::Passed(&platform_handle),
+ remote_message_pipe.second,
+ callback,
+ callback_thread_task_runner));
+ }
+ return rv.Pass();
+}
+
+void DestroyChannelOnIOThread(ChannelInfo* channel_info) {
+ DCHECK(channel_info);
+ if (!channel_info->channel) {
+ // Presumably, |Init()| on the channel failed.
+ return;
+ }
+
+ channel_info->channel->Shutdown();
+ delete channel_info;
+}
+
+MojoResult CreatePlatformHandleWrapper(
+ ScopedPlatformHandle platform_handle,
+ MojoHandle* platform_handle_wrapper_handle) {
+ DCHECK(platform_handle_wrapper_handle);
+
+ scoped_refptr<system::Dispatcher> dispatcher(
+ new system::PlatformHandleDispatcher(platform_handle.Pass()));
+
+ system::Core* core = system::entrypoints::GetCore();
+ DCHECK(core);
+ MojoHandle h = core->AddDispatcher(dispatcher);
+ if (h == MOJO_HANDLE_INVALID) {
+ LOG(ERROR) << "Handle table full";
+ dispatcher->Close();
+ return MOJO_RESULT_RESOURCE_EXHAUSTED;
+ }
+
+ *platform_handle_wrapper_handle = h;
+ return MOJO_RESULT_OK;
+}
+
+MojoResult PassWrappedPlatformHandle(MojoHandle platform_handle_wrapper_handle,
+ ScopedPlatformHandle* platform_handle) {
+ DCHECK(platform_handle);
+
+ system::Core* core = system::entrypoints::GetCore();
+ DCHECK(core);
+ scoped_refptr<system::Dispatcher> dispatcher(
+ core->GetDispatcher(platform_handle_wrapper_handle));
+ if (!dispatcher)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ if (dispatcher->GetType() != system::Dispatcher::kTypePlatformHandle)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ *platform_handle = static_cast<system::PlatformHandleDispatcher*>(
+ dispatcher.get())->PassPlatformHandle().Pass();
+ return MOJO_RESULT_OK;
+}
+
+} // namespace embedder
+} // namespace mojo
diff --git a/chromium/mojo/embedder/embedder.h b/chromium/mojo/embedder/embedder.h
new file mode 100644
index 00000000000..148e5d6d1af
--- /dev/null
+++ b/chromium/mojo/embedder/embedder.h
@@ -0,0 +1,77 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EMBEDDER_EMBEDDER_H_
+#define MOJO_EMBEDDER_EMBEDDER_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/task_runner.h"
+#include "mojo/embedder/scoped_platform_handle.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace embedder {
+
+// Must be called first to initialize the (global, singleton) system.
+MOJO_SYSTEM_IMPL_EXPORT void Init();
+
+// Creates a new "channel", returning a handle to the bootstrap message pipe on
+// that channel. |platform_handle| should be an OS-dependent handle to one side
+// of a suitable bidirectional OS "pipe" (e.g., a file descriptor to a socket on
+// POSIX, a handle to a named pipe on Windows); this "pipe" should be connected
+// and ready for operation (e.g., to be written to or read from).
+// |io_thread_task_runner| should be a |TaskRunner| for the thread on which the
+// "channel" will run (read data and demultiplex).
+//
+// On completion, it will run |callback| with a pointer to a |ChannelInfo|
+// (which is meant to be opaque to the embedder). If
+// |callback_thread_task_runner| is non-null, it the callback will be posted to
+// that task runner. Otherwise, it will be run on the I/O thread directly.
+//
+// Returns an invalid |MOJO_HANDLE_INVALID| on error. Note that this will happen
+// only if, e.g., the handle table is full (operation of the channel begins
+// asynchronously and if, e.g., the other end of the "pipe" is closed, this will
+// report an error to the returned handle in the usual way).
+//
+// Notes: The handle returned is ready for use immediately, with messages
+// written to it queued. E.g., it would be perfectly valid for a message to be
+// immediately written to the returned handle and the handle closed, all before
+// the channel has begun operation on the IO thread. In this case, the channel
+// is expected to connect as usual, send the queued message, and report that the
+// handle was closed to the other side. (This message may well contain another
+// handle, so there may well still be message pipes "on" this channel.)
+//
+// TODO(vtl): Figure out channel teardown.
+struct ChannelInfo;
+typedef base::Callback<void(ChannelInfo*)> DidCreateChannelCallback;
+MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle CreateChannel(
+ ScopedPlatformHandle platform_handle,
+ scoped_refptr<base::TaskRunner> io_thread_task_runner,
+ DidCreateChannelCallback callback,
+ scoped_refptr<base::TaskRunner> callback_thread_task_runner);
+
+MOJO_SYSTEM_IMPL_EXPORT void DestroyChannelOnIOThread(
+ ChannelInfo* channel_info);
+
+// Creates a |MojoHandle| that wraps the given |PlatformHandle| (taking
+// ownership of it). This |MojoHandle| can then, e.g., be passed through message
+// pipes. Note: This takes ownership (and thus closes) |platform_handle| even on
+// failure, which is different from what you'd expect from a Mojo API, but it
+// makes for a more convenient embedder API.
+MOJO_SYSTEM_IMPL_EXPORT MojoResult CreatePlatformHandleWrapper(
+ ScopedPlatformHandle platform_handle,
+ MojoHandle* platform_handle_wrapper_handle);
+// Retrieves the |PlatformHandle| that was wrapped into a |MojoHandle| (using
+// |CreatePlatformHandleWrapper()| above). Note that the |MojoHandle| must still
+// be closed separately.
+MOJO_SYSTEM_IMPL_EXPORT MojoResult PassWrappedPlatformHandle(
+ MojoHandle platform_handle_wrapper_handle,
+ ScopedPlatformHandle* platform_handle);
+
+} // namespace embedder
+} // namespace mojo
+
+#endif // MOJO_EMBEDDER_EMBEDDER_H_
diff --git a/chromium/mojo/embedder/embedder_unittest.cc b/chromium/mojo/embedder/embedder_unittest.cc
new file mode 100644
index 00000000000..32bac635122
--- /dev/null
+++ b/chromium/mojo/embedder/embedder_unittest.cc
@@ -0,0 +1,518 @@
+// Copyright 2014 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 "mojo/embedder/embedder.h"
+
+#include <string.h>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "mojo/common/test/multiprocess_test_helper.h"
+#include "mojo/embedder/platform_channel_pair.h"
+#include "mojo/embedder/test_embedder.h"
+#include "mojo/public/c/system/core.h"
+#include "mojo/system/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace embedder {
+namespace {
+
+class ScopedTestChannel {
+ public:
+ // Creates a channel that lives on a given I/O thread (determined by the given
+ // |TaskRunner|) attached to the given |platform_handle|. After construction,
+ // |bootstrap_message_pipe()| gives the Mojo handle for the bootstrap message
+ // pipe on this channel; it is up to the caller to close this handle.
+ // Note: The I/O thread must outlive this object (and its message loop must
+ // continue pumping messages while this object is alive).
+ ScopedTestChannel(scoped_refptr<base::TaskRunner> io_thread_task_runner,
+ ScopedPlatformHandle platform_handle)
+ : io_thread_task_runner_(io_thread_task_runner),
+ bootstrap_message_pipe_(MOJO_HANDLE_INVALID),
+ did_create_channel_event_(true, false),
+ channel_info_(NULL) {
+ bootstrap_message_pipe_ = CreateChannel(
+ platform_handle.Pass(), io_thread_task_runner_,
+ base::Bind(&ScopedTestChannel::DidCreateChannel,
+ base::Unretained(this)), NULL).release().value();
+ CHECK_NE(bootstrap_message_pipe_, MOJO_HANDLE_INVALID);
+ }
+
+ // Destructor: Shuts down the channel. (As noted above, for this to happen,
+ // the I/O thread must be alive and pumping messages.)
+ ~ScopedTestChannel() {
+ system::test::PostTaskAndWait(
+ io_thread_task_runner_,
+ FROM_HERE,
+ base::Bind(&ScopedTestChannel::DestroyChannel, base::Unretained(this)));
+ }
+
+ // Waits for channel creation to be completed.
+ void WaitForChannelCreationCompletion() {
+ did_create_channel_event_.Wait();
+ }
+
+ MojoHandle bootstrap_message_pipe() const { return bootstrap_message_pipe_; }
+
+ // Call only after |WaitForChannelCreationCompletion()|. Use only to check
+ // that it's not null.
+ const ChannelInfo* channel_info() const { return channel_info_; }
+
+ private:
+ void DidCreateChannel(ChannelInfo* channel_info) {
+ CHECK(channel_info);
+ CHECK(!channel_info_);
+ channel_info_ = channel_info;
+ did_create_channel_event_.Signal();
+ }
+
+ void DestroyChannel() {
+ CHECK(channel_info_);
+ DestroyChannelOnIOThread(channel_info_);
+ channel_info_ = NULL;
+ }
+
+ scoped_refptr<base::TaskRunner> io_thread_task_runner_;
+
+ // Valid from creation until whenever it gets closed (by the "owner" of this
+ // object).
+ // Note: We don't want use the C++ wrappers here, since we want to test the
+ // API at the lowest level.
+ MojoHandle bootstrap_message_pipe_;
+
+ // Set after channel creation has been completed (i.e., the callback to
+ // |CreateChannel()| has been called).
+ base::WaitableEvent did_create_channel_event_;
+
+ // Valid after channel creation completion until destruction.
+ ChannelInfo* channel_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedTestChannel);
+};
+
+class EmbedderTest : public testing::Test {
+ public:
+ EmbedderTest() : test_io_thread_(system::test::TestIOThread::kAutoStart) {}
+ virtual ~EmbedderTest() {}
+
+ protected:
+ system::test::TestIOThread* test_io_thread() { return &test_io_thread_; }
+
+ private:
+ system::test::TestIOThread test_io_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(EmbedderTest);
+};
+
+TEST_F(EmbedderTest, ChannelsBasic) {
+ Init();
+
+ {
+ PlatformChannelPair channel_pair;
+ ScopedTestChannel server_channel(test_io_thread()->task_runner(),
+ channel_pair.PassServerHandle());
+ MojoHandle server_mp = server_channel.bootstrap_message_pipe();
+ EXPECT_NE(server_mp, MOJO_HANDLE_INVALID);
+ ScopedTestChannel client_channel(test_io_thread()->task_runner(),
+ channel_pair.PassClientHandle());
+ MojoHandle client_mp = client_channel.bootstrap_message_pipe();
+ EXPECT_NE(client_mp, MOJO_HANDLE_INVALID);
+
+ // We can write to a message pipe handle immediately.
+ const char kHello[] = "hello";
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWriteMessage(server_mp, kHello,
+ static_cast<uint32_t>(sizeof(kHello)), NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Now wait for the other side to become readable.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE));
+
+ char buffer[1000] = {};
+ uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoReadMessage(client_mp, buffer, &num_bytes, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(sizeof(kHello), num_bytes);
+ EXPECT_STREQ(kHello, buffer);
+
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(server_mp));
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(client_mp));
+
+ // By this point, these waits should basically be no-ops (since we've waited
+ // for the client message pipe to become readable, which implies that both
+ // the server and client channels were completely created).
+ server_channel.WaitForChannelCreationCompletion();
+ client_channel.WaitForChannelCreationCompletion();
+ EXPECT_TRUE(server_channel.channel_info() != NULL);
+ EXPECT_TRUE(client_channel.channel_info() != NULL);
+ }
+
+ EXPECT_TRUE(test::Shutdown());
+}
+
+TEST_F(EmbedderTest, ChannelsHandlePassing) {
+ Init();
+
+ {
+ PlatformChannelPair channel_pair;
+ ScopedTestChannel server_channel(test_io_thread()->task_runner(),
+ channel_pair.PassServerHandle());
+ MojoHandle server_mp = server_channel.bootstrap_message_pipe();
+ EXPECT_NE(server_mp, MOJO_HANDLE_INVALID);
+ ScopedTestChannel client_channel(test_io_thread()->task_runner(),
+ channel_pair.PassClientHandle());
+ MojoHandle client_mp = client_channel.bootstrap_message_pipe();
+ EXPECT_NE(client_mp, MOJO_HANDLE_INVALID);
+
+ MojoHandle h0, h1;
+ EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(NULL, &h0, &h1));
+
+ // Write a message to |h0| (attaching nothing).
+ const char kHello[] = "hello";
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWriteMessage(h0, kHello,
+ static_cast<uint32_t>(sizeof(kHello)), NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Write one message to |server_mp|, attaching |h1|.
+ const char kWorld[] = "world!!!";
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWriteMessage(server_mp, kWorld,
+ static_cast<uint32_t>(sizeof(kWorld)), &h1, 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ h1 = MOJO_HANDLE_INVALID;
+
+ // Write another message to |h0|.
+ const char kFoo[] = "foo";
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWriteMessage(h0, kFoo,
+ static_cast<uint32_t>(sizeof(kFoo)), NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Wait for |client_mp| to become readable.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE));
+
+ // Read a message from |client_mp|.
+ char buffer[1000] = {};
+ uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
+ MojoHandle handles[10] = {};
+ uint32_t num_handles = arraysize(handles);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoReadMessage(client_mp, buffer, &num_bytes, handles,
+ &num_handles, MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(sizeof(kWorld), num_bytes);
+ EXPECT_STREQ(kWorld, buffer);
+ EXPECT_EQ(1u, num_handles);
+ EXPECT_NE(handles[0], MOJO_HANDLE_INVALID);
+ h1 = handles[0];
+
+ // Wait for |h1| to become readable.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWait(h1, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE));
+
+ // Read a message from |h1|.
+ memset(buffer, 0, sizeof(buffer));
+ num_bytes = static_cast<uint32_t>(sizeof(buffer));
+ memset(handles, 0, sizeof(handles));
+ num_handles = arraysize(handles);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoReadMessage(h1, buffer, &num_bytes, handles, &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(sizeof(kHello), num_bytes);
+ EXPECT_STREQ(kHello, buffer);
+ EXPECT_EQ(0u, num_handles);
+
+ // Wait for |h1| to become readable (again).
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWait(h1, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE));
+
+ // Read the second message from |h1|.
+ memset(buffer, 0, sizeof(buffer));
+ num_bytes = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoReadMessage(h1, buffer, &num_bytes, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(sizeof(kFoo), num_bytes);
+ EXPECT_STREQ(kFoo, buffer);
+
+ // Write a message to |h1|.
+ const char kBarBaz[] = "barbaz";
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWriteMessage(h1, kBarBaz,
+ static_cast<uint32_t>(sizeof(kBarBaz)), NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Wait for |h0| to become readable.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWait(h0, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE));
+
+ // Read a message from |h0|.
+ memset(buffer, 0, sizeof(buffer));
+ num_bytes = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoReadMessage(h0, buffer, &num_bytes, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(sizeof(kBarBaz), num_bytes);
+ EXPECT_STREQ(kBarBaz, buffer);
+
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(server_mp));
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(client_mp));
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0));
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1));
+
+ server_channel.WaitForChannelCreationCompletion();
+ client_channel.WaitForChannelCreationCompletion();
+ EXPECT_TRUE(server_channel.channel_info() != NULL);
+ EXPECT_TRUE(client_channel.channel_info() != NULL);
+ }
+
+ EXPECT_TRUE(test::Shutdown());
+}
+
+// The sequence of messages sent is:
+// server_mp client_mp mp0 mp1 mp2 mp3
+// 1. "hello"
+// 2. "world!"
+// 3. "FOO"
+// 4. "Bar"+mp1
+// 5. (close)
+// 6. (close)
+// 7. "baz"
+// 8. (closed)
+// 9. "quux"+mp2
+// 10. (close)
+// 11. (wait/cl.)
+// 12. (wait/cl.)
+TEST_F(EmbedderTest, MultiprocessChannels) {
+ Init();
+ mojo::test::MultiprocessTestHelper multiprocess_test_helper;
+ multiprocess_test_helper.StartChild("MultiprocessChannelsClient");
+
+ {
+ ScopedTestChannel server_channel(
+ test_io_thread()->task_runner(),
+ multiprocess_test_helper.server_platform_handle.Pass());
+ MojoHandle server_mp = server_channel.bootstrap_message_pipe();
+ EXPECT_NE(server_mp, MOJO_HANDLE_INVALID);
+ server_channel.WaitForChannelCreationCompletion();
+ EXPECT_TRUE(server_channel.channel_info() != NULL);
+
+ // 1. Write a message to |server_mp| (attaching nothing).
+ const char kHello[] = "hello";
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWriteMessage(server_mp, kHello,
+ static_cast<uint32_t>(sizeof(kHello)), NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // TODO(vtl): If the scope were ended immediately here (maybe after closing
+ // |server_mp|), we die with a fatal error in |Channel::HandleLocalError()|.
+
+ // 2. Read a message from |server_mp|.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWait(server_mp, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE));
+ char buffer[1000] = {};
+ uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoReadMessage(server_mp, buffer, &num_bytes, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ const char kWorld[] = "world!";
+ EXPECT_EQ(sizeof(kWorld), num_bytes);
+ EXPECT_STREQ(kWorld, buffer);
+
+ // Create a new message pipe (endpoints |mp0| and |mp1|).
+ MojoHandle mp0, mp1;
+ EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(NULL, &mp0, &mp1));
+
+ // 3. Write something to |mp0|.
+ const char kFoo[] = "FOO";
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWriteMessage(mp0, kFoo,
+ static_cast<uint32_t>(sizeof(kFoo)), NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // 4. Write a message to |server_mp|, attaching |mp1|.
+ const char kBar[] = "Bar";
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWriteMessage(server_mp, kBar,
+ static_cast<uint32_t>(sizeof(kBar)), &mp1, 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ mp1 = MOJO_HANDLE_INVALID;
+
+ // 5. Close |server_mp|.
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(server_mp));
+
+ // 9. Read a message from |mp0|, which should have |mp2| attached.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWait(mp0, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE));
+ memset(buffer, 0, sizeof(buffer));
+ num_bytes = static_cast<uint32_t>(sizeof(buffer));
+ MojoHandle mp2 = MOJO_HANDLE_INVALID;
+ uint32_t num_handles = 1;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoReadMessage(mp0, buffer, &num_bytes, &mp2, &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ const char kQuux[] = "quux";
+ EXPECT_EQ(sizeof(kQuux), num_bytes);
+ EXPECT_STREQ(kQuux, buffer);
+ EXPECT_EQ(1u, num_handles);
+ EXPECT_NE(mp2, MOJO_HANDLE_INVALID);
+
+ // 7. Read a message from |mp2|.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWait(mp2, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE));
+ memset(buffer, 0, sizeof(buffer));
+ num_bytes = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoReadMessage(mp2, buffer, &num_bytes, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ const char kBaz[] = "baz";
+ EXPECT_EQ(sizeof(kBaz), num_bytes);
+ EXPECT_STREQ(kBaz, buffer);
+
+ // 10. Close |mp0|.
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mp0));
+
+ // 12. Wait on |mp2| (which should eventually fail) and then close it.
+// TODO(vtl): crbug.com/351768
+#if 0
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ MojoWait(mp2, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE));
+#endif
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mp2));
+ }
+
+ EXPECT_TRUE(multiprocess_test_helper.WaitForChildTestShutdown());
+ EXPECT_TRUE(test::Shutdown());
+}
+
+MOJO_MULTIPROCESS_TEST_CHILD_TEST(MultiprocessChannelsClient) {
+ embedder::ScopedPlatformHandle client_platform_handle =
+ mojo::test::MultiprocessTestHelper::client_platform_handle.Pass();
+ EXPECT_TRUE(client_platform_handle.is_valid());
+
+ system::test::TestIOThread
+ test_io_thread(system::test::TestIOThread::kAutoStart);
+ Init();
+
+ {
+ ScopedTestChannel client_channel(test_io_thread.task_runner(),
+ client_platform_handle.Pass());
+ MojoHandle client_mp = client_channel.bootstrap_message_pipe();
+ EXPECT_NE(client_mp, MOJO_HANDLE_INVALID);
+ client_channel.WaitForChannelCreationCompletion();
+ CHECK(client_channel.channel_info() != NULL);
+
+ // 1. Read the first message from |client_mp|.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE));
+ char buffer[1000] = {};
+ uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoReadMessage(client_mp, buffer, &num_bytes, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ const char kHello[] = "hello";
+ EXPECT_EQ(sizeof(kHello), num_bytes);
+ EXPECT_STREQ(kHello, buffer);
+
+ // 2. Write a message to |client_mp| (attaching nothing).
+ const char kWorld[] = "world!";
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWriteMessage(client_mp, kWorld,
+ static_cast<uint32_t>(sizeof(kWorld)), NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // 4. Read a message from |client_mp|, which should have |mp1| attached.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE));
+ // TODO(vtl): If the scope were to end here (and |client_mp| closed), we'd
+ // die (again due to |Channel::HandleLocalError()|).
+ memset(buffer, 0, sizeof(buffer));
+ num_bytes = static_cast<uint32_t>(sizeof(buffer));
+ MojoHandle mp1 = MOJO_HANDLE_INVALID;
+ uint32_t num_handles = 1;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoReadMessage(client_mp, buffer, &num_bytes, &mp1, &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ const char kBar[] = "Bar";
+ EXPECT_EQ(sizeof(kBar), num_bytes);
+ EXPECT_STREQ(kBar, buffer);
+ EXPECT_EQ(1u, num_handles);
+ EXPECT_NE(mp1, MOJO_HANDLE_INVALID);
+ // TODO(vtl): If the scope were to end here (and the two handles closed),
+ // we'd die due to |Channel::RunRemoteMessagePipeEndpoint()| not handling
+ // write errors (assuming the parent had closed the pipe).
+
+ // 6. Close |client_mp|.
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(client_mp));
+
+ // Create a new message pipe (endpoints |mp2| and |mp3|).
+ MojoHandle mp2, mp3;
+ EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(NULL, &mp2, &mp3));
+
+ // 7. Write a message to |mp3|.
+ const char kBaz[] = "baz";
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWriteMessage(mp3, kBaz,
+ static_cast<uint32_t>(sizeof(kBaz)), NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // 8. Close |mp3|.
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mp3));
+
+ // 9. Write a message to |mp1|, attaching |mp2|.
+ const char kQuux[] = "quux";
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWriteMessage(mp1, kQuux,
+ static_cast<uint32_t>(sizeof(kQuux)), &mp2, 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ mp2 = MOJO_HANDLE_INVALID;
+
+ // 3. Read a message from |mp1|.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE));
+ memset(buffer, 0, sizeof(buffer));
+ num_bytes = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoReadMessage(mp1, buffer, &num_bytes, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ const char kFoo[] = "FOO";
+ EXPECT_EQ(sizeof(kFoo), num_bytes);
+ EXPECT_STREQ(kFoo, buffer);
+
+ // 11. Wait on |mp1| (which should eventually fail) and then close it.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE));
+ EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mp1));
+ }
+
+ EXPECT_TRUE(test::Shutdown());
+}
+
+// TODO(vtl): Test immediate write & close.
+// TODO(vtl): Test broken-connection cases.
+
+} // namespace
+} // namespace embedder
+} // namespace mojo
diff --git a/chromium/mojo/embedder/platform_channel_pair.cc b/chromium/mojo/embedder/platform_channel_pair.cc
new file mode 100644
index 00000000000..783ced8b711
--- /dev/null
+++ b/chromium/mojo/embedder/platform_channel_pair.cc
@@ -0,0 +1,32 @@
+// Copyright 2014 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 "mojo/embedder/platform_channel_pair.h"
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace embedder {
+
+const char PlatformChannelPair::kMojoPlatformChannelHandleSwitch[] =
+ "mojo-platform-channel-handle";
+
+PlatformChannelPair::~PlatformChannelPair() {
+}
+
+ScopedPlatformHandle PlatformChannelPair::PassServerHandle() {
+ return server_handle_.Pass();
+}
+
+ScopedPlatformHandle PlatformChannelPair::PassClientHandle() {
+ return client_handle_.Pass();
+}
+
+void PlatformChannelPair::ChildProcessLaunched() {
+ DCHECK(client_handle_.is_valid());
+ client_handle_.reset();
+}
+
+} // namespace embedder
+} // namespace mojo
diff --git a/chromium/mojo/embedder/platform_channel_pair.h b/chromium/mojo/embedder/platform_channel_pair.h
new file mode 100644
index 00000000000..126bd3d98e1
--- /dev/null
+++ b/chromium/mojo/embedder/platform_channel_pair.h
@@ -0,0 +1,94 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EMBEDDER_PLATFORM_CHANNEL_PAIR_H_
+#define MOJO_EMBEDDER_PLATFORM_CHANNEL_PAIR_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/process/launch.h"
+#include "build/build_config.h"
+#include "mojo/embedder/scoped_platform_handle.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace base {
+class CommandLine;
+}
+
+namespace mojo {
+namespace embedder {
+
+// It would be nice to refactor base/process/launch.h to have a more platform-
+// independent way of representing handles that are passed to child processes.
+#if defined(OS_WIN)
+typedef base::HandlesToInheritVector HandlePassingInformation;
+#elif defined(OS_POSIX)
+typedef base::FileHandleMappingVector HandlePassingInformation;
+#else
+#error "Unsupported."
+#endif
+
+// This is used to create a pair of |PlatformHandle|s that are connected by a
+// suitable (platform-specific) bidirectional "pipe" (e.g., socket on POSIX,
+// named pipe on Windows). The resulting handles can then be used in the same
+// process (e.g., in tests) or between processes. (The "server" handle is the
+// one that will be used in the process that created the pair, whereas the
+// "client" handle is the one that will be used in a different process.)
+//
+// This class provides facilities for passing the client handle to a child
+// process. The parent should call |PrepareToPassClientHandlelToChildProcess()|
+// to get the data needed to do this, spawn the child using that data, and then
+// call |ChildProcessLaunched()|. Note that on Windows this facility (will) only
+// work on Vista and later (TODO(vtl)).
+//
+// Note: |PlatformChannelPair()|, |PassClientHandleFromParentProcess()| and
+// |PrepareToPassClientHandleToChildProcess()| have platform-specific
+// implementations.
+//
+// Note: On POSIX platforms, to write to the "pipe", use
+// |PlatformChannel{Write,Writev}()| (from platform_channel_utils_posix.h)
+// instead of |write()|, |writev()|, etc. Otherwise, you have to worry about
+// platform differences in suppressing |SIGPIPE|.
+class MOJO_SYSTEM_IMPL_EXPORT PlatformChannelPair {
+ public:
+ PlatformChannelPair();
+ ~PlatformChannelPair();
+
+ ScopedPlatformHandle PassServerHandle();
+
+ // For in-process use (e.g., in tests or to pass over another channel).
+ ScopedPlatformHandle PassClientHandle();
+
+ // To be called in the child process, after the parent process called
+ // |PrepareToPassClientHandleToChildProcess()| and launched the child (using
+ // the provided data), to create a client handle connected to the server
+ // handle (in the parent process).
+ static ScopedPlatformHandle PassClientHandleFromParentProcess(
+ const base::CommandLine& command_line);
+
+ // Prepares to pass the client channel to a new child process, to be launched
+ // using |LaunchProcess()| (from base/launch.h). Modifies |*command_line| and
+ // |*handle_passing_info| as needed.
+ // Note: For Windows, this method only works on Vista and later.
+ void PrepareToPassClientHandleToChildProcess(
+ base::CommandLine* command_line,
+ HandlePassingInformation* handle_passing_info) const;
+
+ // To be called once the child process has been successfully launched, to do
+ // any cleanup necessary.
+ void ChildProcessLaunched();
+
+ private:
+ static const char kMojoPlatformChannelHandleSwitch[];
+
+ ScopedPlatformHandle server_handle_;
+ ScopedPlatformHandle client_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformChannelPair);
+};
+
+} // namespace embedder
+} // namespace mojo
+
+#endif // MOJO_EMBEDDER_PLATFORM_CHANNEL_PAIR_H_
diff --git a/chromium/mojo/embedder/platform_channel_pair_posix.cc b/chromium/mojo/embedder/platform_channel_pair_posix.cc
new file mode 100644
index 00000000000..4eaea474c0b
--- /dev/null
+++ b/chromium/mojo/embedder/platform_channel_pair_posix.cc
@@ -0,0 +1,111 @@
+// Copyright 2014 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 "mojo/embedder/platform_channel_pair.h"
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/posix/global_descriptors.h"
+#include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
+#include "mojo/embedder/platform_handle.h"
+
+namespace mojo {
+namespace embedder {
+
+namespace {
+
+bool IsTargetDescriptorUsed(
+ const base::FileHandleMappingVector& file_handle_mapping,
+ int target_fd) {
+ for (size_t i = 0; i < file_handle_mapping.size(); i++) {
+ if (file_handle_mapping[i].second == target_fd)
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+PlatformChannelPair::PlatformChannelPair() {
+ // Create the Unix domain socket and set the ends to nonblocking.
+ int fds[2];
+ // TODO(vtl): Maybe fail gracefully if |socketpair()| fails.
+ PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0);
+ PCHECK(fcntl(fds[0], F_SETFL, O_NONBLOCK) == 0);
+ PCHECK(fcntl(fds[1], F_SETFL, O_NONBLOCK) == 0);
+
+#if defined(OS_MACOSX)
+ // This turns off |SIGPIPE| when writing to a closed socket (causing it to
+ // fail with |EPIPE| instead). On Linux, we have to use |send...()| with
+ // |MSG_NOSIGNAL| -- which is not supported on Mac -- instead.
+ int no_sigpipe = 1;
+ PCHECK(setsockopt(fds[0], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe,
+ sizeof(no_sigpipe)) == 0);
+ PCHECK(setsockopt(fds[1], SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe,
+ sizeof(no_sigpipe)) == 0);
+#endif // defined(OS_MACOSX)
+
+ server_handle_.reset(PlatformHandle(fds[0]));
+ DCHECK(server_handle_.is_valid());
+ client_handle_.reset(PlatformHandle(fds[1]));
+ DCHECK(client_handle_.is_valid());
+}
+
+// static
+ScopedPlatformHandle PlatformChannelPair::PassClientHandleFromParentProcess(
+ const base::CommandLine& command_line) {
+ std::string client_fd_string =
+ command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
+ int client_fd = -1;
+ if (client_fd_string.empty() ||
+ !base::StringToInt(client_fd_string, &client_fd) ||
+ client_fd < base::GlobalDescriptors::kBaseDescriptor) {
+ LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch;
+ return ScopedPlatformHandle();
+ }
+
+ return ScopedPlatformHandle(PlatformHandle(client_fd));
+}
+
+void PlatformChannelPair::PrepareToPassClientHandleToChildProcess(
+ base::CommandLine* command_line,
+ base::FileHandleMappingVector* handle_passing_info) const {
+ DCHECK(command_line);
+ DCHECK(handle_passing_info);
+ // This is an arbitrary sanity check. (Note that this guarantees that the loop
+ // below will terminate sanely.)
+ CHECK_LT(handle_passing_info->size(), 1000u);
+
+ DCHECK(client_handle_.is_valid());
+
+ // Find a suitable FD to map our client handle to in the child process.
+ // This has quadratic time complexity in the size of |*handle_passing_info|,
+ // but |*handle_passing_info| should be very small (usually/often empty).
+ int target_fd = base::GlobalDescriptors::kBaseDescriptor;
+ while (IsTargetDescriptorUsed(*handle_passing_info, target_fd))
+ target_fd++;
+
+ handle_passing_info->push_back(std::pair<int, int>(client_handle_.get().fd,
+ target_fd));
+ // Log a warning if the command line already has the switch, but "clobber" it
+ // anyway, since it's reasonably likely that all the switches were just copied
+ // from the parent.
+ LOG_IF(WARNING, command_line->HasSwitch(kMojoPlatformChannelHandleSwitch))
+ << "Child command line already has switch --"
+ << kMojoPlatformChannelHandleSwitch << "="
+ << command_line->GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
+ // (Any existing switch won't actually be removed from the command line, but
+ // the last one appended takes precedence.)
+ command_line->AppendSwitchASCII(kMojoPlatformChannelHandleSwitch,
+ base::IntToString(target_fd));
+}
+
+} // namespace embedder
+} // namespace mojo
diff --git a/chromium/mojo/embedder/platform_channel_pair_posix_unittest.cc b/chromium/mojo/embedder/platform_channel_pair_posix_unittest.cc
new file mode 100644
index 00000000000..6568e7acdfb
--- /dev/null
+++ b/chromium/mojo/embedder/platform_channel_pair_posix_unittest.cc
@@ -0,0 +1,243 @@
+// Copyright 2014 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 "mojo/embedder/platform_channel_pair.h"
+
+#include <errno.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <deque>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "mojo/common/test/test_utils.h"
+#include "mojo/embedder/platform_channel_utils_posix.h"
+#include "mojo/embedder/platform_handle.h"
+#include "mojo/embedder/platform_handle_vector.h"
+#include "mojo/embedder/scoped_platform_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace embedder {
+namespace {
+
+void WaitReadable(PlatformHandle h) {
+ struct pollfd pfds = {};
+ pfds.fd = h.fd;
+ pfds.events = POLLIN;
+ CHECK_EQ(poll(&pfds, 1, -1), 1);
+}
+
+class PlatformChannelPairPosixTest : public testing::Test {
+ public:
+ PlatformChannelPairPosixTest() {}
+ virtual ~PlatformChannelPairPosixTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ // Make sure |SIGPIPE| isn't being ignored.
+ struct sigaction action = {};
+ action.sa_handler = SIG_DFL;
+ ASSERT_EQ(0, sigaction(SIGPIPE, &action, &old_action_));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ // Restore the |SIGPIPE| handler.
+ ASSERT_EQ(0, sigaction(SIGPIPE, &old_action_, NULL));
+ }
+
+ private:
+ struct sigaction old_action_;
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformChannelPairPosixTest);
+};
+
+TEST_F(PlatformChannelPairPosixTest, NoSigPipe) {
+ PlatformChannelPair channel_pair;
+ ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass();
+ ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass();
+
+ // Write to the client.
+ static const char kHello[] = "hello";
+ EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
+ write(client_handle.get().fd, kHello, sizeof(kHello)));
+
+ // Close the client.
+ client_handle.reset();
+
+ // Read from the server; this should be okay.
+ char buffer[100] = {};
+ EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
+ read(server_handle.get().fd, buffer, sizeof(buffer)));
+ EXPECT_STREQ(kHello, buffer);
+
+ // Try reading again.
+ ssize_t result = read(server_handle.get().fd, buffer, sizeof(buffer));
+ // We should probably get zero (for "end of file"), but -1 would also be okay.
+ EXPECT_TRUE(result == 0 || result == -1);
+ if (result == -1)
+ PLOG(WARNING) << "read (expected 0 for EOF)";
+
+ // Test our replacement for |write()|/|send()|.
+ result = PlatformChannelWrite(server_handle.get(), kHello, sizeof(kHello));
+ EXPECT_EQ(-1, result);
+ if (errno != EPIPE)
+ PLOG(WARNING) << "write (expected EPIPE)";
+
+ // Test our replacement for |writev()|/|sendv()|.
+ struct iovec iov[2] = {
+ { const_cast<char*>(kHello), sizeof(kHello) },
+ { const_cast<char*>(kHello), sizeof(kHello) }
+ };
+ result = PlatformChannelWritev(server_handle.get(), iov, 2);
+ EXPECT_EQ(-1, result);
+ if (errno != EPIPE)
+ PLOG(WARNING) << "write (expected EPIPE)";
+}
+
+TEST_F(PlatformChannelPairPosixTest, SendReceiveData) {
+ PlatformChannelPair channel_pair;
+ ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass();
+ ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass();
+
+ for (size_t i = 0; i < 10; i++) {
+ std::string send_string(1 << i, 'A' + i);
+
+ EXPECT_EQ(static_cast<ssize_t>(send_string.size()),
+ PlatformChannelWrite(server_handle.get(), send_string.data(),
+ send_string.size()));
+
+ WaitReadable(client_handle.get());
+
+ char buf[10000] = {};
+ std::deque<PlatformHandle> received_handles;
+ ssize_t result = PlatformChannelRecvmsg(client_handle.get(), buf,
+ sizeof(buf), &received_handles);
+ EXPECT_EQ(static_cast<ssize_t>(send_string.size()), result);
+ EXPECT_EQ(send_string, std::string(buf, static_cast<size_t>(result)));
+ EXPECT_TRUE(received_handles.empty());
+ }
+}
+
+TEST_F(PlatformChannelPairPosixTest, SendReceiveFDs) {
+ static const char kHello[] = "hello";
+
+ PlatformChannelPair channel_pair;
+ ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass();
+ ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass();
+
+ for (size_t i = 1; i < kPlatformChannelMaxNumHandles; i++) {
+ // Make |i| files, with the j-th file consisting of j copies of the digit i.
+ PlatformHandleVector platform_handles;
+ for (size_t j = 1; j <= i; j++) {
+ base::FilePath ignored;
+ base::ScopedFILE fp(base::CreateAndOpenTemporaryFile(&ignored));
+ ASSERT_TRUE(fp);
+ fwrite(std::string(j, '0' + i).data(), 1, j, fp.get());
+ platform_handles.push_back(
+ test::PlatformHandleFromFILE(fp.Pass()).release());
+ ASSERT_TRUE(platform_handles.back().is_valid());
+ }
+
+ // Send the FDs (+ "hello").
+ struct iovec iov = { const_cast<char*>(kHello), sizeof(kHello) };
+ // We assume that the |sendmsg()| actually sends all the data.
+ EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
+ PlatformChannelSendmsgWithHandles(server_handle.get(), &iov, 1,
+ &platform_handles[0],
+ platform_handles.size()));
+
+ WaitReadable(client_handle.get());
+
+ char buf[100] = {};
+ std::deque<PlatformHandle> received_handles;
+ // We assume that the |recvmsg()| actually reads all the data.
+ EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
+ PlatformChannelRecvmsg(client_handle.get(), buf, sizeof(buf),
+ &received_handles));
+ EXPECT_STREQ(kHello, buf);
+ EXPECT_EQ(i, received_handles.size());
+
+ for (size_t j = 0; !received_handles.empty(); j++) {
+ base::ScopedFILE fp(test::FILEFromPlatformHandle(
+ ScopedPlatformHandle(received_handles.front()), "rb"));
+ received_handles.pop_front();
+ ASSERT_TRUE(fp);
+ rewind(fp.get());
+ char read_buf[100];
+ size_t bytes_read = fread(read_buf, 1, sizeof(read_buf), fp.get());
+ EXPECT_EQ(j + 1, bytes_read);
+ EXPECT_EQ(std::string(j + 1, '0' + i), std::string(read_buf, bytes_read));
+ }
+ }
+}
+
+TEST_F(PlatformChannelPairPosixTest, AppendReceivedFDs) {
+ static const char kHello[] = "hello";
+
+ PlatformChannelPair channel_pair;
+ ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass();
+ ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass();
+
+ const std::string file_contents("hello world");
+
+ {
+ base::FilePath ignored;
+ base::ScopedFILE fp(base::CreateAndOpenTemporaryFile(&ignored));
+ ASSERT_TRUE(fp);
+ fwrite(file_contents.data(), 1, file_contents.size(), fp.get());
+ PlatformHandleVector platform_handles;
+ platform_handles.push_back(
+ test::PlatformHandleFromFILE(fp.Pass()).release());
+ ASSERT_TRUE(platform_handles.back().is_valid());
+
+ // Send the FD (+ "hello").
+ struct iovec iov = { const_cast<char*>(kHello), sizeof(kHello) };
+ // We assume that the |sendmsg()| actually sends all the data.
+ EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
+ PlatformChannelSendmsgWithHandles(server_handle.get(), &iov, 1,
+ &platform_handles[0],
+ platform_handles.size()));
+ }
+
+ WaitReadable(client_handle.get());
+
+ // Start with an invalid handle in the deque.
+ std::deque<PlatformHandle> received_handles;
+ received_handles.push_back(PlatformHandle());
+
+ char buf[100] = {};
+ // We assume that the |recvmsg()| actually reads all the data.
+ EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
+ PlatformChannelRecvmsg(client_handle.get(), buf, sizeof(buf),
+ &received_handles));
+ EXPECT_STREQ(kHello, buf);
+ ASSERT_EQ(2u, received_handles.size());
+ EXPECT_FALSE(received_handles[0].is_valid());
+ EXPECT_TRUE(received_handles[1].is_valid());
+
+ {
+ base::ScopedFILE fp(test::FILEFromPlatformHandle(
+ ScopedPlatformHandle(received_handles[1]), "rb"));
+ received_handles[1] = PlatformHandle();
+ ASSERT_TRUE(fp);
+ rewind(fp.get());
+ char read_buf[100];
+ size_t bytes_read = fread(read_buf, 1, sizeof(read_buf), fp.get());
+ EXPECT_EQ(file_contents.size(), bytes_read);
+ EXPECT_EQ(file_contents, std::string(read_buf, bytes_read));
+ }
+}
+
+} // namespace
+} // namespace embedder
+} // namespace mojo
diff --git a/chromium/mojo/embedder/platform_channel_pair_win.cc b/chromium/mojo/embedder/platform_channel_pair_win.cc
new file mode 100644
index 00000000000..f3eee9277b3
--- /dev/null
+++ b/chromium/mojo/embedder/platform_channel_pair_win.cc
@@ -0,0 +1,117 @@
+// Copyright 2014 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 "mojo/embedder/platform_channel_pair.h"
+
+#include <windows.h>
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/win/windows_version.h"
+#include "mojo/embedder/platform_handle.h"
+
+namespace mojo {
+namespace embedder {
+
+namespace {
+
+std::wstring GeneratePipeName() {
+ return base::StringPrintf(
+ L"\\\\.\\pipe\\mojo.%u.%u.%I64u",
+ GetCurrentProcessId(), GetCurrentThreadId(), base::RandUint64());
+}
+
+} // namespace
+
+PlatformChannelPair::PlatformChannelPair() {
+ std::wstring pipe_name = GeneratePipeName();
+
+ const DWORD kOpenMode = PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
+ FILE_FLAG_FIRST_PIPE_INSTANCE;
+ const DWORD kPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE;
+ server_handle_.reset(PlatformHandle(
+ CreateNamedPipeW(pipe_name.c_str(),
+ kOpenMode,
+ kPipeMode,
+ 1, // Max instances.
+ 4096, // Out buffer size.
+ 4096, // In buffer size.
+ 5000, // Timeout in milliseconds.
+ NULL))); // Default security descriptor.
+ PCHECK(server_handle_.is_valid());
+
+ const DWORD kDesiredAccess = GENERIC_READ | GENERIC_WRITE;
+ // The SECURITY_ANONYMOUS flag means that the server side cannot impersonate
+ // the client.
+ const DWORD kFlags = SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS |
+ FILE_FLAG_OVERLAPPED;
+ // Allow the handle to be inherited by child processes.
+ SECURITY_ATTRIBUTES security_attributes = {
+ sizeof(SECURITY_ATTRIBUTES), NULL, TRUE
+ };
+ client_handle_.reset(PlatformHandle(
+ CreateFileW(pipe_name.c_str(),
+ kDesiredAccess,
+ 0, // No sharing.
+ &security_attributes,
+ OPEN_EXISTING,
+ kFlags,
+ NULL))); // No template file.
+ PCHECK(client_handle_.is_valid());
+
+ // Since a client has connected, ConnectNamedPipe() should return zero and
+ // GetLastError() should return ERROR_PIPE_CONNECTED.
+ CHECK(!ConnectNamedPipe(server_handle_.get().handle, NULL));
+ PCHECK(GetLastError() == ERROR_PIPE_CONNECTED);
+}
+
+// static
+ScopedPlatformHandle PlatformChannelPair::PassClientHandleFromParentProcess(
+ const base::CommandLine& command_line) {
+ std::string client_handle_string =
+ command_line.GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
+
+ int client_handle_value = 0;
+ if (client_handle_string.empty() ||
+ !base::StringToInt(client_handle_string, &client_handle_value)) {
+ LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch;
+ return ScopedPlatformHandle();
+ }
+
+ return ScopedPlatformHandle(
+ PlatformHandle(LongToHandle(client_handle_value)));
+}
+
+void PlatformChannelPair::PrepareToPassClientHandleToChildProcess(
+ base::CommandLine* command_line,
+ base::HandlesToInheritVector* handle_passing_info) const {
+ DCHECK(command_line);
+ DCHECK(handle_passing_info);
+ DCHECK(client_handle_.is_valid());
+
+ CHECK_GE(base::win::GetVersion(), base::win::VERSION_VISTA);
+
+ handle_passing_info->push_back(client_handle_.get().handle);
+
+ // Log a warning if the command line already has the switch, but "clobber" it
+ // anyway, since it's reasonably likely that all the switches were just copied
+ // from the parent.
+ LOG_IF(WARNING, command_line->HasSwitch(kMojoPlatformChannelHandleSwitch))
+ << "Child command line already has switch --"
+ << kMojoPlatformChannelHandleSwitch << "="
+ << command_line->GetSwitchValueASCII(kMojoPlatformChannelHandleSwitch);
+ // (Any existing switch won't actually be removed from the command line, but
+ // the last one appended takes precedence.)
+ command_line->AppendSwitchASCII(
+ kMojoPlatformChannelHandleSwitch,
+ base::IntToString(HandleToLong(client_handle_.get().handle)));
+}
+
+} // namespace embedder
+} // namespace mojo
diff --git a/chromium/mojo/embedder/platform_channel_utils_posix.cc b/chromium/mojo/embedder/platform_channel_utils_posix.cc
new file mode 100644
index 00000000000..8b7c36222c0
--- /dev/null
+++ b/chromium/mojo/embedder/platform_channel_utils_posix.cc
@@ -0,0 +1,187 @@
+// Copyright 2014 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 "mojo/embedder/platform_channel_utils_posix.h"
+
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+
+namespace mojo {
+namespace embedder {
+
+// On Linux, |SIGPIPE| is suppressed by passing |MSG_NOSIGNAL| to
+// |send()|/|sendmsg()|. (There is no way of suppressing |SIGPIPE| on
+// |write()|/|writev().) On Mac, |SIGPIPE| is suppressed by setting the
+// |SO_NOSIGPIPE| option on the socket.
+//
+// Performance notes:
+// - On Linux, we have to use |send()|/|sendmsg()| rather than
+// |write()|/|writev()| in order to suppress |SIGPIPE|. This is okay, since
+// |send()| is (slightly) faster than |write()| (!), while |sendmsg()| is
+// quite comparable to |writev()|.
+// - On Mac, we may use |write()|/|writev()|. Here, |write()| is considerably
+// faster than |send()|, whereas |sendmsg()| is quite comparable to
+// |writev()|.
+// - On both platforms, an appropriate |sendmsg()|/|writev()| is considerably
+// faster than two |send()|s/|write()|s.
+// - Relative numbers (minimum real times from 10 runs) for one |write()| of
+// 1032 bytes, one |send()| of 1032 bytes, one |writev()| of 32+1000 bytes,
+// one |sendmsg()| of 32+1000 bytes, two |write()|s of 32 and 1000 bytes, two
+// |send()|s of 32 and 1000 bytes:
+// - Linux: 0.81 s, 0.77 s, 0.87 s, 0.89 s, 1.31 s, 1.22 s
+// - Mac: 2.21 s, 2.91 s, 2.98 s, 3.08 s, 3.59 s, 4.74 s
+
+// Flags to use with calling |send()| or |sendmsg()| (see above).
+#if defined(OS_MACOSX)
+const int kSendFlags = 0;
+#else
+const int kSendFlags = MSG_NOSIGNAL;
+#endif
+
+ssize_t PlatformChannelWrite(PlatformHandle h,
+ const void* bytes,
+ size_t num_bytes) {
+ DCHECK(h.is_valid());
+ DCHECK(bytes);
+ DCHECK_GT(num_bytes, 0u);
+
+#if defined(OS_MACOSX)
+ return HANDLE_EINTR(write(h.fd, bytes, num_bytes));
+#else
+ return send(h.fd, bytes, num_bytes, kSendFlags);
+#endif
+}
+
+ssize_t PlatformChannelWritev(PlatformHandle h,
+ struct iovec* iov,
+ size_t num_iov) {
+ DCHECK(h.is_valid());
+ DCHECK(iov);
+ DCHECK_GT(num_iov, 0u);
+
+#if defined(OS_MACOSX)
+ return HANDLE_EINTR(writev(h.fd, iov, static_cast<int>(num_iov)));
+#else
+ struct msghdr msg = {};
+ msg.msg_iov = iov;
+ msg.msg_iovlen = num_iov;
+ return HANDLE_EINTR(sendmsg(h.fd, &msg, kSendFlags));
+#endif
+}
+
+ssize_t PlatformChannelSendmsgWithHandles(PlatformHandle h,
+ struct iovec* iov,
+ size_t num_iov,
+ PlatformHandle* platform_handles,
+ size_t num_platform_handles) {
+ DCHECK(iov);
+ DCHECK_GT(num_iov, 0u);
+ DCHECK(platform_handles);
+ DCHECK_GT(num_platform_handles, 0u);
+ DCHECK_LE(num_platform_handles, kPlatformChannelMaxNumHandles);
+
+ char cmsg_buf[CMSG_SPACE(kPlatformChannelMaxNumHandles * sizeof(int))];
+ struct msghdr msg = {};
+ msg.msg_iov = iov;
+ msg.msg_iovlen = num_iov;
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = CMSG_LEN(num_platform_handles * sizeof(int));
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(num_platform_handles * sizeof(int));
+ for (size_t i = 0; i < num_platform_handles; i++) {
+ DCHECK(platform_handles[i].is_valid());
+ reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] = platform_handles[i].fd;
+ }
+
+ return HANDLE_EINTR(sendmsg(h.fd, &msg, kSendFlags));
+}
+
+bool PlatformChannelSendHandles(PlatformHandle h,
+ PlatformHandle* handles,
+ size_t num_handles) {
+ DCHECK(handles);
+ DCHECK_GT(num_handles, 0u);
+ DCHECK_LE(num_handles, kPlatformChannelMaxNumHandles);
+
+ // Note: |sendmsg()| fails on Mac if we don't write at least one character.
+ struct iovec iov = { const_cast<char*>(""), 1 };
+ char cmsg_buf[CMSG_SPACE(kPlatformChannelMaxNumHandles * sizeof(int))];
+ struct msghdr msg = {};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = CMSG_LEN(num_handles * sizeof(int));
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(num_handles * sizeof(int));
+ for (size_t i = 0; i < num_handles; i++) {
+ DCHECK(handles[i].is_valid());
+ reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] = handles[i].fd;
+ }
+
+ ssize_t result = HANDLE_EINTR(sendmsg(h.fd, &msg, kSendFlags));
+ if (result < 1) {
+ DCHECK_EQ(result, -1);
+ return false;
+ }
+
+ for (size_t i = 0; i < num_handles; i++)
+ handles[i].CloseIfNecessary();
+ return true;
+}
+
+ssize_t PlatformChannelRecvmsg(PlatformHandle h,
+ void* buf,
+ size_t num_bytes,
+ std::deque<PlatformHandle>* platform_handles) {
+ DCHECK(buf);
+ DCHECK_GT(num_bytes, 0u);
+ DCHECK(platform_handles);
+
+ struct iovec iov = { buf, num_bytes };
+ char cmsg_buf[CMSG_SPACE(kPlatformChannelMaxNumHandles * sizeof(int))];
+ struct msghdr msg = {};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = sizeof(cmsg_buf);
+
+ ssize_t result = HANDLE_EINTR(recvmsg(h.fd, &msg, MSG_DONTWAIT));
+ if (result < 0)
+ return result;
+
+ // Success; no control messages.
+ if (msg.msg_controllen == 0)
+ return result;
+
+ DCHECK(!(msg.msg_flags & MSG_CTRUNC));
+
+ for (cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ size_t payload_length = cmsg->cmsg_len - CMSG_LEN(0);
+ DCHECK_EQ(payload_length % sizeof(int), 0u);
+ size_t num_fds = payload_length / sizeof(int);
+ const int* fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ for (size_t i = 0; i < num_fds; i++) {
+ platform_handles->push_back(PlatformHandle(fds[i]));
+ DCHECK(platform_handles->back().is_valid());
+ }
+ }
+ }
+
+ return result;
+}
+
+} // namespace embedder
+} // namespace mojo
diff --git a/chromium/mojo/embedder/platform_channel_utils_posix.h b/chromium/mojo/embedder/platform_channel_utils_posix.h
new file mode 100644
index 00000000000..6d0ffcd2de5
--- /dev/null
+++ b/chromium/mojo/embedder/platform_channel_utils_posix.h
@@ -0,0 +1,78 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EMBEDDER_PLATFORM_CHANNEL_UTILS_POSIX_H_
+#define MOJO_EMBEDDER_PLATFORM_CHANNEL_UTILS_POSIX_H_
+
+#include <stddef.h>
+#include <sys/types.h> // For |ssize_t|.
+
+#include <deque>
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/embedder/platform_handle.h"
+#include "mojo/system/system_impl_export.h"
+
+struct iovec; // Declared in <sys/uio.h>.
+
+namespace mojo {
+namespace embedder {
+
+// The maximum number of handles that can be sent "at once" using
+// |PlatformChannelSendmsgWithHandles()|.
+// TODO(vtl): This number is taken from ipc/file_descriptor_set_posix.h:
+// |FileDescriptorSet::kMaxDescriptorsPerMessage|. Where does it come from?
+const size_t kPlatformChannelMaxNumHandles = 7;
+
+// Use these to write to a socket created using |PlatformChannelPair| (or
+// equivalent). These are like |write()| and |writev()|, but handle |EINTR| and
+// never raise |SIGPIPE|. (Note: On Mac, the suppression of |SIGPIPE| is set up
+// by |PlatformChannelPair|.)
+MOJO_SYSTEM_IMPL_EXPORT ssize_t PlatformChannelWrite(PlatformHandle h,
+ const void* bytes,
+ size_t num_bytes);
+MOJO_SYSTEM_IMPL_EXPORT ssize_t PlatformChannelWritev(PlatformHandle h,
+ struct iovec* iov,
+ size_t num_iov);
+
+// Writes data, and the given set of |PlatformHandle|s (i.e., file descriptors)
+// over the Unix domain socket given by |h| (e.g., created using
+// |PlatformChannelPair()|). All the handles must be valid, and there must be at
+// least one and at most |kPlatformChannelMaxNumHandles| handles. The return
+// value is as for |sendmsg()|, namely -1 on failure and otherwise the number of
+// bytes of data sent on success (note that this may not be all the data
+// specified by |iov|). (The handles are not closed, regardless of success or
+// failure.)
+MOJO_SYSTEM_IMPL_EXPORT ssize_t PlatformChannelSendmsgWithHandles(
+ PlatformHandle h,
+ struct iovec* iov,
+ size_t num_iov,
+ PlatformHandle* platform_handles,
+ size_t num_platform_handles);
+
+// TODO(vtl): Remove this once I've switched things over to
+// |PlatformChannelSendmsgWithHandles()|.
+// Sends |PlatformHandle|s (i.e., file descriptors) over the Unix domain socket
+// (e.g., created using PlatformChannelPair|). (These will be sent in a single
+// message having one null byte of data and one control message header with all
+// the file descriptors.) All of the handles must be valid, and there must be at
+// most |kPlatformChannelMaxNumHandles| (and at least one handle). Returns true
+// on success, in which case it closes all the handles.
+MOJO_SYSTEM_IMPL_EXPORT bool PlatformChannelSendHandles(PlatformHandle h,
+ PlatformHandle* handles,
+ size_t num_handles);
+
+// Wrapper around |recvmsg()|, which will extract any attached file descriptors
+// (in the control message) to |PlatformHandle|s (and append them to
+// |platform_handles|). (This also handles |EINTR|.)
+MOJO_SYSTEM_IMPL_EXPORT ssize_t PlatformChannelRecvmsg(
+ PlatformHandle h,
+ void* buf,
+ size_t num_bytes,
+ std::deque<PlatformHandle>* platform_handles);
+
+} // namespace embedder
+} // namespace mojo
+
+#endif // MOJO_EMBEDDER_PLATFORM_CHANNEL_UTILS_POSIX_H_
diff --git a/chromium/mojo/embedder/platform_handle.cc b/chromium/mojo/embedder/platform_handle.cc
new file mode 100644
index 00000000000..6cce5e8aab2
--- /dev/null
+++ b/chromium/mojo/embedder/platform_handle.cc
@@ -0,0 +1,40 @@
+// Copyright 2013 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 "mojo/embedder/platform_handle.h"
+
+#include "build/build_config.h"
+#if defined(OS_POSIX)
+#include <unistd.h>
+#elif defined(OS_WIN)
+#include <windows.h>
+#else
+#error "Platform not yet supported."
+#endif
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+
+namespace mojo {
+namespace embedder {
+
+void PlatformHandle::CloseIfNecessary() {
+ if (!is_valid())
+ return;
+
+#if defined(OS_POSIX)
+ bool success = (close(fd) == 0);
+ DPCHECK(success);
+ fd = -1;
+#elif defined(OS_WIN)
+ bool success = !!CloseHandle(handle);
+ DPCHECK(success);
+ handle = INVALID_HANDLE_VALUE;
+#else
+#error "Platform not yet supported."
+#endif
+}
+
+} // namespace embedder
+} // namespace mojo
diff --git a/chromium/mojo/embedder/platform_handle.h b/chromium/mojo/embedder/platform_handle.h
new file mode 100644
index 00000000000..39da4a96249
--- /dev/null
+++ b/chromium/mojo/embedder/platform_handle.h
@@ -0,0 +1,47 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_EMBEDDER_PLATFORM_HANDLE_H_
+#define MOJO_EMBEDDER_PLATFORM_HANDLE_H_
+
+#include "build/build_config.h"
+#include "mojo/system/system_impl_export.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace mojo {
+namespace embedder {
+
+#if defined(OS_POSIX)
+struct MOJO_SYSTEM_IMPL_EXPORT PlatformHandle {
+ PlatformHandle() : fd(-1) {}
+ explicit PlatformHandle(int fd) : fd(fd) {}
+
+ void CloseIfNecessary();
+
+ bool is_valid() const { return fd != -1; }
+
+ int fd;
+};
+#elif defined(OS_WIN)
+struct MOJO_SYSTEM_IMPL_EXPORT PlatformHandle {
+ PlatformHandle() : handle(INVALID_HANDLE_VALUE) {}
+ explicit PlatformHandle(HANDLE handle) : handle(handle) {}
+
+ void CloseIfNecessary();
+
+ bool is_valid() const { return handle != INVALID_HANDLE_VALUE; }
+
+ HANDLE handle;
+};
+#else
+#error "Platform not yet supported."
+#endif
+
+} // namespace embedder
+} // namespace mojo
+
+#endif // MOJO_EMBEDDER_PLATFORM_HANDLE_H_
diff --git a/chromium/mojo/embedder/platform_handle_utils.h b/chromium/mojo/embedder/platform_handle_utils.h
new file mode 100644
index 00000000000..aad3d2a889c
--- /dev/null
+++ b/chromium/mojo/embedder/platform_handle_utils.h
@@ -0,0 +1,34 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EMBEDDER_PLATFORM_HANDLE_UTILS_H_
+#define MOJO_EMBEDDER_PLATFORM_HANDLE_UTILS_H_
+
+#include "mojo/embedder/platform_handle.h"
+#include "mojo/embedder/scoped_platform_handle.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace embedder {
+
+// Closes all the |PlatformHandle|s in the given container.
+template <typename PlatformHandleContainer>
+MOJO_SYSTEM_IMPL_EXPORT inline void CloseAllPlatformHandles(
+ PlatformHandleContainer* platform_handles) {
+ for (typename PlatformHandleContainer::iterator it =
+ platform_handles->begin();
+ it != platform_handles->end();
+ ++it)
+ it->CloseIfNecessary();
+}
+
+// Duplicates the given |PlatformHandle| (which must be valid). (Returns an
+// invalid |ScopedPlatformHandle| on failure.)
+MOJO_SYSTEM_IMPL_EXPORT ScopedPlatformHandle DuplicatePlatformHandle(
+ PlatformHandle platform_handle);
+
+} // namespace embedder
+} // namespace mojo
+
+#endif // MOJO_EMBEDDER_PLATFORM_HANDLE_UTILS_H_
diff --git a/chromium/mojo/embedder/platform_handle_utils_posix.cc b/chromium/mojo/embedder/platform_handle_utils_posix.cc
new file mode 100644
index 00000000000..b6d93bb41da
--- /dev/null
+++ b/chromium/mojo/embedder/platform_handle_utils_posix.cc
@@ -0,0 +1,22 @@
+// Copyright 2014 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 "mojo/embedder/platform_handle_utils.h"
+
+#include <unistd.h>
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace embedder {
+
+ScopedPlatformHandle DuplicatePlatformHandle(PlatformHandle platform_handle) {
+ DCHECK(platform_handle.is_valid());
+ // Note that |dup()| returns -1 on error (which is exactly the value we use
+ // for invalid |PlatformHandle| FDs).
+ return ScopedPlatformHandle(PlatformHandle(dup(platform_handle.fd)));
+}
+
+} // namespace embedder
+} // namespace mojo
diff --git a/chromium/mojo/embedder/platform_handle_utils_win.cc b/chromium/mojo/embedder/platform_handle_utils_win.cc
new file mode 100644
index 00000000000..446250f4c83
--- /dev/null
+++ b/chromium/mojo/embedder/platform_handle_utils_win.cc
@@ -0,0 +1,31 @@
+// Copyright 2014 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 "mojo/embedder/platform_handle_utils.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace embedder {
+
+ScopedPlatformHandle DuplicatePlatformHandle(PlatformHandle platform_handle) {
+ DCHECK(platform_handle.is_valid());
+
+ HANDLE new_handle;
+ if (!DuplicateHandle(GetCurrentProcess(),
+ platform_handle.handle,
+ GetCurrentProcess(),
+ &new_handle,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS))
+ return ScopedPlatformHandle();
+ DCHECK_NE(new_handle, INVALID_HANDLE_VALUE);
+ return ScopedPlatformHandle(PlatformHandle(new_handle));
+}
+
+} // namespace embedder
+} // namespace mojo
diff --git a/chromium/mojo/embedder/platform_handle_vector.h b/chromium/mojo/embedder/platform_handle_vector.h
new file mode 100644
index 00000000000..db534196233
--- /dev/null
+++ b/chromium/mojo/embedder/platform_handle_vector.h
@@ -0,0 +1,35 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EMBEDDER_PLATFORM_HANDLE_VECTOR_H_
+#define MOJO_EMBEDDER_PLATFORM_HANDLE_VECTOR_H_
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/embedder/platform_handle.h"
+#include "mojo/embedder/platform_handle_utils.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace embedder {
+
+typedef std::vector<PlatformHandle> PlatformHandleVector;
+
+// A deleter (for use with |scoped_ptr|) which closes all handles and then
+// |delete|s the |PlatformHandleVector|.
+struct MOJO_SYSTEM_IMPL_EXPORT PlatformHandleVectorDeleter {
+ void operator()(PlatformHandleVector* platform_handles) const {
+ CloseAllPlatformHandles(platform_handles);
+ delete platform_handles;
+ }
+};
+
+typedef scoped_ptr<PlatformHandleVector, PlatformHandleVectorDeleter>
+ ScopedPlatformHandleVectorPtr;
+
+} // namespace embedder
+} // namespace mojo
+
+#endif // MOJO_EMBEDDER_PLATFORM_HANDLE_VECTOR_H_
diff --git a/chromium/mojo/embedder/scoped_platform_handle.h b/chromium/mojo/embedder/scoped_platform_handle.h
new file mode 100644
index 00000000000..b9cfb58ea05
--- /dev/null
+++ b/chromium/mojo/embedder/scoped_platform_handle.h
@@ -0,0 +1,61 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EMBEDDER_SCOPED_PLATFORM_HANDLE_H_
+#define MOJO_EMBEDDER_SCOPED_PLATFORM_HANDLE_H_
+
+#include "base/compiler_specific.h"
+#include "base/move.h"
+#include "mojo/embedder/platform_handle.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace embedder {
+
+class MOJO_SYSTEM_IMPL_EXPORT ScopedPlatformHandle {
+ MOVE_ONLY_TYPE_FOR_CPP_03(ScopedPlatformHandle, RValue)
+
+ public:
+ ScopedPlatformHandle() {}
+ explicit ScopedPlatformHandle(PlatformHandle handle) : handle_(handle) {}
+ ~ScopedPlatformHandle() { handle_.CloseIfNecessary(); }
+
+ // Move-only constructor and operator=.
+ ScopedPlatformHandle(RValue other) : handle_(other.object->release()) {}
+ ScopedPlatformHandle& operator=(RValue other) {
+ handle_ = other.object->release();
+ return *this;
+ }
+
+ const PlatformHandle& get() const { return handle_; }
+
+ void swap(ScopedPlatformHandle& other) {
+ PlatformHandle temp = handle_;
+ handle_ = other.handle_;
+ other.handle_ = temp;
+ }
+
+ PlatformHandle release() WARN_UNUSED_RESULT {
+ PlatformHandle rv = handle_;
+ handle_ = PlatformHandle();
+ return rv;
+ }
+
+ void reset(PlatformHandle handle = PlatformHandle()) {
+ handle_.CloseIfNecessary();
+ handle_ = handle;
+ }
+
+ bool is_valid() const {
+ return handle_.is_valid();
+ }
+
+ private:
+ PlatformHandle handle_;
+};
+
+} // namespace embedder
+} // namespace mojo
+
+#endif // MOJO_EMBEDDER_SCOPED_PLATFORM_HANDLE_H_
diff --git a/chromium/mojo/embedder/test_embedder.cc b/chromium/mojo/embedder/test_embedder.cc
new file mode 100644
index 00000000000..049855d2504
--- /dev/null
+++ b/chromium/mojo/embedder/test_embedder.cc
@@ -0,0 +1,54 @@
+// Copyright 2014 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 "mojo/embedder/test_embedder.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "mojo/system/core.h"
+#include "mojo/system/entrypoints.h"
+#include "mojo/system/handle_table.h"
+
+namespace mojo {
+
+namespace system {
+namespace internal {
+
+bool ShutdownCheckNoLeaks(Core* core_impl) {
+ // No point in taking the lock.
+ const HandleTable::HandleToEntryMap& handle_to_entry_map =
+ core_impl->handle_table_.handle_to_entry_map_;
+
+ if (handle_to_entry_map.empty())
+ return true;
+
+ for (HandleTable::HandleToEntryMap::const_iterator it =
+ handle_to_entry_map.begin();
+ it != handle_to_entry_map.end();
+ ++it) {
+ LOG(ERROR) << "Mojo embedder shutdown: Leaking handle " << (*it).first;
+ }
+ return false;
+}
+
+} // namespace internal
+} // namespace system
+
+namespace embedder {
+namespace test {
+
+bool Shutdown() {
+ system::Core* core = system::entrypoints::GetCore();
+ CHECK(core);
+ system::entrypoints::SetCore(NULL);
+
+ bool rv = system::internal::ShutdownCheckNoLeaks(core);
+ delete core;
+ return rv;
+}
+
+} // namespace test
+} // namespace embedder
+
+} // namespace mojo
diff --git a/chromium/mojo/embedder/test_embedder.h b/chromium/mojo/embedder/test_embedder.h
new file mode 100644
index 00000000000..a29adafe985
--- /dev/null
+++ b/chromium/mojo/embedder/test_embedder.h
@@ -0,0 +1,25 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EMBEDDER_TEST_EMBEDDER_H_
+#define MOJO_EMBEDDER_TEST_EMBEDDER_H_
+
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace embedder {
+namespace test {
+
+// This shuts down the global, singleton instance. (Note: "Real" embedders are
+// not expected to ever shut down this instance. This |Shutdown()| function will
+// do more work to ensure that tests don't leak, etc.) Returns true if there
+// were no problems, false if there were leaks -- i.e., handles still open -- or
+// any other problems.
+MOJO_SYSTEM_IMPL_EXPORT bool Shutdown();
+
+} // namespace test
+} // namespace embedder
+} // namespace mojo
+
+#endif // MOJO_EMBEDDER_EMBEDDER_H_
diff --git a/chromium/mojo/environment/BUILD.gn b/chromium/mojo/environment/BUILD.gn
new file mode 100644
index 00000000000..d25a94d58ea
--- /dev/null
+++ b/chromium/mojo/environment/BUILD.gn
@@ -0,0 +1,47 @@
+# Copyright 2014 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.
+
+# GYP version: mojo.gyp:mojo_environment_chromium
+static_library("chromium") {
+ output_name = "mojo_environment_chromium"
+
+ sources = [
+ "environment.cc",
+ # TODO(vtl): This is kind of ugly. (See TODO in logging.h.)
+ "../public/cpp/environment/logging.h",
+ "../public/cpp/environment/lib/logging.cc",
+ ]
+
+ deps = [
+ ":chromium_impl",
+ "//mojo/common",
+ ]
+
+ forward_dependent_configs_from = [
+ ":chromium_impl",
+ ]
+}
+
+# GYP version: mojo.gyp:mojo_environment_chromium_impl
+component("chromium_impl") {
+ output_name = "mojo_environment_impl"
+ visibility = "//mojo/*"
+
+ sources = [
+ "default_async_waiter_impl.cc",
+ "default_async_waiter_impl.h",
+ "default_logger_impl.cc",
+ "default_logger_impl.h",
+ ]
+
+ defines = [
+ "MOJO_ENVIRONMENT_IMPL_IMPLEMENTATION",
+ ]
+
+ deps = [
+ "//base",
+ "//base/third_party/dynamic_annotations",
+ "//mojo/common",
+ ]
+}
diff --git a/chromium/mojo/environment/default_async_waiter_impl.cc b/chromium/mojo/environment/default_async_waiter_impl.cc
new file mode 100644
index 00000000000..ff7a524752e
--- /dev/null
+++ b/chromium/mojo/environment/default_async_waiter_impl.cc
@@ -0,0 +1,50 @@
+// Copyright 2014 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 "mojo/environment/default_async_waiter_impl.h"
+
+#include "base/bind.h"
+#include "mojo/common/handle_watcher.h"
+
+namespace mojo {
+namespace internal {
+namespace {
+
+void OnHandleReady(common::HandleWatcher* watcher,
+ MojoAsyncWaitCallback callback,
+ void* closure,
+ MojoResult result) {
+ delete watcher;
+ callback(closure, result);
+}
+
+MojoAsyncWaitID AsyncWait(MojoHandle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline,
+ MojoAsyncWaitCallback callback,
+ void* closure) {
+ // This instance will be deleted when done or cancelled.
+ common::HandleWatcher* watcher = new common::HandleWatcher();
+ watcher->Start(Handle(handle), signals, deadline,
+ base::Bind(&OnHandleReady, watcher, callback, closure));
+ return reinterpret_cast<MojoAsyncWaitID>(watcher);
+}
+
+void CancelWait(MojoAsyncWaitID wait_id) {
+ delete reinterpret_cast<common::HandleWatcher*>(wait_id);
+}
+
+const MojoAsyncWaiter kDefaultAsyncWaiter = {
+ AsyncWait,
+ CancelWait
+};
+
+} // namespace
+
+const MojoAsyncWaiter* GetDefaultAsyncWaiterImpl() {
+ return &kDefaultAsyncWaiter;
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/environment/default_async_waiter_impl.h b/chromium/mojo/environment/default_async_waiter_impl.h
new file mode 100644
index 00000000000..5140e35b4c2
--- /dev/null
+++ b/chromium/mojo/environment/default_async_waiter_impl.h
@@ -0,0 +1,19 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_ENVIRONMENT_DEFAULT_ASYNC_WAITER_IMPL_H_
+#define MOJO_ENVIRONMENT_DEFAULT_ASYNC_WAITER_IMPL_H_
+
+#include "mojo/environment/mojo_environment_impl_export.h"
+#include "mojo/public/c/environment/async_waiter.h"
+
+namespace mojo {
+namespace internal {
+
+MOJO_ENVIRONMENT_IMPL_EXPORT const MojoAsyncWaiter* GetDefaultAsyncWaiterImpl();
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_ENVIRONMENT_DEFAULT_ASYNC_WAITER_IMPL_H_
diff --git a/chromium/mojo/environment/default_logger_impl.cc b/chromium/mojo/environment/default_logger_impl.cc
new file mode 100644
index 00000000000..575feb39891
--- /dev/null
+++ b/chromium/mojo/environment/default_logger_impl.cc
@@ -0,0 +1,71 @@
+// Copyright 2014 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 "mojo/environment/default_logger_impl.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace mojo {
+namespace internal {
+namespace {
+
+// We rely on log levels being the same numerically:
+COMPILE_ASSERT(logging::LOG_VERBOSE == MOJO_LOG_LEVEL_VERBOSE,
+ verbose_log_level_value_mismatch);
+COMPILE_ASSERT(logging::LOG_INFO == MOJO_LOG_LEVEL_INFO,
+ info_log_level_value_mismatch);
+COMPILE_ASSERT(logging::LOG_WARNING == MOJO_LOG_LEVEL_WARNING,
+ warning_log_level_value_mismatch);
+COMPILE_ASSERT(logging::LOG_ERROR == MOJO_LOG_LEVEL_ERROR,
+ error_log_level_value_mismatch);
+COMPILE_ASSERT(logging::LOG_FATAL == MOJO_LOG_LEVEL_FATAL,
+ fatal_log_level_value_mismatch);
+
+int MojoToChromiumLogLevel(MojoLogLevel log_level) {
+ // See the compile asserts above.
+ return static_cast<int>(log_level);
+}
+
+MojoLogLevel ChromiumToMojoLogLevel(int chromium_log_level) {
+ // See the compile asserts above.
+ return static_cast<MojoLogLevel>(chromium_log_level);
+}
+
+void LogMessage(MojoLogLevel log_level, const char* message) {
+ int chromium_log_level = MojoToChromiumLogLevel(log_level);
+ int chromium_min_log_level = logging::GetMinLogLevel();
+ // "Fatal" errors aren't suppressable.
+ DCHECK_LE(chromium_min_log_level, logging::LOG_FATAL);
+ if (chromium_log_level < chromium_min_log_level)
+ return;
+
+ // TODO(vtl): Possibly, we should try to pull out the file and line number
+ // from |message|.
+ logging::LogMessage(__FILE__, __LINE__, chromium_log_level).stream()
+ << message;
+}
+
+MojoLogLevel GetMinimumLogLevel() {
+ return ChromiumToMojoLogLevel(logging::GetMinLogLevel());
+}
+
+void SetMinimumLogLevel(MojoLogLevel log_level) {
+ logging::SetMinLogLevel(MojoToChromiumLogLevel(log_level));
+}
+
+const MojoLogger kDefaultLogger = {
+ LogMessage,
+ GetMinimumLogLevel,
+ SetMinimumLogLevel
+};
+
+} // namespace
+
+const MojoLogger* GetDefaultLoggerImpl() {
+ return &kDefaultLogger;
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/environment/default_logger_impl.h b/chromium/mojo/environment/default_logger_impl.h
new file mode 100644
index 00000000000..c87a7276e43
--- /dev/null
+++ b/chromium/mojo/environment/default_logger_impl.h
@@ -0,0 +1,19 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_ENVIRONMENT_DEFAULT_LOGGER_IMPL_H_
+#define MOJO_ENVIRONMENT_DEFAULT_LOGGER_IMPL_H_
+
+#include "mojo/environment/mojo_environment_impl_export.h"
+#include "mojo/public/c/environment/logger.h"
+
+namespace mojo {
+namespace internal {
+
+MOJO_ENVIRONMENT_IMPL_EXPORT const MojoLogger* GetDefaultLoggerImpl();
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_ENVIRONMENT_DEFAULT_LOGGER_IMPL_H_
diff --git a/chromium/mojo/environment/environment.cc b/chromium/mojo/environment/environment.cc
new file mode 100644
index 00000000000..f8611139381
--- /dev/null
+++ b/chromium/mojo/environment/environment.cc
@@ -0,0 +1,36 @@
+// Copyright 2014 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 "mojo/public/cpp/environment/environment.h"
+
+#include "mojo/environment/default_async_waiter_impl.h"
+#include "mojo/environment/default_logger_impl.h"
+
+namespace mojo {
+
+// These methods are intentionally not implemented so that there is a link
+// error if someone uses them in a Chromium-environment.
+#if 0
+Environment::Environment() {
+}
+
+Environment::Environment(const MojoAsyncWaiter* default_async_waiter,
+ const MojoLogger* default_logger) {
+}
+
+Environment::~Environment() {
+}
+#endif
+
+// static
+const MojoAsyncWaiter* Environment::GetDefaultAsyncWaiter() {
+ return internal::GetDefaultAsyncWaiterImpl();
+}
+
+// static
+const MojoLogger* Environment::GetDefaultLogger() {
+ return internal::GetDefaultLoggerImpl();
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/environment/mojo_environment_impl_export.h b/chromium/mojo/environment/mojo_environment_impl_export.h
new file mode 100644
index 00000000000..025542533c2
--- /dev/null
+++ b/chromium/mojo/environment/mojo_environment_impl_export.h
@@ -0,0 +1,32 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_ENVIRONMENT_MOJO_ENVIRONMENT_IMPL_EXPORT_H_
+#define MOJO_ENVIRONMENT_MOJO_ENVIRONMENT_IMPL_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_ENVIRONMENT_IMPL_IMPLEMENTATION)
+#define MOJO_ENVIRONMENT_IMPL_EXPORT __declspec(dllexport)
+#else
+#define MOJO_ENVIRONMENT_IMPL_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_ENVIRONMENT_IMPL_IMPLEMENTATION)
+#define MOJO_ENVIRONMENT_IMPL_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_ENVIRONMENT_IMPL_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#else // !defined(COMPONENT_BUILD)
+#define MOJO_ENVIRONMENT_IMPL_EXPORT
+#endif
+
+#endif // MOJO_ENVIRONMENT_MOJO_ENVIRONMENT_IMPL_EXPORT_H_
diff --git a/chromium/mojo/examples/aura_demo/DEPS b/chromium/mojo/examples/aura_demo/DEPS
new file mode 100644
index 00000000000..5cbfbf175ca
--- /dev/null
+++ b/chromium/mojo/examples/aura_demo/DEPS
@@ -0,0 +1,9 @@
+include_rules = [
+ "+cc",
+ "+skia/ext",
+ "+ui/aura",
+ "+ui/base/hit_test.h",
+ "+ui/compositor",
+ "+ui/events",
+ "+ui/gfx",
+]
diff --git a/chromium/mojo/examples/aura_demo/aura_demo.cc b/chromium/mojo/examples/aura_demo/aura_demo.cc
new file mode 100644
index 00000000000..de005a77800
--- /dev/null
+++ b/chromium/mojo/examples/aura_demo/aura_demo.cc
@@ -0,0 +1,198 @@
+// Copyright 2013 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 <stdio.h>
+#include <string>
+
+#include "base/bind.h"
+#include "mojo/aura/context_factory_mojo.h"
+#include "mojo/aura/screen_mojo.h"
+#include "mojo/aura/window_tree_host_mojo.h"
+#include "mojo/aura/window_tree_host_mojo_delegate.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+#include "mojo/services/public/cpp/view_manager/node.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/services/public/cpp/view_manager/view_manager.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h"
+#include "ui/aura/client/default_capture_client.h"
+#include "ui/aura/client/window_tree_client.h"
+#include "ui/aura/env.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_delegate.h"
+#include "ui/base/hit_test.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/codec/png_codec.h"
+
+namespace mojo {
+namespace examples {
+
+// Trivial WindowDelegate implementation that draws a colored background.
+class DemoWindowDelegate : public aura::WindowDelegate {
+ public:
+ explicit DemoWindowDelegate(SkColor color) : color_(color) {}
+
+ // Overridden from WindowDelegate:
+ virtual gfx::Size GetMinimumSize() const OVERRIDE {
+ return gfx::Size();
+ }
+
+ virtual gfx::Size GetMaximumSize() const OVERRIDE {
+ return gfx::Size();
+ }
+
+ virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) OVERRIDE {}
+ virtual gfx::NativeCursor GetCursor(const gfx::Point& point) OVERRIDE {
+ return gfx::kNullCursor;
+ }
+ virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE {
+ return HTCAPTION;
+ }
+ virtual bool ShouldDescendIntoChildForEventHandling(
+ aura::Window* child,
+ const gfx::Point& location) OVERRIDE {
+ return true;
+ }
+ virtual bool CanFocus() OVERRIDE { return true; }
+ virtual void OnCaptureLost() OVERRIDE {}
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
+ canvas->DrawColor(color_, SkXfermode::kSrc_Mode);
+ }
+ virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {}
+ virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {}
+ virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE {}
+ virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE {}
+ virtual bool HasHitTestMask() const OVERRIDE { return false; }
+ virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE {}
+
+ private:
+ const SkColor color_;
+
+ DISALLOW_COPY_AND_ASSIGN(DemoWindowDelegate);
+};
+
+class DemoWindowTreeClient : public aura::client::WindowTreeClient {
+ public:
+ explicit DemoWindowTreeClient(aura::Window* window) : window_(window) {
+ aura::client::SetWindowTreeClient(window_, this);
+ }
+
+ virtual ~DemoWindowTreeClient() {
+ aura::client::SetWindowTreeClient(window_, NULL);
+ }
+
+ // Overridden from aura::client::WindowTreeClient:
+ virtual aura::Window* GetDefaultParent(aura::Window* context,
+ aura::Window* window,
+ const gfx::Rect& bounds) OVERRIDE {
+ if (!capture_client_) {
+ capture_client_.reset(
+ new aura::client::DefaultCaptureClient(window_->GetRootWindow()));
+ }
+ return window_;
+ }
+
+ private:
+ aura::Window* window_;
+ scoped_ptr<aura::client::DefaultCaptureClient> capture_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(DemoWindowTreeClient);
+};
+
+class AuraDemo : public Application,
+ public WindowTreeHostMojoDelegate,
+ public view_manager::ViewManagerDelegate {
+ public:
+ AuraDemo()
+ : window1_(NULL),
+ window2_(NULL),
+ window21_(NULL),
+ view_(NULL) {
+ view_manager::ViewManager::Create(this, this);
+ }
+ virtual ~AuraDemo() {}
+
+ private:
+ // Overridden from view_manager::ViewManagerDelegate:
+ virtual void OnRootAdded(view_manager::ViewManager* view_manager,
+ view_manager::Node* root) OVERRIDE {
+ // TODO(beng): this function could be called multiple times!
+ view_ = view_manager::View::Create(view_manager);
+ root->SetActiveView(view_);
+
+ window_tree_host_.reset(new WindowTreeHostMojo(root, this));
+ window_tree_host_->InitHost();
+
+ window_tree_client_.reset(
+ new DemoWindowTreeClient(window_tree_host_->window()));
+
+ delegate1_.reset(new DemoWindowDelegate(SK_ColorBLUE));
+ window1_ = new aura::Window(delegate1_.get());
+ window1_->Init(aura::WINDOW_LAYER_TEXTURED);
+ window1_->SetBounds(gfx::Rect(100, 100, 400, 400));
+ window1_->Show();
+ window_tree_host_->window()->AddChild(window1_);
+
+ delegate2_.reset(new DemoWindowDelegate(SK_ColorRED));
+ window2_ = new aura::Window(delegate2_.get());
+ window2_->Init(aura::WINDOW_LAYER_TEXTURED);
+ window2_->SetBounds(gfx::Rect(200, 200, 350, 350));
+ window2_->Show();
+ window_tree_host_->window()->AddChild(window2_);
+
+ delegate21_.reset(new DemoWindowDelegate(SK_ColorGREEN));
+ window21_ = new aura::Window(delegate21_.get());
+ window21_->Init(aura::WINDOW_LAYER_TEXTURED);
+ window21_->SetBounds(gfx::Rect(10, 10, 50, 50));
+ window21_->Show();
+ window2_->AddChild(window21_);
+
+ window_tree_host_->Show();
+ }
+
+ // WindowTreeHostMojoDelegate:
+ virtual void CompositorContentsChanged(const SkBitmap& bitmap) OVERRIDE {
+ view_->SetContents(bitmap);
+ }
+
+ virtual void Initialize() OVERRIDE {
+ aura::Env::CreateInstance(true);
+ context_factory_.reset(new ContextFactoryMojo);
+ aura::Env::GetInstance()->set_context_factory(context_factory_.get());
+ screen_.reset(ScreenMojo::Create());
+ gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, screen_.get());
+ }
+
+ scoped_ptr<DemoWindowTreeClient> window_tree_client_;
+
+ scoped_ptr<ui::ContextFactory> context_factory_;
+
+ scoped_ptr<ScreenMojo> screen_;
+
+ scoped_ptr<DemoWindowDelegate> delegate1_;
+ scoped_ptr<DemoWindowDelegate> delegate2_;
+ scoped_ptr<DemoWindowDelegate> delegate21_;
+
+ aura::Window* window1_;
+ aura::Window* window2_;
+ aura::Window* window21_;
+
+ view_manager::View* view_;
+
+ scoped_ptr<aura::WindowTreeHost> window_tree_host_;
+
+ DISALLOW_COPY_AND_ASSIGN(AuraDemo);
+};
+
+} // namespace examples
+
+// static
+Application* Application::Create() {
+ return new examples::AuraDemo();
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/examples/aura_demo/view_manager_init.cc b/chromium/mojo/examples/aura_demo/view_manager_init.cc
new file mode 100644
index 00000000000..7a558d3250a
--- /dev/null
+++ b/chromium/mojo/examples/aura_demo/view_manager_init.cc
@@ -0,0 +1,45 @@
+// Copyright 2014 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 "base/basictypes.h"
+#include "base/bind.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+
+namespace mojo {
+namespace examples {
+
+// ViewManagerInit is responsible for establishing the initial connection to
+// the view manager. When established it loads |mojo_aura_demo|.
+class ViewManagerInit : public Application {
+ public:
+ ViewManagerInit() {}
+ virtual ~ViewManagerInit() {}
+
+ virtual void Initialize() OVERRIDE {
+ ConnectTo("mojo:mojo_view_manager", &view_manager_init_);
+ view_manager_init_->EmbedRoot("mojo:mojo_aura_demo",
+ base::Bind(&ViewManagerInit::DidConnect,
+ base::Unretained(this)));
+ }
+
+ private:
+ void DidConnect(bool result) {
+ DCHECK(result);
+ VLOG(1) << "ViewManagerInit::DidConnection result=" << result;
+ }
+
+ view_manager::ViewManagerInitServicePtr view_manager_init_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerInit);
+};
+
+} // namespace examples
+
+// static
+Application* Application::Create() {
+ return new examples::ViewManagerInit();
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/examples/browser/DEPS b/chromium/mojo/examples/browser/DEPS
new file mode 100644
index 00000000000..b65676e92cf
--- /dev/null
+++ b/chromium/mojo/examples/browser/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+ "+cc",
+ "+skia/ext",
+ "+ui/aura",
+ "+ui/base/hit_test.h",
+ "+ui/compositor",
+ "+ui/events",
+ "+ui/gfx",
+ "+ui/views",
+]
diff --git a/chromium/mojo/examples/browser/browser.cc b/chromium/mojo/examples/browser/browser.cc
new file mode 100644
index 00000000000..d5b6caeeead
--- /dev/null
+++ b/chromium/mojo/examples/browser/browser.cc
@@ -0,0 +1,128 @@
+// Copyright 2014 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 "base/basictypes.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/services/public/cpp/view_manager/node.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/services/public/cpp/view_manager/view_manager.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "mojo/services/public/interfaces/navigation/navigation.mojom.h"
+#include "mojo/views/native_widget_view_manager.h"
+#include "mojo/views/views_init.h"
+#include "ui/events/event.h"
+#include "ui/views/controls/textfield/textfield.h"
+#include "ui/views/controls/textfield/textfield_controller.h"
+#include "ui/views/layout/layout_manager.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace examples {
+
+class BrowserLayoutManager : public views::LayoutManager {
+ public:
+ BrowserLayoutManager() {}
+ virtual ~BrowserLayoutManager() {}
+
+ private:
+ // Overridden from views::LayoutManager:
+ virtual void Layout(views::View* host) OVERRIDE {
+ // Browser view has one child, a text input field.
+ DCHECK_EQ(1, host->child_count());
+ views::View* text_field = host->child_at(0);
+ gfx::Size ps = text_field->GetPreferredSize();
+ text_field->SetBoundsRect(gfx::Rect(host->width(), ps.height()));
+ }
+ virtual gfx::Size GetPreferredSize(const views::View* host) const OVERRIDE {
+ return gfx::Size();
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(BrowserLayoutManager);
+};
+
+// This is the basics of creating a views widget with a textfield.
+// TODO: cleanup!
+class Browser : public Application,
+ public view_manager::ViewManagerDelegate,
+ public views::TextfieldController {
+ public:
+ Browser() : view_manager_(NULL) {}
+
+ virtual ~Browser() {
+ }
+
+ private:
+ // Overridden from Application:
+ virtual void Initialize() MOJO_OVERRIDE {
+ views_init_.reset(new ViewsInit);
+ view_manager::ViewManager::Create(this, this);
+ ConnectTo("mojo:mojo_window_manager", &navigator_host_);
+ }
+
+ void CreateWidget(view_manager::Node* node) {
+ views::Textfield* textfield = new views::Textfield;
+ textfield->set_controller(this);
+
+ views::WidgetDelegateView* widget_delegate = new views::WidgetDelegateView;
+ widget_delegate->GetContentsView()->AddChildView(textfield);
+ widget_delegate->GetContentsView()->SetLayoutManager(
+ new BrowserLayoutManager);
+
+ views::Widget* widget = new views::Widget;
+ views::Widget::InitParams params(
+ views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+ params.native_widget = new NativeWidgetViewManager(widget, node);
+ params.delegate = widget_delegate;
+ params.bounds = gfx::Rect(node->bounds().width(), node->bounds().height());
+ widget->Init(params);
+ widget->Show();
+ textfield->RequestFocus();
+ }
+
+ // view_manager::ViewManagerDelegate:
+ virtual void OnRootAdded(view_manager::ViewManager* view_manager,
+ view_manager::Node* root) OVERRIDE {
+ // TODO: deal with OnRootAdded() being invoked multiple times.
+ view_manager_ = view_manager;
+ root->SetActiveView(view_manager::View::Create(view_manager));
+ root->SetFocus();
+ CreateWidget(root);
+ }
+
+ // views::TextfieldController:
+ virtual bool HandleKeyEvent(views::Textfield* sender,
+ const ui::KeyEvent& key_event) OVERRIDE {
+ if (key_event.key_code() == ui::VKEY_RETURN) {
+ GURL url(sender->text());
+ printf("User entered this URL: %s\n", url.spec().c_str());
+ navigation::NavigationDetailsPtr nav_details(
+ navigation::NavigationDetails::New());
+ nav_details->url = url.spec();
+ navigator_host_->RequestNavigate(view_manager_->GetRoots().front()->id(),
+ nav_details.Pass());
+ }
+ return false;
+ }
+
+ scoped_ptr<ViewsInit> views_init_;
+
+ view_manager::ViewManager* view_manager_;
+ navigation::NavigatorHostPtr navigator_host_;
+
+ DISALLOW_COPY_AND_ASSIGN(Browser);
+};
+
+} // namespace examples
+
+// static
+Application* Application::Create() {
+ return new examples::Browser;
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/examples/compositor_app/DEPS b/chromium/mojo/examples/compositor_app/DEPS
new file mode 100644
index 00000000000..8ea9d7b5532
--- /dev/null
+++ b/chromium/mojo/examples/compositor_app/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+cc",
+ "+ui/gfx",
+ "+gpu/command_buffer/client",
+]
diff --git a/chromium/mojo/examples/compositor_app/compositor_app.cc b/chromium/mojo/examples/compositor_app/compositor_app.cc
new file mode 100644
index 00000000000..31752bd4af3
--- /dev/null
+++ b/chromium/mojo/examples/compositor_app/compositor_app.cc
@@ -0,0 +1,69 @@
+// Copyright 2013 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 <stdio.h>
+#include <string>
+
+#include "base/macros.h"
+#include "mojo/examples/compositor_app/compositor_host.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/public/cpp/gles2/gles2.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h"
+#include "ui/gfx/rect.h"
+
+namespace mojo {
+namespace examples {
+
+class SampleApp : public Application, public NativeViewportClient {
+ public:
+ SampleApp() {}
+ virtual ~SampleApp() {}
+
+ virtual void Initialize() OVERRIDE {
+ ConnectTo("mojo:mojo_native_viewport_service", &viewport_);
+ viewport_.set_client(this);
+
+ viewport_->Create(Rect::From(gfx::Rect(10, 10, 800, 600)));
+ viewport_->Show();
+
+ MessagePipe pipe;
+ viewport_->CreateGLES2Context(
+ MakeRequest<CommandBuffer>(pipe.handle0.Pass()));
+ host_.reset(new CompositorHost(pipe.handle1.Pass()));
+ }
+
+ virtual void OnCreated() OVERRIDE {
+ }
+
+ virtual void OnDestroyed() OVERRIDE {
+ base::MessageLoop::current()->Quit();
+ }
+
+ virtual void OnBoundsChanged(RectPtr bounds) OVERRIDE {
+ host_->SetSize(gfx::Size(bounds->width, bounds->height));
+ }
+
+ virtual void OnEvent(EventPtr event,
+ const mojo::Callback<void()>& callback) OVERRIDE {
+ callback.Run();
+ }
+
+ private:
+ mojo::GLES2Initializer gles2;
+ NativeViewportPtr viewport_;
+ scoped_ptr<CompositorHost> host_;
+ DISALLOW_COPY_AND_ASSIGN(SampleApp);
+};
+
+} // namespace examples
+
+// static
+Application* Application::Create() {
+ return new examples::SampleApp();
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/examples/compositor_app/compositor_host.cc b/chromium/mojo/examples/compositor_app/compositor_host.cc
new file mode 100644
index 00000000000..31601456aa4
--- /dev/null
+++ b/chromium/mojo/examples/compositor_app/compositor_host.cc
@@ -0,0 +1,89 @@
+// Copyright 2013 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 "mojo/examples/compositor_app/compositor_host.h"
+
+#include "cc/layers/layer.h"
+#include "cc/layers/solid_color_layer.h"
+#include "cc/output/context_provider.h"
+#include "cc/output/output_surface.h"
+#include "cc/trees/layer_tree_host.h"
+#include "mojo/cc/context_provider_mojo.h"
+
+namespace mojo {
+namespace examples {
+
+CompositorHost::CompositorHost(ScopedMessagePipeHandle command_buffer_handle)
+ : command_buffer_handle_(command_buffer_handle.Pass()),
+ compositor_thread_("compositor") {
+ DCHECK(command_buffer_handle_.is_valid());
+ bool started = compositor_thread_.Start();
+ DCHECK(started);
+
+ cc::LayerTreeSettings settings;
+ tree_ = cc::LayerTreeHost::CreateThreaded(
+ this, NULL, settings, compositor_thread_.message_loop_proxy());
+ SetupScene();
+}
+
+CompositorHost::~CompositorHost() {}
+
+void CompositorHost::SetSize(const gfx::Size& viewport_size) {
+ tree_->SetViewportSize(viewport_size);
+ tree_->SetLayerTreeHostClientReady();
+}
+
+void CompositorHost::SetupScene() {
+ scoped_refptr<cc::Layer> root_layer = cc::SolidColorLayer::Create();
+ root_layer->SetBounds(gfx::Size(500, 500));
+ root_layer->SetBackgroundColor(SK_ColorBLUE);
+ root_layer->SetIsDrawable(true);
+ tree_->SetRootLayer(root_layer);
+
+ child_layer_ = cc::SolidColorLayer::Create();
+ child_layer_->SetBounds(gfx::Size(100, 100));
+ child_layer_->SetBackgroundColor(SK_ColorGREEN);
+ child_layer_->SetIsDrawable(true);
+ gfx::Transform child_transform;
+ child_transform.Translate(200, 200);
+ child_layer_->SetTransform(child_transform);
+ root_layer->AddChild(child_layer_);
+}
+
+void CompositorHost::WillBeginMainFrame(int frame_id) {}
+void CompositorHost::DidBeginMainFrame() {}
+
+void CompositorHost::Animate(base::TimeTicks frame_begin_time) {
+ // TODO(jamesr): Should use cc's animation system.
+ static const double kDegreesPerSecond = 70.0;
+ double time_in_seconds = (frame_begin_time - base::TimeTicks()).InSecondsF();
+ double child_rotation_degrees = kDegreesPerSecond * time_in_seconds;
+ gfx::Transform child_transform;
+ child_transform.Translate(200, 200);
+ child_transform.Rotate(child_rotation_degrees);
+ child_layer_->SetTransform(child_transform);
+ tree_->SetNeedsAnimate();
+}
+
+void CompositorHost::Layout() {}
+void CompositorHost::ApplyScrollAndScale(const gfx::Vector2d& scroll_delta,
+ float page_scale) {}
+
+scoped_ptr<cc::OutputSurface> CompositorHost::CreateOutputSurface(
+ bool fallback) {
+ return make_scoped_ptr(
+ new cc::OutputSurface(
+ new ContextProviderMojo(command_buffer_handle_.Pass())));
+}
+
+void CompositorHost::DidInitializeOutputSurface() {
+}
+
+void CompositorHost::WillCommit() {}
+void CompositorHost::DidCommit() {}
+void CompositorHost::DidCommitAndDrawFrame() {}
+void CompositorHost::DidCompleteSwapBuffers() {}
+
+} // namespace examples
+} // namespace mojo
diff --git a/chromium/mojo/examples/compositor_app/compositor_host.h b/chromium/mojo/examples/compositor_app/compositor_host.h
new file mode 100644
index 00000000000..ea49e280411
--- /dev/null
+++ b/chromium/mojo/examples/compositor_app/compositor_host.h
@@ -0,0 +1,58 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_EXAMPLES_COMPOSITOR_APP_COMPOSITOR_HOST_H_
+#define MOJO_EXAMPLES_COMPOSITOR_APP_COMPOSITOR_HOST_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread.h"
+#include "cc/trees/layer_tree_host_client.h"
+#include "mojo/public/cpp/system/core.h"
+#include "ui/gfx/size.h"
+
+namespace cc {
+class Layer;
+class LayerTreeHost;
+} // namespace cc
+
+namespace mojo {
+namespace examples {
+class GLES2ClientImpl;
+
+class CompositorHost : public cc::LayerTreeHostClient {
+ public:
+ explicit CompositorHost(ScopedMessagePipeHandle command_buffer_handle);
+ virtual ~CompositorHost();
+
+ void SetSize(const gfx::Size& viewport_size);
+
+ // cc::LayerTreeHostClient implementation.
+ virtual void WillBeginMainFrame(int frame_id) OVERRIDE;
+ virtual void DidBeginMainFrame() OVERRIDE;
+ virtual void Animate(base::TimeTicks frame_begin_time) OVERRIDE;
+ virtual void Layout() OVERRIDE;
+ virtual void ApplyScrollAndScale(const gfx::Vector2d& scroll_delta,
+ float page_scale) OVERRIDE;
+ virtual scoped_ptr<cc::OutputSurface> CreateOutputSurface(bool fallback)
+ OVERRIDE;
+ virtual void DidInitializeOutputSurface() OVERRIDE;
+ virtual void WillCommit() OVERRIDE;
+ virtual void DidCommit() OVERRIDE;
+ virtual void DidCommitAndDrawFrame() OVERRIDE;
+ virtual void DidCompleteSwapBuffers() OVERRIDE;
+
+ private:
+ void SetupScene();
+
+ ScopedMessagePipeHandle command_buffer_handle_;
+ scoped_ptr<cc::LayerTreeHost> tree_;
+ scoped_refptr<cc::Layer> child_layer_;
+ base::Thread compositor_thread_;
+};
+
+} // namespace examples
+} // namespace mojo
+
+#endif // MOJO_EXAMPLES_COMPOSITOR_APP_COMPOSITOR_HOST_H_
diff --git a/chromium/mojo/examples/dbus_echo/dbus_echo_app.cc b/chromium/mojo/examples/dbus_echo/dbus_echo_app.cc
new file mode 100644
index 00000000000..5a9b80f0c15
--- /dev/null
+++ b/chromium/mojo/examples/dbus_echo/dbus_echo_app.cc
@@ -0,0 +1,51 @@
+// Copyright 2014 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 <stdio.h>
+#include <string>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+#include "mojo/services/dbus_echo/echo.mojom.h"
+
+namespace mojo {
+namespace examples {
+
+class DBusEchoApp : public Application {
+ public:
+ DBusEchoApp() {}
+ virtual ~DBusEchoApp() {}
+
+ virtual void Initialize() MOJO_OVERRIDE {
+ ConnectTo("dbus:org.chromium.EchoService/org/chromium/MojoImpl",
+ &echo_service_);
+
+ echo_service_->Echo(
+ String::From("who"),
+ base::Bind(&DBusEchoApp::OnEcho, base::Unretained(this)));
+ }
+
+ private:
+ void OnEcho(String echoed) {
+ LOG(INFO) << "echo'd " << echoed;
+ }
+
+ EchoServicePtr echo_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(DBusEchoApp);
+};
+
+} // namespace examples
+
+// static
+Application* Application::Create() {
+ return new examples::DBusEchoApp();
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/examples/demo_launcher/demo_launcher.cc b/chromium/mojo/examples/demo_launcher/demo_launcher.cc
new file mode 100644
index 00000000000..141a49ada8e
--- /dev/null
+++ b/chromium/mojo/examples/demo_launcher/demo_launcher.cc
@@ -0,0 +1,43 @@
+// Copyright 2014 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 "base/basictypes.h"
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+
+namespace mojo {
+namespace examples {
+
+class DemoLauncher : public Application {
+ public:
+ DemoLauncher() {}
+ virtual ~DemoLauncher() {}
+
+ private:
+ // Overridden from Application:
+ virtual void Initialize() MOJO_OVERRIDE {
+ ConnectTo<view_manager::ViewManagerInitService>("mojo:mojo_view_manager",
+ &view_manager_init_);
+ view_manager_init_->EmbedRoot("mojo:mojo_window_manager",
+ base::Bind(&DemoLauncher::OnConnect,
+ base::Unretained(this)));
+ }
+
+ void OnConnect(bool success) {}
+
+ view_manager::ViewManagerInitServicePtr view_manager_init_;
+
+ DISALLOW_COPY_AND_ASSIGN(DemoLauncher);
+};
+
+} // namespace examples
+
+// static
+Application* Application::Create() {
+ return new examples::DemoLauncher;
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/examples/embedded_app/DEPS b/chromium/mojo/examples/embedded_app/DEPS
new file mode 100644
index 00000000000..fe1d98e366d
--- /dev/null
+++ b/chromium/mojo/examples/embedded_app/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+ui/events",
+]
diff --git a/chromium/mojo/examples/embedded_app/embedded_app.cc b/chromium/mojo/examples/embedded_app/embedded_app.cc
new file mode 100644
index 00000000000..840df0730fe
--- /dev/null
+++ b/chromium/mojo/examples/embedded_app/embedded_app.cc
@@ -0,0 +1,155 @@
+// Copyright 2014 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 "base/basictypes.h"
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "mojo/examples/window_manager/window_manager.mojom.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/services/public/cpp/view_manager/node.h"
+#include "mojo/services/public/cpp/view_manager/node_observer.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/services/public/cpp/view_manager/view_manager.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "mojo/services/public/interfaces/navigation/navigation.mojom.h"
+#include "ui/events/event_constants.h"
+#include "url/gurl.h"
+#include "url/url_util.h"
+
+using mojo::view_manager::Node;
+using mojo::view_manager::NodeObserver;
+using mojo::view_manager::View;
+using mojo::view_manager::ViewManager;
+using mojo::view_manager::ViewManagerDelegate;
+using mojo::view_manager::ViewObserver;
+
+namespace mojo {
+namespace examples {
+
+class EmbeddedApp : public Application,
+ public ViewManagerDelegate,
+ public ViewObserver,
+ public NodeObserver {
+ public:
+ EmbeddedApp() : view_manager_(NULL) {
+ url::AddStandardScheme("mojo");
+ }
+ virtual ~EmbeddedApp() {}
+
+ void SetNodeColor(uint32 node_id, SkColor color) {
+ pending_node_colors_[node_id] = color;
+ ProcessPendingNodeColor(node_id);
+ }
+
+ private:
+ class Navigator : public InterfaceImpl<navigation::Navigator> {
+ public:
+ explicit Navigator(EmbeddedApp* app) : app_(app) {}
+ private:
+ virtual void Navigate(
+ uint32 node_id,
+ navigation::NavigationDetailsPtr navigation_details,
+ navigation::ResponseDetailsPtr response_details) OVERRIDE {
+ GURL url(navigation_details->url.To<std::string>());
+ if (!url.is_valid()) {
+ LOG(ERROR) << "URL is invalid.";
+ return;
+ }
+ // TODO(aa): Verify new URL is same origin as current origin.
+ SkColor color = 0x00;
+ if (!base::HexStringToUInt(url.path().substr(1), &color)) {
+ LOG(ERROR) << "Invalid URL, path not convertible to integer";
+ return;
+ }
+ app_->SetNodeColor(node_id, color);
+ }
+ EmbeddedApp* app_;
+ DISALLOW_COPY_AND_ASSIGN(Navigator);
+ };
+
+ // Overridden from Application:
+ virtual void Initialize() MOJO_OVERRIDE {
+ ViewManager::Create(this, this);
+ // TODO(aa): Weird for embeddee to talk to embedder by URL. Seems like
+ // embedder should be able to specify the SP embeddee receives, then
+ // communication can be anonymous.
+ ConnectTo<IWindowManager>("mojo:mojo_window_manager", &window_manager_);
+ AddService<Navigator>(this);
+ }
+
+ // Overridden from ViewManagerDelegate:
+ virtual void OnRootAdded(ViewManager* view_manager, Node* root) OVERRIDE {
+ View* view = View::Create(view_manager);
+ view->AddObserver(this);
+ root->SetActiveView(view);
+ root->AddObserver(this);
+
+ roots_[root->id()] = root;
+ ProcessPendingNodeColor(root->id());
+ }
+
+ // Overridden from ViewObserver:
+ virtual void OnViewInputEvent(View* view, const EventPtr& event) OVERRIDE {
+ if (event->action == ui::ET_MOUSE_RELEASED)
+ window_manager_->CloseWindow(view->node()->id());
+ }
+
+ // Overridden from NodeObserver:
+ virtual void OnNodeActiveViewChange(
+ Node* node,
+ View* old_view,
+ View* new_view,
+ NodeObserver::DispositionChangePhase phase) OVERRIDE {
+ if (new_view == 0)
+ views_to_reap_[node] = old_view;
+ }
+ virtual void OnNodeDestroy(
+ Node* node,
+ NodeObserver::DispositionChangePhase phase) OVERRIDE {
+ if (phase != NodeObserver::DISPOSITION_CHANGED)
+ return;
+ DCHECK(roots_.find(node->id()) != roots_.end());
+ roots_.erase(node->id());
+ std::map<Node*, View*>::const_iterator it = views_to_reap_.find(node);
+ if (it != views_to_reap_.end())
+ it->second->Destroy();
+ }
+
+ void ProcessPendingNodeColor(uint32 node_id) {
+ RootMap::iterator root = roots_.find(node_id);
+ if (root == roots_.end())
+ return;
+
+ PendingNodeColors::iterator color = pending_node_colors_.find(node_id);
+ if (color == pending_node_colors_.end())
+ return;
+
+ root->second->active_view()->SetColor(color->second);
+ pending_node_colors_.erase(color);
+ }
+
+ view_manager::ViewManager* view_manager_;
+ IWindowManagerPtr window_manager_;
+ std::map<Node*, View*> views_to_reap_;
+
+ typedef std::map<view_manager::Id, Node*> RootMap;
+ RootMap roots_;
+
+ // We can receive navigations for nodes we don't have yet.
+ typedef std::map<uint32, SkColor> PendingNodeColors;
+ PendingNodeColors pending_node_colors_;
+
+ DISALLOW_COPY_AND_ASSIGN(EmbeddedApp);
+};
+
+} // namespace examples
+
+// static
+Application* Application::Create() {
+ return new examples::EmbeddedApp;
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/examples/html_viewer/DEPS b/chromium/mojo/examples/html_viewer/DEPS
new file mode 100644
index 00000000000..83af040ba4a
--- /dev/null
+++ b/chromium/mojo/examples/html_viewer/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "+skia",
+ "+net/base",
+ "+third_party/skia/include",
+ "+third_party/WebKit/public",
+]
diff --git a/chromium/mojo/examples/html_viewer/blink_platform_impl.cc b/chromium/mojo/examples/html_viewer/blink_platform_impl.cc
new file mode 100644
index 00000000000..3128d43e753
--- /dev/null
+++ b/chromium/mojo/examples/html_viewer/blink_platform_impl.cc
@@ -0,0 +1,213 @@
+// Copyright 2014 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 "mojo/examples/html_viewer/blink_platform_impl.h"
+
+#include <cmath>
+
+#include "base/rand_util.h"
+#include "base/stl_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/time/time.h"
+#include "mojo/examples/html_viewer/webthread_impl.h"
+#include "mojo/examples/html_viewer/weburlloader_impl.h"
+#include "mojo/public/cpp/application/application.h"
+#include "net/base/data_url.h"
+#include "net/base/mime_util.h"
+#include "net/base/net_errors.h"
+#include "third_party/WebKit/public/platform/WebWaitableEvent.h"
+
+namespace mojo {
+namespace examples {
+namespace {
+
+// TODO(darin): Figure out what our UA should really be.
+const char kUserAgentString[] =
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) "
+ "Chrome/35.0.1916.153 Safari/537.36";
+
+class WebWaitableEventImpl : public blink::WebWaitableEvent {
+ public:
+ WebWaitableEventImpl() : impl_(new base::WaitableEvent(false, false)) {}
+ virtual ~WebWaitableEventImpl() {}
+
+ virtual void wait() { impl_->Wait(); }
+ virtual void signal() { impl_->Signal(); }
+
+ base::WaitableEvent* impl() {
+ return impl_.get();
+ }
+
+ private:
+ scoped_ptr<base::WaitableEvent> impl_;
+ DISALLOW_COPY_AND_ASSIGN(WebWaitableEventImpl);
+};
+
+} // namespace
+
+BlinkPlatformImpl::BlinkPlatformImpl(Application* app)
+ : main_loop_(base::MessageLoop::current()),
+ shared_timer_func_(NULL),
+ shared_timer_fire_time_(0.0),
+ shared_timer_fire_time_was_set_while_suspended_(false),
+ shared_timer_suspended_(0),
+ current_thread_slot_(&DestroyCurrentThread) {
+ app->ConnectTo("mojo:mojo_network_service", &network_service_);
+}
+
+BlinkPlatformImpl::~BlinkPlatformImpl() {
+}
+
+blink::WebMimeRegistry* BlinkPlatformImpl::mimeRegistry() {
+ return &mime_registry_;
+}
+
+blink::WebThemeEngine* BlinkPlatformImpl::themeEngine() {
+ return &dummy_theme_engine_;
+}
+
+blink::WebString BlinkPlatformImpl::defaultLocale() {
+ return blink::WebString::fromUTF8("en-US");
+}
+
+double BlinkPlatformImpl::currentTime() {
+ return base::Time::Now().ToDoubleT();
+}
+
+double BlinkPlatformImpl::monotonicallyIncreasingTime() {
+ return base::TimeTicks::Now().ToInternalValue() /
+ static_cast<double>(base::Time::kMicrosecondsPerSecond);
+}
+
+void BlinkPlatformImpl::cryptographicallyRandomValues(unsigned char* buffer,
+ size_t length) {
+ base::RandBytes(buffer, length);
+}
+
+void BlinkPlatformImpl::setSharedTimerFiredFunction(void (*func)()) {
+ shared_timer_func_ = func;
+}
+
+void BlinkPlatformImpl::setSharedTimerFireInterval(
+ double interval_seconds) {
+ shared_timer_fire_time_ = interval_seconds + monotonicallyIncreasingTime();
+ if (shared_timer_suspended_) {
+ shared_timer_fire_time_was_set_while_suspended_ = true;
+ return;
+ }
+
+ // By converting between double and int64 representation, we run the risk
+ // of losing precision due to rounding errors. Performing computations in
+ // microseconds reduces this risk somewhat. But there still is the potential
+ // of us computing a fire time for the timer that is shorter than what we
+ // need.
+ // As the event loop will check event deadlines prior to actually firing
+ // them, there is a risk of needlessly rescheduling events and of
+ // needlessly looping if sleep times are too short even by small amounts.
+ // This results in measurable performance degradation unless we use ceil() to
+ // always round up the sleep times.
+ int64 interval = static_cast<int64>(
+ ceil(interval_seconds * base::Time::kMillisecondsPerSecond)
+ * base::Time::kMicrosecondsPerMillisecond);
+
+ if (interval < 0)
+ interval = 0;
+
+ shared_timer_.Stop();
+ shared_timer_.Start(FROM_HERE, base::TimeDelta::FromMicroseconds(interval),
+ this, &BlinkPlatformImpl::DoTimeout);
+}
+
+void BlinkPlatformImpl::stopSharedTimer() {
+ shared_timer_.Stop();
+}
+
+void BlinkPlatformImpl::callOnMainThread(
+ void (*func)(void*), void* context) {
+ main_loop_->PostTask(FROM_HERE, base::Bind(func, context));
+}
+
+const unsigned char* BlinkPlatformImpl::getTraceCategoryEnabledFlag(
+ const char* category_name) {
+ static const unsigned char buf[] = "*";
+ return buf;
+}
+
+blink::WebURLLoader* BlinkPlatformImpl::createURLLoader() {
+ return new WebURLLoaderImpl(network_service_.get());
+}
+
+blink::WebString BlinkPlatformImpl::userAgent() {
+ return blink::WebString::fromUTF8(kUserAgentString);
+}
+
+blink::WebData BlinkPlatformImpl::parseDataURL(
+ const blink::WebURL& url,
+ blink::WebString& mimetype_out,
+ blink::WebString& charset_out) {
+ std::string mimetype, charset, data;
+ if (net::DataURL::Parse(url, &mimetype, &charset, &data)
+ && net::IsSupportedMimeType(mimetype)) {
+ mimetype_out = blink::WebString::fromUTF8(mimetype);
+ charset_out = blink::WebString::fromUTF8(charset);
+ return data;
+ }
+ return blink::WebData();
+}
+
+blink::WebURLError BlinkPlatformImpl::cancelledError(const blink::WebURL& url)
+ const {
+ blink::WebURLError error;
+ error.domain = blink::WebString::fromUTF8(net::kErrorDomain);
+ error.reason = net::ERR_ABORTED;
+ error.unreachableURL = url;
+ error.staleCopyInCache = false;
+ error.isCancellation = true;
+ return error;
+}
+
+blink::WebThread* BlinkPlatformImpl::createThread(const char* name) {
+ return new WebThreadImpl(name);
+}
+
+blink::WebThread* BlinkPlatformImpl::currentThread() {
+ WebThreadImplForMessageLoop* thread =
+ static_cast<WebThreadImplForMessageLoop*>(current_thread_slot_.Get());
+ if (thread)
+ return (thread);
+
+ scoped_refptr<base::MessageLoopProxy> message_loop =
+ base::MessageLoopProxy::current();
+ if (!message_loop.get())
+ return NULL;
+
+ thread = new WebThreadImplForMessageLoop(message_loop.get());
+ current_thread_slot_.Set(thread);
+ return thread;
+}
+
+blink::WebWaitableEvent* BlinkPlatformImpl::createWaitableEvent() {
+ return new WebWaitableEventImpl();
+}
+
+blink::WebWaitableEvent* BlinkPlatformImpl::waitMultipleEvents(
+ const blink::WebVector<blink::WebWaitableEvent*>& web_events) {
+ std::vector<base::WaitableEvent*> events;
+ for (size_t i = 0; i < web_events.size(); ++i)
+ events.push_back(static_cast<WebWaitableEventImpl*>(web_events[i])->impl());
+ size_t idx = base::WaitableEvent::WaitMany(
+ vector_as_array(&events), events.size());
+ DCHECK_LT(idx, web_events.size());
+ return web_events[idx];
+}
+
+// static
+void BlinkPlatformImpl::DestroyCurrentThread(void* thread) {
+ WebThreadImplForMessageLoop* impl =
+ static_cast<WebThreadImplForMessageLoop*>(thread);
+ delete impl;
+}
+
+} // namespace examples
+} // namespace mojo
diff --git a/chromium/mojo/examples/html_viewer/blink_platform_impl.h b/chromium/mojo/examples/html_viewer/blink_platform_impl.h
new file mode 100644
index 00000000000..dfe7744fede
--- /dev/null
+++ b/chromium/mojo/examples/html_viewer/blink_platform_impl.h
@@ -0,0 +1,78 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EXAMPLE_HTML_VIEWER_BLINK_PLATFORM_IMPL_H_
+#define MOJO_EXAMPLE_HTML_VIEWER_BLINK_PLATFORM_IMPL_H_
+
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread_local_storage.h"
+#include "base/timer/timer.h"
+#include "mojo/examples/html_viewer/webmimeregistry_impl.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "third_party/WebKit/public/platform/Platform.h"
+#include "third_party/WebKit/public/platform/WebThemeEngine.h"
+
+namespace mojo {
+class Application;
+
+namespace examples {
+
+class BlinkPlatformImpl : public blink::Platform {
+ public:
+ explicit BlinkPlatformImpl(Application* app);
+ virtual ~BlinkPlatformImpl();
+
+ // blink::Platform methods:
+ virtual blink::WebMimeRegistry* mimeRegistry();
+ virtual blink::WebThemeEngine* themeEngine();
+ virtual blink::WebString defaultLocale();
+ virtual double currentTime();
+ virtual double monotonicallyIncreasingTime();
+ virtual void cryptographicallyRandomValues(
+ unsigned char* buffer, size_t length);
+ virtual void setSharedTimerFiredFunction(void (*func)());
+ virtual void setSharedTimerFireInterval(double interval_seconds);
+ virtual void stopSharedTimer();
+ virtual void callOnMainThread(void (*func)(void*), void* context);
+ virtual blink::WebURLLoader* createURLLoader();
+ virtual blink::WebString userAgent();
+ virtual blink::WebData parseDataURL(
+ const blink::WebURL& url, blink::WebString& mime_type,
+ blink::WebString& charset);
+ virtual blink::WebURLError cancelledError(const blink::WebURL& url) const;
+ virtual blink::WebThread* createThread(const char* name);
+ virtual blink::WebThread* currentThread();
+ virtual blink::WebWaitableEvent* createWaitableEvent();
+ virtual blink::WebWaitableEvent* waitMultipleEvents(
+ const blink::WebVector<blink::WebWaitableEvent*>& events);
+ virtual const unsigned char* getTraceCategoryEnabledFlag(
+ const char* category_name);
+
+ private:
+ void SuspendSharedTimer();
+ void ResumeSharedTimer();
+
+ void DoTimeout() {
+ if (shared_timer_func_ && !shared_timer_suspended_)
+ shared_timer_func_();
+ }
+
+ static void DestroyCurrentThread(void*);
+
+ NetworkServicePtr network_service_;
+ base::MessageLoop* main_loop_;
+ base::OneShotTimer<BlinkPlatformImpl> shared_timer_;
+ void (*shared_timer_func_)();
+ double shared_timer_fire_time_;
+ bool shared_timer_fire_time_was_set_while_suspended_;
+ int shared_timer_suspended_; // counter
+ base::ThreadLocalStorage::Slot current_thread_slot_;
+ blink::WebThemeEngine dummy_theme_engine_;
+ WebMimeRegistryImpl mime_registry_;
+};
+
+} // namespace examples
+} // namespace mojo
+
+#endif // MOJO_EXAMPLE_HTML_VIEWER_BLINK_PLATFORM_IMPL_H_
diff --git a/chromium/mojo/examples/html_viewer/html_document_view.cc b/chromium/mojo/examples/html_viewer/html_document_view.cc
new file mode 100644
index 00000000000..868230c2d26
--- /dev/null
+++ b/chromium/mojo/examples/html_viewer/html_document_view.cc
@@ -0,0 +1,140 @@
+// Copyright 2014 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 "mojo/examples/html_viewer/html_document_view.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "mojo/services/public/cpp/view_manager/node.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "skia/ext/refptr.h"
+#include "third_party/WebKit/public/web/WebConsoleMessage.h"
+#include "third_party/WebKit/public/web/WebDocument.h"
+#include "third_party/WebKit/public/web/WebElement.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
+#include "third_party/WebKit/public/web/WebScriptSource.h"
+#include "third_party/WebKit/public/web/WebSettings.h"
+#include "third_party/WebKit/public/web/WebView.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkDevice.h"
+
+namespace mojo {
+namespace examples {
+namespace {
+
+blink::WebData CopyToWebData(DataPipeConsumerHandle handle) {
+ std::vector<char> data;
+ for (;;) {
+ char buf[4096];
+ uint32_t num_bytes = sizeof(buf);
+ MojoResult result = ReadDataRaw(
+ handle,
+ buf,
+ &num_bytes,
+ MOJO_READ_DATA_FLAG_NONE);
+ if (result == MOJO_RESULT_SHOULD_WAIT) {
+ Wait(handle,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE);
+ } else if (result == MOJO_RESULT_OK) {
+ data.insert(data.end(), buf, buf + num_bytes);
+ } else {
+ break;
+ }
+ }
+ return blink::WebData(data);
+}
+
+void ConfigureSettings(blink::WebSettings* settings) {
+ settings->setAcceleratedCompositingEnabled(false);
+ settings->setLoadsImagesAutomatically(true);
+ settings->setJavaScriptEnabled(true);
+}
+
+} // namespace
+
+HTMLDocumentView::HTMLDocumentView(view_manager::ViewManager* view_manager)
+ : view_manager_(view_manager),
+ view_(view_manager::View::Create(view_manager_)),
+ web_view_(NULL),
+ repaint_pending_(false),
+ weak_factory_(this) {
+}
+
+HTMLDocumentView::~HTMLDocumentView() {
+ if (web_view_)
+ web_view_->close();
+}
+
+void HTMLDocumentView::AttachToNode(view_manager::Node* node) {
+ node->SetActiveView(view_);
+ view_->SetColor(SK_ColorCYAN); // Dummy background color.
+
+ web_view_ = blink::WebView::create(this);
+ ConfigureSettings(web_view_->settings());
+ web_view_->setMainFrame(blink::WebLocalFrame::create(this));
+
+ // TODO(darin): Track size of view_manager::Node.
+ web_view_->resize(gfx::Size(800, 600));
+}
+
+void HTMLDocumentView::Load(URLResponsePtr response,
+ ScopedDataPipeConsumerHandle response_body_stream) {
+ DCHECK(web_view_);
+
+ // TODO(darin): A better solution would be to use loadRequest, but intercept
+ // the network request and connect it to the response we already have.
+ blink::WebData data = CopyToWebData(response_body_stream.get());
+ web_view_->mainFrame()->loadHTMLString(
+ data, GURL(response->url), GURL(response->url));
+}
+
+void HTMLDocumentView::didInvalidateRect(const blink::WebRect& rect) {
+ if (!repaint_pending_) {
+ repaint_pending_ = true;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&HTMLDocumentView::Repaint, weak_factory_.GetWeakPtr()));
+ }
+}
+
+bool HTMLDocumentView::allowsBrokenNullLayerTreeView() const {
+ // TODO(darin): Switch to using compositor bindings.
+ //
+ // NOTE: Note to Blink maintainers, feel free to just break this code if it
+ // is the last using compositor bindings and you want to delete the old path.
+ //
+ return true;
+}
+
+void HTMLDocumentView::didAddMessageToConsole(
+ const blink::WebConsoleMessage& message,
+ const blink::WebString& source_name,
+ unsigned source_line,
+ const blink::WebString& stack_trace) {
+ printf("### console: %s\n", std::string(message.text.utf8()).c_str());
+}
+
+void HTMLDocumentView::Repaint() {
+ repaint_pending_ = false;
+
+ web_view_->animate(0.0);
+ web_view_->layout();
+
+ int width = web_view_->size().width;
+ int height = web_view_->size().height;
+
+ skia::RefPtr<SkCanvas> canvas = skia::AdoptRef(SkCanvas::NewRaster(
+ SkImageInfo::MakeN32(width, height, kOpaque_SkAlphaType)));
+
+ web_view_->paint(canvas.get(), gfx::Rect(0, 0, width, height));
+
+ view_->SetContents(canvas->getDevice()->accessBitmap(false));
+}
+
+} // namespace examples
+} // namespace mojo
diff --git a/chromium/mojo/examples/html_viewer/html_document_view.h b/chromium/mojo/examples/html_viewer/html_document_view.h
new file mode 100644
index 00000000000..9ad76e42dc4
--- /dev/null
+++ b/chromium/mojo/examples/html_viewer/html_document_view.h
@@ -0,0 +1,62 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EXAMPLES_HTML_VIEWER_HTML_DOCUMENT_VIEW_H_
+#define MOJO_EXAMPLES_HTML_VIEWER_HTML_DOCUMENT_VIEW_H_
+
+#include "base/memory/weak_ptr.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+#include "third_party/WebKit/public/web/WebFrameClient.h"
+#include "third_party/WebKit/public/web/WebViewClient.h"
+
+namespace mojo {
+
+namespace view_manager {
+class Node;
+class ViewManager;
+class View;
+}
+
+namespace examples {
+
+// A view for a single HTML document.
+class HTMLDocumentView : public blink::WebViewClient,
+ public blink::WebFrameClient {
+ public:
+ explicit HTMLDocumentView(view_manager::ViewManager* view_manager);
+ virtual ~HTMLDocumentView();
+
+ void AttachToNode(view_manager::Node* node);
+
+ void Load(URLResponsePtr response,
+ ScopedDataPipeConsumerHandle response_body_stream);
+
+ private:
+ // WebWidgetClient methods:
+ virtual void didInvalidateRect(const blink::WebRect& rect);
+ virtual bool allowsBrokenNullLayerTreeView() const;
+
+ // WebFrameClient methods:
+ virtual void didAddMessageToConsole(
+ const blink::WebConsoleMessage& message,
+ const blink::WebString& source_name,
+ unsigned source_line,
+ const blink::WebString& stack_trace);
+
+ void Repaint();
+
+ view_manager::ViewManager* view_manager_;
+ view_manager::View* view_;
+ blink::WebView* web_view_;
+ bool repaint_pending_;
+
+ base::WeakPtrFactory<HTMLDocumentView> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(HTMLDocumentView);
+};
+
+} // namespace examples
+} // namespace mojo
+
+#endif // MOJO_EXAMPLES_HTML_VIEWER_HTML_DOCUMENT_VIEW_H_
diff --git a/chromium/mojo/examples/html_viewer/html_viewer.cc b/chromium/mojo/examples/html_viewer/html_viewer.cc
new file mode 100644
index 00000000000..05bd7b5da3e
--- /dev/null
+++ b/chromium/mojo/examples/html_viewer/html_viewer.cc
@@ -0,0 +1,105 @@
+// Copyright 2014 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 "mojo/examples/html_viewer/blink_platform_impl.h"
+#include "mojo/examples/html_viewer/html_document_view.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/services/public/cpp/view_manager/node.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/services/public/cpp/view_manager/view_manager.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/interfaces/navigation/navigation.mojom.h"
+#include "third_party/WebKit/public/web/WebKit.h"
+
+namespace mojo {
+namespace examples {
+
+class HTMLViewer;
+
+class NavigatorImpl : public InterfaceImpl<navigation::Navigator> {
+ public:
+ explicit NavigatorImpl(HTMLViewer* viewer) : viewer_(viewer) {}
+ virtual ~NavigatorImpl() {}
+
+ private:
+ // Overridden from navigation::Navigator:
+ virtual void Navigate(
+ uint32_t node_id,
+ navigation::NavigationDetailsPtr navigation_details,
+ navigation::ResponseDetailsPtr response_details) OVERRIDE;
+
+ HTMLViewer* viewer_;
+
+ DISALLOW_COPY_AND_ASSIGN(NavigatorImpl);
+};
+
+class HTMLViewer : public Application,
+ public view_manager::ViewManagerDelegate {
+ public:
+ HTMLViewer() : document_view_(NULL) {
+ }
+ virtual ~HTMLViewer() {
+ blink::shutdown();
+ }
+
+ void Load(URLResponsePtr response,
+ ScopedDataPipeConsumerHandle response_body_stream) {
+ // Need to wait for OnRootAdded.
+ response_ = response.Pass();
+ response_body_stream_ = response_body_stream.Pass();
+ MaybeLoad();
+ }
+
+ private:
+ // Overridden from Application:
+ virtual void Initialize() OVERRIDE {
+ blink_platform_impl_.reset(new BlinkPlatformImpl(this));
+ blink::initialize(blink_platform_impl_.get());
+
+ AddService<NavigatorImpl>(this);
+ view_manager::ViewManager::Create(this, this);
+ }
+
+ // Overridden from view_manager::ViewManagerDelegate:
+ virtual void OnRootAdded(view_manager::ViewManager* view_manager,
+ view_manager::Node* root) OVERRIDE {
+ document_view_ = new HTMLDocumentView(view_manager);
+ document_view_->AttachToNode(root);
+ MaybeLoad();
+ }
+
+ void MaybeLoad() {
+ if (document_view_ && response_.get())
+ document_view_->Load(response_.Pass(), response_body_stream_.Pass());
+ }
+
+ scoped_ptr<BlinkPlatformImpl> blink_platform_impl_;
+
+ // TODO(darin): Figure out proper ownership of this instance.
+ HTMLDocumentView* document_view_;
+ URLResponsePtr response_;
+ ScopedDataPipeConsumerHandle response_body_stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(HTMLViewer);
+};
+
+void NavigatorImpl::Navigate(
+ uint32_t node_id,
+ navigation::NavigationDetailsPtr navigation_details,
+ navigation::ResponseDetailsPtr response_details) {
+ printf("In HTMLViewer, rendering url: %s\n",
+ response_details->response->url.data());
+ viewer_->Load(response_details->response.Pass(),
+ response_details->response_body_stream.Pass());
+}
+
+}
+
+// static
+Application* Application::Create() {
+ return new examples::HTMLViewer;
+}
+
+}
diff --git a/chromium/mojo/examples/html_viewer/webmimeregistry_impl.cc b/chromium/mojo/examples/html_viewer/webmimeregistry_impl.cc
new file mode 100644
index 00000000000..4f4bbedb606
--- /dev/null
+++ b/chromium/mojo/examples/html_viewer/webmimeregistry_impl.cc
@@ -0,0 +1,97 @@
+// Copyright 2014 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 "mojo/examples/html_viewer/webmimeregistry_impl.h"
+
+#include "base/files/file_path.h"
+#include "base/strings/string_util.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "net/base/mime_util.h"
+#include "third_party/WebKit/public/platform/WebString.h"
+
+namespace mojo {
+namespace examples {
+namespace {
+
+std::string ToASCIIOrEmpty(const blink::WebString& string) {
+ return base::IsStringASCII(string) ? base::UTF16ToASCII(string)
+ : std::string();
+}
+
+} // namespace
+
+blink::WebMimeRegistry::SupportsType WebMimeRegistryImpl::supportsMIMEType(
+ const blink::WebString& mime_type) {
+ return net::IsSupportedMimeType(ToASCIIOrEmpty(mime_type)) ?
+ blink::WebMimeRegistry::IsSupported :
+ blink::WebMimeRegistry::IsNotSupported;
+}
+
+blink::WebMimeRegistry::SupportsType WebMimeRegistryImpl::supportsImageMIMEType(
+ const blink::WebString& mime_type) {
+ return net::IsSupportedImageMimeType(ToASCIIOrEmpty(mime_type)) ?
+ blink::WebMimeRegistry::IsSupported :
+ blink::WebMimeRegistry::IsNotSupported;
+}
+
+blink::WebMimeRegistry::SupportsType
+ WebMimeRegistryImpl::supportsJavaScriptMIMEType(
+ const blink::WebString& mime_type) {
+ return net::IsSupportedJavascriptMimeType(ToASCIIOrEmpty(mime_type)) ?
+ blink::WebMimeRegistry::IsSupported :
+ blink::WebMimeRegistry::IsNotSupported;
+}
+
+blink::WebMimeRegistry::SupportsType WebMimeRegistryImpl::supportsMediaMIMEType(
+ const blink::WebString& mime_type,
+ const blink::WebString& codecs,
+ const blink::WebString& key_system) {
+ NOTIMPLEMENTED();
+ return IsNotSupported;
+}
+
+bool WebMimeRegistryImpl::supportsMediaSourceMIMEType(
+ const blink::WebString& mime_type,
+ const blink::WebString& codecs) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool WebMimeRegistryImpl::supportsEncryptedMediaMIMEType(
+ const blink::WebString& key_system,
+ const blink::WebString& mime_type,
+ const blink::WebString& codecs) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+blink::WebMimeRegistry::SupportsType
+ WebMimeRegistryImpl::supportsNonImageMIMEType(
+ const blink::WebString& mime_type) {
+ return net::IsSupportedNonImageMimeType(ToASCIIOrEmpty(mime_type)) ?
+ blink::WebMimeRegistry::IsSupported :
+ blink::WebMimeRegistry::IsNotSupported;
+}
+
+blink::WebString WebMimeRegistryImpl::mimeTypeForExtension(
+ const blink::WebString& file_extension) {
+ NOTIMPLEMENTED();
+ return blink::WebString();
+}
+
+blink::WebString WebMimeRegistryImpl::wellKnownMimeTypeForExtension(
+ const blink::WebString& file_extension) {
+ NOTIMPLEMENTED();
+ return blink::WebString();
+}
+
+blink::WebString WebMimeRegistryImpl::mimeTypeFromFile(
+ const blink::WebString& file_path) {
+ NOTIMPLEMENTED();
+ return blink::WebString();
+}
+
+} // namespace examples
+} // namespace mojo
diff --git a/chromium/mojo/examples/html_viewer/webmimeregistry_impl.h b/chromium/mojo/examples/html_viewer/webmimeregistry_impl.h
new file mode 100644
index 00000000000..ec7b577961e
--- /dev/null
+++ b/chromium/mojo/examples/html_viewer/webmimeregistry_impl.h
@@ -0,0 +1,50 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EXAMPLES_HTML_VIEWER_WEBMIMEREGISTRY_IMPL_H_
+#define MOJO_EXAMPLES_HTML_VIEWER_WEBMIMEREGISTRY_IMPL_H_
+
+#include "base/compiler_specific.h"
+#include "third_party/WebKit/public/platform/WebMimeRegistry.h"
+
+namespace mojo {
+namespace examples {
+
+class WebMimeRegistryImpl : public blink::WebMimeRegistry {
+ public:
+ WebMimeRegistryImpl() {}
+ virtual ~WebMimeRegistryImpl() {}
+
+ // WebMimeRegistry methods:
+ virtual blink::WebMimeRegistry::SupportsType supportsMIMEType(
+ const blink::WebString& mime_type);
+ virtual blink::WebMimeRegistry::SupportsType supportsImageMIMEType(
+ const blink::WebString& mime_type);
+ virtual blink::WebMimeRegistry::SupportsType supportsJavaScriptMIMEType(
+ const blink::WebString& mime_type);
+ virtual blink::WebMimeRegistry::SupportsType supportsMediaMIMEType(
+ const blink::WebString& mime_type,
+ const blink::WebString& codecs,
+ const blink::WebString& key_system);
+ virtual bool supportsMediaSourceMIMEType(
+ const blink::WebString& mime_type,
+ const blink::WebString& codecs);
+ virtual bool supportsEncryptedMediaMIMEType(
+ const blink::WebString& key_system,
+ const blink::WebString& mime_type,
+ const blink::WebString& codecs);
+ virtual blink::WebMimeRegistry::SupportsType supportsNonImageMIMEType(
+ const blink::WebString& mime_type);
+ virtual blink::WebString mimeTypeForExtension(
+ const blink::WebString& extension);
+ virtual blink::WebString wellKnownMimeTypeForExtension(
+ const blink::WebString& extension);
+ virtual blink::WebString mimeTypeFromFile(
+ const blink::WebString& path);
+};
+
+} // namespace examples
+} // namespace mojo
+
+#endif // MOJO_EXAMPLES_HTML_VIEWER_WEBMIMEREGISTRY_IMPL_H_
diff --git a/chromium/mojo/examples/html_viewer/webthread_impl.cc b/chromium/mojo/examples/html_viewer/webthread_impl.cc
new file mode 100644
index 00000000000..1173a257ea2
--- /dev/null
+++ b/chromium/mojo/examples/html_viewer/webthread_impl.cc
@@ -0,0 +1,133 @@
+// Copyright 2014 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.
+
+// An implementation of WebThread in terms of base::MessageLoop and
+// base::Thread
+
+#include "mojo/examples/html_viewer/webthread_impl.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/message_loop/message_loop.h"
+#include "base/pending_task.h"
+#include "base/threading/platform_thread.h"
+
+namespace mojo {
+namespace examples {
+
+WebThreadBase::WebThreadBase() {}
+WebThreadBase::~WebThreadBase() {}
+
+class WebThreadBase::TaskObserverAdapter
+ : public base::MessageLoop::TaskObserver {
+ public:
+ TaskObserverAdapter(WebThread::TaskObserver* observer)
+ : observer_(observer) {}
+
+ virtual void WillProcessTask(const base::PendingTask& pending_task) OVERRIDE {
+ observer_->willProcessTask();
+ }
+
+ virtual void DidProcessTask(const base::PendingTask& pending_task) OVERRIDE {
+ observer_->didProcessTask();
+ }
+
+private:
+ WebThread::TaskObserver* observer_;
+};
+
+void WebThreadBase::addTaskObserver(TaskObserver* observer) {
+ CHECK(isCurrentThread());
+ std::pair<TaskObserverMap::iterator, bool> result = task_observer_map_.insert(
+ std::make_pair(observer, static_cast<TaskObserverAdapter*>(NULL)));
+ if (result.second)
+ result.first->second = new TaskObserverAdapter(observer);
+ base::MessageLoop::current()->AddTaskObserver(result.first->second);
+}
+
+void WebThreadBase::removeTaskObserver(TaskObserver* observer) {
+ CHECK(isCurrentThread());
+ TaskObserverMap::iterator iter = task_observer_map_.find(observer);
+ if (iter == task_observer_map_.end())
+ return;
+ base::MessageLoop::current()->RemoveTaskObserver(iter->second);
+ delete iter->second;
+ task_observer_map_.erase(iter);
+}
+
+WebThreadImpl::WebThreadImpl(const char* name)
+ : thread_(new base::Thread(name)) {
+ thread_->Start();
+}
+
+void WebThreadImpl::postTask(Task* task) {
+ thread_->message_loop()->PostTask(
+ FROM_HERE, base::Bind(&blink::WebThread::Task::run, base::Owned(task)));
+}
+
+void WebThreadImpl::postDelayedTask(Task* task, long long delay_ms) {
+ thread_->message_loop()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&blink::WebThread::Task::run, base::Owned(task)),
+ base::TimeDelta::FromMilliseconds(delay_ms));
+}
+
+void WebThreadImpl::enterRunLoop() {
+ CHECK(isCurrentThread());
+ CHECK(!thread_->message_loop()->is_running()); // We don't support nesting.
+ thread_->message_loop()->Run();
+}
+
+void WebThreadImpl::exitRunLoop() {
+ CHECK(isCurrentThread());
+ CHECK(thread_->message_loop()->is_running());
+ thread_->message_loop()->Quit();
+}
+
+bool WebThreadImpl::isCurrentThread() const {
+ return thread_->thread_id() == base::PlatformThread::CurrentId();
+}
+
+WebThreadImpl::~WebThreadImpl() {
+ thread_->Stop();
+}
+
+WebThreadImplForMessageLoop::WebThreadImplForMessageLoop(
+ base::MessageLoopProxy* message_loop)
+ : message_loop_(message_loop) {}
+
+void WebThreadImplForMessageLoop::postTask(Task* task) {
+ message_loop_->PostTask(
+ FROM_HERE, base::Bind(&blink::WebThread::Task::run, base::Owned(task)));
+}
+
+void WebThreadImplForMessageLoop::postDelayedTask(Task* task,
+ long long delay_ms) {
+ message_loop_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&blink::WebThread::Task::run, base::Owned(task)),
+ base::TimeDelta::FromMilliseconds(delay_ms));
+}
+
+void WebThreadImplForMessageLoop::enterRunLoop() {
+ CHECK(isCurrentThread());
+ // We don't support nesting.
+ CHECK(!base::MessageLoop::current()->is_running());
+ base::MessageLoop::current()->Run();
+}
+
+void WebThreadImplForMessageLoop::exitRunLoop() {
+ CHECK(isCurrentThread());
+ CHECK(base::MessageLoop::current()->is_running());
+ base::MessageLoop::current()->Quit();
+}
+
+bool WebThreadImplForMessageLoop::isCurrentThread() const {
+ return message_loop_->BelongsToCurrentThread();
+}
+
+WebThreadImplForMessageLoop::~WebThreadImplForMessageLoop() {}
+
+} // namespace examples
+} // namespace mojo
diff --git a/chromium/mojo/examples/html_viewer/webthread_impl.h b/chromium/mojo/examples/html_viewer/webthread_impl.h
new file mode 100644
index 00000000000..ea068ccba8a
--- /dev/null
+++ b/chromium/mojo/examples/html_viewer/webthread_impl.h
@@ -0,0 +1,75 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EXAMPLES_HTML_VIEWER_WEBTHREAD_IMPL_H_
+#define MOJO_EXAMPLES_HTML_VIEWER_WEBTHREAD_IMPL_H_
+
+#include <map>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread.h"
+#include "third_party/WebKit/public/platform/WebThread.h"
+
+namespace mojo {
+namespace examples {
+
+class WebThreadBase : public blink::WebThread {
+ public:
+ virtual ~WebThreadBase();
+
+ virtual void addTaskObserver(TaskObserver* observer);
+ virtual void removeTaskObserver(TaskObserver* observer);
+
+ virtual bool isCurrentThread() const = 0;
+
+ protected:
+ WebThreadBase();
+
+ private:
+ class TaskObserverAdapter;
+
+ typedef std::map<TaskObserver*, TaskObserverAdapter*> TaskObserverMap;
+ TaskObserverMap task_observer_map_;
+};
+
+class WebThreadImpl : public WebThreadBase {
+ public:
+ explicit WebThreadImpl(const char* name);
+ virtual ~WebThreadImpl();
+
+ virtual void postTask(Task* task);
+ virtual void postDelayedTask(Task* task, long long delay_ms);
+
+ virtual void enterRunLoop();
+ virtual void exitRunLoop();
+
+ base::MessageLoop* message_loop() const { return thread_->message_loop(); }
+
+ virtual bool isCurrentThread() const;
+
+ private:
+ scoped_ptr<base::Thread> thread_;
+};
+
+class WebThreadImplForMessageLoop : public WebThreadBase {
+ public:
+ explicit WebThreadImplForMessageLoop(
+ base::MessageLoopProxy* message_loop);
+ virtual ~WebThreadImplForMessageLoop();
+
+ virtual void postTask(Task* task);
+ virtual void postDelayedTask(Task* task, long long delay_ms);
+
+ virtual void enterRunLoop();
+ virtual void exitRunLoop();
+
+ private:
+ virtual bool isCurrentThread() const;
+ scoped_refptr<base::MessageLoopProxy> message_loop_;
+};
+
+} // namespace examples
+} // namespace mojo
+
+#endif // MOJO_EXAMPLES_HTML_VIEWER_WEBTHREAD_IMPL_H_
diff --git a/chromium/mojo/examples/html_viewer/weburlloader_impl.cc b/chromium/mojo/examples/html_viewer/weburlloader_impl.cc
new file mode 100644
index 00000000000..fa57e911db7
--- /dev/null
+++ b/chromium/mojo/examples/html_viewer/weburlloader_impl.cc
@@ -0,0 +1,139 @@
+// Copyright 2014 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 "mojo/examples/html_viewer/weburlloader_impl.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "third_party/WebKit/public/platform/WebURLError.h"
+#include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
+#include "third_party/WebKit/public/platform/WebURLRequest.h"
+#include "third_party/WebKit/public/platform/WebURLResponse.h"
+
+namespace mojo {
+namespace examples {
+namespace {
+
+blink::WebURLResponse ToWebURLResponse(const URLResponsePtr& url_response) {
+ blink::WebURLResponse result;
+ result.initialize();
+ result.setURL(GURL(url_response->url));
+ // TODO(darin): Copy other fields.
+ return result;
+}
+
+} // namespace
+
+WebURLLoaderImpl::WebURLLoaderImpl(NetworkService* network_service)
+ : client_(NULL),
+ weak_factory_(this) {
+ network_service->CreateURLLoader(Get(&url_loader_));
+ url_loader_.set_client(this);
+}
+
+WebURLLoaderImpl::~WebURLLoaderImpl() {
+}
+
+void WebURLLoaderImpl::loadSynchronously(
+ const blink::WebURLRequest& request,
+ blink::WebURLResponse& response,
+ blink::WebURLError& error,
+ blink::WebData& data) {
+ NOTIMPLEMENTED();
+}
+
+void WebURLLoaderImpl::loadAsynchronously(const blink::WebURLRequest& request,
+ blink::WebURLLoaderClient* client) {
+ client_ = client;
+
+ URLRequestPtr url_request(URLRequest::New());
+ url_request->url = request.url().spec();
+ url_request->auto_follow_redirects = false;
+ // TODO(darin): Copy other fields.
+
+ DataPipe pipe;
+ url_loader_->Start(url_request.Pass(), pipe.producer_handle.Pass());
+ response_body_stream_ = pipe.consumer_handle.Pass();
+}
+
+void WebURLLoaderImpl::cancel() {
+ url_loader_.reset();
+ response_body_stream_.reset();
+ // TODO(darin): Need to asynchronously call didFail.
+}
+
+void WebURLLoaderImpl::setDefersLoading(bool defers_loading) {
+ NOTIMPLEMENTED();
+}
+
+void WebURLLoaderImpl::OnReceivedRedirect(URLResponsePtr url_response,
+ const String& new_url,
+ const String& new_method) {
+ blink::WebURLRequest new_request;
+ new_request.initialize();
+ new_request.setURL(GURL(new_url));
+
+ client_->willSendRequest(this, new_request, ToWebURLResponse(url_response));
+ // TODO(darin): Check if new_request was rejected.
+
+ url_loader_->FollowRedirect();
+}
+
+void WebURLLoaderImpl::OnReceivedResponse(URLResponsePtr url_response) {
+ client_->didReceiveResponse(this, ToWebURLResponse(url_response));
+
+ // Start streaming data
+ ReadMore();
+}
+
+void WebURLLoaderImpl::OnReceivedError(NetworkErrorPtr error) {
+ // TODO(darin): Construct a meaningful WebURLError.
+ client_->didFail(this, blink::WebURLError());
+}
+
+void WebURLLoaderImpl::OnReceivedEndOfResponseBody() {
+ // This is the signal that the response body was not truncated.
+}
+
+void WebURLLoaderImpl::ReadMore() {
+ const void* buf;
+ uint32_t buf_size;
+ MojoResult rv = BeginReadDataRaw(response_body_stream_.get(),
+ &buf,
+ &buf_size,
+ MOJO_READ_DATA_FLAG_NONE);
+ if (rv == MOJO_RESULT_OK) {
+ client_->didReceiveData(this, static_cast<const char*>(buf), buf_size, -1);
+ EndReadDataRaw(response_body_stream_.get(), buf_size);
+ WaitToReadMore();
+ } else if (rv == MOJO_RESULT_SHOULD_WAIT) {
+ WaitToReadMore();
+ } else if (rv == MOJO_RESULT_FAILED_PRECONDITION) {
+ // We reached end-of-file.
+ double finish_time = base::Time::Now().ToDoubleT();
+ client_->didFinishLoading(
+ this,
+ finish_time,
+ blink::WebURLLoaderClient::kUnknownEncodedDataLength);
+ } else {
+ // TODO(darin): Oops!
+ }
+}
+
+void WebURLLoaderImpl::WaitToReadMore() {
+ handle_watcher_.Start(
+ response_body_stream_.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ base::Bind(&WebURLLoaderImpl::OnResponseBodyStreamReady,
+ weak_factory_.GetWeakPtr()));
+}
+
+void WebURLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) {
+ ReadMore();
+}
+
+} // namespace examples
+} // namespace mojo
diff --git a/chromium/mojo/examples/html_viewer/weburlloader_impl.h b/chromium/mojo/examples/html_viewer/weburlloader_impl.h
new file mode 100644
index 00000000000..3d2a570aa3c
--- /dev/null
+++ b/chromium/mojo/examples/html_viewer/weburlloader_impl.h
@@ -0,0 +1,60 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EXAMPLE_HTML_VIEWER_WEBURLLOADER_IMPL_H_
+#define MOJO_EXAMPLE_HTML_VIEWER_WEBURLLOADER_IMPL_H_
+
+#include "base/memory/weak_ptr.h"
+#include "mojo/common/handle_watcher.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+#include "third_party/WebKit/public/platform/WebURLLoader.h"
+
+namespace mojo {
+class NetworkService;
+
+namespace examples {
+
+class WebURLLoaderImpl : public blink::WebURLLoader,
+ public URLLoaderClient {
+ public:
+ explicit WebURLLoaderImpl(NetworkService* network_service);
+
+ private:
+ virtual ~WebURLLoaderImpl();
+
+ // blink::WebURLLoader methods:
+ virtual void loadSynchronously(
+ const blink::WebURLRequest& request, blink::WebURLResponse& response,
+ blink::WebURLError& error, blink::WebData& data) OVERRIDE;
+ virtual void loadAsynchronously(
+ const blink::WebURLRequest&, blink::WebURLLoaderClient* client) OVERRIDE;
+ virtual void cancel() OVERRIDE;
+ virtual void setDefersLoading(bool defers_loading) OVERRIDE;
+
+ // URLLoaderClient methods:
+ virtual void OnReceivedRedirect(URLResponsePtr response,
+ const String& new_url,
+ const String& new_method) OVERRIDE;
+ virtual void OnReceivedResponse(URLResponsePtr response) OVERRIDE;
+ virtual void OnReceivedError(NetworkErrorPtr error) OVERRIDE;
+ virtual void OnReceivedEndOfResponseBody() OVERRIDE;
+
+ void ReadMore();
+ void WaitToReadMore();
+ void OnResponseBodyStreamReady(MojoResult result);
+
+ URLLoaderPtr url_loader_;
+ blink::WebURLLoaderClient* client_;
+ ScopedDataPipeConsumerHandle response_body_stream_;
+ common::HandleWatcher handle_watcher_;
+
+ base::WeakPtrFactory<WebURLLoaderImpl> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebURLLoaderImpl);
+};
+
+} // namespace examples
+} // namespace mojo
+
+#endif // MOJO_EXAMPLE_HTML_VIEWER_WEBURLLOADER_IMPL_H_
diff --git a/chromium/mojo/examples/image_viewer/DEPS b/chromium/mojo/examples/image_viewer/DEPS
new file mode 100644
index 00000000000..41134f52feb
--- /dev/null
+++ b/chromium/mojo/examples/image_viewer/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+third_party/skia/include",
+ "+ui/gfx",
+]
diff --git a/chromium/mojo/examples/image_viewer/image_viewer.cc b/chromium/mojo/examples/image_viewer/image_viewer.cc
new file mode 100644
index 00000000000..4eb987c1ec5
--- /dev/null
+++ b/chromium/mojo/examples/image_viewer/image_viewer.cc
@@ -0,0 +1,138 @@
+// Copyright 2014 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 <algorithm>
+
+#include "base/strings/string_tokenizer.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/services/public/cpp/view_manager/node.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/services/public/cpp/view_manager/view_manager.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/interfaces/navigation/navigation.mojom.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/codec/png_codec.h"
+
+namespace mojo {
+namespace examples {
+
+class ImageViewer;
+
+class NavigatorImpl : public InterfaceImpl<navigation::Navigator> {
+ public:
+ explicit NavigatorImpl(ImageViewer* viewer) : viewer_(viewer) {}
+ virtual ~NavigatorImpl() {}
+
+ private:
+ // Overridden from navigation::Navigate:
+ virtual void Navigate(
+ uint32_t node_id,
+ navigation::NavigationDetailsPtr navigation_details,
+ navigation::ResponseDetailsPtr response_details) OVERRIDE {
+ int content_length = GetContentLength(response_details->response->headers);
+ unsigned char* data = new unsigned char[content_length];
+ unsigned char* buf = data;
+ uint32_t bytes_remaining = content_length;
+ uint32_t num_bytes = bytes_remaining;
+ while (bytes_remaining > 0) {
+ MojoResult result = ReadDataRaw(
+ response_details->response_body_stream.get(),
+ buf,
+ &num_bytes,
+ MOJO_READ_DATA_FLAG_NONE);
+ if (result == MOJO_RESULT_SHOULD_WAIT) {
+ Wait(response_details->response_body_stream.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE);
+ } else if (result == MOJO_RESULT_OK) {
+ buf += num_bytes;
+ num_bytes = bytes_remaining -= num_bytes;
+ } else {
+ break;
+ }
+ }
+
+ SkBitmap bitmap;
+ gfx::PNGCodec::Decode(static_cast<const unsigned char*>(data),
+ content_length, &bitmap);
+ UpdateView(node_id, bitmap);
+
+ delete[] data;
+ }
+
+ void UpdateView(view_manager::Id node_id, const SkBitmap& bitmap);
+
+ int GetContentLength(const Array<String>& headers) {
+ for (size_t i = 0; i < headers.size(); ++i) {
+ base::StringTokenizer t(headers[i], ": ;=");
+ while (t.GetNext()) {
+ if (!t.token_is_delim() && t.token() == "Content-Length") {
+ while (t.GetNext()) {
+ if (!t.token_is_delim())
+ return atoi(t.token().c_str());
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ ImageViewer* viewer_;
+
+ DISALLOW_COPY_AND_ASSIGN(NavigatorImpl);
+};
+
+class ImageViewer : public Application,
+ public view_manager::ViewManagerDelegate {
+ public:
+ ImageViewer() : content_view_(NULL) {}
+ virtual ~ImageViewer() {}
+
+ void UpdateView(view_manager::Id node_id, const SkBitmap& bitmap) {
+ bitmap_ = bitmap;
+ DrawBitmap();
+ }
+
+ private:
+ // Overridden from Application:
+ virtual void Initialize() OVERRIDE {
+ AddService<NavigatorImpl>(this);
+ view_manager::ViewManager::Create(this, this);
+ }
+
+ // Overridden from view_manager::ViewManagerDelegate:
+ virtual void OnRootAdded(view_manager::ViewManager* view_manager,
+ view_manager::Node* root) OVERRIDE {
+ content_view_ = view_manager::View::Create(view_manager);
+ root->SetActiveView(content_view_);
+ content_view_->SetColor(SK_ColorRED);
+ if (!bitmap_.isNull())
+ DrawBitmap();
+ }
+
+ void DrawBitmap() {
+ if (content_view_)
+ content_view_->SetContents(bitmap_);
+ }
+
+ view_manager::View* content_view_;
+ SkBitmap bitmap_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageViewer);
+};
+
+void NavigatorImpl::UpdateView(view_manager::Id node_id,
+ const SkBitmap& bitmap) {
+ viewer_->UpdateView(node_id, bitmap);
+}
+
+} // namespace examples
+
+// static
+Application* Application::Create() {
+ return new examples::ImageViewer;
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/examples/nesting_app/DEPS b/chromium/mojo/examples/nesting_app/DEPS
new file mode 100644
index 00000000000..fe1d98e366d
--- /dev/null
+++ b/chromium/mojo/examples/nesting_app/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+ui/events",
+]
diff --git a/chromium/mojo/examples/nesting_app/nesting_app.cc b/chromium/mojo/examples/nesting_app/nesting_app.cc
new file mode 100644
index 00000000000..7eccc726f30
--- /dev/null
+++ b/chromium/mojo/examples/nesting_app/nesting_app.cc
@@ -0,0 +1,136 @@
+// Copyright 2014 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 "base/basictypes.h"
+#include "base/bind.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/examples/window_manager/window_manager.mojom.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/services/public/cpp/view_manager/node.h"
+#include "mojo/services/public/cpp/view_manager/node_observer.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/services/public/cpp/view_manager/view_manager.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "mojo/services/public/interfaces/navigation/navigation.mojom.h"
+#include "ui/events/event_constants.h"
+#include "url/gurl.h"
+
+using mojo::view_manager::Node;
+using mojo::view_manager::NodeObserver;
+using mojo::view_manager::View;
+using mojo::view_manager::ViewManager;
+using mojo::view_manager::ViewManagerDelegate;
+using mojo::view_manager::ViewObserver;
+
+namespace mojo {
+namespace examples {
+
+namespace {
+const char kEmbeddedAppURL[] = "mojo:mojo_embedded_app";
+}
+
+// An app that embeds another app.
+class NestingApp : public Application,
+ public ViewManagerDelegate,
+ public ViewObserver,
+ public NodeObserver {
+ public:
+ NestingApp() : nested_(NULL) {}
+ virtual ~NestingApp() {}
+
+ private:
+ class Navigator : public InterfaceImpl<navigation::Navigator> {
+ public:
+ explicit Navigator(NestingApp* app) : app_(app) {}
+ private:
+ virtual void Navigate(
+ uint32 node_id,
+ navigation::NavigationDetailsPtr navigation_details,
+ navigation::ResponseDetailsPtr response_details) OVERRIDE {
+ GURL url(navigation_details->url.To<std::string>());
+ if (!url.is_valid()) {
+ LOG(ERROR) << "URL is invalid.";
+ return;
+ }
+ app_->color_ = url.path().substr(1);
+ app_->NavigateChild();
+ }
+ NestingApp* app_;
+ DISALLOW_COPY_AND_ASSIGN(Navigator);
+ };
+
+ // Overridden from Application:
+ virtual void Initialize() MOJO_OVERRIDE {
+ ViewManager::Create(this, this);
+ ConnectTo<IWindowManager>("mojo:mojo_window_manager", &window_manager_);
+ AddService<Navigator>(this);
+ }
+
+ // Overridden from ViewManagerDelegate:
+ virtual void OnRootAdded(ViewManager* view_manager, Node* root) OVERRIDE {
+ root->AddObserver(this);
+
+ View* view = View::Create(view_manager);
+ root->SetActiveView(view);
+ view->SetColor(SK_ColorCYAN);
+ view->AddObserver(this);
+
+ nested_ = Node::Create(view_manager);
+ root->AddChild(nested_);
+ nested_->SetBounds(gfx::Rect(20, 20, 50, 50));
+ nested_->Embed(kEmbeddedAppURL);
+
+ if (!navigator_.get())
+ ConnectTo(kEmbeddedAppURL, &navigator_);
+
+ NavigateChild();
+ }
+
+ // Overridden from ViewObserver:
+ virtual void OnViewInputEvent(View* view, const EventPtr& event) OVERRIDE {
+ if (event->action == ui::ET_MOUSE_RELEASED)
+ window_manager_->CloseWindow(view->node()->id());
+ }
+
+ // Overridden from NodeObserver:
+ virtual void OnNodeDestroy(
+ Node* node,
+ NodeObserver::DispositionChangePhase phase) OVERRIDE {
+ if (phase != NodeObserver::DISPOSITION_CHANGED)
+ return;
+ // TODO(beng): reap views & child nodes.
+ nested_ = NULL;
+ }
+
+ void NavigateChild() {
+ if (!color_.empty() && nested_) {
+ navigation::NavigationDetailsPtr details(
+ navigation::NavigationDetails::New());
+ details->url =
+ base::StringPrintf("%s/%s", kEmbeddedAppURL, color_.c_str());
+ navigation::ResponseDetailsPtr response_details(
+ navigation::ResponseDetails::New());
+ navigator_->Navigate(nested_->id(),
+ details.Pass(),
+ response_details.Pass());
+ }
+ }
+
+ std::string color_;
+ Node* nested_;
+ navigation::NavigatorPtr navigator_;
+ IWindowManagerPtr window_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(NestingApp);
+};
+
+} // namespace examples
+
+// static
+Application* Application::Create() {
+ return new examples::NestingApp;
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/examples/pepper_container_app/DEPS b/chromium/mojo/examples/pepper_container_app/DEPS
new file mode 100644
index 00000000000..b1e8ef3dd81
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "+base",
+ "+ppapi/c",
+ "+ppapi/shared_impl",
+ "+ppapi/thunk",
+]
diff --git a/chromium/mojo/examples/pepper_container_app/OWNERS b/chromium/mojo/examples/pepper_container_app/OWNERS
new file mode 100644
index 00000000000..30159636d2f
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/OWNERS
@@ -0,0 +1,6 @@
+bbudge@chromium.org
+brettw@chromium.org
+dmichael@chromium.org
+raymes@chromium.org
+teravest@chromium.org
+yzshen@chromium.org
diff --git a/chromium/mojo/examples/pepper_container_app/graphics_3d_resource.cc b/chromium/mojo/examples/pepper_container_app/graphics_3d_resource.cc
new file mode 100644
index 00000000000..2ad7dcd4591
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/graphics_3d_resource.cc
@@ -0,0 +1,160 @@
+// Copyright 2014 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 "mojo/examples/pepper_container_app/graphics_3d_resource.h"
+
+#include "base/logging.h"
+#include "mojo/examples/pepper_container_app/mojo_ppapi_globals.h"
+#include "mojo/examples/pepper_container_app/plugin_instance.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "ppapi/c/pp_errors.h"
+
+namespace mojo {
+namespace examples {
+
+namespace {
+
+gpu::CommandBuffer::State GetErrorState() {
+ gpu::CommandBuffer::State error_state;
+ error_state.error = gpu::error::kGenericError;
+ return error_state;
+}
+
+} // namespace
+
+Graphics3DResource::Graphics3DResource(PP_Instance instance)
+ : Resource(ppapi::OBJECT_IS_IMPL, instance) {
+ ScopedMessagePipeHandle pipe = MojoPpapiGlobals::Get()->CreateGLES2Context();
+ context_ = MojoGLES2CreateContext(pipe.release().value(),
+ &ContextLostThunk,
+ &DrawAnimationFrameThunk,
+ this);
+}
+
+bool Graphics3DResource::IsBoundGraphics() const {
+ PluginInstance* plugin_instance =
+ MojoPpapiGlobals::Get()->GetInstance(pp_instance());
+ return plugin_instance && plugin_instance->IsBoundGraphics(pp_resource());
+}
+
+void Graphics3DResource::BindGraphics() {
+ MojoGLES2MakeCurrent(context_);
+}
+
+ppapi::thunk::PPB_Graphics3D_API* Graphics3DResource::AsPPB_Graphics3D_API() {
+ return this;
+}
+
+int32_t Graphics3DResource::GetAttribs(int32_t attrib_list[]) {
+ NOTIMPLEMENTED();
+ return PP_ERROR_FAILED;
+}
+
+int32_t Graphics3DResource::SetAttribs(const int32_t attrib_list[]) {
+ NOTIMPLEMENTED();
+ return PP_ERROR_FAILED;
+}
+
+int32_t Graphics3DResource::GetError() {
+ NOTIMPLEMENTED();
+ return PP_ERROR_FAILED;
+}
+
+int32_t Graphics3DResource::ResizeBuffers(int32_t width, int32_t height) {
+ NOTIMPLEMENTED();
+ return PP_ERROR_FAILED;
+}
+
+int32_t Graphics3DResource::SwapBuffers(
+ scoped_refptr<ppapi::TrackedCallback> callback) {
+ if (!IsBoundGraphics())
+ return PP_ERROR_FAILED;
+
+ MojoGLES2SwapBuffers();
+ return PP_OK;
+}
+
+int32_t Graphics3DResource::GetAttribMaxValue(int32_t attribute,
+ int32_t* value) {
+ NOTIMPLEMENTED();
+ return PP_ERROR_FAILED;
+}
+
+PP_Bool Graphics3DResource::SetGetBuffer(int32_t shm_id) {
+ NOTIMPLEMENTED();
+ return PP_FALSE;
+}
+
+scoped_refptr<gpu::Buffer> Graphics3DResource::CreateTransferBuffer(
+ uint32_t size,
+ int32* id) {
+ NOTIMPLEMENTED();
+ return NULL;
+}
+
+PP_Bool Graphics3DResource::DestroyTransferBuffer(int32_t id) {
+ NOTIMPLEMENTED();
+ return PP_FALSE;
+}
+
+PP_Bool Graphics3DResource::Flush(int32_t put_offset) {
+ NOTIMPLEMENTED();
+ return PP_FALSE;
+}
+
+gpu::CommandBuffer::State Graphics3DResource::WaitForTokenInRange(int32_t start,
+ int32_t end) {
+ NOTIMPLEMENTED();
+ return GetErrorState();
+}
+
+gpu::CommandBuffer::State Graphics3DResource::WaitForGetOffsetInRange(
+ int32_t start, int32_t end) {
+ NOTIMPLEMENTED();
+ return GetErrorState();
+}
+
+void* Graphics3DResource::MapTexSubImage2DCHROMIUM(GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ GLenum access) {
+ NOTIMPLEMENTED();
+ return NULL;
+}
+
+void Graphics3DResource::UnmapTexSubImage2DCHROMIUM(const void* mem) {
+ NOTIMPLEMENTED();
+}
+
+uint32_t Graphics3DResource::InsertSyncPoint() {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+Graphics3DResource::~Graphics3DResource() {
+ MojoGLES2DestroyContext(context_);
+}
+
+void Graphics3DResource::ContextLostThunk(void* closure) {
+ static_cast<Graphics3DResource*>(closure)->ContextLost();
+}
+
+void Graphics3DResource::DrawAnimationFrameThunk(void* closure) {
+ // TODO(yzshen): Use this notification to drive the SwapBuffers() callback.
+}
+
+void Graphics3DResource::ContextLost() {
+ PluginInstance* plugin_instance =
+ MojoPpapiGlobals::Get()->GetInstance(pp_instance());
+ if (plugin_instance)
+ plugin_instance->Graphics3DContextLost();
+}
+
+} // namespace examples
+} // namespace mojo
diff --git a/chromium/mojo/examples/pepper_container_app/graphics_3d_resource.h b/chromium/mojo/examples/pepper_container_app/graphics_3d_resource.h
new file mode 100644
index 00000000000..5dba92cc144
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/graphics_3d_resource.h
@@ -0,0 +1,72 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EXAMPLES_PEPPER_CONTAINER_APP_GRAPHICS_3D_RESOURCE_H_
+#define MOJO_EXAMPLES_PEPPER_CONTAINER_APP_GRAPHICS_3D_RESOURCE_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "mojo/public/c/gles2/gles2_types.h"
+#include "ppapi/shared_impl/resource.h"
+#include "ppapi/shared_impl/tracked_callback.h"
+#include "ppapi/thunk/ppb_graphics_3d_api.h"
+
+namespace mojo {
+namespace examples {
+
+class Graphics3DResource : public ppapi::Resource,
+ public ppapi::thunk::PPB_Graphics3D_API {
+ public:
+ explicit Graphics3DResource(PP_Instance instance);
+
+ bool IsBoundGraphics() const;
+ void BindGraphics();
+
+ // ppapi::Resource overrides.
+ virtual ppapi::thunk::PPB_Graphics3D_API* AsPPB_Graphics3D_API() OVERRIDE;
+
+ // ppapi::thunk::PPB_Graphics3D_API implementation.
+ virtual int32_t GetAttribs(int32_t attrib_list[]) OVERRIDE;
+ virtual int32_t SetAttribs(const int32_t attrib_list[]) OVERRIDE;
+ virtual int32_t GetError() OVERRIDE;
+ virtual int32_t ResizeBuffers(int32_t width, int32_t height) OVERRIDE;
+ virtual int32_t SwapBuffers(
+ scoped_refptr<ppapi::TrackedCallback> callback) OVERRIDE;
+ virtual int32_t GetAttribMaxValue(int32_t attribute, int32_t* value) OVERRIDE;
+ virtual PP_Bool SetGetBuffer(int32_t shm_id) OVERRIDE;
+ virtual scoped_refptr<gpu::Buffer> CreateTransferBuffer(uint32_t size,
+ int32* id) OVERRIDE;
+ virtual PP_Bool DestroyTransferBuffer(int32_t id) OVERRIDE;
+ virtual PP_Bool Flush(int32_t put_offset) OVERRIDE;
+ virtual gpu::CommandBuffer::State WaitForTokenInRange(int32_t start,
+ int32_t end) OVERRIDE;
+ virtual gpu::CommandBuffer::State WaitForGetOffsetInRange(
+ int32_t start, int32_t end) OVERRIDE;
+ virtual void* MapTexSubImage2DCHROMIUM(GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ GLenum access) OVERRIDE;
+ virtual void UnmapTexSubImage2DCHROMIUM(const void* mem) OVERRIDE;
+ virtual uint32_t InsertSyncPoint() OVERRIDE;
+
+ private:
+ virtual ~Graphics3DResource();
+
+ static void ContextLostThunk(void* closure);
+ static void DrawAnimationFrameThunk(void* closure);
+ void ContextLost();
+
+ MojoGLES2Context context_;
+ DISALLOW_COPY_AND_ASSIGN(Graphics3DResource);
+};
+
+} // namespace examples
+} // namespace mojo
+
+#endif // MOJO_EXAMPLES_PEPPER_CONTAINER_APP_GRAPHICS_3D_RESOURCE_H_
diff --git a/chromium/mojo/examples/pepper_container_app/interface_list.cc b/chromium/mojo/examples/pepper_container_app/interface_list.cc
new file mode 100644
index 00000000000..7f4d2ea613a
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/interface_list.cc
@@ -0,0 +1,50 @@
+// Copyright 2014 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 "mojo/examples/pepper_container_app/interface_list.h"
+
+#include "base/memory/singleton.h"
+#include "mojo/examples/pepper_container_app/thunk.h"
+#include "ppapi/c/ppb_core.h"
+#include "ppapi/c/ppb_graphics_3d.h"
+#include "ppapi/c/ppb_instance.h"
+#include "ppapi/c/ppb_opengles2.h"
+#include "ppapi/c/ppb_view.h"
+#include "ppapi/thunk/thunk.h"
+
+namespace mojo {
+namespace examples {
+
+InterfaceList::InterfaceList() {
+ browser_interfaces_[PPB_CORE_INTERFACE_1_0] = GetPPB_Core_1_0_Thunk();
+ browser_interfaces_[PPB_GRAPHICS_3D_INTERFACE_1_0] =
+ ppapi::thunk::GetPPB_Graphics3D_1_0_Thunk();
+ browser_interfaces_[PPB_OPENGLES2_INTERFACE_1_0] =
+ GetPPB_OpenGLES2_Thunk();
+ browser_interfaces_[PPB_INSTANCE_INTERFACE_1_0] =
+ ppapi::thunk::GetPPB_Instance_1_0_Thunk();
+ browser_interfaces_[PPB_VIEW_INTERFACE_1_0] =
+ ppapi::thunk::GetPPB_View_1_0_Thunk();
+ browser_interfaces_[PPB_VIEW_INTERFACE_1_1] =
+ ppapi::thunk::GetPPB_View_1_1_Thunk();
+}
+
+InterfaceList::~InterfaceList() {}
+
+// static
+InterfaceList* InterfaceList::GetInstance() {
+ return Singleton<InterfaceList>::get();
+}
+
+const void* InterfaceList::GetBrowserInterface(const std::string& name) const {
+ NameToInterfaceMap::const_iterator iter = browser_interfaces_.find(name);
+
+ if (iter == browser_interfaces_.end())
+ return NULL;
+
+ return iter->second;
+}
+
+} // namespace examples
+} // namespace mojo
diff --git a/chromium/mojo/examples/pepper_container_app/interface_list.h b/chromium/mojo/examples/pepper_container_app/interface_list.h
new file mode 100644
index 00000000000..5cff06c5d83
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/interface_list.h
@@ -0,0 +1,37 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EXAMPLES_PEPPER_CONTAINER_APP_INTERFACE_LIST_H_
+#define MOJO_EXAMPLES_PEPPER_CONTAINER_APP_INTERFACE_LIST_H_
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+
+namespace mojo {
+namespace examples {
+
+// InterfaceList maintains the mapping from Pepper interface names to
+// interface pointers.
+class InterfaceList {
+ public:
+ InterfaceList();
+ ~InterfaceList();
+
+ static InterfaceList* GetInstance();
+
+ const void* GetBrowserInterface(const std::string& name) const;
+
+ private:
+ typedef std::map<std::string, const void*> NameToInterfaceMap;
+ NameToInterfaceMap browser_interfaces_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterfaceList);
+};
+
+} // namespace examples
+} // namespace mojo
+
+#endif // MOJO_EXAMPLES_PEPPER_CONTAINER_APP_INTERFACE_LIST_H_
diff --git a/chromium/mojo/examples/pepper_container_app/mojo_ppapi_globals.cc b/chromium/mojo/examples/pepper_container_app/mojo_ppapi_globals.cc
new file mode 100644
index 00000000000..33f87176051
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/mojo_ppapi_globals.cc
@@ -0,0 +1,184 @@
+// Copyright 2014 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 "mojo/examples/pepper_container_app/mojo_ppapi_globals.h"
+
+#include "base/logging.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/time/time.h"
+#include "mojo/examples/pepper_container_app/plugin_instance.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/shared_impl/ppb_message_loop_shared.h"
+
+namespace mojo {
+namespace examples {
+
+namespace {
+
+const PP_Instance kInstanceId = 1;
+
+} // namespace
+
+// A non-abstract subclass of ppapi::MessageLoopShared that represents the
+// message loop of the main thread.
+// TODO(yzshen): Build a more general ppapi::MessageLoopShared subclass to fully
+// support PPB_MessageLoop.
+class MojoPpapiGlobals::MainThreadMessageLoopResource
+ : public ppapi::MessageLoopShared {
+ public:
+ explicit MainThreadMessageLoopResource(
+ base::MessageLoopProxy* main_thread_message_loop)
+ : MessageLoopShared(ForMainThread()),
+ main_thread_message_loop_(main_thread_message_loop) {}
+
+ // ppapi::MessageLoopShared implementation.
+ virtual void PostClosure(const tracked_objects::Location& from_here,
+ const base::Closure& closure,
+ int64 delay_ms) OVERRIDE {
+ main_thread_message_loop_->PostDelayedTask(
+ from_here, closure, base::TimeDelta::FromMilliseconds(delay_ms));
+ }
+
+ virtual base::MessageLoopProxy* GetMessageLoopProxy() OVERRIDE {
+ return main_thread_message_loop_.get();
+ }
+
+ // ppapi::thunk::PPB_MessageLoop_API implementation.
+ virtual int32_t AttachToCurrentThread() OVERRIDE {
+ NOTIMPLEMENTED();
+ return PP_ERROR_FAILED;
+ }
+
+ virtual int32_t Run() OVERRIDE {
+ NOTIMPLEMENTED();
+ return PP_ERROR_FAILED;
+ }
+
+ virtual int32_t PostWork(PP_CompletionCallback callback,
+ int64_t delay_ms) OVERRIDE {
+ NOTIMPLEMENTED();
+ return PP_ERROR_FAILED;
+ }
+
+ virtual int32_t PostQuit(PP_Bool should_destroy) OVERRIDE {
+ NOTIMPLEMENTED();
+ return PP_ERROR_FAILED;
+ }
+
+ private:
+ virtual ~MainThreadMessageLoopResource() {}
+
+ scoped_refptr<base::MessageLoopProxy> main_thread_message_loop_;
+ DISALLOW_COPY_AND_ASSIGN(MainThreadMessageLoopResource);
+};
+
+MojoPpapiGlobals::MojoPpapiGlobals(Delegate* delegate)
+ : delegate_(delegate),
+ plugin_instance_(NULL),
+ resource_tracker_(ppapi::ResourceTracker::THREAD_SAFE) {}
+
+MojoPpapiGlobals::~MojoPpapiGlobals() {}
+
+PP_Instance MojoPpapiGlobals::AddInstance(PluginInstance* instance) {
+ DCHECK(!plugin_instance_);
+ plugin_instance_ = instance;
+ resource_tracker_.DidCreateInstance(kInstanceId);
+ return kInstanceId;
+}
+
+void MojoPpapiGlobals::InstanceDeleted(PP_Instance instance) {
+ DCHECK_EQ(instance, kInstanceId);
+ DCHECK(plugin_instance_);
+ resource_tracker_.DidDeleteInstance(instance);
+ plugin_instance_ = NULL;
+}
+
+PluginInstance* MojoPpapiGlobals::GetInstance(PP_Instance instance) {
+ if (instance == kInstanceId)
+ return plugin_instance_;
+ return NULL;
+}
+
+ScopedMessagePipeHandle MojoPpapiGlobals::CreateGLES2Context() {
+ return delegate_->CreateGLES2Context();
+}
+
+ppapi::ResourceTracker* MojoPpapiGlobals::GetResourceTracker() {
+ return &resource_tracker_;
+}
+
+ppapi::VarTracker* MojoPpapiGlobals::GetVarTracker() {
+ NOTIMPLEMENTED();
+ return NULL;
+}
+
+ppapi::CallbackTracker* MojoPpapiGlobals::GetCallbackTrackerForInstance(
+ PP_Instance instance) {
+ if (instance == kInstanceId && plugin_instance_)
+ return plugin_instance_->plugin_module()->callback_tracker();
+ return NULL;
+}
+
+void MojoPpapiGlobals::LogWithSource(PP_Instance instance,
+ PP_LogLevel level,
+ const std::string& source,
+ const std::string& value) {
+ NOTIMPLEMENTED();
+}
+
+void MojoPpapiGlobals::BroadcastLogWithSource(PP_Module module,
+ PP_LogLevel level,
+ const std::string& source,
+ const std::string& value) {
+ NOTIMPLEMENTED();
+}
+
+ppapi::thunk::PPB_Instance_API* MojoPpapiGlobals::GetInstanceAPI(
+ PP_Instance instance) {
+ if (instance == kInstanceId && plugin_instance_)
+ return plugin_instance_;
+ return NULL;
+}
+
+ppapi::thunk::ResourceCreationAPI* MojoPpapiGlobals::GetResourceCreationAPI(
+ PP_Instance instance) {
+ if (instance == kInstanceId && plugin_instance_)
+ return plugin_instance_->resource_creation();
+ return NULL;
+}
+
+PP_Module MojoPpapiGlobals::GetModuleForInstance(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+ppapi::MessageLoopShared* MojoPpapiGlobals::GetCurrentMessageLoop() {
+ if (base::MessageLoopProxy::current().get() == GetMainThreadMessageLoop()) {
+ if (!main_thread_message_loop_resource_) {
+ main_thread_message_loop_resource_ = new MainThreadMessageLoopResource(
+ GetMainThreadMessageLoop());
+ }
+ return main_thread_message_loop_resource_.get();
+ }
+
+ NOTIMPLEMENTED();
+ return NULL;
+}
+
+base::TaskRunner* MojoPpapiGlobals::GetFileTaskRunner() {
+ NOTIMPLEMENTED();
+ return NULL;
+}
+
+std::string MojoPpapiGlobals::GetCmdLine() {
+ NOTIMPLEMENTED();
+ return std::string();
+}
+
+void MojoPpapiGlobals::PreCacheFontForFlash(const void* logfontw) {
+ NOTIMPLEMENTED();
+}
+
+} // namespace examples
+} // namespace mojo
diff --git a/chromium/mojo/examples/pepper_container_app/mojo_ppapi_globals.h b/chromium/mojo/examples/pepper_container_app/mojo_ppapi_globals.h
new file mode 100644
index 00000000000..349f885b56c
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/mojo_ppapi_globals.h
@@ -0,0 +1,90 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EXAMPLES_PEPPER_CONTAINER_APP_MOJO_PPAPI_GLOBALS_H_
+#define MOJO_EXAMPLES_PEPPER_CONTAINER_APP_MOJO_PPAPI_GLOBALS_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/public/cpp/system/core.h"
+#include "ppapi/shared_impl/ppapi_globals.h"
+#include "ppapi/shared_impl/resource_tracker.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+namespace mojo {
+namespace examples {
+
+class PluginInstance;
+
+class MojoPpapiGlobals : public ppapi::PpapiGlobals {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ virtual ScopedMessagePipeHandle CreateGLES2Context() = 0;
+ };
+
+ // |delegate| must live longer than this object.
+ explicit MojoPpapiGlobals(Delegate* delegate);
+ virtual ~MojoPpapiGlobals();
+
+ inline static MojoPpapiGlobals* Get() {
+ return static_cast<MojoPpapiGlobals*>(PpapiGlobals::Get());
+ }
+
+ PP_Instance AddInstance(PluginInstance* instance);
+ void InstanceDeleted(PP_Instance instance);
+ PluginInstance* GetInstance(PP_Instance instance);
+
+ ScopedMessagePipeHandle CreateGLES2Context();
+
+ // ppapi::PpapiGlobals implementation.
+ virtual ppapi::ResourceTracker* GetResourceTracker() OVERRIDE;
+ virtual ppapi::VarTracker* GetVarTracker() OVERRIDE;
+ virtual ppapi::CallbackTracker* GetCallbackTrackerForInstance(
+ PP_Instance instance) OVERRIDE;
+ virtual void LogWithSource(PP_Instance instance,
+ PP_LogLevel level,
+ const std::string& source,
+ const std::string& value) OVERRIDE;
+ virtual void BroadcastLogWithSource(PP_Module module,
+ PP_LogLevel level,
+ const std::string& source,
+ const std::string& value) OVERRIDE;
+ virtual ppapi::thunk::PPB_Instance_API* GetInstanceAPI(
+ PP_Instance instance) OVERRIDE;
+ virtual ppapi::thunk::ResourceCreationAPI* GetResourceCreationAPI(
+ PP_Instance instance) OVERRIDE;
+ virtual PP_Module GetModuleForInstance(PP_Instance instance) OVERRIDE;
+ virtual ppapi::MessageLoopShared* GetCurrentMessageLoop() OVERRIDE;
+ virtual base::TaskRunner* GetFileTaskRunner() OVERRIDE;
+ virtual std::string GetCmdLine() OVERRIDE;
+ virtual void PreCacheFontForFlash(const void* logfontw) OVERRIDE;
+
+ private:
+ class MainThreadMessageLoopResource;
+
+ // Non-owning pointer.
+ Delegate* const delegate_;
+
+ // Non-owning pointer.
+ PluginInstance* plugin_instance_;
+
+ ppapi::ResourceTracker resource_tracker_;
+
+ scoped_refptr<MainThreadMessageLoopResource>
+ main_thread_message_loop_resource_;
+
+ DISALLOW_COPY_AND_ASSIGN(MojoPpapiGlobals);
+};
+
+} // namespace examples
+} // namespace mojo
+
+#endif // MOJO_EXAMPLES_PEPPER_CONTAINER_APP_MOJO_PPAPI_GLOBALS_H_
diff --git a/chromium/mojo/examples/pepper_container_app/pepper_container_app.cc b/chromium/mojo/examples/pepper_container_app/pepper_container_app.cc
new file mode 100644
index 00000000000..3c6cf76e4ec
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/pepper_container_app.cc
@@ -0,0 +1,110 @@
+// Copyright 2014 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 "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "build/build_config.h"
+#include "mojo/examples/pepper_container_app/mojo_ppapi_globals.h"
+#include "mojo/examples/pepper_container_app/plugin_instance.h"
+#include "mojo/examples/pepper_container_app/plugin_module.h"
+#include "mojo/examples/pepper_container_app/type_converters.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/public/cpp/gles2/gles2.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h"
+#include "ppapi/c/pp_rect.h"
+#include "ppapi/shared_impl/proxy_lock.h"
+
+namespace mojo {
+namespace examples {
+
+class PepperContainerApp: public Application,
+ public NativeViewportClient,
+ public MojoPpapiGlobals::Delegate {
+ public:
+ explicit PepperContainerApp()
+ : Application(),
+ ppapi_globals_(this),
+ plugin_module_(new PluginModule) {}
+
+ virtual ~PepperContainerApp() {}
+
+ virtual void Initialize() MOJO_OVERRIDE {
+ ConnectTo("mojo:mojo_native_viewport_service", &viewport_);
+ viewport_.set_client(this);
+
+ RectPtr rect(Rect::New());
+ rect->x = 10;
+ rect->y = 10;
+ rect->width = 800;
+ rect->height = 600;
+ viewport_->Create(rect.Pass());
+ viewport_->Show();
+ }
+
+ // NativeViewportClient implementation.
+ virtual void OnCreated() OVERRIDE {
+ ppapi::ProxyAutoLock lock;
+
+ plugin_instance_ = plugin_module_->CreateInstance().Pass();
+ if (!plugin_instance_->DidCreate())
+ plugin_instance_.reset();
+ }
+
+ virtual void OnDestroyed() OVERRIDE {
+ ppapi::ProxyAutoLock lock;
+
+ if (plugin_instance_) {
+ plugin_instance_->DidDestroy();
+ plugin_instance_.reset();
+ }
+
+ base::MessageLoop::current()->Quit();
+ }
+
+ virtual void OnBoundsChanged(RectPtr bounds) OVERRIDE {
+ ppapi::ProxyAutoLock lock;
+
+ if (plugin_instance_)
+ plugin_instance_->DidChangeView(bounds.To<PP_Rect>());
+ }
+
+ virtual void OnEvent(EventPtr event,
+ const mojo::Callback<void()>& callback) OVERRIDE {
+ if (!event->location.is_null()) {
+ ppapi::ProxyAutoLock lock;
+
+ // TODO(yzshen): Handle events.
+ }
+ callback.Run();
+ }
+
+ // MojoPpapiGlobals::Delegate implementation.
+ virtual ScopedMessagePipeHandle CreateGLES2Context() OVERRIDE {
+ CommandBufferPtr command_buffer;
+ viewport_->CreateGLES2Context(Get(&command_buffer));
+ return command_buffer.PassMessagePipe();
+ }
+
+ private:
+ MojoPpapiGlobals ppapi_globals_;
+
+ NativeViewportPtr viewport_;
+ scoped_refptr<PluginModule> plugin_module_;
+ scoped_ptr<PluginInstance> plugin_instance_;
+
+ DISALLOW_COPY_AND_ASSIGN(PepperContainerApp);
+};
+
+} // namespace examples
+
+// static
+Application* Application::Create() {
+ return new examples::PepperContainerApp();
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/examples/pepper_container_app/plugin_instance.cc b/chromium/mojo/examples/pepper_container_app/plugin_instance.cc
new file mode 100644
index 00000000000..c0d2e312d64
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/plugin_instance.cc
@@ -0,0 +1,416 @@
+// Copyright 2014 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 "mojo/examples/pepper_container_app/plugin_instance.h"
+
+#include "base/logging.h"
+#include "mojo/examples/pepper_container_app/graphics_3d_resource.h"
+#include "mojo/examples/pepper_container_app/mojo_ppapi_globals.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/c/pp_var.h"
+#include "ppapi/c/ppp_graphics_3d.h"
+#include "ppapi/c/ppp_instance.h"
+#include "ppapi/shared_impl/ppb_view_shared.h"
+#include "ppapi/shared_impl/proxy_lock.h"
+#include "ppapi/shared_impl/tracked_callback.h"
+#include "ppapi/thunk/enter.h"
+#include "ppapi/thunk/ppb_graphics_3d_api.h"
+
+namespace mojo {
+namespace examples {
+
+PluginInstance::PluginInstance(scoped_refptr<PluginModule> plugin_module)
+ : pp_instance_(0),
+ plugin_module_(plugin_module) {
+ pp_instance_ = MojoPpapiGlobals::Get()->AddInstance(this);
+}
+
+PluginInstance::~PluginInstance() {
+ MojoPpapiGlobals::Get()->InstanceDeleted(pp_instance_);
+}
+
+bool PluginInstance::DidCreate() {
+ ppapi::ProxyAutoUnlock unlock;
+ const PPP_Instance_1_1* instance_interface =
+ static_cast<const PPP_Instance_1_1*>(plugin_module_->GetPluginInterface(
+ PPP_INSTANCE_INTERFACE_1_1));
+ return !!instance_interface->DidCreate(pp_instance(), 0, NULL, NULL);
+}
+
+void PluginInstance::DidDestroy() {
+ ppapi::ProxyAutoUnlock unlock;
+ const PPP_Instance_1_1* instance_interface =
+ static_cast<const PPP_Instance_1_1*>(plugin_module_->GetPluginInterface(
+ PPP_INSTANCE_INTERFACE_1_1));
+ instance_interface->DidDestroy(pp_instance());
+}
+
+void PluginInstance::DidChangeView(const PP_Rect& bounds) {
+ ppapi::ViewData view_data;
+ view_data.rect = bounds;
+ view_data.is_fullscreen = false;
+ view_data.is_page_visible = true;
+ view_data.clip_rect = bounds;
+ view_data.device_scale = 1.0f;
+ view_data.css_scale = 1.0f;
+
+ ppapi::ScopedPPResource resource(ppapi::ScopedPPResource::PassRef(),
+ (new ppapi::PPB_View_Shared(
+ ppapi::OBJECT_IS_IMPL, pp_instance(), view_data))->GetReference());
+ {
+ ppapi::ProxyAutoUnlock unlock;
+ const PPP_Instance_1_1* instance_interface =
+ static_cast<const PPP_Instance_1_1*>(plugin_module_->GetPluginInterface(
+ PPP_INSTANCE_INTERFACE_1_1));
+ instance_interface->DidChangeView(pp_instance(), resource);
+ }
+}
+
+void PluginInstance::Graphics3DContextLost() {
+ ppapi::ProxyAutoUnlock unlock;
+ const PPP_Graphics3D_1_0* graphic_3d_interface =
+ static_cast<const PPP_Graphics3D_1_0*>(plugin_module_->GetPluginInterface(
+ PPP_GRAPHICS_3D_INTERFACE_1_0));
+ // TODO(yzshen): Maybe we only need to notify for the bound graphics context?
+ graphic_3d_interface->Graphics3DContextLost(pp_instance());
+}
+
+bool PluginInstance::IsBoundGraphics(PP_Resource device) const {
+ return device != 0 && device == bound_graphics_.get();
+}
+
+PP_Bool PluginInstance::BindGraphics(PP_Instance instance, PP_Resource device) {
+ if (bound_graphics_.get() == device)
+ return PP_TRUE;
+
+ ppapi::thunk::EnterResourceNoLock<ppapi::thunk::PPB_Graphics3D_API>
+ enter(device, false);
+ if (enter.failed())
+ return PP_FALSE;
+
+ bound_graphics_ = device;
+ static_cast<Graphics3DResource*>(enter.object())->BindGraphics();
+
+ return PP_TRUE;
+}
+
+PP_Bool PluginInstance::IsFullFrame(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return PP_FALSE;
+}
+
+const ppapi::ViewData* PluginInstance::GetViewData(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return NULL;
+}
+
+PP_Bool PluginInstance::FlashIsFullscreen(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return PP_FALSE;
+}
+
+PP_Var PluginInstance::GetWindowObject(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return PP_MakeUndefined();
+}
+
+PP_Var PluginInstance::GetOwnerElementObject(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return PP_MakeUndefined();
+}
+
+PP_Var PluginInstance::ExecuteScript(PP_Instance instance,
+ PP_Var script,
+ PP_Var* exception) {
+ NOTIMPLEMENTED();
+ return PP_MakeUndefined();
+}
+
+uint32_t PluginInstance::GetAudioHardwareOutputSampleRate(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+uint32_t PluginInstance::GetAudioHardwareOutputBufferSize(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Var PluginInstance::GetDefaultCharSet(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return PP_MakeUndefined();
+}
+
+void PluginInstance::Log(PP_Instance instance,
+ PP_LogLevel log_level,
+ PP_Var value) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::LogWithSource(PP_Instance instance,
+ PP_LogLevel log_level,
+ PP_Var source,
+ PP_Var value) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::SetPluginToHandleFindRequests(PP_Instance instance) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::NumberOfFindResultsChanged(PP_Instance instance,
+ int32_t total,
+ PP_Bool final_result) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::SelectedFindResultChanged(PP_Instance instance,
+ int32_t index) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::SetTickmarks(PP_Instance instance,
+ const PP_Rect* tickmarks,
+ uint32_t count) {
+ NOTIMPLEMENTED();
+}
+
+PP_Bool PluginInstance::IsFullscreen(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return PP_FALSE;
+}
+
+PP_Bool PluginInstance::SetFullscreen(PP_Instance instance,
+ PP_Bool fullscreen) {
+ NOTIMPLEMENTED();
+ return PP_FALSE;
+}
+
+PP_Bool PluginInstance::GetScreenSize(PP_Instance instance, PP_Size* size) {
+ NOTIMPLEMENTED();
+ return PP_FALSE;
+}
+
+ppapi::Resource* PluginInstance::GetSingletonResource(
+ PP_Instance instance,
+ ppapi::SingletonResourceID id) {
+ NOTIMPLEMENTED();
+ return NULL;
+}
+
+int32_t PluginInstance::RequestInputEvents(PP_Instance instance,
+ uint32_t event_classes) {
+ NOTIMPLEMENTED();
+ return PP_ERROR_FAILED;
+}
+
+int32_t PluginInstance::RequestFilteringInputEvents(PP_Instance instance,
+ uint32_t event_classes) {
+ NOTIMPLEMENTED();
+ return PP_ERROR_FAILED;
+}
+
+void PluginInstance::ClearInputEventRequest(PP_Instance instance,
+ uint32_t event_classes) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::StartTrackingLatency(PP_Instance instance) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::PostMessage(PP_Instance instance, PP_Var message) {
+ NOTIMPLEMENTED();
+}
+
+int32_t PluginInstance::RegisterMessageHandler(
+ PP_Instance instance,
+ void* user_data,
+ const PPP_MessageHandler_0_1* handler,
+ PP_Resource message_loop) {
+ NOTIMPLEMENTED();
+ return PP_ERROR_FAILED;
+}
+
+void PluginInstance::UnregisterMessageHandler(PP_Instance instance) {
+ NOTIMPLEMENTED();
+}
+
+PP_Bool PluginInstance::SetCursor(PP_Instance instance,
+ PP_MouseCursor_Type type,
+ PP_Resource image,
+ const PP_Point* hot_spot) {
+ NOTIMPLEMENTED();
+ return PP_FALSE;
+}
+
+int32_t PluginInstance::LockMouse(
+ PP_Instance instance,
+ scoped_refptr<ppapi::TrackedCallback> callback) {
+ NOTIMPLEMENTED();
+ return PP_ERROR_FAILED;
+}
+
+void PluginInstance::UnlockMouse(PP_Instance instance) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::SetTextInputType(PP_Instance instance,
+ PP_TextInput_Type type) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::UpdateCaretPosition(PP_Instance instance,
+ const PP_Rect& caret,
+ const PP_Rect& bounding_box) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::CancelCompositionText(PP_Instance instance) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::SelectionChanged(PP_Instance instance) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::UpdateSurroundingText(PP_Instance instance,
+ const char* text,
+ uint32_t caret,
+ uint32_t anchor) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::ZoomChanged(PP_Instance instance, double factor) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::ZoomLimitsChanged(PP_Instance instance,
+ double minimum_factor,
+ double maximum_factor) {
+ NOTIMPLEMENTED();
+}
+
+PP_Var PluginInstance::GetDocumentURL(PP_Instance instance,
+ PP_URLComponents_Dev* components) {
+ NOTIMPLEMENTED();
+ return PP_MakeUndefined();
+}
+
+void PluginInstance::PromiseResolved(PP_Instance instance, uint32 promise_id) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::PromiseResolvedWithSession(PP_Instance instance,
+ uint32 promise_id,
+ PP_Var web_session_id_var) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::PromiseRejected(PP_Instance instance,
+ uint32 promise_id,
+ PP_CdmExceptionCode exception_code,
+ uint32 system_code,
+ PP_Var error_description_var) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::SessionMessage(PP_Instance instance,
+ PP_Var web_session_id_var,
+ PP_Var message_var,
+ PP_Var destination_url_var) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::SessionReady(PP_Instance instance,
+ PP_Var web_session_id_var) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::SessionClosed(PP_Instance instance,
+ PP_Var web_session_id_var) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::SessionError(PP_Instance instance,
+ PP_Var web_session_id_var,
+ PP_CdmExceptionCode exception_code,
+ uint32 system_code,
+ PP_Var error_description_var) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::DeliverBlock(PP_Instance instance,
+ PP_Resource decrypted_block,
+ const PP_DecryptedBlockInfo* block_info) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::DecoderInitializeDone(PP_Instance instance,
+ PP_DecryptorStreamType decoder_type,
+ uint32_t request_id,
+ PP_Bool success) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::DecoderDeinitializeDone(
+ PP_Instance instance,
+ PP_DecryptorStreamType decoder_type,
+ uint32_t request_id) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::DecoderResetDone(PP_Instance instance,
+ PP_DecryptorStreamType decoder_type,
+ uint32_t request_id) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::DeliverFrame(PP_Instance instance,
+ PP_Resource decrypted_frame,
+ const PP_DecryptedFrameInfo* frame_info) {
+ NOTIMPLEMENTED();
+}
+
+void PluginInstance::DeliverSamples(PP_Instance instance,
+ PP_Resource audio_frames,
+ const PP_DecryptedSampleInfo* sample_info) {
+ NOTIMPLEMENTED();
+}
+
+PP_Var PluginInstance::ResolveRelativeToDocument(
+ PP_Instance instance,
+ PP_Var relative,
+ PP_URLComponents_Dev* components) {
+ NOTIMPLEMENTED();
+ return PP_MakeUndefined();
+}
+
+PP_Bool PluginInstance::DocumentCanRequest(PP_Instance instance, PP_Var url) {
+ NOTIMPLEMENTED();
+ return PP_FALSE;
+}
+
+PP_Bool PluginInstance::DocumentCanAccessDocument(PP_Instance instance,
+ PP_Instance target) {
+ NOTIMPLEMENTED();
+ return PP_FALSE;
+}
+
+PP_Var PluginInstance::GetPluginInstanceURL(PP_Instance instance,
+ PP_URLComponents_Dev* components) {
+ NOTIMPLEMENTED();
+ return PP_MakeUndefined();
+}
+
+PP_Var PluginInstance::GetPluginReferrerURL(PP_Instance instance,
+ PP_URLComponents_Dev* components) {
+ NOTIMPLEMENTED();
+ return PP_MakeUndefined();
+}
+
+} // namespace examples
+} // namespace mojo
diff --git a/chromium/mojo/examples/pepper_container_app/plugin_instance.h b/chromium/mojo/examples/pepper_container_app/plugin_instance.h
new file mode 100644
index 00000000000..f76621a0eea
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/plugin_instance.h
@@ -0,0 +1,185 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EXAMPLES_PEPPER_CONTAINER_APP_PLUGIN_INSTANCE_H_
+#define MOJO_EXAMPLES_PEPPER_CONTAINER_APP_PLUGIN_INSTANCE_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/examples/pepper_container_app/plugin_module.h"
+#include "mojo/examples/pepper_container_app/resource_creation_impl.h"
+#include "ppapi/c/pp_rect.h"
+#include "ppapi/shared_impl/scoped_pp_resource.h"
+#include "ppapi/thunk/ppb_instance_api.h"
+
+namespace mojo {
+namespace examples {
+
+class PluginInstance : public ppapi::thunk::PPB_Instance_API {
+ public:
+ explicit PluginInstance(scoped_refptr<PluginModule> plugin_module);
+ virtual ~PluginInstance();
+
+ // Notifies the plugin that a new instance has been created.
+ bool DidCreate();
+ // Notifies the plugin that the instance has been destroyed.
+ void DidDestroy();
+ // Notifies the plugin that the position or size of the instance has changed.
+ void DidChangeView(const PP_Rect& bounds);
+ // Notifies the plugin that the Graphics 3D context has been invalidated.
+ void Graphics3DContextLost();
+
+ // Returns true if |device| has been bound as the current display surface.
+ bool IsBoundGraphics(PP_Resource device) const;
+
+ PP_Instance pp_instance() const { return pp_instance_; }
+ ResourceCreationImpl* resource_creation() { return &resource_creation_; }
+ PluginModule* plugin_module() { return plugin_module_.get(); }
+
+ // ppapi::thunk::PPB_Instance_API implementation.
+ virtual PP_Bool BindGraphics(PP_Instance instance,
+ PP_Resource device) OVERRIDE;
+ virtual PP_Bool IsFullFrame(PP_Instance instance) OVERRIDE;
+ virtual const ppapi::ViewData* GetViewData(PP_Instance instance) OVERRIDE;
+ virtual PP_Bool FlashIsFullscreen(PP_Instance instance) OVERRIDE;
+ virtual PP_Var GetWindowObject(PP_Instance instance) OVERRIDE;
+ virtual PP_Var GetOwnerElementObject(PP_Instance instance) OVERRIDE;
+ virtual PP_Var ExecuteScript(PP_Instance instance,
+ PP_Var script,
+ PP_Var* exception) OVERRIDE;
+ virtual uint32_t GetAudioHardwareOutputSampleRate(
+ PP_Instance instance) OVERRIDE;
+ virtual uint32_t GetAudioHardwareOutputBufferSize(
+ PP_Instance instance) OVERRIDE;
+ virtual PP_Var GetDefaultCharSet(PP_Instance instance) OVERRIDE;
+ virtual void Log(PP_Instance instance,
+ PP_LogLevel log_level,
+ PP_Var value) OVERRIDE;
+ virtual void LogWithSource(PP_Instance instance,
+ PP_LogLevel log_level,
+ PP_Var source,
+ PP_Var value) OVERRIDE;
+ virtual void SetPluginToHandleFindRequests(PP_Instance instance) OVERRIDE;
+ virtual void NumberOfFindResultsChanged(PP_Instance instance,
+ int32_t total,
+ PP_Bool final_result) OVERRIDE;
+ virtual void SelectedFindResultChanged(PP_Instance instance,
+ int32_t index) OVERRIDE;
+ virtual void SetTickmarks(PP_Instance instance,
+ const PP_Rect* tickmarks,
+ uint32_t count) OVERRIDE;
+ virtual PP_Bool IsFullscreen(PP_Instance instance) OVERRIDE;
+ virtual PP_Bool SetFullscreen(PP_Instance instance,
+ PP_Bool fullscreen) OVERRIDE;
+ virtual PP_Bool GetScreenSize(PP_Instance instance, PP_Size* size) OVERRIDE;
+ virtual ppapi::Resource* GetSingletonResource(
+ PP_Instance instance, ppapi::SingletonResourceID id) OVERRIDE;
+ virtual int32_t RequestInputEvents(PP_Instance instance,
+ uint32_t event_classes) OVERRIDE;
+ virtual int32_t RequestFilteringInputEvents(PP_Instance instance,
+ uint32_t event_classes) OVERRIDE;
+ virtual void ClearInputEventRequest(PP_Instance instance,
+ uint32_t event_classes) OVERRIDE;
+ virtual void StartTrackingLatency(PP_Instance instance) OVERRIDE;
+ virtual void PostMessage(PP_Instance instance, PP_Var message) OVERRIDE;
+ virtual int32_t RegisterMessageHandler(PP_Instance instance,
+ void* user_data,
+ const PPP_MessageHandler_0_1* handler,
+ PP_Resource message_loop) OVERRIDE;
+ virtual void UnregisterMessageHandler(PP_Instance instance) OVERRIDE;
+ virtual PP_Bool SetCursor(PP_Instance instance,
+ PP_MouseCursor_Type type,
+ PP_Resource image,
+ const PP_Point* hot_spot) OVERRIDE;
+ virtual int32_t LockMouse(
+ PP_Instance instance,
+ scoped_refptr<ppapi::TrackedCallback> callback) OVERRIDE;
+ virtual void UnlockMouse(PP_Instance instance) OVERRIDE;
+ virtual void SetTextInputType(PP_Instance instance,
+ PP_TextInput_Type type) OVERRIDE;
+ virtual void UpdateCaretPosition(PP_Instance instance,
+ const PP_Rect& caret,
+ const PP_Rect& bounding_box) OVERRIDE;
+ virtual void CancelCompositionText(PP_Instance instance) OVERRIDE;
+ virtual void SelectionChanged(PP_Instance instance) OVERRIDE;
+ virtual void UpdateSurroundingText(PP_Instance instance,
+ const char* text,
+ uint32_t caret,
+ uint32_t anchor) OVERRIDE;
+ virtual void ZoomChanged(PP_Instance instance, double factor) OVERRIDE;
+ virtual void ZoomLimitsChanged(PP_Instance instance,
+ double minimum_factor,
+ double maximum_factor) OVERRIDE;
+ virtual PP_Var GetDocumentURL(PP_Instance instance,
+ PP_URLComponents_Dev* components) OVERRIDE;
+ virtual void PromiseResolved(PP_Instance instance,
+ uint32 promise_id) OVERRIDE;
+ virtual void PromiseResolvedWithSession(PP_Instance instance,
+ uint32 promise_id,
+ PP_Var web_session_id_var) OVERRIDE;
+ virtual void PromiseRejected(PP_Instance instance,
+ uint32 promise_id,
+ PP_CdmExceptionCode exception_code,
+ uint32 system_code,
+ PP_Var error_description_var) OVERRIDE;
+ virtual void SessionMessage(PP_Instance instance,
+ PP_Var web_session_id_var,
+ PP_Var message_var,
+ PP_Var destination_url_var) OVERRIDE;
+ virtual void SessionReady(PP_Instance instance,
+ PP_Var web_session_id_var) OVERRIDE;
+ virtual void SessionClosed(PP_Instance instance,
+ PP_Var web_session_id_var) OVERRIDE;
+ virtual void SessionError(PP_Instance instance,
+ PP_Var web_session_id_var,
+ PP_CdmExceptionCode exception_code,
+ uint32 system_code,
+ PP_Var error_description_var) OVERRIDE;
+ virtual void DeliverBlock(PP_Instance instance,
+ PP_Resource decrypted_block,
+ const PP_DecryptedBlockInfo* block_info) OVERRIDE;
+ virtual void DecoderInitializeDone(PP_Instance instance,
+ PP_DecryptorStreamType decoder_type,
+ uint32_t request_id,
+ PP_Bool success) OVERRIDE;
+ virtual void DecoderDeinitializeDone(PP_Instance instance,
+ PP_DecryptorStreamType decoder_type,
+ uint32_t request_id) OVERRIDE;
+ virtual void DecoderResetDone(PP_Instance instance,
+ PP_DecryptorStreamType decoder_type,
+ uint32_t request_id) OVERRIDE;
+ virtual void DeliverFrame(PP_Instance instance,
+ PP_Resource decrypted_frame,
+ const PP_DecryptedFrameInfo* frame_info) OVERRIDE;
+ virtual void DeliverSamples(
+ PP_Instance instance,
+ PP_Resource audio_frames,
+ const PP_DecryptedSampleInfo* sample_info) OVERRIDE;
+ virtual PP_Var ResolveRelativeToDocument(
+ PP_Instance instance,
+ PP_Var relative,
+ PP_URLComponents_Dev* components) OVERRIDE;
+ virtual PP_Bool DocumentCanRequest(PP_Instance instance, PP_Var url) OVERRIDE;
+ virtual PP_Bool DocumentCanAccessDocument(PP_Instance instance,
+ PP_Instance target) OVERRIDE;
+ virtual PP_Var GetPluginInstanceURL(
+ PP_Instance instance,
+ PP_URLComponents_Dev* components) OVERRIDE;
+ virtual PP_Var GetPluginReferrerURL(
+ PP_Instance instance,
+ PP_URLComponents_Dev* components) OVERRIDE;
+
+ private:
+ PP_Instance pp_instance_;
+ ResourceCreationImpl resource_creation_;
+ scoped_refptr<PluginModule> plugin_module_;
+ ppapi::ScopedPPResource bound_graphics_;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginInstance);
+};
+
+} // namespace examples
+} // namespace mojo
+
+#endif // MOJO_EXAMPLES_PEPPER_CONTAINER_APP_PLUGIN_INSTANCE_H_
diff --git a/chromium/mojo/examples/pepper_container_app/plugin_module.cc b/chromium/mojo/examples/pepper_container_app/plugin_module.cc
new file mode 100644
index 00000000000..d09d42d36d4
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/plugin_module.cc
@@ -0,0 +1,110 @@
+// Copyright 2014 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 "mojo/examples/pepper_container_app/plugin_module.h"
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "mojo/examples/pepper_container_app/interface_list.h"
+#include "mojo/examples/pepper_container_app/plugin_instance.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/shared_impl/callback_tracker.h"
+
+namespace mojo {
+namespace examples {
+
+namespace {
+
+const void* GetInterface(const char* name) {
+ const void* interface =
+ InterfaceList::GetInstance()->GetBrowserInterface(name);
+
+ if (!interface)
+ LOG(WARNING) << "Interface requested " << name;
+
+ return interface;
+}
+
+} // namespace
+
+PluginModule::EntryPoints::EntryPoints() : get_interface(NULL),
+ initialize_module(NULL),
+ shutdown_module(NULL) {}
+
+PluginModule::PluginModule() : callback_tracker_(new ppapi::CallbackTracker) {
+ Initialize();
+}
+
+PluginModule::~PluginModule() {
+ callback_tracker_->AbortAll();
+
+ if (entry_points_.shutdown_module)
+ entry_points_.shutdown_module();
+}
+
+scoped_ptr<PluginInstance> PluginModule::CreateInstance() {
+ return make_scoped_ptr(new PluginInstance(this));
+}
+
+const void* PluginModule::GetPluginInterface(const char* name) const {
+ if (entry_points_.get_interface)
+ return entry_points_.get_interface(name);
+ return NULL;
+}
+
+void PluginModule::Initialize() {
+ // Platform-specific filename.
+ // TODO(yzshen): Don't hard-code it.
+#if defined(OS_WIN)
+ static const wchar_t plugin_name[] = L"ppapi_example_gles2_spinning_cube.dll";
+#elif defined(OS_MACOSX)
+ static const char plugin_name[] = "ppapi_example_gles2_spinning_cube.plugin";
+#elif defined(OS_POSIX)
+ static const char plugin_name[] = "libppapi_example_gles2_spinning_cube.so";
+#endif
+
+ base::FilePath plugin_path(plugin_name);
+
+ base::NativeLibraryLoadError error;
+ plugin_module_.Reset(base::LoadNativeLibrary(plugin_path, &error));
+
+ if (!plugin_module_.is_valid()) {
+ LOG(WARNING) << "Cannot load " << plugin_path.AsUTF8Unsafe()
+ << ". Error: " << error.ToString();
+ return;
+ }
+
+ entry_points_.get_interface =
+ reinterpret_cast<PP_GetInterface_Func>(
+ plugin_module_.GetFunctionPointer("PPP_GetInterface"));
+ if (!entry_points_.get_interface) {
+ LOG(WARNING) << "No PPP_GetInterface in plugin library";
+ return;
+ }
+
+ entry_points_.initialize_module =
+ reinterpret_cast<PP_InitializeModule_Func>(
+ plugin_module_.GetFunctionPointer("PPP_InitializeModule"));
+ if (!entry_points_.initialize_module) {
+ LOG(WARNING) << "No PPP_InitializeModule in plugin library";
+ return;
+ }
+
+ // It's okay for PPP_ShutdownModule to not be defined and |shutdown_module| to
+ // be NULL.
+ entry_points_.shutdown_module =
+ reinterpret_cast<PP_ShutdownModule_Func>(
+ plugin_module_.GetFunctionPointer("PPP_ShutdownModule"));
+
+ int32_t result = entry_points_.initialize_module(pp_module(),
+ &GetInterface);
+ if (result != PP_OK)
+ LOG(WARNING) << "Initializing module failed with error " << result;
+}
+
+} // namespace examples
+} // namespace mojo
diff --git a/chromium/mojo/examples/pepper_container_app/plugin_module.h b/chromium/mojo/examples/pepper_container_app/plugin_module.h
new file mode 100644
index 00000000000..05aad3b1a65
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/plugin_module.h
@@ -0,0 +1,61 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EXAMPLES_PEPPER_CONTAINER_APP_PLUGIN_MODULE_H_
+#define MOJO_EXAMPLES_PEPPER_CONTAINER_APP_PLUGIN_MODULE_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/native_library.h"
+#include "base/scoped_native_library.h"
+#include "ppapi/c/pp_module.h"
+#include "ppapi/c/ppp.h"
+
+namespace ppapi {
+class CallbackTracker;
+}
+
+namespace mojo {
+namespace examples {
+
+class PluginInstance;
+
+class PluginModule : public base::RefCounted<PluginModule> {
+ public:
+ PluginModule();
+
+ scoped_ptr<PluginInstance> CreateInstance();
+
+ const void* GetPluginInterface(const char* name) const;
+
+ PP_Module pp_module() const { return 1; }
+ ppapi::CallbackTracker* callback_tracker() { return callback_tracker_.get(); }
+
+ private:
+ friend class base::RefCounted<PluginModule>;
+
+ struct EntryPoints {
+ EntryPoints();
+
+ PP_GetInterface_Func get_interface;
+ PP_InitializeModule_Func initialize_module;
+ PP_ShutdownModule_Func shutdown_module; // Optional, may be NULL.
+ };
+
+ ~PluginModule();
+
+ void Initialize();
+
+ base::ScopedNativeLibrary plugin_module_;
+ EntryPoints entry_points_;
+ scoped_refptr<ppapi::CallbackTracker> callback_tracker_;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginModule);
+};
+
+} // namespace examples
+} // namespace mojo
+
+#endif // MOJO_EXAMPLES_PEPPER_CONTAINER_APP_PLUGIN_MODULE_H_
diff --git a/chromium/mojo/examples/pepper_container_app/ppb_core_thunk.cc b/chromium/mojo/examples/pepper_container_app/ppb_core_thunk.cc
new file mode 100644
index 00000000000..a76a9764785
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/ppb_core_thunk.cc
@@ -0,0 +1,64 @@
+// Copyright 2014 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 "base/logging.h"
+#include "mojo/examples/pepper_container_app/thunk.h"
+#include "ppapi/c/ppb_core.h"
+#include "ppapi/shared_impl/ppapi_globals.h"
+#include "ppapi/shared_impl/proxy_lock.h"
+#include "ppapi/shared_impl/resource_tracker.h"
+
+namespace mojo {
+namespace examples {
+
+namespace {
+
+void AddRefResource(PP_Resource resource) {
+ ppapi::ProxyAutoLock lock;
+ ppapi::PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(resource);
+}
+
+void ReleaseResource(PP_Resource resource) {
+ ppapi::ProxyAutoLock lock;
+ ppapi::PpapiGlobals::Get()->GetResourceTracker()->ReleaseResource(resource);
+}
+
+PP_Time GetTime() {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_TimeTicks GetTimeTicks() {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+void CallOnMainThread(int32_t delay_in_milliseconds,
+ PP_CompletionCallback callback,
+ int32_t result) {
+ NOTIMPLEMENTED();
+}
+
+PP_Bool IsMainThread() {
+ NOTIMPLEMENTED();
+ return PP_TRUE;
+}
+
+} // namespace
+
+const PPB_Core_1_0 g_ppb_core_thunk_1_0 = {
+ &AddRefResource,
+ &ReleaseResource,
+ &GetTime,
+ &GetTimeTicks,
+ &CallOnMainThread,
+ &IsMainThread
+};
+
+const PPB_Core_1_0* GetPPB_Core_1_0_Thunk() {
+ return &g_ppb_core_thunk_1_0;
+}
+
+} // namespace examples
+} // namespace mojo
diff --git a/chromium/mojo/examples/pepper_container_app/ppb_opengles2_thunk.cc b/chromium/mojo/examples/pepper_container_app/ppb_opengles2_thunk.cc
new file mode 100644
index 00000000000..c5097bb0ddb
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/ppb_opengles2_thunk.cc
@@ -0,0 +1,1454 @@
+// Copyright 2014 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 "base/logging.h"
+#include "mojo/examples/pepper_container_app/graphics_3d_resource.h"
+#include "mojo/examples/pepper_container_app/thunk.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "ppapi/thunk/enter.h"
+#include "ppapi/thunk/ppb_graphics_3d_api.h"
+
+namespace mojo {
+namespace examples {
+
+namespace {
+
+typedef ppapi::thunk::EnterResource<ppapi::thunk::PPB_Graphics3D_API> Enter3D;
+
+bool IsBoundGraphics(Enter3D* enter) {
+ return enter->succeeded() &&
+ static_cast<Graphics3DResource*>(enter->object())->IsBoundGraphics();
+}
+
+void ActiveTexture(PP_Resource context_id, GLenum texture) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glActiveTexture(texture);
+ }
+}
+
+void AttachShader(PP_Resource context_id, GLuint program, GLuint shader) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glAttachShader(program, shader);
+ }
+}
+
+void BindAttribLocation(PP_Resource context_id,
+ GLuint program,
+ GLuint index,
+ const char* name) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glBindAttribLocation(program, index, name);
+ }
+}
+
+void BindBuffer(PP_Resource context_id, GLenum target, GLuint buffer) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glBindBuffer(target, buffer);
+ }
+}
+
+void BindFramebuffer(PP_Resource context_id,
+ GLenum target,
+ GLuint framebuffer) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glBindFramebuffer(target, framebuffer);
+ }
+}
+
+void BindRenderbuffer(PP_Resource context_id,
+ GLenum target,
+ GLuint renderbuffer) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glBindRenderbuffer(target, renderbuffer);
+ }
+}
+
+void BindTexture(PP_Resource context_id, GLenum target, GLuint texture) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glBindTexture(target, texture);
+ }
+}
+
+void BlendColor(PP_Resource context_id,
+ GLclampf red,
+ GLclampf green,
+ GLclampf blue,
+ GLclampf alpha) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glBlendColor(red, green, blue, alpha);
+ }
+}
+
+void BlendEquation(PP_Resource context_id, GLenum mode) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glBlendEquation(mode);
+ }
+}
+
+void BlendEquationSeparate(PP_Resource context_id,
+ GLenum modeRGB,
+ GLenum modeAlpha) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glBlendEquationSeparate(modeRGB, modeAlpha);
+ }
+}
+
+void BlendFunc(PP_Resource context_id, GLenum sfactor, GLenum dfactor) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glBlendFunc(sfactor, dfactor);
+ }
+}
+
+void BlendFuncSeparate(PP_Resource context_id,
+ GLenum srcRGB,
+ GLenum dstRGB,
+ GLenum srcAlpha,
+ GLenum dstAlpha) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
+ }
+}
+
+void BufferData(PP_Resource context_id,
+ GLenum target,
+ GLsizeiptr size,
+ const void* data,
+ GLenum usage) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glBufferData(target, size, data, usage);
+ }
+}
+
+void BufferSubData(PP_Resource context_id,
+ GLenum target,
+ GLintptr offset,
+ GLsizeiptr size,
+ const void* data) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glBufferSubData(target, offset, size, data);
+ }
+}
+
+GLenum CheckFramebufferStatus(PP_Resource context_id, GLenum target) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ return glCheckFramebufferStatus(target);
+ } else {
+ return 0;
+ }
+}
+
+void Clear(PP_Resource context_id, GLbitfield mask) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glClear(mask);
+ }
+}
+
+void ClearColor(PP_Resource context_id,
+ GLclampf red,
+ GLclampf green,
+ GLclampf blue,
+ GLclampf alpha) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glClearColor(red, green, blue, alpha);
+ }
+}
+
+void ClearDepthf(PP_Resource context_id, GLclampf depth) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glClearDepthf(depth);
+ }
+}
+
+void ClearStencil(PP_Resource context_id, GLint s) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glClearStencil(s);
+ }
+}
+
+void ColorMask(PP_Resource context_id,
+ GLboolean red,
+ GLboolean green,
+ GLboolean blue,
+ GLboolean alpha) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glColorMask(red, green, blue, alpha);
+ }
+}
+
+void CompileShader(PP_Resource context_id, GLuint shader) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glCompileShader(shader);
+ }
+}
+
+void CompressedTexImage2D(PP_Resource context_id,
+ GLenum target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLsizei imageSize,
+ const void* data) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glCompressedTexImage2D(
+ target, level, internalformat, width, height, border, imageSize, data);
+ }
+}
+
+void CompressedTexSubImage2D(PP_Resource context_id,
+ GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLsizei imageSize,
+ const void* data) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glCompressedTexSubImage2D(target,
+ level,
+ xoffset,
+ yoffset,
+ width,
+ height,
+ format,
+ imageSize,
+ data);
+ }
+}
+
+void CopyTexImage2D(PP_Resource context_id,
+ GLenum target,
+ GLint level,
+ GLenum internalformat,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLint border) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glCopyTexImage2D(
+ target, level, internalformat, x, y, width, height, border);
+ }
+}
+
+void CopyTexSubImage2D(PP_Resource context_id,
+ GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
+ }
+}
+
+GLuint CreateProgram(PP_Resource context_id) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ return glCreateProgram();
+ } else {
+ return 0;
+ }
+}
+
+GLuint CreateShader(PP_Resource context_id, GLenum type) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ return glCreateShader(type);
+ } else {
+ return 0;
+ }
+}
+
+void CullFace(PP_Resource context_id, GLenum mode) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glCullFace(mode);
+ }
+}
+
+void DeleteBuffers(PP_Resource context_id, GLsizei n, const GLuint* buffers) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glDeleteBuffers(n, buffers);
+ }
+}
+
+void DeleteFramebuffers(PP_Resource context_id,
+ GLsizei n,
+ const GLuint* framebuffers) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glDeleteFramebuffers(n, framebuffers);
+ }
+}
+
+void DeleteProgram(PP_Resource context_id, GLuint program) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glDeleteProgram(program);
+ }
+}
+
+void DeleteRenderbuffers(PP_Resource context_id,
+ GLsizei n,
+ const GLuint* renderbuffers) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glDeleteRenderbuffers(n, renderbuffers);
+ }
+}
+
+void DeleteShader(PP_Resource context_id, GLuint shader) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glDeleteShader(shader);
+ }
+}
+
+void DeleteTextures(PP_Resource context_id, GLsizei n, const GLuint* textures) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glDeleteTextures(n, textures);
+ }
+}
+
+void DepthFunc(PP_Resource context_id, GLenum func) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glDepthFunc(func);
+ }
+}
+
+void DepthMask(PP_Resource context_id, GLboolean flag) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glDepthMask(flag);
+ }
+}
+
+void DepthRangef(PP_Resource context_id, GLclampf zNear, GLclampf zFar) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glDepthRangef(zNear, zFar);
+ }
+}
+
+void DetachShader(PP_Resource context_id, GLuint program, GLuint shader) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glDetachShader(program, shader);
+ }
+}
+
+void Disable(PP_Resource context_id, GLenum cap) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glDisable(cap);
+ }
+}
+
+void DisableVertexAttribArray(PP_Resource context_id, GLuint index) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glDisableVertexAttribArray(index);
+ }
+}
+
+void DrawArrays(PP_Resource context_id,
+ GLenum mode,
+ GLint first,
+ GLsizei count) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glDrawArrays(mode, first, count);
+ }
+}
+
+void DrawElements(PP_Resource context_id,
+ GLenum mode,
+ GLsizei count,
+ GLenum type,
+ const void* indices) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glDrawElements(mode, count, type, indices);
+ }
+}
+
+void Enable(PP_Resource context_id, GLenum cap) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glEnable(cap);
+ }
+}
+
+void EnableVertexAttribArray(PP_Resource context_id, GLuint index) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glEnableVertexAttribArray(index);
+ }
+}
+
+void Finish(PP_Resource context_id) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glFinish();
+ }
+}
+
+void Flush(PP_Resource context_id) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glFlush();
+ }
+}
+
+void FramebufferRenderbuffer(PP_Resource context_id,
+ GLenum target,
+ GLenum attachment,
+ GLenum renderbuffertarget,
+ GLuint renderbuffer) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glFramebufferRenderbuffer(
+ target, attachment, renderbuffertarget, renderbuffer);
+ }
+}
+
+void FramebufferTexture2D(PP_Resource context_id,
+ GLenum target,
+ GLenum attachment,
+ GLenum textarget,
+ GLuint texture,
+ GLint level) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glFramebufferTexture2D(target, attachment, textarget, texture, level);
+ }
+}
+
+void FrontFace(PP_Resource context_id, GLenum mode) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glFrontFace(mode);
+ }
+}
+
+void GenBuffers(PP_Resource context_id, GLsizei n, GLuint* buffers) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGenBuffers(n, buffers);
+ }
+}
+
+void GenerateMipmap(PP_Resource context_id, GLenum target) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGenerateMipmap(target);
+ }
+}
+
+void GenFramebuffers(PP_Resource context_id, GLsizei n, GLuint* framebuffers) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGenFramebuffers(n, framebuffers);
+ }
+}
+
+void GenRenderbuffers(PP_Resource context_id,
+ GLsizei n,
+ GLuint* renderbuffers) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGenRenderbuffers(n, renderbuffers);
+ }
+}
+
+void GenTextures(PP_Resource context_id, GLsizei n, GLuint* textures) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGenTextures(n, textures);
+ }
+}
+
+void GetActiveAttrib(PP_Resource context_id,
+ GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei* length,
+ GLint* size,
+ GLenum* type,
+ char* name) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetActiveAttrib(program, index, bufsize, length, size, type, name);
+ }
+}
+
+void GetActiveUniform(PP_Resource context_id,
+ GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei* length,
+ GLint* size,
+ GLenum* type,
+ char* name) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetActiveUniform(program, index, bufsize, length, size, type, name);
+ }
+}
+
+void GetAttachedShaders(PP_Resource context_id,
+ GLuint program,
+ GLsizei maxcount,
+ GLsizei* count,
+ GLuint* shaders) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetAttachedShaders(program, maxcount, count, shaders);
+ }
+}
+
+GLint GetAttribLocation(PP_Resource context_id,
+ GLuint program,
+ const char* name) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ return glGetAttribLocation(program, name);
+ } else {
+ return -1;
+ }
+}
+
+void GetBooleanv(PP_Resource context_id, GLenum pname, GLboolean* params) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetBooleanv(pname, params);
+ }
+}
+
+void GetBufferParameteriv(PP_Resource context_id,
+ GLenum target,
+ GLenum pname,
+ GLint* params) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetBufferParameteriv(target, pname, params);
+ }
+}
+
+GLenum GetError(PP_Resource context_id) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ return glGetError();
+ } else {
+ return 0;
+ }
+}
+
+void GetFloatv(PP_Resource context_id, GLenum pname, GLfloat* params) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetFloatv(pname, params);
+ }
+}
+
+void GetFramebufferAttachmentParameteriv(PP_Resource context_id,
+ GLenum target,
+ GLenum attachment,
+ GLenum pname,
+ GLint* params) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetFramebufferAttachmentParameteriv(target, attachment, pname, params);
+ }
+}
+
+void GetIntegerv(PP_Resource context_id, GLenum pname, GLint* params) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetIntegerv(pname, params);
+ }
+}
+
+void GetProgramiv(PP_Resource context_id,
+ GLuint program,
+ GLenum pname,
+ GLint* params) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetProgramiv(program, pname, params);
+ }
+}
+
+void GetProgramInfoLog(PP_Resource context_id,
+ GLuint program,
+ GLsizei bufsize,
+ GLsizei* length,
+ char* infolog) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetProgramInfoLog(program, bufsize, length, infolog);
+ }
+}
+
+void GetRenderbufferParameteriv(PP_Resource context_id,
+ GLenum target,
+ GLenum pname,
+ GLint* params) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetRenderbufferParameteriv(target, pname, params);
+ }
+}
+
+void GetShaderiv(PP_Resource context_id,
+ GLuint shader,
+ GLenum pname,
+ GLint* params) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetShaderiv(shader, pname, params);
+ }
+}
+
+void GetShaderInfoLog(PP_Resource context_id,
+ GLuint shader,
+ GLsizei bufsize,
+ GLsizei* length,
+ char* infolog) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetShaderInfoLog(shader, bufsize, length, infolog);
+ }
+}
+
+void GetShaderPrecisionFormat(PP_Resource context_id,
+ GLenum shadertype,
+ GLenum precisiontype,
+ GLint* range,
+ GLint* precision) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetShaderPrecisionFormat(shadertype, precisiontype, range, precision);
+ }
+}
+
+void GetShaderSource(PP_Resource context_id,
+ GLuint shader,
+ GLsizei bufsize,
+ GLsizei* length,
+ char* source) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetShaderSource(shader, bufsize, length, source);
+ }
+}
+
+const GLubyte* GetString(PP_Resource context_id, GLenum name) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ return glGetString(name);
+ } else {
+ return NULL;
+ }
+}
+
+void GetTexParameterfv(PP_Resource context_id,
+ GLenum target,
+ GLenum pname,
+ GLfloat* params) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetTexParameterfv(target, pname, params);
+ }
+}
+
+void GetTexParameteriv(PP_Resource context_id,
+ GLenum target,
+ GLenum pname,
+ GLint* params) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetTexParameteriv(target, pname, params);
+ }
+}
+
+void GetUniformfv(PP_Resource context_id,
+ GLuint program,
+ GLint location,
+ GLfloat* params) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetUniformfv(program, location, params);
+ }
+}
+
+void GetUniformiv(PP_Resource context_id,
+ GLuint program,
+ GLint location,
+ GLint* params) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetUniformiv(program, location, params);
+ }
+}
+
+GLint GetUniformLocation(PP_Resource context_id,
+ GLuint program,
+ const char* name) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ return glGetUniformLocation(program, name);
+ } else {
+ return -1;
+ }
+}
+
+void GetVertexAttribfv(PP_Resource context_id,
+ GLuint index,
+ GLenum pname,
+ GLfloat* params) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetVertexAttribfv(index, pname, params);
+ }
+}
+
+void GetVertexAttribiv(PP_Resource context_id,
+ GLuint index,
+ GLenum pname,
+ GLint* params) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetVertexAttribiv(index, pname, params);
+ }
+}
+
+void GetVertexAttribPointerv(PP_Resource context_id,
+ GLuint index,
+ GLenum pname,
+ void** pointer) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glGetVertexAttribPointerv(index, pname, pointer);
+ }
+}
+
+void Hint(PP_Resource context_id, GLenum target, GLenum mode) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glHint(target, mode);
+ }
+}
+
+GLboolean IsBuffer(PP_Resource context_id, GLuint buffer) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ return glIsBuffer(buffer);
+ } else {
+ return GL_FALSE;
+ }
+}
+
+GLboolean IsEnabled(PP_Resource context_id, GLenum cap) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ return glIsEnabled(cap);
+ } else {
+ return GL_FALSE;
+ }
+}
+
+GLboolean IsFramebuffer(PP_Resource context_id, GLuint framebuffer) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ return glIsFramebuffer(framebuffer);
+ } else {
+ return GL_FALSE;
+ }
+}
+
+GLboolean IsProgram(PP_Resource context_id, GLuint program) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ return glIsProgram(program);
+ } else {
+ return GL_FALSE;
+ }
+}
+
+GLboolean IsRenderbuffer(PP_Resource context_id, GLuint renderbuffer) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ return glIsRenderbuffer(renderbuffer);
+ } else {
+ return GL_FALSE;
+ }
+}
+
+GLboolean IsShader(PP_Resource context_id, GLuint shader) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ return glIsShader(shader);
+ } else {
+ return GL_FALSE;
+ }
+}
+
+GLboolean IsTexture(PP_Resource context_id, GLuint texture) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ return glIsTexture(texture);
+ } else {
+ return GL_FALSE;
+ }
+}
+
+void LineWidth(PP_Resource context_id, GLfloat width) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glLineWidth(width);
+ }
+}
+
+void LinkProgram(PP_Resource context_id, GLuint program) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glLinkProgram(program);
+ }
+}
+
+void PixelStorei(PP_Resource context_id, GLenum pname, GLint param) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glPixelStorei(pname, param);
+ }
+}
+
+void PolygonOffset(PP_Resource context_id, GLfloat factor, GLfloat units) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glPolygonOffset(factor, units);
+ }
+}
+
+void ReadPixels(PP_Resource context_id,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ void* pixels) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glReadPixels(x, y, width, height, format, type, pixels);
+ }
+}
+
+void ReleaseShaderCompiler(PP_Resource context_id) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glReleaseShaderCompiler();
+ }
+}
+
+void RenderbufferStorage(PP_Resource context_id,
+ GLenum target,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glRenderbufferStorage(target, internalformat, width, height);
+ }
+}
+
+void SampleCoverage(PP_Resource context_id, GLclampf value, GLboolean invert) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glSampleCoverage(value, invert);
+ }
+}
+
+void Scissor(PP_Resource context_id,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glScissor(x, y, width, height);
+ }
+}
+
+void ShaderBinary(PP_Resource context_id,
+ GLsizei n,
+ const GLuint* shaders,
+ GLenum binaryformat,
+ const void* binary,
+ GLsizei length) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glShaderBinary(n, shaders, binaryformat, binary, length);
+ }
+}
+
+void ShaderSource(PP_Resource context_id,
+ GLuint shader,
+ GLsizei count,
+ const char** str,
+ const GLint* length) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glShaderSource(shader, count, str, length);
+ }
+}
+
+void StencilFunc(PP_Resource context_id, GLenum func, GLint ref, GLuint mask) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glStencilFunc(func, ref, mask);
+ }
+}
+
+void StencilFuncSeparate(PP_Resource context_id,
+ GLenum face,
+ GLenum func,
+ GLint ref,
+ GLuint mask) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glStencilFuncSeparate(face, func, ref, mask);
+ }
+}
+
+void StencilMask(PP_Resource context_id, GLuint mask) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glStencilMask(mask);
+ }
+}
+
+void StencilMaskSeparate(PP_Resource context_id, GLenum face, GLuint mask) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glStencilMaskSeparate(face, mask);
+ }
+}
+
+void StencilOp(PP_Resource context_id,
+ GLenum fail,
+ GLenum zfail,
+ GLenum zpass) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glStencilOp(fail, zfail, zpass);
+ }
+}
+
+void StencilOpSeparate(PP_Resource context_id,
+ GLenum face,
+ GLenum fail,
+ GLenum zfail,
+ GLenum zpass) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glStencilOpSeparate(face, fail, zfail, zpass);
+ }
+}
+
+void TexImage2D(PP_Resource context_id,
+ GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const void* pixels) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glTexImage2D(target,
+ level,
+ internalformat,
+ width,
+ height,
+ border,
+ format,
+ type,
+ pixels);
+ }
+}
+
+void TexParameterf(PP_Resource context_id,
+ GLenum target,
+ GLenum pname,
+ GLfloat param) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glTexParameterf(target, pname, param);
+ }
+}
+
+void TexParameterfv(PP_Resource context_id,
+ GLenum target,
+ GLenum pname,
+ const GLfloat* params) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glTexParameterfv(target, pname, params);
+ }
+}
+
+void TexParameteri(PP_Resource context_id,
+ GLenum target,
+ GLenum pname,
+ GLint param) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glTexParameteri(target, pname, param);
+ }
+}
+
+void TexParameteriv(PP_Resource context_id,
+ GLenum target,
+ GLenum pname,
+ const GLint* params) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glTexParameteriv(target, pname, params);
+ }
+}
+
+void TexSubImage2D(PP_Resource context_id,
+ GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ const void* pixels) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glTexSubImage2D(
+ target, level, xoffset, yoffset, width, height, format, type, pixels);
+ }
+}
+
+void Uniform1f(PP_Resource context_id, GLint location, GLfloat x) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniform1f(location, x);
+ }
+}
+
+void Uniform1fv(PP_Resource context_id,
+ GLint location,
+ GLsizei count,
+ const GLfloat* v) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniform1fv(location, count, v);
+ }
+}
+
+void Uniform1i(PP_Resource context_id, GLint location, GLint x) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniform1i(location, x);
+ }
+}
+
+void Uniform1iv(PP_Resource context_id,
+ GLint location,
+ GLsizei count,
+ const GLint* v) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniform1iv(location, count, v);
+ }
+}
+
+void Uniform2f(PP_Resource context_id, GLint location, GLfloat x, GLfloat y) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniform2f(location, x, y);
+ }
+}
+
+void Uniform2fv(PP_Resource context_id,
+ GLint location,
+ GLsizei count,
+ const GLfloat* v) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniform2fv(location, count, v);
+ }
+}
+
+void Uniform2i(PP_Resource context_id, GLint location, GLint x, GLint y) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniform2i(location, x, y);
+ }
+}
+
+void Uniform2iv(PP_Resource context_id,
+ GLint location,
+ GLsizei count,
+ const GLint* v) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniform2iv(location, count, v);
+ }
+}
+
+void Uniform3f(PP_Resource context_id,
+ GLint location,
+ GLfloat x,
+ GLfloat y,
+ GLfloat z) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniform3f(location, x, y, z);
+ }
+}
+
+void Uniform3fv(PP_Resource context_id,
+ GLint location,
+ GLsizei count,
+ const GLfloat* v) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniform3fv(location, count, v);
+ }
+}
+
+void Uniform3i(PP_Resource context_id,
+ GLint location,
+ GLint x,
+ GLint y,
+ GLint z) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniform3i(location, x, y, z);
+ }
+}
+
+void Uniform3iv(PP_Resource context_id,
+ GLint location,
+ GLsizei count,
+ const GLint* v) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniform3iv(location, count, v);
+ }
+}
+
+void Uniform4f(PP_Resource context_id,
+ GLint location,
+ GLfloat x,
+ GLfloat y,
+ GLfloat z,
+ GLfloat w) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniform4f(location, x, y, z, w);
+ }
+}
+
+void Uniform4fv(PP_Resource context_id,
+ GLint location,
+ GLsizei count,
+ const GLfloat* v) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniform4fv(location, count, v);
+ }
+}
+
+void Uniform4i(PP_Resource context_id,
+ GLint location,
+ GLint x,
+ GLint y,
+ GLint z,
+ GLint w) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniform4i(location, x, y, z, w);
+ }
+}
+
+void Uniform4iv(PP_Resource context_id,
+ GLint location,
+ GLsizei count,
+ const GLint* v) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniform4iv(location, count, v);
+ }
+}
+
+void UniformMatrix2fv(PP_Resource context_id,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniformMatrix2fv(location, count, transpose, value);
+ }
+}
+
+void UniformMatrix3fv(PP_Resource context_id,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniformMatrix3fv(location, count, transpose, value);
+ }
+}
+
+void UniformMatrix4fv(PP_Resource context_id,
+ GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUniformMatrix4fv(location, count, transpose, value);
+ }
+}
+
+void UseProgram(PP_Resource context_id, GLuint program) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glUseProgram(program);
+ }
+}
+
+void ValidateProgram(PP_Resource context_id, GLuint program) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glValidateProgram(program);
+ }
+}
+
+void VertexAttrib1f(PP_Resource context_id, GLuint indx, GLfloat x) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glVertexAttrib1f(indx, x);
+ }
+}
+
+void VertexAttrib1fv(PP_Resource context_id,
+ GLuint indx,
+ const GLfloat* values) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glVertexAttrib1fv(indx, values);
+ }
+}
+
+void VertexAttrib2f(PP_Resource context_id, GLuint indx, GLfloat x, GLfloat y) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glVertexAttrib2f(indx, x, y);
+ }
+}
+
+void VertexAttrib2fv(PP_Resource context_id,
+ GLuint indx,
+ const GLfloat* values) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glVertexAttrib2fv(indx, values);
+ }
+}
+
+void VertexAttrib3f(PP_Resource context_id,
+ GLuint indx,
+ GLfloat x,
+ GLfloat y,
+ GLfloat z) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glVertexAttrib3f(indx, x, y, z);
+ }
+}
+
+void VertexAttrib3fv(PP_Resource context_id,
+ GLuint indx,
+ const GLfloat* values) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glVertexAttrib3fv(indx, values);
+ }
+}
+
+void VertexAttrib4f(PP_Resource context_id,
+ GLuint indx,
+ GLfloat x,
+ GLfloat y,
+ GLfloat z,
+ GLfloat w) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glVertexAttrib4f(indx, x, y, z, w);
+ }
+}
+
+void VertexAttrib4fv(PP_Resource context_id,
+ GLuint indx,
+ const GLfloat* values) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glVertexAttrib4fv(indx, values);
+ }
+}
+
+void VertexAttribPointer(PP_Resource context_id,
+ GLuint indx,
+ GLint size,
+ GLenum type,
+ GLboolean normalized,
+ GLsizei stride,
+ const void* ptr) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glVertexAttribPointer(indx, size, type, normalized, stride, ptr);
+ }
+}
+
+void Viewport(PP_Resource context_id,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height) {
+ Enter3D enter(context_id, true);
+ if (IsBoundGraphics(&enter)) {
+ glViewport(x, y, width, height);
+ }
+}
+
+} // namespace
+
+const PPB_OpenGLES2* GetPPB_OpenGLES2_Thunk() {
+ static const struct PPB_OpenGLES2 ppb_opengles2 = {
+ &ActiveTexture, &AttachShader,
+ &BindAttribLocation, &BindBuffer,
+ &BindFramebuffer, &BindRenderbuffer,
+ &BindTexture, &BlendColor,
+ &BlendEquation, &BlendEquationSeparate,
+ &BlendFunc, &BlendFuncSeparate,
+ &BufferData, &BufferSubData,
+ &CheckFramebufferStatus, &Clear,
+ &ClearColor, &ClearDepthf,
+ &ClearStencil, &ColorMask,
+ &CompileShader, &CompressedTexImage2D,
+ &CompressedTexSubImage2D, &CopyTexImage2D,
+ &CopyTexSubImage2D, &CreateProgram,
+ &CreateShader, &CullFace,
+ &DeleteBuffers, &DeleteFramebuffers,
+ &DeleteProgram, &DeleteRenderbuffers,
+ &DeleteShader, &DeleteTextures,
+ &DepthFunc, &DepthMask,
+ &DepthRangef, &DetachShader,
+ &Disable, &DisableVertexAttribArray,
+ &DrawArrays, &DrawElements,
+ &Enable, &EnableVertexAttribArray,
+ &Finish, &Flush,
+ &FramebufferRenderbuffer, &FramebufferTexture2D,
+ &FrontFace, &GenBuffers,
+ &GenerateMipmap, &GenFramebuffers,
+ &GenRenderbuffers, &GenTextures,
+ &GetActiveAttrib, &GetActiveUniform,
+ &GetAttachedShaders, &GetAttribLocation,
+ &GetBooleanv, &GetBufferParameteriv,
+ &GetError, &GetFloatv,
+ &GetFramebufferAttachmentParameteriv, &GetIntegerv,
+ &GetProgramiv, &GetProgramInfoLog,
+ &GetRenderbufferParameteriv, &GetShaderiv,
+ &GetShaderInfoLog, &GetShaderPrecisionFormat,
+ &GetShaderSource, &GetString,
+ &GetTexParameterfv, &GetTexParameteriv,
+ &GetUniformfv, &GetUniformiv,
+ &GetUniformLocation, &GetVertexAttribfv,
+ &GetVertexAttribiv, &GetVertexAttribPointerv,
+ &Hint, &IsBuffer,
+ &IsEnabled, &IsFramebuffer,
+ &IsProgram, &IsRenderbuffer,
+ &IsShader, &IsTexture,
+ &LineWidth, &LinkProgram,
+ &PixelStorei, &PolygonOffset,
+ &ReadPixels, &ReleaseShaderCompiler,
+ &RenderbufferStorage, &SampleCoverage,
+ &Scissor, &ShaderBinary,
+ &ShaderSource, &StencilFunc,
+ &StencilFuncSeparate, &StencilMask,
+ &StencilMaskSeparate, &StencilOp,
+ &StencilOpSeparate, &TexImage2D,
+ &TexParameterf, &TexParameterfv,
+ &TexParameteri, &TexParameteriv,
+ &TexSubImage2D, &Uniform1f,
+ &Uniform1fv, &Uniform1i,
+ &Uniform1iv, &Uniform2f,
+ &Uniform2fv, &Uniform2i,
+ &Uniform2iv, &Uniform3f,
+ &Uniform3fv, &Uniform3i,
+ &Uniform3iv, &Uniform4f,
+ &Uniform4fv, &Uniform4i,
+ &Uniform4iv, &UniformMatrix2fv,
+ &UniformMatrix3fv, &UniformMatrix4fv,
+ &UseProgram, &ValidateProgram,
+ &VertexAttrib1f, &VertexAttrib1fv,
+ &VertexAttrib2f, &VertexAttrib2fv,
+ &VertexAttrib3f, &VertexAttrib3fv,
+ &VertexAttrib4f, &VertexAttrib4fv,
+ &VertexAttribPointer, &Viewport};
+ return &ppb_opengles2;
+}
+
+} // namespace examples
+} // namespace mojo
diff --git a/chromium/mojo/examples/pepper_container_app/resource_creation_impl.cc b/chromium/mojo/examples/pepper_container_app/resource_creation_impl.cc
new file mode 100644
index 00000000000..832602d9687
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/resource_creation_impl.cc
@@ -0,0 +1,409 @@
+// Copyright 2014 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 "mojo/examples/pepper_container_app/resource_creation_impl.h"
+
+#include "base/logging.h"
+#include "mojo/examples/pepper_container_app/graphics_3d_resource.h"
+
+namespace mojo {
+namespace examples {
+
+ResourceCreationImpl::ResourceCreationImpl() {}
+
+ResourceCreationImpl::~ResourceCreationImpl() {}
+
+PP_Resource ResourceCreationImpl::CreateFileIO(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateFileRef(
+ PP_Instance instance,
+ const ppapi::FileRefCreateInfo& create_info) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateFileSystem(
+ PP_Instance instance,
+ PP_FileSystemType type) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateIMEInputEvent(
+ PP_Instance instance,
+ PP_InputEvent_Type type,
+ PP_TimeTicks time_stamp,
+ struct PP_Var text,
+ uint32_t segment_number,
+ const uint32_t* segment_offsets,
+ int32_t target_segment,
+ uint32_t selection_start,
+ uint32_t selection_end) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateKeyboardInputEvent_1_0(
+ PP_Instance instance,
+ PP_InputEvent_Type type,
+ PP_TimeTicks time_stamp,
+ uint32_t modifiers,
+ uint32_t key_code,
+ struct PP_Var character_text) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateKeyboardInputEvent_1_2(
+ PP_Instance instance,
+ PP_InputEvent_Type type,
+ PP_TimeTicks time_stamp,
+ uint32_t modifiers,
+ uint32_t key_code,
+ struct PP_Var character_text,
+ struct PP_Var code) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateMouseInputEvent(
+ PP_Instance instance,
+ PP_InputEvent_Type type,
+ PP_TimeTicks time_stamp,
+ uint32_t modifiers,
+ PP_InputEvent_MouseButton mouse_button,
+ const PP_Point* mouse_position,
+ int32_t click_count,
+ const PP_Point* mouse_movement) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateTouchInputEvent(
+ PP_Instance instance,
+ PP_InputEvent_Type type,
+ PP_TimeTicks time_stamp,
+ uint32_t modifiers) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateTrueTypeFont(
+ PP_Instance instance,
+ const PP_TrueTypeFontDesc_Dev* desc) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateURLLoader(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateURLRequestInfo(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateWheelInputEvent(
+ PP_Instance instance,
+ PP_TimeTicks time_stamp,
+ uint32_t modifiers,
+ const PP_FloatPoint* wheel_delta,
+ const PP_FloatPoint* wheel_ticks,
+ PP_Bool scroll_by_page) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateAudio1_0(
+ PP_Instance instance,
+ PP_Resource config_id,
+ PPB_Audio_Callback_1_0 audio_callback,
+ void* user_data) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateAudio(
+ PP_Instance instance,
+ PP_Resource config_id,
+ PPB_Audio_Callback audio_callback,
+ void* user_data) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateAudioTrusted(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateAudioConfig(
+ PP_Instance instance,
+ PP_AudioSampleRate sample_rate,
+ uint32_t sample_frame_count) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateCompositor(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateFileChooser(
+ PP_Instance instance,
+ PP_FileChooserMode_Dev mode,
+ const PP_Var& accept_types) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateGraphics2D(PP_Instance instance,
+ const PP_Size* size,
+ PP_Bool is_always_opaque) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateGraphics3D(
+ PP_Instance instance,
+ PP_Resource share_context,
+ const int32_t* attrib_list) {
+ return (new Graphics3DResource(instance))->GetReference();
+}
+
+PP_Resource ResourceCreationImpl::CreateGraphics3DRaw(
+ PP_Instance instance,
+ PP_Resource share_context,
+ const int32_t* attrib_list) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateHostResolver(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateHostResolverPrivate(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateImageData(
+ PP_Instance instance,
+ PP_ImageDataFormat format,
+ const PP_Size* size,
+ PP_Bool init_to_zero) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateImageDataSimple(
+ PP_Instance instance,
+ PP_ImageDataFormat format,
+ const PP_Size* size,
+ PP_Bool init_to_zero) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateMediaStreamVideoTrack(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateNetAddressFromIPv4Address(
+ PP_Instance instance,
+ const PP_NetAddress_IPv4* ipv4_addr) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateNetAddressFromIPv6Address(
+ PP_Instance instance,
+ const PP_NetAddress_IPv6* ipv6_addr) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateNetAddressFromNetAddressPrivate(
+ PP_Instance instance,
+ const PP_NetAddress_Private& private_addr) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateNetworkMonitor(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateOutputProtectionPrivate(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreatePrinting(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateTCPServerSocketPrivate(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateTCPSocket1_0(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateTCPSocket(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateTCPSocketPrivate(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateUDPSocket(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateUDPSocketPrivate(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateVideoDecoder(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateVideoDestination(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateVideoSource(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateWebSocket(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateX509CertificatePrivate(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+#if !defined(OS_NACL)
+PP_Resource ResourceCreationImpl::CreateAudioInput(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateBroker(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateBrowserFont(
+ PP_Instance instance,
+ const PP_BrowserFont_Trusted_Description* description) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateBuffer(PP_Instance instance,
+ uint32_t size) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateFlashDRM(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateFlashFontFile(
+ PP_Instance instance,
+ const PP_BrowserFont_Trusted_Description* description,
+ PP_PrivateFontCharset charset) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateFlashMenu(
+ PP_Instance instance,
+ const PP_Flash_Menu* menu_data) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateFlashMessageLoop(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreatePlatformVerificationPrivate(
+ PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateScrollbar(PP_Instance instance,
+ PP_Bool vertical) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateTalk(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateVideoCapture(PP_Instance instance) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+PP_Resource ResourceCreationImpl::CreateVideoDecoderDev(
+ PP_Instance instance,
+ PP_Resource context3d_id,
+ PP_VideoDecoder_Profile profile) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+#endif // !defined(OS_NACL)
+
+} // namespace examples
+} // namespace mojo
diff --git a/chromium/mojo/examples/pepper_container_app/resource_creation_impl.h b/chromium/mojo/examples/pepper_container_app/resource_creation_impl.h
new file mode 100644
index 00000000000..14f4ca3f03c
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/resource_creation_impl.h
@@ -0,0 +1,178 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EXAMPLES_PEPPER_CONTAINER_APP_RESOURCE_CREATION_IMPL_H_
+#define MOJO_EXAMPLES_PEPPER_CONTAINER_APP_RESOURCE_CREATION_IMPL_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "ppapi/thunk/resource_creation_api.h"
+
+namespace mojo {
+namespace examples {
+
+class ResourceCreationImpl : public ppapi::thunk::ResourceCreationAPI {
+ public:
+ ResourceCreationImpl();
+ virtual ~ResourceCreationImpl();
+
+ // ppapi::thunk::ResourceCreationAPI implementation.
+ virtual PP_Resource CreateFileIO(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateFileRef(
+ PP_Instance instance,
+ const ppapi::FileRefCreateInfo& create_info) OVERRIDE;
+ virtual PP_Resource CreateFileSystem(PP_Instance instance,
+ PP_FileSystemType type) OVERRIDE;
+ virtual PP_Resource CreateIMEInputEvent(PP_Instance instance,
+ PP_InputEvent_Type type,
+ PP_TimeTicks time_stamp,
+ struct PP_Var text,
+ uint32_t segment_number,
+ const uint32_t* segment_offsets,
+ int32_t target_segment,
+ uint32_t selection_start,
+ uint32_t selection_end) OVERRIDE;
+ virtual PP_Resource CreateKeyboardInputEvent_1_0(
+ PP_Instance instance,
+ PP_InputEvent_Type type,
+ PP_TimeTicks time_stamp,
+ uint32_t modifiers,
+ uint32_t key_code,
+ PP_Var character_text) OVERRIDE;
+ virtual PP_Resource CreateKeyboardInputEvent_1_2(
+ PP_Instance instance,
+ PP_InputEvent_Type type,
+ PP_TimeTicks time_stamp,
+ uint32_t modifiers,
+ uint32_t key_code,
+ PP_Var character_text,
+ PP_Var code) OVERRIDE;
+ virtual PP_Resource CreateMouseInputEvent(
+ PP_Instance instance,
+ PP_InputEvent_Type type,
+ PP_TimeTicks time_stamp,
+ uint32_t modifiers,
+ PP_InputEvent_MouseButton mouse_button,
+ const PP_Point* mouse_position,
+ int32_t click_count,
+ const PP_Point* mouse_movement) OVERRIDE;
+ virtual PP_Resource CreateTouchInputEvent(
+ PP_Instance instance,
+ PP_InputEvent_Type type,
+ PP_TimeTicks time_stamp,
+ uint32_t modifiers) OVERRIDE;
+ virtual PP_Resource CreateTrueTypeFont(
+ PP_Instance instance,
+ const PP_TrueTypeFontDesc_Dev* desc) OVERRIDE;
+ virtual PP_Resource CreateURLLoader(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateURLRequestInfo(
+ PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateWheelInputEvent(
+ PP_Instance instance,
+ PP_TimeTicks time_stamp,
+ uint32_t modifiers,
+ const PP_FloatPoint* wheel_delta,
+ const PP_FloatPoint* wheel_ticks,
+ PP_Bool scroll_by_page) OVERRIDE;
+ virtual PP_Resource CreateAudio1_0(PP_Instance instance,
+ PP_Resource config_id,
+ PPB_Audio_Callback_1_0 audio_callback,
+ void* user_data) OVERRIDE;
+ virtual PP_Resource CreateAudio(PP_Instance instance,
+ PP_Resource config_id,
+ PPB_Audio_Callback audio_callback,
+ void* user_data) OVERRIDE;
+ virtual PP_Resource CreateAudioTrusted(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateAudioConfig(PP_Instance instance,
+ PP_AudioSampleRate sample_rate,
+ uint32_t sample_frame_count) OVERRIDE;
+ virtual PP_Resource CreateCompositor(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateFileChooser(PP_Instance instance,
+ PP_FileChooserMode_Dev mode,
+ const PP_Var& accept_types) OVERRIDE;
+ virtual PP_Resource CreateGraphics2D(PP_Instance pp_instance,
+ const PP_Size* size,
+ PP_Bool is_always_opaque) OVERRIDE;
+ virtual PP_Resource CreateGraphics3D(PP_Instance instance,
+ PP_Resource share_context,
+ const int32_t* attrib_list) OVERRIDE;
+ virtual PP_Resource CreateGraphics3DRaw(
+ PP_Instance instance,
+ PP_Resource share_context,
+ const int32_t* attrib_list) OVERRIDE;
+ virtual PP_Resource CreateHostResolver(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateHostResolverPrivate(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateImageData(PP_Instance instance,
+ PP_ImageDataFormat format,
+ const PP_Size* size,
+ PP_Bool init_to_zero) OVERRIDE;
+ virtual PP_Resource CreateImageDataSimple(PP_Instance instance,
+ PP_ImageDataFormat format,
+ const PP_Size* size,
+ PP_Bool init_to_zero) OVERRIDE;
+ virtual PP_Resource CreateMediaStreamVideoTrack(
+ PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateNetAddressFromIPv4Address(
+ PP_Instance instance,
+ const PP_NetAddress_IPv4* ipv4_addr) OVERRIDE;
+ virtual PP_Resource CreateNetAddressFromIPv6Address(
+ PP_Instance instance,
+ const PP_NetAddress_IPv6* ipv6_addr) OVERRIDE;
+ virtual PP_Resource CreateNetAddressFromNetAddressPrivate(
+ PP_Instance instance,
+ const PP_NetAddress_Private& private_addr) OVERRIDE;
+ virtual PP_Resource CreateNetworkMonitor(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateOutputProtectionPrivate(
+ PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreatePrinting(PP_Instance) OVERRIDE;
+ virtual PP_Resource CreateTCPServerSocketPrivate(
+ PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateTCPSocket1_0(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateTCPSocket(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateTCPSocketPrivate(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateUDPSocket(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateUDPSocketPrivate(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateVideoDecoder(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateVideoDestination(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateVideoSource(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateWebSocket(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateX509CertificatePrivate(
+ PP_Instance instance) OVERRIDE;
+#if !defined(OS_NACL)
+ virtual PP_Resource CreateAudioInput(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateBroker(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateBrowserFont(
+ PP_Instance instance,
+ const PP_BrowserFont_Trusted_Description* description) OVERRIDE;
+ virtual PP_Resource CreateBuffer(PP_Instance instance,
+ uint32_t size) OVERRIDE;
+ virtual PP_Resource CreateFlashDRM(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateFlashFontFile(
+ PP_Instance instance,
+ const PP_BrowserFont_Trusted_Description* description,
+ PP_PrivateFontCharset charset) OVERRIDE;
+ virtual PP_Resource CreateFlashMenu(PP_Instance instance,
+ const PP_Flash_Menu* menu_data) OVERRIDE;
+ virtual PP_Resource CreateFlashMessageLoop(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreatePlatformVerificationPrivate(
+ PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateScrollbar(PP_Instance instance,
+ PP_Bool vertical) OVERRIDE;
+ virtual PP_Resource CreateTalk(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateVideoCapture(PP_Instance instance) OVERRIDE;
+ virtual PP_Resource CreateVideoDecoderDev(
+ PP_Instance instance,
+ PP_Resource context3d_id,
+ PP_VideoDecoder_Profile profile) OVERRIDE;
+#endif // !defined(OS_NACL)
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResourceCreationImpl);
+};
+
+} // namespace examples
+} // namespace mojo
+
+#endif // MOJO_EXAMPLES_PEPPER_CONTAINER_APP_RESOURCE_CREATION_IMPL_H_
diff --git a/chromium/mojo/examples/pepper_container_app/thunk.h b/chromium/mojo/examples/pepper_container_app/thunk.h
new file mode 100644
index 00000000000..f3b3d548257
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/thunk.h
@@ -0,0 +1,20 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EXAMPLES_PEPPER_CONTAINER_APP_THUNK_H_
+#define MOJO_EXAMPLES_PEPPER_CONTAINER_APP_THUNK_H_
+
+#include "ppapi/c/ppb_core.h"
+#include "ppapi/c/ppb_opengles2.h"
+
+namespace mojo {
+namespace examples {
+
+const PPB_Core_1_0* GetPPB_Core_1_0_Thunk();
+const PPB_OpenGLES2* GetPPB_OpenGLES2_Thunk();
+
+} // namespace examples
+} // namespace mojo
+
+#endif // MOJO_EXAMPLES_PEPPER_CONTAINER_APP_THUNK_H_
diff --git a/chromium/mojo/examples/pepper_container_app/type_converters.h b/chromium/mojo/examples/pepper_container_app/type_converters.h
new file mode 100644
index 00000000000..0f02a0a521b
--- /dev/null
+++ b/chromium/mojo/examples/pepper_container_app/type_converters.h
@@ -0,0 +1,74 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_EXAMPLES_PEPPER_CONTAINER_APP_TYPE_CONVERTERS_H_
+#define MOJO_EXAMPLES_PEPPER_CONTAINER_APP_TYPE_CONVERTERS_H_
+
+#include "mojo/public/cpp/bindings/type_converter.h"
+#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h"
+#include "ppapi/c/pp_point.h"
+#include "ppapi/c/pp_rect.h"
+#include "ppapi/c/pp_size.h"
+
+namespace mojo {
+
+template <>
+class TypeConverter<PointPtr, PP_Point> {
+ public:
+ static PointPtr ConvertFrom(const PP_Point& input) {
+ PointPtr point(Point::New());
+ point->x = input.x;
+ point->y = input.y;
+ return point.Pass();
+ }
+
+ static PP_Point ConvertTo(const PointPtr& input) {
+ if (!input)
+ return PP_MakePoint(0, 0);
+ return PP_MakePoint(static_cast<int32_t>(input->x),
+ static_cast<int32_t>(input->y));
+ }
+};
+
+template <>
+class TypeConverter<SizePtr, PP_Size> {
+ public:
+ static SizePtr ConvertFrom(const PP_Size& input) {
+ SizePtr size(Size::New());
+ size->width = input.width;
+ size->height = input.height;
+ return size.Pass();
+ }
+
+ static PP_Size ConvertTo(const SizePtr& input) {
+ if (!input)
+ return PP_MakeSize(0, 0);
+ return PP_MakeSize(static_cast<int32_t>(input->width),
+ static_cast<int32_t>(input->height));
+ }
+};
+
+template <>
+class TypeConverter<RectPtr, PP_Rect> {
+ public:
+ static RectPtr ConvertFrom(const PP_Rect& input) {
+ RectPtr rect(Rect::New());
+ rect->x = input.point.x;
+ rect->y = input.point.y;
+ rect->width = input.size.width;
+ rect->height = input.size.height;
+ return rect.Pass();
+ }
+
+ static PP_Rect ConvertTo(const RectPtr& input) {
+ if (!input)
+ return PP_MakeRectFromXYWH(0, 0, 0, 0);
+ return PP_MakeRectFromXYWH(input->x, input->y,
+ input->width, input->height);
+ }
+};
+
+} // namespace mojo
+
+#endif // MOJO_EXAMPLES_PEPPER_CONTAINER_APP_TYPE_CONVERTERS_H_
diff --git a/chromium/mojo/examples/sample_app/DEPS b/chromium/mojo/examples/sample_app/DEPS
new file mode 100644
index 00000000000..49b9c281a7d
--- /dev/null
+++ b/chromium/mojo/examples/sample_app/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+ui/events/event_constants.h", # pending enum support in mojom
+ "+ui/gfx", # TODO(beng): trim the size of this dep.
+]
diff --git a/chromium/mojo/examples/sample_app/gles2_client_impl.cc b/chromium/mojo/examples/sample_app/gles2_client_impl.cc
new file mode 100644
index 00000000000..eb571b49a77
--- /dev/null
+++ b/chromium/mojo/examples/sample_app/gles2_client_impl.cc
@@ -0,0 +1,134 @@
+// Copyright 2013 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 "mojo/examples/sample_app/gles2_client_impl.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "mojo/public/c/gles2/gles2.h"
+#include "ui/events/event_constants.h"
+
+namespace mojo {
+namespace examples {
+namespace {
+
+float CalculateDragDistance(const gfx::PointF& start, const Point& end) {
+ return hypot(start.x() - end.x, start.y() - end.y);
+}
+
+float GetRandomColor() {
+ return static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
+}
+
+}
+
+GLES2ClientImpl::GLES2ClientImpl(CommandBufferPtr command_buffer)
+ : getting_animation_frames_(false) {
+ context_ = MojoGLES2CreateContext(
+ command_buffer.PassMessagePipe().release().value(),
+ &ContextLostThunk,
+ &DrawAnimationFrameThunk,
+ this);
+ MojoGLES2MakeCurrent(context_);
+}
+
+GLES2ClientImpl::~GLES2ClientImpl() {
+ MojoGLES2DestroyContext(context_);
+}
+
+void GLES2ClientImpl::SetSize(const Size& size) {
+ size_ = gfx::Size(size.width, size.height);
+ if (size_.IsEmpty())
+ return;
+ cube_.Init(size_.width(), size_.height());
+ RequestAnimationFrames();
+}
+
+void GLES2ClientImpl::HandleInputEvent(const Event& event) {
+ switch (event.action) {
+ case ui::ET_MOUSE_PRESSED:
+ case ui::ET_TOUCH_PRESSED:
+ if (event.flags & ui::EF_RIGHT_MOUSE_BUTTON)
+ break;
+ CancelAnimationFrames();
+ capture_point_.SetPoint(event.location->x, event.location->y);
+ last_drag_point_ = capture_point_;
+ drag_start_time_ = GetTimeTicksNow();
+ break;
+ case ui::ET_MOUSE_DRAGGED:
+ case ui::ET_TOUCH_MOVED:
+ if (event.flags & ui::EF_RIGHT_MOUSE_BUTTON)
+ break;
+ if (!getting_animation_frames_) {
+ int direction = event.location->y < last_drag_point_.y() ||
+ event.location->x > last_drag_point_.x() ? 1 : -1;
+ cube_.set_direction(direction);
+ cube_.UpdateForDragDistance(
+ CalculateDragDistance(last_drag_point_, *event.location));
+ cube_.Draw();
+ MojoGLES2SwapBuffers();
+
+ last_drag_point_.SetPoint(event.location->x, event.location->y);
+ }
+ break;
+ case ui::ET_MOUSE_RELEASED:
+ case ui::ET_TOUCH_RELEASED: {
+ if (event.flags & ui::EF_RIGHT_MOUSE_BUTTON) {
+ cube_.set_color(GetRandomColor(), GetRandomColor(), GetRandomColor());
+ break;
+ }
+ MojoTimeTicks offset = GetTimeTicksNow() - drag_start_time_;
+ float delta = static_cast<float>(offset) / 1000000.;
+ cube_.SetFlingMultiplier(
+ CalculateDragDistance(capture_point_, *event.location),
+ delta);
+
+ capture_point_ = last_drag_point_ = gfx::PointF();
+ RequestAnimationFrames();
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void GLES2ClientImpl::ContextLost() {
+ CancelAnimationFrames();
+}
+
+void GLES2ClientImpl::ContextLostThunk(void* closure) {
+ static_cast<GLES2ClientImpl*>(closure)->ContextLost();
+}
+
+void GLES2ClientImpl::DrawAnimationFrame() {
+ MojoTimeTicks now = GetTimeTicksNow();
+ MojoTimeTicks offset = now - last_time_;
+ float delta = static_cast<float>(offset) / 1000000.;
+ last_time_ = now;
+ cube_.UpdateForTimeDelta(delta);
+ cube_.Draw();
+
+ MojoGLES2SwapBuffers();
+}
+
+void GLES2ClientImpl::DrawAnimationFrameThunk(void* closure) {
+ static_cast<GLES2ClientImpl*>(closure)->DrawAnimationFrame();
+}
+
+void GLES2ClientImpl::RequestAnimationFrames() {
+ getting_animation_frames_ = true;
+ MojoGLES2RequestAnimationFrames(context_);
+ last_time_ = GetTimeTicksNow();
+}
+
+void GLES2ClientImpl::CancelAnimationFrames() {
+ getting_animation_frames_ = false;
+ MojoGLES2CancelAnimationFrames(context_);
+}
+
+} // namespace examples
+} // namespace mojo
diff --git a/chromium/mojo/examples/sample_app/gles2_client_impl.h b/chromium/mojo/examples/sample_app/gles2_client_impl.h
new file mode 100644
index 00000000000..6791504fbc1
--- /dev/null
+++ b/chromium/mojo/examples/sample_app/gles2_client_impl.h
@@ -0,0 +1,50 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_EXAMPLES_SAMPLE_APP_GLES2_CLIENT_IMPL_H_
+#define MOJO_EXAMPLES_SAMPLE_APP_GLES2_CLIENT_IMPL_H_
+
+#include "mojo/examples/sample_app/spinning_cube.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h"
+#include "ui/gfx/point_f.h"
+#include "ui/gfx/size.h"
+
+namespace mojo {
+namespace examples {
+
+class GLES2ClientImpl {
+ public:
+ explicit GLES2ClientImpl(CommandBufferPtr command_buffer);
+ virtual ~GLES2ClientImpl();
+
+ void SetSize(const Size& size);
+ void HandleInputEvent(const Event& event);
+
+ private:
+ void ContextLost();
+ static void ContextLostThunk(void* closure);
+ void DrawAnimationFrame();
+ static void DrawAnimationFrameThunk(void* closure);
+
+ void RequestAnimationFrames();
+ void CancelAnimationFrames();
+
+ MojoTimeTicks last_time_;
+ gfx::Size size_;
+ SpinningCube cube_;
+ gfx::PointF capture_point_;
+ gfx::PointF last_drag_point_;
+ MojoTimeTicks drag_start_time_;
+ bool getting_animation_frames_;
+
+ MojoGLES2Context context_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(GLES2ClientImpl);
+};
+
+} // namespace examples
+} // namespace mojo
+
+#endif // MOJO_EXAMPLES_SAMPLE_APP_GLES2_CLIENT_IMPL_H_
diff --git a/chromium/mojo/examples/sample_app/sample_app.cc b/chromium/mojo/examples/sample_app/sample_app.cc
new file mode 100644
index 00000000000..b6a20b71709
--- /dev/null
+++ b/chromium/mojo/examples/sample_app/sample_app.cc
@@ -0,0 +1,84 @@
+// Copyright 2013 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 <stdio.h>
+#include <string>
+
+#include "mojo/examples/sample_app/gles2_client_impl.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/public/cpp/gles2/gles2.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h"
+
+namespace mojo {
+namespace examples {
+
+class SampleApp : public Application, public NativeViewportClient {
+ public:
+ SampleApp() {}
+
+ virtual ~SampleApp() {
+ // TODO(darin): Fix shutdown so we don't need to leak this.
+ MOJO_ALLOW_UNUSED GLES2ClientImpl* leaked = gles2_client_.release();
+ }
+
+ virtual void Initialize() MOJO_OVERRIDE {
+ ConnectTo("mojo:mojo_native_viewport_service", &viewport_);
+ viewport_.set_client(this);
+
+ RectPtr rect(Rect::New());
+ rect->x = 10;
+ rect->y = 10;
+ rect->width = 800;
+ rect->height = 600;
+ viewport_->Create(rect.Pass());
+ viewport_->Show();
+
+ CommandBufferPtr command_buffer;
+ viewport_->CreateGLES2Context(Get(&command_buffer));
+ gles2_client_.reset(new GLES2ClientImpl(command_buffer.Pass()));
+ }
+
+ virtual void OnCreated() MOJO_OVERRIDE {
+ }
+
+ virtual void OnDestroyed() MOJO_OVERRIDE {
+ RunLoop::current()->Quit();
+ }
+
+ virtual void OnBoundsChanged(RectPtr bounds) MOJO_OVERRIDE {
+ assert(bounds);
+ SizePtr size(Size::New());
+ size->width = bounds->width;
+ size->height = bounds->height;
+ gles2_client_->SetSize(*size);
+ }
+
+ virtual void OnEvent(EventPtr event,
+ const Callback<void()>& callback) MOJO_OVERRIDE {
+ assert(event);
+ if (event->location)
+ gles2_client_->HandleInputEvent(*event);
+ callback.Run();
+ }
+
+ private:
+ mojo::GLES2Initializer gles2;
+ scoped_ptr<GLES2ClientImpl> gles2_client_;
+ NativeViewportPtr viewport_;
+
+ DISALLOW_COPY_AND_ASSIGN(SampleApp);
+};
+
+} // namespace examples
+
+// static
+Application* Application::Create() {
+ return new examples::SampleApp();
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/examples/sample_app/spinning_cube.cc b/chromium/mojo/examples/sample_app/spinning_cube.cc
new file mode 100644
index 00000000000..024fdca93bf
--- /dev/null
+++ b/chromium/mojo/examples/sample_app/spinning_cube.cc
@@ -0,0 +1,472 @@
+// Copyright 2013 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.
+
+// This example program is based on Simple_VertexShader.c from:
+
+//
+// Book: OpenGL(R) ES 2.0 Programming Guide
+// Authors: Aaftab Munshi, Dan Ginsburg, Dave Shreiner
+// ISBN-10: 0321502795
+// ISBN-13: 9780321502797
+// Publisher: Addison-Wesley Professional
+// URLs: http://safari.informit.com/9780321563835
+// http://www.opengles-book.com
+//
+
+#include "mojo/examples/sample_app/spinning_cube.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+namespace mojo {
+namespace examples {
+
+namespace {
+
+const float kPi = 3.14159265359f;
+
+int GenerateCube(GLuint *vbo_vertices,
+ GLuint *vbo_indices) {
+ const int num_indices = 36;
+
+ const GLfloat cube_vertices[] = {
+ -0.5f, -0.5f, -0.5f,
+ -0.5f, -0.5f, 0.5f,
+ 0.5f, -0.5f, 0.5f,
+ 0.5f, -0.5f, -0.5f,
+ -0.5f, 0.5f, -0.5f,
+ -0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, -0.5f,
+ -0.5f, -0.5f, -0.5f,
+ -0.5f, 0.5f, -0.5f,
+ 0.5f, 0.5f, -0.5f,
+ 0.5f, -0.5f, -0.5f,
+ -0.5f, -0.5f, 0.5f,
+ -0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, 0.5f,
+ 0.5f, -0.5f, 0.5f,
+ -0.5f, -0.5f, -0.5f,
+ -0.5f, -0.5f, 0.5f,
+ -0.5f, 0.5f, 0.5f,
+ -0.5f, 0.5f, -0.5f,
+ 0.5f, -0.5f, -0.5f,
+ 0.5f, -0.5f, 0.5f,
+ 0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, -0.5f,
+ };
+
+ const GLushort cube_indices[] = {
+ 0, 2, 1,
+ 0, 3, 2,
+ 4, 5, 6,
+ 4, 6, 7,
+ 8, 9, 10,
+ 8, 10, 11,
+ 12, 15, 14,
+ 12, 14, 13,
+ 16, 17, 18,
+ 16, 18, 19,
+ 20, 23, 22,
+ 20, 22, 21
+ };
+
+ if (vbo_vertices) {
+ glGenBuffers(1, vbo_vertices);
+ glBindBuffer(GL_ARRAY_BUFFER, *vbo_vertices);
+ glBufferData(GL_ARRAY_BUFFER,
+ sizeof(cube_vertices),
+ cube_vertices,
+ GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ }
+
+ if (vbo_indices) {
+ glGenBuffers(1, vbo_indices);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *vbo_indices);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER,
+ sizeof(cube_indices),
+ cube_indices,
+ GL_STATIC_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+
+ return num_indices;
+}
+
+GLuint LoadShader(GLenum type,
+ const char* shader_source) {
+ GLuint shader = glCreateShader(type);
+ glShaderSource(shader, 1, &shader_source, NULL);
+ glCompileShader(shader);
+
+ GLint compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+
+ if (!compiled) {
+ glDeleteShader(shader);
+ return 0;
+ }
+
+ return shader;
+}
+
+GLuint LoadProgram(const char* vertext_shader_source,
+ const char* fragment_shader_source) {
+ GLuint vertex_shader = LoadShader(GL_VERTEX_SHADER,
+ vertext_shader_source);
+ if (!vertex_shader)
+ return 0;
+
+ GLuint fragment_shader = LoadShader(GL_FRAGMENT_SHADER,
+ fragment_shader_source);
+ if (!fragment_shader) {
+ glDeleteShader(vertex_shader);
+ return 0;
+ }
+
+ GLuint program_object = glCreateProgram();
+ glAttachShader(program_object, vertex_shader);
+ glAttachShader(program_object, fragment_shader);
+
+ glLinkProgram(program_object);
+
+ glDeleteShader(vertex_shader);
+ glDeleteShader(fragment_shader);
+
+ GLint linked = 0;
+ glGetProgramiv(program_object, GL_LINK_STATUS, &linked);
+
+ if (!linked) {
+ glDeleteProgram(program_object);
+ return 0;
+ }
+
+ return program_object;
+}
+
+class ESMatrix {
+ public:
+ GLfloat m[4][4];
+
+ ESMatrix() {
+ LoadZero();
+ }
+
+ void LoadZero() {
+ memset(this, 0x0, sizeof(ESMatrix));
+ }
+
+ void LoadIdentity() {
+ LoadZero();
+ m[0][0] = 1.0f;
+ m[1][1] = 1.0f;
+ m[2][2] = 1.0f;
+ m[3][3] = 1.0f;
+ }
+
+ void Multiply(ESMatrix* a, ESMatrix* b) {
+ ESMatrix result;
+ for (int i = 0; i < 4; ++i) {
+ result.m[i][0] = (a->m[i][0] * b->m[0][0]) +
+ (a->m[i][1] * b->m[1][0]) +
+ (a->m[i][2] * b->m[2][0]) +
+ (a->m[i][3] * b->m[3][0]);
+
+ result.m[i][1] = (a->m[i][0] * b->m[0][1]) +
+ (a->m[i][1] * b->m[1][1]) +
+ (a->m[i][2] * b->m[2][1]) +
+ (a->m[i][3] * b->m[3][1]);
+
+ result.m[i][2] = (a->m[i][0] * b->m[0][2]) +
+ (a->m[i][1] * b->m[1][2]) +
+ (a->m[i][2] * b->m[2][2]) +
+ (a->m[i][3] * b->m[3][2]);
+
+ result.m[i][3] = (a->m[i][0] * b->m[0][3]) +
+ (a->m[i][1] * b->m[1][3]) +
+ (a->m[i][2] * b->m[2][3]) +
+ (a->m[i][3] * b->m[3][3]);
+ }
+ *this = result;
+ }
+
+ void Frustum(float left,
+ float right,
+ float bottom,
+ float top,
+ float near_z,
+ float far_z) {
+ float delta_x = right - left;
+ float delta_y = top - bottom;
+ float delta_z = far_z - near_z;
+
+ if ((near_z <= 0.0f) ||
+ (far_z <= 0.0f) ||
+ (delta_z <= 0.0f) ||
+ (delta_y <= 0.0f) ||
+ (delta_y <= 0.0f))
+ return;
+
+ ESMatrix frust;
+ frust.m[0][0] = 2.0f * near_z / delta_x;
+ frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f;
+
+ frust.m[1][1] = 2.0f * near_z / delta_y;
+ frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f;
+
+ frust.m[2][0] = (right + left) / delta_x;
+ frust.m[2][1] = (top + bottom) / delta_y;
+ frust.m[2][2] = -(near_z + far_z) / delta_z;
+ frust.m[2][3] = -1.0f;
+
+ frust.m[3][2] = -2.0f * near_z * far_z / delta_z;
+ frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0f;
+
+ Multiply(&frust, this);
+ }
+
+ void Perspective(float fov_y, float aspect, float near_z, float far_z) {
+ GLfloat frustum_h = tanf(fov_y / 360.0f * kPi) * near_z;
+ GLfloat frustum_w = frustum_h * aspect;
+ Frustum(-frustum_w, frustum_w, -frustum_h, frustum_h, near_z, far_z);
+ }
+
+ void Translate(GLfloat tx, GLfloat ty, GLfloat tz) {
+ m[3][0] += m[0][0] * tx + m[1][0] * ty + m[2][0] * tz;
+ m[3][1] += m[0][1] * tx + m[1][1] * ty + m[2][1] * tz;
+ m[3][2] += m[0][2] * tx + m[1][2] * ty + m[2][2] * tz;
+ m[3][3] += m[0][3] * tx + m[1][3] * ty + m[2][3] * tz;
+ }
+
+ void Rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) {
+ GLfloat mag = sqrtf(x * x + y * y + z * z);
+
+ GLfloat sin_angle = sinf(angle * kPi / 180.0f);
+ GLfloat cos_angle = cosf(angle * kPi / 180.0f);
+ if (mag > 0.0f) {
+ GLfloat xx, yy, zz, xy, yz, zx, xs, ys, zs;
+ GLfloat one_minus_cos;
+ ESMatrix rotation;
+
+ x /= mag;
+ y /= mag;
+ z /= mag;
+
+ xx = x * x;
+ yy = y * y;
+ zz = z * z;
+ xy = x * y;
+ yz = y * z;
+ zx = z * x;
+ xs = x * sin_angle;
+ ys = y * sin_angle;
+ zs = z * sin_angle;
+ one_minus_cos = 1.0f - cos_angle;
+
+ rotation.m[0][0] = (one_minus_cos * xx) + cos_angle;
+ rotation.m[0][1] = (one_minus_cos * xy) - zs;
+ rotation.m[0][2] = (one_minus_cos * zx) + ys;
+ rotation.m[0][3] = 0.0F;
+
+ rotation.m[1][0] = (one_minus_cos * xy) + zs;
+ rotation.m[1][1] = (one_minus_cos * yy) + cos_angle;
+ rotation.m[1][2] = (one_minus_cos * yz) - xs;
+ rotation.m[1][3] = 0.0F;
+
+ rotation.m[2][0] = (one_minus_cos * zx) - ys;
+ rotation.m[2][1] = (one_minus_cos * yz) + xs;
+ rotation.m[2][2] = (one_minus_cos * zz) + cos_angle;
+ rotation.m[2][3] = 0.0F;
+
+ rotation.m[3][0] = 0.0F;
+ rotation.m[3][1] = 0.0F;
+ rotation.m[3][2] = 0.0F;
+ rotation.m[3][3] = 1.0F;
+
+ Multiply(&rotation, this);
+ }
+ }
+};
+
+float RotationForTimeDelta(float delta_time) {
+ return delta_time * 40.0f;
+}
+
+float RotationForDragDistance(float drag_distance) {
+ return drag_distance / 5; // Arbitrary damping.
+}
+
+} // namespace
+
+class SpinningCube::GLState {
+ public:
+ GLState();
+
+ void OnGLContextLost();
+
+ GLfloat angle_; // Survives losing the GL context.
+
+ GLuint program_object_;
+ GLint position_location_;
+ GLint mvp_location_;
+ GLint color_location_;
+ GLuint vbo_vertices_;
+ GLuint vbo_indices_;
+ int num_indices_;
+ ESMatrix mvp_matrix_;
+};
+
+SpinningCube::GLState::GLState()
+ : angle_(0) {
+ OnGLContextLost();
+}
+
+void SpinningCube::GLState::OnGLContextLost() {
+ program_object_ = 0;
+ position_location_ = 0;
+ mvp_location_ = 0;
+ color_location_ = 0;
+ vbo_vertices_ = 0;
+ vbo_indices_ = 0;
+ num_indices_ = 0;
+}
+
+SpinningCube::SpinningCube()
+ : initialized_(false),
+ width_(0),
+ height_(0),
+ state_(new GLState()),
+ fling_multiplier_(1.0f),
+ direction_(1),
+ color_() {
+ state_->angle_ = 45.0f;
+ set_color(0.0, 1.0, 0.0);
+}
+
+SpinningCube::~SpinningCube() {
+ if (!initialized_)
+ return;
+ if (state_->vbo_vertices_)
+ glDeleteBuffers(1, &state_->vbo_vertices_);
+ if (state_->vbo_indices_)
+ glDeleteBuffers(1, &state_->vbo_indices_);
+ if (state_->program_object_)
+ glDeleteProgram(state_->program_object_);
+}
+
+void SpinningCube::Init(uint32_t width, uint32_t height) {
+ width_ = width;
+ height_ = height;
+
+ const char vertext_shader_source[] =
+ "uniform mat4 u_mvpMatrix; \n"
+ "attribute vec4 a_position; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_Position = u_mvpMatrix * a_position; \n"
+ "} \n";
+
+ const char fragment_shader_source[] =
+ "precision mediump float; \n"
+ "uniform vec4 u_color; \n"
+ "void main() \n"
+ "{ \n"
+ " gl_FragColor = u_color; \n"
+ "} \n";
+
+ state_->program_object_ = LoadProgram(
+ vertext_shader_source, fragment_shader_source);
+ state_->position_location_ = glGetAttribLocation(
+ state_->program_object_, "a_position");
+ state_->mvp_location_ = glGetUniformLocation(
+ state_->program_object_, "u_mvpMatrix");
+ state_->color_location_ = glGetUniformLocation(
+ state_->program_object_, "u_color");
+ state_->num_indices_ = GenerateCube(
+ &state_->vbo_vertices_, &state_->vbo_indices_);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ initialized_ = true;
+}
+
+void SpinningCube::OnGLContextLost() {
+ initialized_ = false;
+ height_ = 0;
+ width_ = 0;
+ state_->OnGLContextLost();
+}
+
+void SpinningCube::SetFlingMultiplier(float drag_distance,
+ float drag_time) {
+ fling_multiplier_ = RotationForDragDistance(drag_distance) /
+ RotationForTimeDelta(drag_time);
+
+}
+
+void SpinningCube::UpdateForTimeDelta(float delta_time) {
+ state_->angle_ += RotationForTimeDelta(delta_time) * fling_multiplier_;
+ if (state_->angle_ >= 360.0f)
+ state_->angle_ -= 360.0f;
+
+ // Arbitrary 50-step linear reduction in spin speed.
+ if (fling_multiplier_ > 1.0f) {
+ fling_multiplier_ =
+ std::max(1.0f, fling_multiplier_ - (fling_multiplier_ - 1.0f) / 50);
+ }
+
+ Update();
+}
+
+void SpinningCube::UpdateForDragDistance(float distance) {
+ state_->angle_ += RotationForDragDistance(distance);
+ if (state_->angle_ >= 360.0f )
+ state_->angle_ -= 360.0f;
+
+ Update();
+}
+
+void SpinningCube::Draw() {
+ glViewport(0, 0, width_, height_);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glUseProgram(state_->program_object_);
+ glBindBuffer(GL_ARRAY_BUFFER, state_->vbo_vertices_);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, state_->vbo_indices_);
+ glVertexAttribPointer(state_->position_location_,
+ 3,
+ GL_FLOAT,
+ GL_FALSE, 3 * sizeof(GLfloat),
+ 0);
+ glEnableVertexAttribArray(state_->position_location_);
+ glUniformMatrix4fv(state_->mvp_location_,
+ 1,
+ GL_FALSE,
+ (GLfloat*) &state_->mvp_matrix_.m[0][0]);
+ glUniform4f(state_->color_location_, color_[0], color_[1], color_[2], 1.0);
+ glDrawElements(GL_TRIANGLES,
+ state_->num_indices_,
+ GL_UNSIGNED_SHORT,
+ 0);
+}
+
+void SpinningCube::Update() {
+ float aspect = static_cast<GLfloat>(width_) / static_cast<GLfloat>(height_);
+
+ ESMatrix perspective;
+ perspective.LoadIdentity();
+ perspective.Perspective(60.0f, aspect, 1.0f, 20.0f );
+
+ ESMatrix modelview;
+ modelview.LoadIdentity();
+ modelview.Translate(0.0, 0.0, -2.0);
+ modelview.Rotate(state_->angle_ * direction_, 1.0, 0.0, 1.0);
+
+ state_->mvp_matrix_.Multiply(&modelview, &perspective);
+}
+
+} // namespace examples
+} // namespace mojo
diff --git a/chromium/mojo/examples/sample_app/spinning_cube.h b/chromium/mojo/examples/sample_app/spinning_cube.h
new file mode 100644
index 00000000000..9f7cdc427a6
--- /dev/null
+++ b/chromium/mojo/examples/sample_app/spinning_cube.h
@@ -0,0 +1,51 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_EXAMPLES_SAMPLE_APP_SPINNING_CUBE_H_
+#define MOJO_EXAMPLES_SAMPLE_APP_SPINNING_CUBE_H_
+
+#include <stdint.h>
+
+#include "base/memory/scoped_ptr.h"
+
+namespace mojo {
+namespace examples {
+
+class SpinningCube {
+ public:
+ SpinningCube();
+ ~SpinningCube();
+
+ void Init(uint32_t width, uint32_t height);
+ void set_direction(int direction) { direction_ = direction; }
+ void set_color(float r, float g, float b) {
+ color_[0] = r;
+ color_[1] = g;
+ color_[2] = b;
+ }
+ void SetFlingMultiplier(float drag_distance, float drag_time);
+ void UpdateForTimeDelta(float delta_time);
+ void UpdateForDragDistance(float distance);
+ void Draw();
+
+ void OnGLContextLost();
+
+ private:
+ class GLState;
+
+ void Update();
+
+ bool initialized_;
+ uint32_t width_;
+ uint32_t height_;
+ scoped_ptr<GLState> state_;
+ float fling_multiplier_;
+ int direction_;
+ float color_[3];
+};
+
+} // namespace examples
+} // namespace mojo
+
+#endif // MOJO_EXAMPLES_SAMPLE_APP_SPINNING_CUBE_H_
diff --git a/chromium/mojo/examples/wget/wget.cc b/chromium/mojo/examples/wget/wget.cc
new file mode 100644
index 00000000000..8a0ad5ecd9e
--- /dev/null
+++ b/chromium/mojo/examples/wget/wget.cc
@@ -0,0 +1,116 @@
+// Copyright 2014 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 <stdio.h>
+
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+
+namespace mojo {
+namespace examples {
+
+class WGetApp : public Application, public URLLoaderClient {
+ public:
+ virtual void Initialize() MOJO_OVERRIDE {
+ ConnectTo("mojo:mojo_network_service", &network_service_);
+ Start();
+ }
+
+ private:
+ virtual void OnReceivedRedirect(URLResponsePtr response,
+ const String& new_url,
+ const String& new_method) MOJO_OVERRIDE {
+ PrintResponse(response);
+ }
+
+ virtual void OnReceivedResponse(URLResponsePtr response) MOJO_OVERRIDE {
+ PrintResponse(response);
+ PrintResponseBody();
+ Start();
+ }
+
+ virtual void OnReceivedError(NetworkErrorPtr error) MOJO_OVERRIDE {
+ printf("Got error: %d (%s)\n",
+ error->code, error->description.get().c_str());
+ }
+
+ virtual void OnReceivedEndOfResponseBody() MOJO_OVERRIDE {
+ // Ignored.
+ }
+
+ void Start() {
+ std::string url = PromptForURL();
+ printf("Loading: %s\n", url.c_str());
+
+ network_service_->CreateURLLoader(Get(&url_loader_));
+ url_loader_.set_client(this);
+
+ URLRequestPtr request(URLRequest::New());
+ request->url = url;
+ request->method = "GET";
+ request->auto_follow_redirects = true;
+
+ DataPipe data_pipe;
+ response_body_stream_ = data_pipe.consumer_handle.Pass();
+
+ url_loader_->Start(request.Pass(), data_pipe.producer_handle.Pass());
+ }
+
+ std::string PromptForURL() {
+ printf("Enter URL> ");
+ char buf[1024];
+ if (scanf("%1023s", buf) != 1)
+ buf[0] = '\0';
+ return buf;
+ }
+
+ void PrintResponse(const URLResponsePtr& response) {
+ printf(">>> Headers <<< \n");
+ printf(" %s\n", response->status_line.get().c_str());
+ if (response->headers) {
+ for (size_t i = 0; i < response->headers.size(); ++i)
+ printf(" %s\n", response->headers[i].get().c_str());
+ }
+ }
+
+ void PrintResponseBody() {
+ // Read response body in blocking fashion.
+ printf(">>> Body <<<\n");
+
+ for (;;) {
+ char buf[512];
+ uint32_t num_bytes = sizeof(buf);
+ MojoResult result = ReadDataRaw(
+ response_body_stream_.get(),
+ buf,
+ &num_bytes,
+ MOJO_READ_DATA_FLAG_NONE);
+ if (result == MOJO_RESULT_SHOULD_WAIT) {
+ Wait(response_body_stream_.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE);
+ } else if (result == MOJO_RESULT_OK) {
+ fwrite(buf, num_bytes, 1, stdout);
+ } else {
+ break;
+ }
+ }
+
+ printf("\n>>> EOF <<<\n");
+ }
+
+ NetworkServicePtr network_service_;
+ URLLoaderPtr url_loader_;
+ ScopedDataPipeConsumerHandle response_body_stream_;
+};
+
+} // namespace examples
+
+// static
+Application* Application::Create() {
+ return new examples::WGetApp();
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/examples/window_manager/DEPS b/chromium/mojo/examples/window_manager/DEPS
new file mode 100644
index 00000000000..fe1d98e366d
--- /dev/null
+++ b/chromium/mojo/examples/window_manager/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+ui/events",
+]
diff --git a/chromium/mojo/examples/window_manager/window_manager.cc b/chromium/mojo/examples/window_manager/window_manager.cc
new file mode 100644
index 00000000000..71e910b08ff
--- /dev/null
+++ b/chromium/mojo/examples/window_manager/window_manager.cc
@@ -0,0 +1,226 @@
+// Copyright 2014 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 "base/basictypes.h"
+#include "base/bind.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/examples/window_manager/window_manager.mojom.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/services/public/cpp/view_manager/node.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "mojo/services/public/cpp/view_manager/view_manager.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "mojo/services/public/interfaces/launcher/launcher.mojom.h"
+#include "mojo/services/public/interfaces/navigation/navigation.mojom.h"
+#include "ui/events/event_constants.h"
+
+#if defined CreateWindow
+#undef CreateWindow
+#endif
+
+using mojo::view_manager::Id;
+using mojo::view_manager::Node;
+using mojo::view_manager::NodeObserver;
+using mojo::view_manager::View;
+using mojo::view_manager::ViewManager;
+using mojo::view_manager::ViewManagerDelegate;
+using mojo::view_manager::ViewObserver;
+
+namespace mojo {
+namespace examples {
+
+class WindowManager;
+
+namespace {
+
+const SkColor kColors[] = { SK_ColorYELLOW,
+ SK_ColorRED,
+ SK_ColorGREEN,
+ SK_ColorMAGENTA };
+
+} // namespace
+
+class WindowManagerConnection : public InterfaceImpl<IWindowManager> {
+ public:
+ explicit WindowManagerConnection(WindowManager* window_manager)
+ : window_manager_(window_manager) {}
+ virtual ~WindowManagerConnection() {}
+
+ private:
+ // Overridden from IWindowManager:
+ virtual void CloseWindow(Id node_id) OVERRIDE;
+
+ WindowManager* window_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerConnection);
+};
+
+class NavigatorHost : public InterfaceImpl<navigation::NavigatorHost> {
+ public:
+ explicit NavigatorHost(WindowManager* window_manager)
+ : window_manager_(window_manager) {
+ }
+ virtual ~NavigatorHost() {
+ }
+ private:
+ virtual void RequestNavigate(
+ uint32 source_node_id,
+ navigation::NavigationDetailsPtr nav_details) OVERRIDE;
+ WindowManager* window_manager_;
+ DISALLOW_COPY_AND_ASSIGN(NavigatorHost);
+};
+
+class WindowManager : public Application,
+ public ViewObserver,
+ public ViewManagerDelegate,
+ public InterfaceImpl<launcher::LauncherClient> {
+ public:
+ WindowManager() : launcher_ui_(NULL), view_manager_(NULL) {}
+ virtual ~WindowManager() {}
+
+ void CloseWindow(Id node_id) {
+ Node* node = view_manager_->GetNodeById(node_id);
+ DCHECK(node);
+ std::vector<Node*>::iterator iter =
+ std::find(windows_.begin(), windows_.end(), node);
+ DCHECK(iter != windows_.end());
+ windows_.erase(iter);
+ node->Destroy();
+ }
+
+ void RequestNavigate(
+ uint32 source_node_id,
+ navigation::NavigationDetailsPtr nav_details) {
+ if (!launcher_.get()) {
+ ConnectTo("mojo:mojo_launcher", &launcher_);
+ launcher_.set_client(this);
+ }
+ launcher_->Launch(nav_details->url);
+ }
+
+ private:
+ // Overridden from Application:
+ virtual void Initialize() MOJO_OVERRIDE {
+ AddService<WindowManagerConnection>(this);
+ AddService<NavigatorHost>(this);
+ ViewManager::Create(this, this);
+ }
+
+ // Overridden from ViewObserver:
+ virtual void OnViewInputEvent(View* view, const EventPtr& event) OVERRIDE {
+ if (event->action == ui::ET_MOUSE_RELEASED) {
+ std::string app_url;
+ if (event->flags & ui::EF_LEFT_MOUSE_BUTTON)
+ app_url = "mojo://mojo_embedded_app";
+ else if (event->flags & ui::EF_RIGHT_MOUSE_BUTTON)
+ app_url = "mojo://mojo_nesting_app";
+ if (app_url.empty())
+ return;
+
+ Node* node = view_manager_->GetNodeById(parent_node_id_);
+ navigation::NavigationDetailsPtr nav_details(
+ navigation::NavigationDetails::New());
+ size_t index = node->children().size() - 1;
+ nav_details->url = base::StringPrintf(
+ "%s/%x", app_url.c_str(), kColors[index % arraysize(kColors)]);
+ navigation::ResponseDetailsPtr response;
+ CreateWindow(app_url, nav_details.Pass(), response.Pass());
+ }
+ }
+
+ // Overridden from ViewManagerDelegate:
+ virtual void OnRootAdded(ViewManager* view_manager, Node* root) OVERRIDE {
+ DCHECK(!view_manager_);
+ view_manager_ = view_manager;
+
+ Node* node = Node::Create(view_manager);
+ view_manager->GetRoots().front()->AddChild(node);
+ node->SetBounds(gfx::Rect(800, 600));
+ parent_node_id_ = node->id();
+
+ View* view = View::Create(view_manager);
+ node->SetActiveView(view);
+ view->SetColor(SK_ColorBLUE);
+ view->AddObserver(this);
+
+ CreateLauncherUI();
+ }
+
+ // Overridden from LauncherClient:
+ virtual void OnLaunch(
+ const mojo::String& requested_url, const mojo::String& handler_url,
+ navigation::ResponseDetailsPtr response) OVERRIDE {
+ navigation::NavigationDetailsPtr nav_details(
+ navigation::NavigationDetails::New());
+ nav_details->url = requested_url;
+ CreateWindow(handler_url, nav_details.Pass(), response.Pass());
+ }
+
+ void CreateLauncherUI() {
+ navigation::NavigationDetailsPtr nav_details;
+ navigation::ResponseDetailsPtr response;
+ launcher_ui_ = CreateChild("mojo:mojo_browser", gfx::Rect(25, 25, 400, 25),
+ nav_details.Pass(), response.Pass());
+ }
+
+ void CreateWindow(const std::string& handler_url,
+ navigation::NavigationDetailsPtr nav_details,
+ navigation::ResponseDetailsPtr response) {
+ gfx::Rect bounds(25, 75, 400, 400);
+ if (!windows_.empty()) {
+ gfx::Point position = windows_.back()->bounds().origin();
+ position.Offset(35, 35);
+ bounds.set_origin(position);
+ }
+ windows_.push_back(CreateChild(handler_url, bounds, nav_details.Pass(),
+ response.Pass()));
+ }
+
+ Node* CreateChild(const std::string& url,
+ const gfx::Rect& bounds,
+ navigation::NavigationDetailsPtr nav_details,
+ navigation::ResponseDetailsPtr response) {
+ Node* node = view_manager_->GetNodeById(parent_node_id_);
+ Node* embedded = Node::Create(view_manager_);
+ node->AddChild(embedded);
+ embedded->SetBounds(bounds);
+ embedded->Embed(url);
+
+ if (nav_details.get()) {
+ navigation::NavigatorPtr navigator;
+ ConnectTo(url, &navigator);
+ navigator->Navigate(embedded->id(), nav_details.Pass(), response.Pass());
+ }
+
+ return embedded;
+ }
+
+ launcher::LauncherPtr launcher_;
+ Node* launcher_ui_;
+ std::vector<Node*> windows_;
+ ViewManager* view_manager_;
+ Id parent_node_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowManager);
+};
+
+void WindowManagerConnection::CloseWindow(Id node_id) {
+ window_manager_->CloseWindow(node_id);
+}
+
+void NavigatorHost::RequestNavigate(
+ uint32 source_node_id,
+ navigation::NavigationDetailsPtr nav_details) {
+ window_manager_->RequestNavigate(source_node_id, nav_details.Pass());
+}
+
+} // namespace examples
+
+// static
+Application* Application::Create() {
+ return new examples::WindowManager;
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/examples/window_manager/window_manager.mojom b/chromium/mojo/examples/window_manager/window_manager.mojom
new file mode 100644
index 00000000000..213c5e5ceb5
--- /dev/null
+++ b/chromium/mojo/examples/window_manager/window_manager.mojom
@@ -0,0 +1,11 @@
+// Copyright 2014 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.
+
+module mojo {
+
+interface IWindowManager {
+ CloseWindow(uint32 node_id);
+};
+
+}
diff --git a/chromium/mojo/gles2/DEPS b/chromium/mojo/gles2/DEPS
new file mode 100644
index 00000000000..a0e373e85c1
--- /dev/null
+++ b/chromium/mojo/gles2/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+gpu/command_buffer/client",
+ "+gpu/command_buffer/common",
+]
diff --git a/chromium/mojo/gles2/README.md b/chromium/mojo/gles2/README.md
new file mode 100644
index 00000000000..94b3997a5cc
--- /dev/null
+++ b/chromium/mojo/gles2/README.md
@@ -0,0 +1,5 @@
+Mojo GLES2
+==========
+
+We export this dynamically linked library via mojo/public/gles2 in order to
+hide the gpu/command_buffer/client dependency from clients of the Mojo API.
diff --git a/chromium/mojo/gles2/command_buffer_client_impl.cc b/chromium/mojo/gles2/command_buffer_client_impl.cc
new file mode 100644
index 00000000000..78ebf13d998
--- /dev/null
+++ b/chromium/mojo/gles2/command_buffer_client_impl.cc
@@ -0,0 +1,269 @@
+// Copyright 2014 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 "mojo/gles2/command_buffer_client_impl.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/process/process_handle.h"
+#include "mojo/public/cpp/bindings/sync_dispatcher.h"
+#include "mojo/services/gles2/command_buffer_type_conversions.h"
+#include "mojo/services/gles2/mojo_buffer_backing.h"
+
+namespace mojo {
+namespace gles2 {
+
+namespace {
+
+bool CreateMapAndDupSharedBuffer(size_t size,
+ void** memory,
+ mojo::ScopedSharedBufferHandle* handle,
+ mojo::ScopedSharedBufferHandle* duped) {
+ MojoResult result = mojo::CreateSharedBuffer(NULL, size, handle);
+ if (result != MOJO_RESULT_OK)
+ return false;
+ DCHECK(handle->is_valid());
+
+ result = mojo::DuplicateBuffer(handle->get(), NULL, duped);
+ if (result != MOJO_RESULT_OK)
+ return false;
+ DCHECK(duped->is_valid());
+
+ result = mojo::MapBuffer(
+ handle->get(), 0, size, memory, MOJO_MAP_BUFFER_FLAG_NONE);
+ if (result != MOJO_RESULT_OK)
+ return false;
+ DCHECK(*memory);
+
+ return true;
+}
+}
+
+CommandBufferDelegate::~CommandBufferDelegate() {}
+
+void CommandBufferDelegate::ContextLost() {}
+void CommandBufferDelegate::DrawAnimationFrame() {}
+
+CommandBufferClientImpl::CommandBufferClientImpl(
+ CommandBufferDelegate* delegate,
+ const MojoAsyncWaiter* async_waiter,
+ ScopedMessagePipeHandle command_buffer_handle)
+ : delegate_(delegate),
+ shared_state_(NULL),
+ last_put_offset_(-1),
+ next_transfer_buffer_id_(0),
+ initialize_result_(false),
+ async_waiter_(async_waiter) {
+ command_buffer_.Bind(command_buffer_handle.Pass(), async_waiter);
+ command_buffer_.set_error_handler(this);
+ command_buffer_.set_client(this);
+}
+
+CommandBufferClientImpl::~CommandBufferClientImpl() {}
+
+bool CommandBufferClientImpl::Initialize() {
+ const size_t kSharedStateSize = sizeof(gpu::CommandBufferSharedState);
+ void* memory = NULL;
+ mojo::ScopedSharedBufferHandle duped;
+ bool result = CreateMapAndDupSharedBuffer(
+ kSharedStateSize, &memory, &shared_state_handle_, &duped);
+ if (!result)
+ return false;
+
+ shared_state_ = static_cast<gpu::CommandBufferSharedState*>(memory);
+
+ shared_state()->Initialize();
+
+ // TODO(darin): We need better sugar for sync calls.
+ MessagePipe sync_pipe;
+ sync_dispatcher_.reset(new SyncDispatcher<CommandBufferSyncClient>(
+ sync_pipe.handle0.Pass(), this));
+ CommandBufferSyncClientPtr sync_client =
+ MakeProxy<CommandBufferSyncClient>(sync_pipe.handle1.Pass(),
+ async_waiter_);
+ command_buffer_->Initialize(sync_client.Pass(), duped.Pass());
+ // Wait for DidInitialize to come on the sync client pipe.
+ if (!sync_dispatcher_->WaitAndDispatchOneMessage()) {
+ VLOG(1) << "Channel encountered error while creating command buffer";
+ return false;
+ }
+ return initialize_result_;
+}
+
+gpu::CommandBuffer::State CommandBufferClientImpl::GetLastState() {
+ return last_state_;
+}
+
+int32 CommandBufferClientImpl::GetLastToken() {
+ TryUpdateState();
+ return last_state_.token;
+}
+
+void CommandBufferClientImpl::Flush(int32 put_offset) {
+ if (last_put_offset_ == put_offset)
+ return;
+
+ last_put_offset_ = put_offset;
+ command_buffer_->Flush(put_offset);
+}
+
+void CommandBufferClientImpl::WaitForTokenInRange(int32 start, int32 end) {
+ TryUpdateState();
+ while (!InRange(start, end, last_state_.token) &&
+ last_state_.error == gpu::error::kNoError) {
+ MakeProgressAndUpdateState();
+ TryUpdateState();
+ }
+}
+
+void CommandBufferClientImpl::WaitForGetOffsetInRange(int32 start, int32 end) {
+ TryUpdateState();
+ while (!InRange(start, end, last_state_.get_offset) &&
+ last_state_.error == gpu::error::kNoError) {
+ MakeProgressAndUpdateState();
+ TryUpdateState();
+ }
+}
+
+void CommandBufferClientImpl::SetGetBuffer(int32 shm_id) {
+ command_buffer_->SetGetBuffer(shm_id);
+ last_put_offset_ = -1;
+}
+
+scoped_refptr<gpu::Buffer> CommandBufferClientImpl::CreateTransferBuffer(
+ size_t size,
+ int32* id) {
+ if (size >= std::numeric_limits<uint32_t>::max())
+ return NULL;
+
+ void* memory = NULL;
+ mojo::ScopedSharedBufferHandle handle;
+ mojo::ScopedSharedBufferHandle duped;
+ if (!CreateMapAndDupSharedBuffer(size, &memory, &handle, &duped))
+ return NULL;
+
+ *id = ++next_transfer_buffer_id_;
+
+ command_buffer_->RegisterTransferBuffer(
+ *id, duped.Pass(), static_cast<uint32_t>(size));
+
+ scoped_ptr<gpu::BufferBacking> backing(
+ new MojoBufferBacking(handle.Pass(), memory, size));
+ scoped_refptr<gpu::Buffer> buffer(new gpu::Buffer(backing.Pass()));
+ return buffer;
+}
+
+void CommandBufferClientImpl::DestroyTransferBuffer(int32 id) {
+ command_buffer_->DestroyTransferBuffer(id);
+}
+
+gpu::Capabilities CommandBufferClientImpl::GetCapabilities() {
+ // TODO(piman)
+ NOTIMPLEMENTED();
+ return gpu::Capabilities();
+}
+
+gfx::GpuMemoryBuffer* CommandBufferClientImpl::CreateGpuMemoryBuffer(
+ size_t width,
+ size_t height,
+ unsigned internalformat,
+ unsigned usage,
+ int32* id) {
+ // TODO(piman)
+ NOTIMPLEMENTED();
+ return NULL;
+}
+
+void CommandBufferClientImpl::DestroyGpuMemoryBuffer(int32 id) {
+ // TODO(piman)
+ NOTIMPLEMENTED();
+}
+
+uint32 CommandBufferClientImpl::InsertSyncPoint() {
+ // TODO(piman)
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+void CommandBufferClientImpl::SignalSyncPoint(uint32 sync_point,
+ const base::Closure& callback) {
+ // TODO(piman)
+ NOTIMPLEMENTED();
+}
+
+void CommandBufferClientImpl::SignalQuery(uint32 query,
+ const base::Closure& callback) {
+ // TODO(piman)
+ NOTIMPLEMENTED();
+}
+
+void CommandBufferClientImpl::SetSurfaceVisible(bool visible) {
+ // TODO(piman)
+ NOTIMPLEMENTED();
+}
+
+void CommandBufferClientImpl::Echo(const base::Closure& callback) {
+ command_buffer_->Echo(callback);
+}
+
+uint32 CommandBufferClientImpl::CreateStreamTexture(uint32 texture_id) {
+ // TODO(piman)
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+void CommandBufferClientImpl::RequestAnimationFrames() {
+ command_buffer_->RequestAnimationFrames();
+}
+
+void CommandBufferClientImpl::CancelAnimationFrames() {
+ command_buffer_->CancelAnimationFrames();
+}
+
+void CommandBufferClientImpl::DidInitialize(bool success) {
+ initialize_result_ = success;
+}
+
+void CommandBufferClientImpl::DidMakeProgress(CommandBufferStatePtr state) {
+ if (state->generation - last_state_.generation < 0x80000000U)
+ last_state_ = state.To<State>();
+}
+
+void CommandBufferClientImpl::DidDestroy() {
+ LostContext(gpu::error::kUnknown);
+}
+
+void CommandBufferClientImpl::LostContext(int32_t lost_reason) {
+ last_state_.error = gpu::error::kLostContext;
+ last_state_.context_lost_reason =
+ static_cast<gpu::error::ContextLostReason>(lost_reason);
+ delegate_->ContextLost();
+}
+
+void CommandBufferClientImpl::OnConnectionError() {
+ LostContext(gpu::error::kUnknown);
+}
+
+void CommandBufferClientImpl::TryUpdateState() {
+ if (last_state_.error == gpu::error::kNoError)
+ shared_state()->Read(&last_state_);
+}
+
+void CommandBufferClientImpl::MakeProgressAndUpdateState() {
+ command_buffer_->MakeProgress(last_state_.get_offset);
+ if (!sync_dispatcher_->WaitAndDispatchOneMessage()) {
+ VLOG(1) << "Channel encountered error while waiting for command buffer";
+ // TODO(piman): is it ok for this to re-enter?
+ DidDestroy();
+ return;
+ }
+}
+
+void CommandBufferClientImpl::DrawAnimationFrame() {
+ delegate_->DrawAnimationFrame();
+}
+
+} // namespace gles2
+} // namespace mojo
diff --git a/chromium/mojo/gles2/command_buffer_client_impl.h b/chromium/mojo/gles2/command_buffer_client_impl.h
new file mode 100644
index 00000000000..68312e08537
--- /dev/null
+++ b/chromium/mojo/gles2/command_buffer_client_impl.h
@@ -0,0 +1,114 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_GLES2_COMMAND_BUFFER_CLIENT_IMPL_H_
+#define MOJO_GLES2_COMMAND_BUFFER_CLIENT_IMPL_H_
+
+#include <map>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "gpu/command_buffer/client/gpu_control.h"
+#include "gpu/command_buffer/common/command_buffer.h"
+#include "gpu/command_buffer/common/command_buffer_shared.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/services/gles2/command_buffer.mojom.h"
+
+namespace base {
+class RunLoop;
+}
+
+namespace mojo {
+template <typename S>
+class SyncDispatcher;
+
+namespace gles2 {
+class CommandBufferClientImpl;
+
+class CommandBufferDelegate {
+ public:
+ virtual ~CommandBufferDelegate();
+ virtual void ContextLost();
+ virtual void DrawAnimationFrame();
+};
+
+class CommandBufferClientImpl : public CommandBufferClient,
+ public CommandBufferSyncClient,
+ public ErrorHandler,
+ public gpu::CommandBuffer,
+ public gpu::GpuControl {
+ public:
+ explicit CommandBufferClientImpl(
+ CommandBufferDelegate* delegate,
+ const MojoAsyncWaiter* async_waiter,
+ ScopedMessagePipeHandle command_buffer_handle);
+ virtual ~CommandBufferClientImpl();
+
+ // CommandBuffer implementation:
+ virtual bool Initialize() OVERRIDE;
+ virtual State GetLastState() OVERRIDE;
+ virtual int32 GetLastToken() OVERRIDE;
+ virtual void Flush(int32 put_offset) OVERRIDE;
+ virtual void WaitForTokenInRange(int32 start, int32 end) OVERRIDE;
+ virtual void WaitForGetOffsetInRange(int32 start, int32 end) OVERRIDE;
+ virtual void SetGetBuffer(int32 shm_id) OVERRIDE;
+ virtual scoped_refptr<gpu::Buffer> CreateTransferBuffer(size_t size,
+ int32* id) OVERRIDE;
+ virtual void DestroyTransferBuffer(int32 id) OVERRIDE;
+
+ // gpu::GpuControl implementation:
+ virtual gpu::Capabilities GetCapabilities() OVERRIDE;
+ virtual gfx::GpuMemoryBuffer* CreateGpuMemoryBuffer(size_t width,
+ size_t height,
+ unsigned internalformat,
+ unsigned usage,
+ int32* id) OVERRIDE;
+ virtual void DestroyGpuMemoryBuffer(int32 id) OVERRIDE;
+ virtual uint32 InsertSyncPoint() OVERRIDE;
+ virtual void SignalSyncPoint(uint32 sync_point,
+ const base::Closure& callback) OVERRIDE;
+ virtual void SignalQuery(uint32 query,
+ const base::Closure& callback) OVERRIDE;
+ virtual void SetSurfaceVisible(bool visible) OVERRIDE;
+ virtual void Echo(const base::Closure& callback) OVERRIDE;
+ virtual uint32 CreateStreamTexture(uint32 texture_id) OVERRIDE;
+
+ void RequestAnimationFrames();
+ void CancelAnimationFrames();
+
+ private:
+ // CommandBufferClient implementation:
+ virtual void DidInitialize(bool success) OVERRIDE;
+ virtual void DidMakeProgress(CommandBufferStatePtr state) OVERRIDE;
+ virtual void DidDestroy() OVERRIDE;
+ virtual void LostContext(int32_t lost_reason) OVERRIDE;
+
+ // ErrorHandler implementation:
+ virtual void OnConnectionError() OVERRIDE;
+
+ virtual void DrawAnimationFrame() OVERRIDE;
+
+ void TryUpdateState();
+ void MakeProgressAndUpdateState();
+
+ gpu::CommandBufferSharedState* shared_state() const { return shared_state_; }
+
+ CommandBufferDelegate* delegate_;
+ CommandBufferPtr command_buffer_;
+ scoped_ptr<SyncDispatcher<CommandBufferSyncClient> > sync_dispatcher_;
+
+ State last_state_;
+ mojo::ScopedSharedBufferHandle shared_state_handle_;
+ gpu::CommandBufferSharedState* shared_state_;
+ int32 last_put_offset_;
+ int32 next_transfer_buffer_id_;
+
+ bool initialize_result_;
+ const MojoAsyncWaiter* async_waiter_;
+};
+
+} // gles2
+} // mojo
+
+#endif // MOJO_GLES2_COMMAND_BUFFER_CLIENT_IMPL_H_
diff --git a/chromium/mojo/gles2/gles2_context.cc b/chromium/mojo/gles2/gles2_context.cc
new file mode 100644
index 00000000000..701b0fed13d
--- /dev/null
+++ b/chromium/mojo/gles2/gles2_context.cc
@@ -0,0 +1,73 @@
+// Copyright 2014 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 "mojo/gles2/gles2_context.h"
+
+#include "gpu/command_buffer/client/gles2_cmd_helper.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/client/transfer_buffer.h"
+#include "mojo/public/c/gles2/gles2.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace gles2 {
+
+namespace {
+const size_t kDefaultCommandBufferSize = 1024 * 1024;
+const size_t kDefaultStartTransferBufferSize = 1 * 1024 * 1024;
+const size_t kDefaultMinTransferBufferSize = 1 * 256 * 1024;
+const size_t kDefaultMaxTransferBufferSize = 16 * 1024 * 1024;
+}
+
+GLES2Context::GLES2Context(const MojoAsyncWaiter* async_waiter,
+ ScopedMessagePipeHandle command_buffer_handle,
+ MojoGLES2ContextLost lost_callback,
+ MojoGLES2DrawAnimationFrame animation_callback,
+ void* closure)
+ : command_buffer_(this, async_waiter, command_buffer_handle.Pass()),
+ lost_callback_(lost_callback),
+ animation_callback_(animation_callback),
+ closure_(closure) {}
+
+GLES2Context::~GLES2Context() {}
+
+bool GLES2Context::Initialize() {
+ if (!command_buffer_.Initialize())
+ return false;
+ gles2_helper_.reset(new gpu::gles2::GLES2CmdHelper(&command_buffer_));
+ if (!gles2_helper_->Initialize(kDefaultCommandBufferSize))
+ return false;
+ gles2_helper_->SetAutomaticFlushes(false);
+ transfer_buffer_.reset(new gpu::TransferBuffer(gles2_helper_.get()));
+ bool bind_generates_resource = true;
+ // TODO(piman): Some contexts (such as compositor) want this to be true, so
+ // this needs to be a public parameter.
+ bool lose_context_when_out_of_memory = false;
+ implementation_.reset(
+ new gpu::gles2::GLES2Implementation(gles2_helper_.get(),
+ NULL,
+ transfer_buffer_.get(),
+ bind_generates_resource,
+ lose_context_when_out_of_memory,
+ &command_buffer_));
+ return implementation_->Initialize(kDefaultStartTransferBufferSize,
+ kDefaultMinTransferBufferSize,
+ kDefaultMaxTransferBufferSize,
+ gpu::gles2::GLES2Implementation::kNoLimit);
+}
+
+void GLES2Context::RequestAnimationFrames() {
+ command_buffer_.RequestAnimationFrames();
+}
+
+void GLES2Context::CancelAnimationFrames() {
+ command_buffer_.CancelAnimationFrames();
+}
+
+void GLES2Context::ContextLost() { lost_callback_(closure_); }
+
+void GLES2Context::DrawAnimationFrame() { animation_callback_(closure_); }
+
+} // namespace gles2
+} // namespace mojo
diff --git a/chromium/mojo/gles2/gles2_context.h b/chromium/mojo/gles2/gles2_context.h
new file mode 100644
index 00000000000..bc73082a976
--- /dev/null
+++ b/chromium/mojo/gles2/gles2_context.h
@@ -0,0 +1,63 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_GLES2_GLES2_CONTEXT_H_
+#define MOJO_GLES2_GLES2_CONTEXT_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "mojo/gles2/command_buffer_client_impl.h"
+#include "mojo/public/c/gles2/gles2.h"
+
+struct MojoGLES2ContextPrivate {};
+
+namespace gpu {
+class TransferBuffer;
+namespace gles2 {
+class GLES2CmdHelper;
+class GLES2Implementation;
+}
+}
+
+namespace mojo {
+namespace gles2 {
+
+class GLES2Context : public CommandBufferDelegate,
+ public MojoGLES2ContextPrivate {
+ public:
+ explicit GLES2Context(const MojoAsyncWaiter* async_waiter,
+ ScopedMessagePipeHandle command_buffer_handle,
+ MojoGLES2ContextLost lost_callback,
+ MojoGLES2DrawAnimationFrame animation_callback,
+ void* closure);
+ virtual ~GLES2Context();
+ bool Initialize();
+
+ gpu::gles2::GLES2Interface* interface() const {
+ return implementation_.get();
+ }
+ gpu::ContextSupport* context_support() const { return implementation_.get(); }
+ void RequestAnimationFrames();
+ void CancelAnimationFrames();
+
+ private:
+ virtual void ContextLost() OVERRIDE;
+ virtual void DrawAnimationFrame() OVERRIDE;
+
+ CommandBufferClientImpl command_buffer_;
+ scoped_ptr<gpu::gles2::GLES2CmdHelper> gles2_helper_;
+ scoped_ptr<gpu::TransferBuffer> transfer_buffer_;
+ scoped_ptr<gpu::gles2::GLES2Implementation> implementation_;
+ MojoGLES2ContextLost lost_callback_;
+ MojoGLES2DrawAnimationFrame animation_callback_;
+ void* closure_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(GLES2Context);
+};
+
+} // namespace gles2
+} // namespace mojo
+
+#endif // MOJO_GLES2_GLES2_CONTEXT_H_
diff --git a/chromium/mojo/gles2/gles2_impl_export.h b/chromium/mojo/gles2/gles2_impl_export.h
new file mode 100644
index 00000000000..3299ced152e
--- /dev/null
+++ b/chromium/mojo/gles2/gles2_impl_export.h
@@ -0,0 +1,31 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_GLES2_GLES2_IMPL_EXPORT_H_
+#define MOJO_GLES2_GLES2_IMPL_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(MOJO_GLES2_IMPL_IMPLEMENTATION)
+#define MOJO_GLES2_IMPL_EXPORT __declspec(dllexport)
+#else
+#define MOJO_GLES2_IMPL_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_GLES2_IMPL_IMPLEMENTATION)
+#define MOJO_GLES2_IMPL_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_GLES2_IMPL_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#else // defined(COMPONENT_BUILD)
+#define MOJO_GLES2_IMPL_EXPORT
+#endif
+
+#endif // MOJO_GLES2_GLES2_IMPL_EXPORT_H_
diff --git a/chromium/mojo/gles2/gles2_support_impl.cc b/chromium/mojo/gles2/gles2_support_impl.cc
new file mode 100644
index 00000000000..7fd86081dc2
--- /dev/null
+++ b/chromium/mojo/gles2/gles2_support_impl.cc
@@ -0,0 +1,116 @@
+// Copyright 2014 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 "mojo/gles2/gles2_support_impl.h"
+
+#include "base/lazy_instance.h"
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "mojo/gles2/gles2_context.h"
+#include "mojo/public/gles2/gles2_interface.h"
+#include "mojo/public/gles2/gles2_private.h"
+
+namespace mojo {
+namespace gles2 {
+
+namespace {
+
+class GLES2ImplForCommandBuffer : public GLES2Interface {
+ public:
+ GLES2ImplForCommandBuffer() : gpu_interface_(NULL) {}
+
+ void set_gpu_interface(gpu::gles2::GLES2Interface* gpu_interface) {
+ gpu_interface_ = gpu_interface;
+ }
+ gpu::gles2::GLES2Interface* gpu_interface() const { return gpu_interface_; }
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+ virtual ReturnType Function PARAMETERS OVERRIDE { \
+ return gpu_interface_->Function ARGUMENTS; \
+ }
+#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h"
+#undef VISIT_GL_CALL
+
+ private:
+ gpu::gles2::GLES2Interface* gpu_interface_;
+ DISALLOW_COPY_AND_ASSIGN(GLES2ImplForCommandBuffer);
+};
+
+base::LazyInstance<GLES2ImplForCommandBuffer> g_gles2_interface =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // anonymous namespace
+
+GLES2SupportImpl::GLES2SupportImpl() : async_waiter_(NULL) {}
+GLES2SupportImpl::~GLES2SupportImpl() {}
+
+// static
+void GLES2SupportImpl::Init() { GLES2Support::Init(new GLES2SupportImpl()); }
+
+void GLES2SupportImpl::Initialize(const MojoAsyncWaiter* async_waiter) {
+ DCHECK(!async_waiter_);
+ DCHECK(async_waiter);
+ async_waiter_ = async_waiter;
+}
+
+void GLES2SupportImpl::Terminate() {
+ DCHECK(async_waiter_);
+ async_waiter_ = NULL;
+}
+
+MojoGLES2Context GLES2SupportImpl::CreateContext(
+ MessagePipeHandle handle,
+ MojoGLES2ContextLost lost_callback,
+ MojoGLES2DrawAnimationFrame animation_callback,
+ void* closure) {
+ ScopedMessagePipeHandle scoped_handle(handle);
+ scoped_ptr<GLES2Context> client(new GLES2Context(async_waiter_,
+ scoped_handle.Pass(),
+ lost_callback,
+ animation_callback,
+ closure));
+ if (!client->Initialize())
+ client.reset();
+ return client.release();
+}
+
+void GLES2SupportImpl::DestroyContext(MojoGLES2Context context) {
+ delete static_cast<GLES2Context*>(context);
+}
+
+void GLES2SupportImpl::MakeCurrent(MojoGLES2Context context) {
+ gpu::gles2::GLES2Interface* interface = NULL;
+ if (context) {
+ GLES2Context* client = static_cast<GLES2Context*>(context);
+ interface = client->interface();
+ DCHECK(interface);
+ }
+ g_gles2_interface.Get().set_gpu_interface(interface);
+}
+
+void GLES2SupportImpl::SwapBuffers() {
+ g_gles2_interface.Get().gpu_interface()->SwapBuffers();
+}
+
+void GLES2SupportImpl::RequestAnimationFrames(MojoGLES2Context context) {
+ static_cast<GLES2Context*>(context)->RequestAnimationFrames();
+}
+
+void GLES2SupportImpl::CancelAnimationFrames(MojoGLES2Context context) {
+ static_cast<GLES2Context*>(context)->CancelAnimationFrames();
+}
+
+void* GLES2SupportImpl::GetGLES2Interface(MojoGLES2Context context) {
+ return static_cast<GLES2Context*>(context)->interface();
+}
+
+void* GLES2SupportImpl::GetContextSupport(MojoGLES2Context context) {
+ return static_cast<GLES2Context*>(context)->context_support();
+}
+
+GLES2Interface* GLES2SupportImpl::GetGLES2InterfaceForCurrentContext() {
+ return &g_gles2_interface.Get();
+}
+
+} // namespace gles2
+} // namespace mojo
diff --git a/chromium/mojo/gles2/gles2_support_impl.h b/chromium/mojo/gles2/gles2_support_impl.h
new file mode 100644
index 00000000000..3305bf6ed4e
--- /dev/null
+++ b/chromium/mojo/gles2/gles2_support_impl.h
@@ -0,0 +1,46 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_GLES2_GLES2_SUPPORT_IMPL_H_
+#define MOJO_GLES2_GLES2_SUPPORT_IMPL_H_
+
+#include "base/compiler_specific.h"
+#include "mojo/gles2/gles2_impl_export.h"
+#include "mojo/public/gles2/gles2_private.h"
+
+namespace mojo {
+namespace gles2 {
+
+class MOJO_GLES2_IMPL_EXPORT GLES2SupportImpl : public GLES2Support {
+ public:
+ virtual ~GLES2SupportImpl();
+
+ static void Init();
+
+ virtual void Initialize(const MojoAsyncWaiter* async_waiter) OVERRIDE;
+ virtual void Terminate() OVERRIDE;
+ virtual MojoGLES2Context CreateContext(
+ MessagePipeHandle handle,
+ MojoGLES2ContextLost lost_callback,
+ MojoGLES2DrawAnimationFrame animation_callback,
+ void* closure) OVERRIDE;
+ virtual void DestroyContext(MojoGLES2Context context) OVERRIDE;
+ virtual void MakeCurrent(MojoGLES2Context context) OVERRIDE;
+ virtual void SwapBuffers() OVERRIDE;
+ virtual void RequestAnimationFrames(MojoGLES2Context context) OVERRIDE;
+ virtual void CancelAnimationFrames(MojoGLES2Context context) OVERRIDE;
+ virtual void* GetGLES2Interface(MojoGLES2Context context) OVERRIDE;
+ virtual void* GetContextSupport(MojoGLES2Context context) OVERRIDE;
+ virtual GLES2Interface* GetGLES2InterfaceForCurrentContext() OVERRIDE;
+
+ private:
+ GLES2SupportImpl();
+
+ const MojoAsyncWaiter* async_waiter_;
+};
+
+} // namespace gles2
+} // namespace mojo
+
+#endif // MOJO_GLES2_GLES2_SUPPORT_IMPL_H_
diff --git a/chromium/mojo/mojo.gyp b/chromium/mojo/mojo.gyp
index 9151d4259d9..9421a9a90b7 100644
--- a/chromium/mojo/mojo.gyp
+++ b/chromium/mojo/mojo.gyp
@@ -3,8 +3,19 @@
# found in the LICENSE file.
{
+ 'target_defaults': {
+ 'conditions': [
+ ['mojo_shell_debug_url != ""', {
+ 'defines': [
+ 'MOJO_SHELL_DEBUG=1',
+ 'MOJO_SHELL_DEBUG_URL="<(mojo_shell_debug_url)"',
+ ],
+ }],
+ ],
+ },
'variables': {
'chromium_code': 1,
+ 'mojo_shell_debug_url%': "",
},
'includes': [
'mojo_apps.gypi',
@@ -17,24 +28,86 @@
'target_name': 'mojo',
'type': 'none',
'dependencies': [
- 'mojo_bindings',
- 'mojo_bindings_unittests',
+ 'mojo_apps_js_unittests',
+ 'mojo_compositor_app',
'mojo_common_lib',
'mojo_common_unittests',
- 'mojo_hello_world_service',
+ 'mojo_cpp_bindings',
+ 'mojo_geometry_lib',
+ 'mojo_html_viewer',
+ 'mojo_image_viewer',
'mojo_js',
+ 'mojo_js_bindings',
'mojo_js_unittests',
- 'mojo_public_perftests',
- 'mojo_public_test_support',
- 'mojo_public_unittests',
+ 'mojo_launcher',
+ 'mojo_message_generator',
+ 'mojo_native_viewport_service',
+ 'mojo_network_service',
+ 'mojo_pepper_container_app',
+ 'mojo_public_test_utils',
+ 'mojo_public_bindings_unittests',
+ 'mojo_public_environment_unittests',
+ 'mojo_public_system_perftests',
+ 'mojo_public_system_unittests',
+ 'mojo_public_utility_unittests',
'mojo_sample_app',
+ 'mojo_service_manager',
+ 'mojo_service_manager_unittests',
'mojo_shell',
'mojo_shell_lib',
+ 'mojo_shell_tests',
'mojo_system',
'mojo_system_impl',
'mojo_system_unittests',
+ 'mojo_test_service',
'mojo_utility',
- 'mojo_utility_unittests',
+ 'mojo_view_manager_lib',
+ 'mojo_view_manager_lib_unittests',
+ 'mojo_wget',
+ ],
+ 'conditions': [
+ ['use_aura==1', {
+ 'dependencies': [
+ 'mojo_aura_demo',
+ 'mojo_aura_demo_init',
+ 'mojo_browser',
+ 'mojo_demo_launcher',
+ 'mojo_embedded_app',
+ 'mojo_nesting_app',
+ 'mojo_window_manager',
+ 'mojo_view_manager',
+ 'mojo_view_manager_unittests',
+ ],
+ }],
+ ['OS == "android"', {
+ 'dependencies': [
+ 'mojo_bindings_java',
+ 'mojo_public_java',
+ 'mojo_system_java',
+ 'libmojo_system_java',
+ 'mojo_test_apk',
+ ],
+ }],
+ ['OS == "linux"', {
+ 'dependencies': [
+ 'mojo_dbus_echo',
+ 'mojo_dbus_echo_service',
+ ],
+ }],
+ ]
+ },
+ {
+ 'target_name': 'mojo_external_service_bindings',
+ 'type': 'static_library',
+ 'sources': [
+ 'shell/external_service.mojom',
+ ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+ 'export_dependent_settings': [
+ 'mojo_cpp_bindings',
+ ],
+ 'dependencies': [
+ 'mojo_cpp_bindings',
],
},
{
@@ -44,8 +117,9 @@
'../base/base.gyp:base',
'../base/base.gyp:test_support_base',
'../testing/gtest.gyp:gtest',
- 'mojo_system',
'mojo_system_impl',
+ 'mojo_test_support',
+ 'mojo_test_support_impl',
],
'sources': [
'common/test/run_all_unittests.cc',
@@ -56,29 +130,50 @@
'type': 'static_library',
'dependencies': [
'../base/base.gyp:test_support_base',
- 'mojo_system',
'mojo_system_impl',
+ 'mojo_test_support',
+ 'mojo_test_support_impl',
],
'sources': [
'common/test/run_all_perftests.cc',
],
},
{
+ # GN version: //mojo/system
'target_name': 'mojo_system_impl',
'type': '<(component)',
'dependencies': [
- 'mojo_system',
'../base/base.gyp:base',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
],
'defines': [
'MOJO_SYSTEM_IMPL_IMPLEMENTATION',
+ 'MOJO_SYSTEM_IMPLEMENTATION',
+ 'MOJO_USE_SYSTEM_IMPL',
],
'sources': [
+ 'embedder/channel_init.cc',
+ 'embedder/channel_init.h',
+ 'embedder/embedder.cc',
+ 'embedder/embedder.h',
+ 'embedder/platform_channel_pair.cc',
+ 'embedder/platform_channel_pair.h',
+ 'embedder/platform_channel_pair_posix.cc',
+ 'embedder/platform_channel_pair_win.cc',
+ 'embedder/platform_channel_utils_posix.cc',
+ 'embedder/platform_channel_utils_posix.h',
+ 'embedder/platform_handle.cc',
+ 'embedder/platform_handle.h',
+ 'embedder/platform_handle_utils.h',
+ 'embedder/platform_handle_utils_posix.cc',
+ 'embedder/platform_handle_utils_win.cc',
+ 'embedder/platform_handle_vector.h',
+ 'embedder/scoped_platform_handle.h',
'system/channel.cc',
'system/channel.h',
'system/constants.h',
- 'system/core_impl.cc',
- 'system/core_impl.h',
+ 'system/core.cc',
+ 'system/core.h',
'system/data_pipe.cc',
'system/data_pipe.h',
'system/data_pipe_consumer_dispatcher.cc',
@@ -87,58 +182,93 @@
'system/data_pipe_producer_dispatcher.h',
'system/dispatcher.cc',
'system/dispatcher.h',
+ 'system/entrypoints.cc',
+ 'system/handle_signals_state.h',
+ 'system/handle_table.cc',
+ 'system/handle_table.h',
'system/local_data_pipe.cc',
'system/local_data_pipe.h',
'system/local_message_pipe_endpoint.cc',
'system/local_message_pipe_endpoint.h',
+ 'system/mapping_table.cc',
+ 'system/mapping_table.h',
'system/memory.cc',
'system/memory.h',
'system/message_in_transit.cc',
'system/message_in_transit.h',
+ 'system/message_in_transit_queue.cc',
+ 'system/message_in_transit_queue.h',
'system/message_pipe.cc',
'system/message_pipe.h',
'system/message_pipe_dispatcher.cc',
'system/message_pipe_dispatcher.h',
'system/message_pipe_endpoint.cc',
'system/message_pipe_endpoint.h',
- 'system/platform_channel.cc',
- 'system/platform_channel.h',
- 'system/platform_channel_handle.cc',
- 'system/platform_channel_handle.h',
- 'system/platform_channel_posix.cc',
+ 'system/options_validation.h',
+ 'system/platform_handle_dispatcher.cc',
+ 'system/platform_handle_dispatcher.h',
'system/proxy_message_pipe_endpoint.cc',
'system/proxy_message_pipe_endpoint.h',
+ 'system/raw_channel.cc',
'system/raw_channel.h',
'system/raw_channel_posix.cc',
'system/raw_channel_win.cc',
+ 'system/raw_shared_buffer.cc',
+ 'system/raw_shared_buffer.h',
+ 'system/raw_shared_buffer_posix.cc',
+ 'system/raw_shared_buffer_win.cc',
+ 'system/shared_buffer_dispatcher.cc',
+ 'system/shared_buffer_dispatcher.h',
'system/simple_dispatcher.cc',
'system/simple_dispatcher.h',
+ 'system/transport_data.cc',
+ 'system/transport_data.h',
'system/waiter.cc',
'system/waiter.h',
'system/waiter_list.cc',
'system/waiter_list.h',
+ # Test-only code:
+ # TODO(vtl): It's a little unfortunate that these end up in the same
+ # component as non-test-only code. In the static build, this code should
+ # hopefully be dead-stripped.
+ 'embedder/test_embedder.cc',
+ 'embedder/test_embedder.h',
],
+ 'all_dependent_settings': {
+ # Ensures that dependent projects import the core functions on Windows.
+ 'defines': ['MOJO_USE_SYSTEM_IMPL'],
+ }
},
{
'target_name': 'mojo_system_unittests',
'type': 'executable',
'dependencies': [
+ '../base/base.gyp:base',
'../base/base.gyp:run_all_unittests',
'../testing/gtest.gyp:gtest',
'mojo_common_test_support',
- 'mojo_system',
'mojo_system_impl',
],
'sources': [
- 'system/core_impl_unittest.cc',
+ 'embedder/embedder_unittest.cc',
+ 'embedder/platform_channel_pair_posix_unittest.cc',
+ 'system/channel_unittest.cc',
+ 'system/core_unittest.cc',
'system/core_test_base.cc',
'system/core_test_base.h',
+ 'system/data_pipe_unittest.cc',
'system/dispatcher_unittest.cc',
+ 'system/local_data_pipe_unittest.cc',
+ 'system/memory_unittest.cc',
'system/message_pipe_dispatcher_unittest.cc',
'system/message_pipe_unittest.cc',
'system/multiprocess_message_pipe_unittest.cc',
- 'system/raw_channel_posix_unittest.cc',
- 'system/remote_message_pipe_posix_unittest.cc',
+ 'system/options_validation_unittest.cc',
+ 'system/platform_handle_dispatcher_unittest.cc',
+ 'system/raw_channel_unittest.cc',
+ 'system/raw_shared_buffer_unittest.cc',
+ 'system/remote_message_pipe_unittest.cc',
+ 'system/shared_buffer_dispatcher_unittest.cc',
'system/simple_dispatcher_unittest.cc',
'system/test_utils.cc',
'system/test_utils.h',
@@ -152,19 +282,43 @@
'target_name': 'mojo_gles2_impl',
'type': '<(component)',
'dependencies': [
- '../gpu/gpu.gyp:gles2_c_lib',
+ '../base/base.gyp:base',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '../gpu/gpu.gyp:command_buffer_client',
+ '../gpu/gpu.gyp:command_buffer_common',
+ '../gpu/gpu.gyp:gles2_cmd_helper',
+ '../gpu/gpu.gyp:gles2_implementation',
'mojo_gles2',
+ 'mojo_gles2_bindings',
+ 'mojo_environment_chromium',
+ 'mojo_system_impl',
],
'defines': [
'MOJO_GLES2_IMPL_IMPLEMENTATION',
],
'sources': [
- 'gles2/export.h',
- 'gles2/gles2_impl.cc',
- 'gles2/gles2_impl.h',
+ 'gles2/command_buffer_client_impl.cc',
+ 'gles2/command_buffer_client_impl.h',
+ 'gles2/gles2_impl_export.h',
+ 'gles2/gles2_support_impl.cc',
+ 'gles2/gles2_support_impl.h',
+ 'gles2/gles2_context.cc',
+ 'gles2/gles2_context.h',
+ ],
+ },
+ {
+ 'target_name': 'mojo_test_support_impl',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'sources': [
+ 'common/test/test_support_impl.cc',
+ 'common/test/test_support_impl.h',
],
},
{
+ # GN version: //mojo/common
'target_name': 'mojo_common_lib',
'type': '<(component)',
'defines': [
@@ -173,26 +327,24 @@
'dependencies': [
'../base/base.gyp:base',
'../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
- 'mojo_system',
+ 'mojo_system_impl',
+ ],
+ 'export_dependent_settings': [
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ 'mojo_system_impl',
],
'sources': [
- 'common/bindings_support_impl.cc',
- 'common/bindings_support_impl.h',
'common/common_type_converters.cc',
'common/common_type_converters.h',
+ 'common/data_pipe_utils.cc',
+ 'common/data_pipe_utils.h',
'common/handle_watcher.cc',
'common/handle_watcher.h',
'common/message_pump_mojo.cc',
'common/message_pump_mojo.h',
'common/message_pump_mojo_handler.h',
- ],
- 'conditions': [
- ['OS == "win"', {
- # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
- 'msvs_disabled_warnings': [
- 4267,
- ],
- }],
+ 'common/time_helper.cc',
+ 'common/time_helper.h',
],
},
{
@@ -202,12 +354,14 @@
'../base/base.gyp:base',
'../base/base.gyp:test_support_base',
'../testing/gtest.gyp:gtest',
- 'mojo_system',
'mojo_system_impl',
],
'sources': [
- 'common/test/multiprocess_test_base.cc',
- 'common/test/multiprocess_test_base.h',
+ 'common/test/multiprocess_test_helper.cc',
+ 'common/test/multiprocess_test_helper.h',
+ 'common/test/test_utils.h',
+ 'common/test/test_utils_posix.cc',
+ 'common/test/test_utils_win.cc',
],
},
{
@@ -217,27 +371,108 @@
'../base/base.gyp:base',
'../base/base.gyp:base_message_loop_tests',
'../testing/gtest.gyp:gtest',
- 'mojo_bindings',
+ 'mojo_cpp_bindings',
+ 'mojo_environment_chromium',
'mojo_common_lib',
'mojo_common_test_support',
- 'mojo_public_test_support',
+ 'mojo_public_test_utils',
'mojo_run_all_unittests',
- 'mojo_system',
- 'mojo_system_impl',
],
'sources': [
'common/common_type_converters_unittest.cc',
'common/handle_watcher_unittest.cc',
'common/message_pump_mojo_unittest.cc',
- 'common/test/multiprocess_test_base_unittest.cc',
+ 'common/test/multiprocess_test_helper_unittest.cc',
],
- 'conditions': [
- ['OS == "win"', {
- # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
- 'msvs_disabled_warnings': [
- 4267,
- ],
- }],
+ },
+ {
+ # GN version: //mojo/environment:chromium
+ 'target_name': 'mojo_environment_chromium',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'mojo_common_lib',
+ 'mojo_environment_chromium_impl',
+ ],
+ 'sources': [
+ 'environment/environment.cc',
+ # TODO(vtl): This is kind of ugly. (See TODO in logging.h.)
+ "public/cpp/environment/logging.h",
+ "public/cpp/environment/lib/logging.h",
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'export_dependent_settings': [
+ 'mojo_environment_chromium_impl',
+ ],
+ },
+ {
+ # GN version: //mojo/environment:chromium_impl
+ 'target_name': 'mojo_environment_chromium_impl',
+ 'type': '<(component)',
+ 'defines': [
+ 'MOJO_ENVIRONMENT_IMPL_IMPLEMENTATION',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ 'mojo_common_lib'
+ ],
+ 'sources': [
+ 'environment/default_async_waiter_impl.cc',
+ 'environment/default_async_waiter_impl.h',
+ 'environment/default_logger_impl.cc',
+ 'environment/default_logger_impl.h',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ {
+ # GN version: //mojo/service_manager
+ 'target_name': 'mojo_service_manager',
+ 'type': '<(component)',
+ 'defines': [
+ 'MOJO_SERVICE_MANAGER_IMPLEMENTATION',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '../net/net.gyp:net',
+ '../url/url.gyp:url_lib',
+ 'mojo_common_lib',
+ 'mojo_environment_chromium',
+ 'mojo_service_provider_bindings',
+ 'mojo_system_impl',
+ ],
+ 'sources': [
+ 'service_manager/background_service_loader.cc',
+ 'service_manager/background_service_loader.h',
+ 'service_manager/service_loader.h',
+ 'service_manager/service_manager.cc',
+ 'service_manager/service_manager.h',
+ 'service_manager/service_manager_export.h',
+ ],
+ 'export_dependent_settings': [
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ 'mojo_service_provider_bindings',
+ ],
+ },
+ {
+ 'target_name': 'mojo_spy',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:base_static',
+ '../net/net.gyp:http_server',
+ '../url/url.gyp:url_lib',
+ 'mojo_service_manager',
+ ],
+ 'sources': [
+ 'spy/spy.cc',
+ 'spy/spy.h',
+ 'spy/websocket_server.cc',
+ 'spy/websocket_server.h',
],
},
{
@@ -245,43 +480,96 @@
'type': 'static_library',
'dependencies': [
'../base/base.gyp:base',
+ '../base/base.gyp:base_static',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
'../net/net.gyp:net',
'../url/url.gyp:url_lib',
- 'mojo_bindings',
+ 'mojo_external_service_bindings',
'mojo_gles2_impl',
'mojo_native_viewport_service',
- 'mojo_system',
+ 'mojo_network_bindings',
+ 'mojo_service_manager',
+ 'mojo_service_provider_bindings',
+ 'mojo_spy',
'mojo_system_impl',
],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
'sources': [
- 'shell/app_container.cc',
- 'shell/app_container.h',
+ 'shell/app_child_process.cc',
+ 'shell/app_child_process.h',
+ 'shell/app_child_process.mojom',
+ 'shell/app_child_process_host.cc',
+ 'shell/app_child_process_host.h',
+ 'shell/child_process.cc',
+ 'shell/child_process.h',
+ 'shell/child_process_host.cc',
+ 'shell/child_process_host.h',
'shell/context.cc',
'shell/context.h',
+ 'shell/dbus_service_loader_linux.cc',
+ 'shell/dbus_service_loader_linux.h',
+ 'shell/dynamic_service_loader.cc',
+ 'shell/dynamic_service_loader.h',
+ 'shell/dynamic_service_runner.h',
'shell/init.cc',
'shell/init.h',
- 'shell/loader.cc',
- 'shell/loader.h',
- 'shell/network_delegate.cc',
- 'shell/network_delegate.h',
+ 'shell/in_process_dynamic_service_runner.cc',
+ 'shell/in_process_dynamic_service_runner.h',
+ 'shell/keep_alive.cc',
+ 'shell/keep_alive.h',
+ 'shell/mojo_url_resolver.cc',
+ 'shell/mojo_url_resolver.h',
+ 'shell/out_of_process_dynamic_service_runner.cc',
+ 'shell/out_of_process_dynamic_service_runner.h',
'shell/run.cc',
'shell/run.h',
- 'shell/storage.cc',
- 'shell/storage.h',
'shell/switches.cc',
'shell/switches.h',
'shell/task_runners.cc',
'shell/task_runners.h',
- 'shell/url_request_context_getter.cc',
- 'shell/url_request_context_getter.h',
+ 'shell/test_child_process.cc',
+ 'shell/test_child_process.h',
+ 'shell/view_manager_loader.cc',
+ 'shell/view_manager_loader.h',
],
'conditions': [
- ['OS == "win"', {
- # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
- 'msvs_disabled_warnings': [
- 4267,
+ ['OS=="linux"', {
+ 'dependencies': [
+ '../build/linux/system.gyp:dbus',
+ '../dbus/dbus.gyp:dbus',
],
}],
+ ['use_aura==1', {
+ 'dependencies': [
+ # These are only necessary as long as we hard code use of ViewManager.
+ '../skia/skia.gyp:skia',
+ 'mojo_gles2',
+ 'mojo_application',
+ 'mojo_view_manager',
+ 'mojo_view_manager_bindings',
+ ],
+ }, { # use_aura==0
+ 'sources!': [
+ 'shell/view_manager_loader.cc',
+ 'shell/view_manager_loader.h',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'mojo_shell_test_support',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:base_static',
+ '../url/url.gyp:url_lib',
+ 'mojo_service_manager',
+ 'mojo_shell_lib',
+ 'mojo_system_impl',
+ ],
+ 'sources': [
+ 'shell/shell_test_helper.cc',
+ 'shell/shell_test_helper.h',
],
},
{
@@ -292,65 +580,248 @@
'../ui/gl/gl.gyp:gl',
'../url/url.gyp:url_lib',
'mojo_common_lib',
+ 'mojo_environment_chromium',
+ 'mojo_service_manager',
'mojo_shell_lib',
- 'mojo_system',
'mojo_system_impl',
],
'sources': [
'shell/desktop/mojo_main.cc',
],
+ },
+ {
+ 'target_name': 'mojo_shell_tests',
+ 'type': '<(gtest_target_type)',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:test_support_base',
+ '../testing/gtest.gyp:gtest',
+ # TODO(vtl): We don't currently need this, but I imagine we will soon.
+ # '../ui/gl/gl.gyp:gl',
+ '../url/url.gyp:url_lib',
+ 'mojo_common_lib',
+ 'mojo_environment_chromium',
+ 'mojo_service_manager',
+ 'mojo_shell_lib',
+ 'mojo_system_impl',
+ 'mojo_test_service_bindings',
+ ],
+ 'sources': [
+ 'shell/child_process_host_unittest.cc',
+ 'shell/shell_test_base.cc',
+ 'shell/shell_test_base.h',
+ 'shell/shell_test_base_unittest.cc',
+ 'shell/shell_test_main.cc',
+ ],
'conditions': [
- ['OS == "win"', {
- # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
- 'msvs_disabled_warnings': [
- 4267,
+ ['OS == "android"', {
+ 'dependencies': [
+ '../testing/android/native_test.gyp:native_test_native_code',
],
}],
],
},
+ {
+ 'target_name': 'mojo_service_manager_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../testing/gtest.gyp:gtest',
+ '../url/url.gyp:url_lib',
+ 'mojo_common_lib',
+ 'mojo_cpp_bindings',
+ 'mojo_environment_chromium',
+ 'mojo_run_all_unittests',
+ 'mojo_service_manager',
+ 'mojo_application',
+ ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+ 'sources': [
+ 'service_manager/service_manager_unittest.cc',
+ 'service_manager/test.mojom',
+ ],
+ },
+ {
+ 'target_name': 'mojo_js_bindings_lib',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../gin/gin.gyp:gin',
+ '../v8/tools/gyp/v8.gyp:v8',
+ 'mojo_common_lib',
+ ],
+ 'export_dependent_settings': [
+ '../base/base.gyp:base',
+ '../gin/gin.gyp:gin',
+ 'mojo_common_lib',
+ ],
+ 'sources': [
+ 'bindings/js/core.cc',
+ 'bindings/js/core.h',
+ 'bindings/js/handle.cc',
+ 'bindings/js/handle.h',
+ 'bindings/js/support.cc',
+ 'bindings/js/support.h',
+ 'bindings/js/waiting_callback.cc',
+ 'bindings/js/waiting_callback.h',
+ ],
+ },
+ {
+ 'target_name': 'mojo_js_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../gin/gin.gyp:gin_test',
+ 'mojo_common_test_support',
+ 'mojo_js_bindings_lib',
+ 'mojo_run_all_unittests',
+ 'mojo_public_test_interfaces',
+ ],
+ 'sources': [
+ 'bindings/js/run_js_tests.cc',
+ ],
+ },
+ {
+ 'target_name': 'mojo_message_generator',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../testing/gtest.gyp:gtest',
+ 'mojo_common_lib',
+ 'mojo_cpp_bindings',
+ 'mojo_environment_chromium',
+ 'mojo_system_impl',
+ ],
+ 'sources': [
+ 'tools/message_generator.cc',
+ ],
+ },
+ {
+ 'target_name': 'mojo_cc_support',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../cc/cc.gyp:cc',
+ '../skia/skia.gyp:skia',
+ '../gpu/gpu.gyp:gles2_implementation',
+ 'mojo_gles2',
+ ],
+ 'sources': [
+ 'cc/context_provider_mojo.cc',
+ 'cc/context_provider_mojo.h',
+ ],
+ },
],
'conditions': [
['OS=="android"', {
'targets': [
{
- 'target_name': 'mojo_native_viewport_java',
+ 'target_name': 'mojo_jni_headers',
+ 'type': 'none',
+ 'dependencies': [
+ 'mojo_java_set_jni_headers',
+ ],
+ 'sources': [
+ 'android/javatests/src/org/chromium/mojo/MojoTestCase.java',
+ 'android/system/src/org/chromium/mojo/system/impl/CoreImpl.java',
+ 'services/native_viewport/android/src/org/chromium/mojo/NativeViewportAndroid.java',
+ 'shell/android/apk/src/org/chromium/mojo_shell_apk/MojoMain.java',
+ ],
+ 'variables': {
+ 'jni_gen_package': 'mojo',
+ },
+ 'includes': [ '../build/jni_generator.gypi' ],
+ },
+ {
+ 'target_name': 'mojo_system_java',
'type': 'none',
'dependencies': [
'../base/base.gyp:base_java',
+ 'mojo_public_java',
],
'variables': {
- 'java_in_dir': '<(DEPTH)/mojo/services/native_viewport/android',
+ 'java_in_dir': '<(DEPTH)/mojo/android/system',
},
'includes': [ '../build/java.gypi' ],
},
{
- 'target_name': 'mojo_java_set_jni_headers',
+ 'target_name': 'libmojo_system_java',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ 'mojo_common_lib',
+ 'mojo_environment_chromium',
+ 'mojo_jni_headers',
+ 'mojo_service_provider_bindings',
+ 'mojo_shell_lib',
+ ],
+ 'sources': [
+ 'android/system/core_impl.cc',
+ 'android/system/core_impl.h',
+ ],
+ },
+ {
+ 'target_name': 'libmojo_java_unittest',
+ 'type': 'shared_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:test_support_base',
+ 'libmojo_system_java',
+ 'mojo_jni_headers',
+ ],
+ 'defines': [
+ 'UNIT_TEST' # As exported from testing/gtest.gyp:gtest.
+ ],
+ 'sources': [
+ 'android/javatests/mojo_test_case.cc',
+ 'android/javatests/mojo_test_case.h',
+ 'android/javatests/init_library.cc',
+ ],
+ },
+ {
+ 'target_name': 'mojo_test_apk',
'type': 'none',
+ 'dependencies': [
+ 'mojo_bindings_java',
+ 'mojo_public_test_interfaces',
+ 'mojo_system_java',
+ '../base/base.gyp:base_java_test_support',
+ ],
'variables': {
- 'jni_gen_package': 'mojo',
- 'input_java_class': 'java/util/HashSet.class',
+ 'apk_name': 'MojoTest',
+ 'java_in_dir': '<(DEPTH)/mojo/android/javatests',
+ 'resource_dir': '<(DEPTH)/mojo/android/javatests/apk',
+ 'native_lib_target': 'libmojo_java_unittest',
+ 'is_test_apk': 1,
+ # Given that this apk tests itself, it needs to bring emma with it
+ # when instrumented.
+ 'conditions': [
+ ['emma_coverage != 0', {
+ 'emma_instrument': 1,
+ }],
+ ],
},
- 'includes': [ '../build/jar_file_jni_generator.gypi' ],
+ 'includes': [ '../build/java_apk.gypi' ],
},
{
- 'target_name': 'mojo_jni_headers',
+ 'target_name': 'mojo_native_viewport_java',
'type': 'none',
'dependencies': [
- 'mojo_java_set_jni_headers',
+ '../base/base.gyp:base_java',
],
- 'direct_dependent_settings': {
- 'include_dirs': [
- '<(SHARED_INTERMEDIATE_DIR)/mojo',
- ],
+ 'variables': {
+ 'java_in_dir': '<(DEPTH)/mojo/services/native_viewport/android',
},
- 'sources': [
- 'services/native_viewport/android/src/org/chromium/mojo/NativeViewportAndroid.java',
- 'shell/android/apk/src/org/chromium/mojo_shell_apk/MojoMain.java',
- ],
+ 'includes': [ '../build/java.gypi' ],
+ },
+ {
+ 'target_name': 'mojo_java_set_jni_headers',
+ 'type': 'none',
'variables': {
- 'jni_gen_package': 'mojo'
+ 'jni_gen_package': 'mojo',
+ 'input_java_class': 'java/util/HashSet.class',
},
- 'includes': [ '../build/jni_generator.gypi' ],
+ 'includes': [ '../build/jar_file_jni_generator.gypi' ],
},
{
'target_name': 'libmojo_shell',
@@ -359,9 +830,12 @@
'../base/base.gyp:base',
'../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
'../ui/gfx/gfx.gyp:gfx',
+ '../ui/gfx/gfx.gyp:gfx_geometry',
'../ui/gl/gl.gyp:gl',
'mojo_common_lib',
+ 'mojo_environment_chromium',
'mojo_jni_headers',
+ 'mojo_service_provider_bindings',
'mojo_shell_lib',
],
'sources': [
@@ -389,5 +863,98 @@
}
],
}],
+ ['OS=="linux"', {
+ 'targets': [
+ {
+ 'target_name': 'mojo_dbus_service',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../build/linux/system.gyp:dbus',
+ '../dbus/dbus.gyp:dbus',
+ 'mojo_common_lib',
+ 'mojo_external_service_bindings',
+ 'mojo_application',
+ 'mojo_system_impl',
+ ],
+ 'sources': [
+ 'dbus/dbus_external_service.h',
+ 'dbus/dbus_external_service.cc',
+ ],
+ },
+ ],
+ }],
+ ['test_isolation_mode != "noop"', {
+ 'targets': [
+ {
+ 'target_name': 'mojo_js_unittests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'mojo_js_unittests',
+ ],
+ 'includes': [
+ '../build/isolate.gypi',
+ 'mojo_js_unittests.isolate',
+ ],
+ 'sources': [
+ 'mojo_js_unittests.isolate',
+ ],
+ },
+ ],
+ }],
+ ['use_aura==1', {
+ 'targets': [
+ {
+ 'target_name': 'mojo_aura_support',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../cc/cc.gyp:cc',
+ '../ui/aura/aura.gyp:aura',
+ '../ui/compositor/compositor.gyp:compositor',
+ '../ui/events/events.gyp:events',
+ '../ui/events/events.gyp:events_base',
+ '../ui/gl/gl.gyp:gl',
+ '../webkit/common/gpu/webkit_gpu.gyp:webkit_gpu',
+ 'mojo_cc_support',
+ 'mojo_gles2',
+ 'mojo_native_viewport_bindings',
+ ],
+ 'sources': [
+ 'aura/aura_init.cc',
+ 'aura/aura_init.h',
+ 'aura/context_factory_mojo.cc',
+ 'aura/context_factory_mojo.h',
+ 'aura/screen_mojo.cc',
+ 'aura/screen_mojo.h',
+ 'aura/window_tree_host_mojo.cc',
+ 'aura/window_tree_host_mojo.h',
+ 'aura/window_tree_host_mojo_delegate.h',
+ ],
+ },
+ {
+ 'target_name': 'mojo_views_support',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:base_i18n',
+ '../skia/skia.gyp:skia',
+ '../skia/skia.gyp:skia',
+ '../third_party/icu/icu.gyp:icui18n',
+ '../third_party/icu/icu.gyp:icuuc',
+ '../ui/aura/aura.gyp:aura',
+ '../ui/base/ui_base.gyp:ui_base',
+ '../ui/views/views.gyp:views',
+ '../ui/wm/wm.gyp:wm',
+ 'mojo_aura_support',
+ ],
+ 'sources': [
+ 'views/native_widget_view_manager.cc',
+ 'views/native_widget_view_manager.h',
+ 'views/views_init.cc',
+ 'views/views_init.h',
+ ],
+ },
+ ],
+ }],
],
}
diff --git a/chromium/mojo/mojo_apps.gypi b/chromium/mojo/mojo_apps.gypi
index fbcd134539b..5e1d2ca75e0 100644
--- a/chromium/mojo/mojo_apps.gypi
+++ b/chromium/mojo/mojo_apps.gypi
@@ -5,15 +5,15 @@
'type': 'static_library',
'dependencies': [
'../base/base.gyp:base',
- '../gpu/gpu.gyp:gles2_c_lib',
'../gin/gin.gyp:gin',
'../ui/gl/gl.gyp:gl',
'../v8/tools/gyp/v8.gyp:v8',
'mojo_common_lib',
+ 'mojo_environment_chromium',
'mojo_gles2',
'mojo_gles2_bindings',
+ 'mojo_js_bindings_lib',
'mojo_native_viewport_bindings',
- 'mojo_system',
],
'export_dependent_settings': [
'../base/base.gyp:base',
@@ -22,40 +22,49 @@
'mojo_gles2',
'mojo_gles2_bindings',
'mojo_native_viewport_bindings',
- 'mojo_system',
],
'sources': [
'apps/js/mojo_runner_delegate.cc',
'apps/js/mojo_runner_delegate.h',
'apps/js/bindings/threading.cc',
'apps/js/bindings/threading.h',
- 'apps/js/bindings/core.cc',
- 'apps/js/bindings/core.h',
'apps/js/bindings/gl/context.cc',
'apps/js/bindings/gl/context.h',
'apps/js/bindings/gl/module.cc',
'apps/js/bindings/gl/module.h',
- 'apps/js/bindings/gl/opaque.cc',
- 'apps/js/bindings/gl/opaque.h',
- 'apps/js/bindings/handle.cc',
- 'apps/js/bindings/handle.h',
- 'apps/js/bindings/support.cc',
- 'apps/js/bindings/support.h',
- 'apps/js/bindings/waiting_callback.cc',
- 'apps/js/bindings/waiting_callback.h',
+ 'apps/js/bindings/monotonic_clock.cc',
+ 'apps/js/bindings/monotonic_clock.h',
],
},
{
- 'target_name': 'mojo_js_unittests',
+ 'target_name': 'mojo_apps_js_bindings',
+ 'type': 'static_library',
+ 'sources': [
+ 'apps/js/test/js_to_cpp.mojom',
+ ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+ 'export_dependent_settings': [
+ 'mojo_cpp_bindings',
+ ],
+ 'dependencies': [
+ 'mojo_cpp_bindings',
+ ],
+ },
+ {
+ 'target_name': 'mojo_apps_js_unittests',
'type': 'executable',
'dependencies': [
'../gin/gin.gyp:gin_test',
+ 'mojo_apps_js_bindings',
+ 'mojo_common_lib',
+ 'mojo_common_test_support',
'mojo_js_lib',
'mojo_run_all_unittests',
- 'mojo_sample_service',
+ 'mojo_public_test_interfaces',
],
'sources': [
- 'apps/js/test/run_js_tests.cc',
+ 'apps/js/test/js_to_cpp_unittest.cc',
+ 'apps/js/test/run_apps_js_tests.cc',
],
},
{
@@ -63,10 +72,31 @@
'type': 'shared_library',
'dependencies': [
'mojo_js_lib',
+ 'mojo_system_impl',
],
'sources': [
'apps/js/main.cc',
],
},
],
+ 'conditions': [
+ ['test_isolation_mode != "noop"', {
+ 'targets': [
+ {
+ 'target_name': 'mojo_apps_js_unittests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'mojo_apps_js_unittests',
+ ],
+ 'includes': [
+ '../build/isolate.gypi',
+ 'mojo_apps_js_unittests.isolate',
+ ],
+ 'sources': [
+ 'mojo_apps_js_unittests.isolate',
+ ],
+ },
+ ],
+ }],
+ ],
}
diff --git a/chromium/mojo/mojo_apps_js_unittests.isolate b/chromium/mojo/mojo_apps_js_unittests.isolate
new file mode 100644
index 00000000000..4fdeb7296cb
--- /dev/null
+++ b/chromium/mojo/mojo_apps_js_unittests.isolate
@@ -0,0 +1,54 @@
+# Copyright 2014 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.
+{
+ 'includes': [
+ '../third_party/icu/icu.isolate',
+ ],
+ 'conditions': [
+ ['OS=="win" or OS=="mac" or OS=="linux"', {
+ 'variables': {
+ 'command': [
+ '../testing/test_env.py',
+ '<(PRODUCT_DIR)/mojo_apps_js_unittests<(EXECUTABLE_SUFFIX)',
+ '--brave-new-test-launcher',
+ '--test-launcher-bot-mode',
+ ],
+ 'isolate_dependency_tracked': [
+ '../gin/test/expect.js',
+ '../testing/test_env.py',
+ '<(PRODUCT_DIR)/mojo_apps_js_unittests<(EXECUTABLE_SUFFIX)',
+ ],
+ 'isolate_dependency_untracked': [
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/',
+ 'apps/js/',
+ 'bindings/js/',
+ 'public/js/bindings/',
+ ],
+ },
+ }],
+ ['OS=="win"', {
+ 'variables': {
+ 'isolate_dependency_tracked': [
+ '<(PRODUCT_DIR)/mojo_system.dll',
+ '<(PRODUCT_DIR)/mojo_test_support.dll',
+ ],
+ },
+ }],
+ ['OS=="linux"', {
+ 'variables': {
+ 'isolate_dependency_tracked': [
+ '<(PRODUCT_DIR)/lib/libmojo_test_support.so',
+ ],
+ },
+ }],
+ ['OS=="mac"', {
+ 'variables': {
+ 'isolate_dependency_tracked': [
+ '<(PRODUCT_DIR)/libmojo_gles2.dylib',
+ '<(PRODUCT_DIR)/libmojo_test_support.dylib',
+ ],
+ },
+ }],
+ ],
+}
diff --git a/chromium/mojo/mojo_examples.gypi b/chromium/mojo/mojo_examples.gypi
index dbc46109b33..254d5faea3a 100644
--- a/chromium/mojo/mojo_examples.gypi
+++ b/chromium/mojo/mojo_examples.gypi
@@ -1,27 +1,32 @@
+# Copyright 2013 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.
+
{
'targets': [
{
'target_name': 'mojo_sample_app',
'type': 'shared_library',
'dependencies': [
- '../base/base.gyp:base',
- '../gpu/gpu.gyp:gles2_c_lib',
- '../ui/gfx/gfx.gyp:gfx',
- '../ui/gl/gl.gyp:gl',
- 'mojo_common_lib',
+ # TODO(darin): we should not be linking against these libraries!
+ '../ui/events/events.gyp:events',
+ '../ui/gfx/gfx.gyp:gfx_geometry',
+ 'mojo_application',
+ 'mojo_cpp_bindings',
+ 'mojo_environment_standalone',
+ 'mojo_geometry_bindings',
'mojo_gles2',
- 'mojo_gles2_bindings',
'mojo_native_viewport_bindings',
'mojo_system',
+ 'mojo_utility',
],
'sources': [
'examples/sample_app/gles2_client_impl.cc',
'examples/sample_app/gles2_client_impl.cc',
- 'examples/sample_app/native_viewport_client_impl.cc',
- 'examples/sample_app/native_viewport_client_impl.h',
'examples/sample_app/sample_app.cc',
'examples/sample_app/spinning_cube.cc',
'examples/sample_app/spinning_cube.h',
+ 'public/cpp/application/lib/mojo_main_standalone.cc',
],
},
{
@@ -32,31 +37,430 @@
'includes': [ 'build/package_app.gypi' ],
},
{
- 'target_name': 'mojo_hello_world_bindings',
- 'type': 'static_library',
+ 'target_name': 'mojo_compositor_app',
+ 'type': 'shared_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../cc/cc.gyp:cc',
+ '../ui/gfx/gfx.gyp:gfx',
+ '../ui/gfx/gfx.gyp:gfx_geometry',
+ 'mojo_application',
+ 'mojo_cc_support',
+ 'mojo_common_lib',
+ 'mojo_environment_chromium',
+ 'mojo_geometry_bindings',
+ 'mojo_geometry_lib',
+ 'mojo_gles2',
+ 'mojo_native_viewport_bindings',
+ 'mojo_system_impl',
+ ],
'sources': [
- 'examples/hello_world_service/hello_world_service.mojom',
+ 'examples/compositor_app/compositor_app.cc',
+ 'examples/compositor_app/compositor_host.cc',
+ 'examples/compositor_app/compositor_host.h',
+ 'public/cpp/application/lib/mojo_main_chromium.cc',
],
- 'includes': [ 'public/bindings/mojom_bindings_generator.gypi' ],
- 'export_dependent_settings': [
- 'mojo_bindings',
+ },
+ {
+ 'target_name': 'package_mojo_compositor_app',
+ 'variables': {
+ 'app_name': 'mojo_compositor_app',
+ },
+ 'includes': [ 'build/package_app.gypi' ],
+ },
+ {
+ 'target_name': 'mojo_wget',
+ 'type': 'shared_library',
+ 'dependencies': [
+ 'mojo_application',
+ 'mojo_cpp_bindings',
+ 'mojo_environment_standalone',
+ 'mojo_network_bindings',
'mojo_system',
+ 'mojo_utility',
+ ],
+ 'sources': [
+ 'examples/wget/wget.cc',
+ 'public/cpp/application/lib/mojo_main_standalone.cc',
+ ],
+ },
+ {
+ 'target_name': 'package_mojo_wget',
+ 'variables': {
+ 'app_name': 'mojo_wget',
+ },
+ 'includes': [ 'build/package_app.gypi' ],
+ },
+ {
+ 'target_name': 'mojo_html_viewer',
+ 'type': 'shared_library',
+ 'dependencies': [
+ '../net/net.gyp:net',
+ '../skia/skia.gyp:skia',
+ '../third_party/WebKit/public/blink.gyp:blink',
+ '../url/url.gyp:url_lib',
+ 'mojo_application',
+ 'mojo_cpp_bindings',
+ 'mojo_environment_chromium',
+ 'mojo_navigation_bindings',
+ 'mojo_network_bindings',
+ 'mojo_launcher_bindings',
+ 'mojo_system_impl',
+ 'mojo_utility',
+ 'mojo_view_manager_lib',
+ ],
+ 'include_dirs': [
+ 'third_party/WebKit'
+ ],
+ 'sources': [
+ 'examples/html_viewer/blink_platform_impl.cc',
+ 'examples/html_viewer/blink_platform_impl.h',
+ 'examples/html_viewer/html_viewer.cc',
+ 'examples/html_viewer/html_document_view.cc',
+ 'examples/html_viewer/html_document_view.h',
+ 'examples/html_viewer/webmimeregistry_impl.cc',
+ 'examples/html_viewer/webmimeregistry_impl.h',
+ 'examples/html_viewer/webthread_impl.cc',
+ 'examples/html_viewer/webthread_impl.h',
+ 'examples/html_viewer/weburlloader_impl.cc',
+ 'examples/html_viewer/weburlloader_impl.h',
+ 'public/cpp/application/lib/mojo_main_chromium.cc',
],
},
{
- 'target_name': 'mojo_hello_world_service',
- 'type': 'static_library',
+ 'target_name': 'mojo_image_viewer',
+ 'type': 'shared_library',
+ 'dependencies': [
+ '../skia/skia.gyp:skia',
+ '../ui/gfx/gfx.gyp:gfx',
+ 'mojo_application',
+ 'mojo_cpp_bindings',
+ 'mojo_environment_chromium',
+ 'mojo_navigation_bindings',
+ 'mojo_network_bindings',
+ 'mojo_launcher_bindings',
+ 'mojo_system_impl',
+ 'mojo_utility',
+ 'mojo_view_manager_lib',
+ ],
+ 'sources': [
+ 'examples/image_viewer/image_viewer.cc',
+ 'public/cpp/application/lib/mojo_main_chromium.cc',
+ ],
+ },
+ {
+ 'target_name': 'mojo_pepper_container_app',
+ 'type': 'shared_library',
'dependencies': [
'../base/base.gyp:base',
- 'mojo_hello_world_bindings',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '../gpu/gpu.gyp:command_buffer_common',
+ '../ppapi/ppapi.gyp:ppapi_c',
+ '../ppapi/ppapi_internal.gyp:ppapi_example_gles2_spinning_cube',
+ '../ui/events/events.gyp:events_base',
+ 'mojo_application',
+ 'mojo_common_lib',
+ 'mojo_environment_chromium',
+ 'mojo_geometry_bindings',
+ 'mojo_gles2',
+ 'mojo_native_viewport_bindings',
+ 'mojo_system_impl',
],
- 'export_dependent_settings': [
- 'mojo_hello_world_bindings',
+ 'defines': [
+ # We don't really want to export. We could change how
+ # ppapi_{shared,thunk}_export.h are defined to avoid this.
+ 'PPAPI_SHARED_IMPLEMENTATION',
+ 'PPAPI_THUNK_IMPLEMENTATION',
],
'sources': [
- 'examples/hello_world_service/hello_world_service_impl.cc',
- 'examples/hello_world_service/hello_world_service_impl.h',
+ # Source files from ppapi/.
+ # An alternative is to depend on
+ # '../ppapi/ppapi_internal.gyp:ppapi_shared', but that target includes
+ # a lot of things that we don't need.
+ # TODO(yzshen): Consider extracting these files into a separate target
+ # which mojo_pepper_container_app and ppapi_shared both depend on.
+ '../ppapi/shared_impl/api_id.h',
+ '../ppapi/shared_impl/callback_tracker.cc',
+ '../ppapi/shared_impl/callback_tracker.h',
+ '../ppapi/shared_impl/host_resource.cc',
+ '../ppapi/shared_impl/host_resource.h',
+ '../ppapi/shared_impl/id_assignment.cc',
+ '../ppapi/shared_impl/id_assignment.h',
+ '../ppapi/shared_impl/ppapi_globals.cc',
+ '../ppapi/shared_impl/ppapi_globals.h',
+ '../ppapi/shared_impl/ppapi_shared_export.h',
+ '../ppapi/shared_impl/ppb_message_loop_shared.cc',
+ '../ppapi/shared_impl/ppb_message_loop_shared.h',
+ '../ppapi/shared_impl/ppb_view_shared.cc',
+ '../ppapi/shared_impl/ppb_view_shared.h',
+ '../ppapi/shared_impl/proxy_lock.cc',
+ '../ppapi/shared_impl/proxy_lock.h',
+ '../ppapi/shared_impl/resource.cc',
+ '../ppapi/shared_impl/resource.h',
+ '../ppapi/shared_impl/resource_tracker.cc',
+ '../ppapi/shared_impl/resource_tracker.h',
+ '../ppapi/shared_impl/scoped_pp_resource.cc',
+ '../ppapi/shared_impl/scoped_pp_resource.h',
+ '../ppapi/shared_impl/singleton_resource_id.h',
+ '../ppapi/shared_impl/tracked_callback.cc',
+ '../ppapi/shared_impl/tracked_callback.h',
+ '../ppapi/thunk/enter.cc',
+ '../ppapi/thunk/enter.h',
+ '../ppapi/thunk/interfaces_ppb_private.h',
+ '../ppapi/thunk/interfaces_ppb_private_flash.h',
+ '../ppapi/thunk/interfaces_ppb_private_no_permissions.h',
+ '../ppapi/thunk/interfaces_ppb_public_dev.h',
+ '../ppapi/thunk/interfaces_ppb_public_dev_channel.h',
+ '../ppapi/thunk/interfaces_ppb_public_stable.h',
+ '../ppapi/thunk/interfaces_preamble.h',
+ '../ppapi/thunk/ppapi_thunk_export.h',
+ '../ppapi/thunk/ppb_graphics_3d_api.h',
+ '../ppapi/thunk/ppb_graphics_3d_thunk.cc',
+ '../ppapi/thunk/ppb_instance_api.h',
+ '../ppapi/thunk/ppb_instance_thunk.cc',
+ '../ppapi/thunk/ppb_message_loop_api.h',
+ '../ppapi/thunk/ppb_view_api.h',
+ '../ppapi/thunk/ppb_view_thunk.cc',
+ '../ppapi/thunk/resource_creation_api.h',
+ '../ppapi/thunk/thunk.h',
+
+ 'examples/pepper_container_app/graphics_3d_resource.cc',
+ 'examples/pepper_container_app/graphics_3d_resource.h',
+ 'examples/pepper_container_app/interface_list.cc',
+ 'examples/pepper_container_app/interface_list.h',
+ 'examples/pepper_container_app/mojo_ppapi_globals.cc',
+ 'examples/pepper_container_app/mojo_ppapi_globals.h',
+ 'examples/pepper_container_app/pepper_container_app.cc',
+ 'examples/pepper_container_app/plugin_instance.cc',
+ 'examples/pepper_container_app/plugin_instance.h',
+ 'examples/pepper_container_app/plugin_module.cc',
+ 'examples/pepper_container_app/plugin_module.h',
+ 'examples/pepper_container_app/ppb_core_thunk.cc',
+ 'examples/pepper_container_app/ppb_opengles2_thunk.cc',
+ 'examples/pepper_container_app/resource_creation_impl.cc',
+ 'examples/pepper_container_app/resource_creation_impl.h',
+ 'examples/pepper_container_app/thunk.h',
+ 'examples/pepper_container_app/type_converters.h',
+ 'public/cpp/application/lib/mojo_main_chromium.cc',
],
},
],
+ 'conditions': [
+ ['use_aura==1', {
+ 'targets': [
+ {
+ 'target_name': 'mojo_aura_demo',
+ 'type': 'shared_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../cc/cc.gyp:cc',
+ '../ui/aura/aura.gyp:aura',
+ '../ui/base/ui_base.gyp:ui_base',
+ '../ui/compositor/compositor.gyp:compositor',
+ '../ui/gfx/gfx.gyp:gfx',
+ '../ui/gfx/gfx.gyp:gfx_geometry',
+ 'mojo_application',
+ 'mojo_aura_support',
+ 'mojo_common_lib',
+ 'mojo_environment_chromium',
+ 'mojo_geometry_bindings',
+ 'mojo_geometry_lib',
+ 'mojo_system_impl',
+ 'mojo_view_manager_lib',
+ ],
+ 'sources': [
+ 'examples/aura_demo/aura_demo.cc',
+ 'public/cpp/application/lib/mojo_main_chromium.cc',
+ ],
+ },
+ {
+ 'target_name': 'mojo_aura_demo_init',
+ 'type': 'shared_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'mojo_application',
+ 'mojo_environment_chromium',
+ 'mojo_system_impl',
+ 'mojo_view_manager_bindings',
+ ],
+ 'sources': [
+ 'examples/aura_demo/view_manager_init.cc',
+ 'public/cpp/application/lib/mojo_main_chromium.cc',
+ ],
+ },
+ {
+ 'target_name': 'mojo_browser',
+ 'type': 'shared_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../cc/cc.gyp:cc',
+ '../third_party/icu/icu.gyp:icui18n',
+ '../third_party/icu/icu.gyp:icuuc',
+ '../ui/aura/aura.gyp:aura',
+ '../ui/base/ui_base.gyp:ui_base',
+ '../ui/compositor/compositor.gyp:compositor',
+ '../ui/gfx/gfx.gyp:gfx',
+ '../ui/gfx/gfx.gyp:gfx_geometry',
+ '../ui/resources/ui_resources.gyp:ui_resources',
+ '../ui/resources/ui_resources.gyp:ui_test_pak',
+ '../ui/views/views.gyp:views',
+ '../url/url.gyp:url_lib',
+ 'mojo_application',
+ 'mojo_aura_support',
+ 'mojo_common_lib',
+ 'mojo_environment_chromium',
+ 'mojo_geometry_bindings',
+ 'mojo_geometry_lib',
+ 'mojo_input_events_lib',
+ 'mojo_navigation_bindings',
+ 'mojo_system_impl',
+ 'mojo_views_support',
+ 'mojo_view_manager_bindings',
+ 'mojo_view_manager_lib',
+ ],
+ 'sources': [
+ 'examples/browser/browser.cc',
+ 'public/cpp/application/lib/mojo_main_chromium.cc',
+ ],
+ },
+ {
+ 'target_name': 'package_mojo_aura_demo',
+ 'variables': {
+ 'app_name': 'mojo_aura_demo',
+ },
+ 'includes': [ 'build/package_app.gypi' ],
+ },
+ {
+ 'target_name': 'mojo_demo_launcher',
+ 'type': 'shared_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../skia/skia.gyp:skia',
+ '../ui/gfx/gfx.gyp:gfx',
+ '../ui/gfx/gfx.gyp:gfx_geometry',
+ '../ui/gl/gl.gyp:gl',
+ 'mojo_application',
+ 'mojo_cpp_bindings',
+ 'mojo_environment_chromium',
+ 'mojo_geometry_bindings',
+ 'mojo_gles2',
+ 'mojo_view_manager_bindings',
+ 'mojo_system_impl',
+ 'mojo_utility',
+ ],
+ 'sources': [
+ 'examples/demo_launcher/demo_launcher.cc',
+ 'public/cpp/application/lib/mojo_main_chromium.cc',
+ ],
+ },
+ {
+ 'target_name': 'mojo_window_manager_bindings',
+ 'type': 'static_library',
+ 'sources': [
+ 'examples/window_manager/window_manager.mojom',
+ ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+ 'export_dependent_settings': [
+ 'mojo_cpp_bindings',
+ ],
+ 'dependencies': [
+ 'mojo_cpp_bindings',
+ ],
+ },
+ {
+ 'target_name': 'mojo_window_manager',
+ 'type': 'shared_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../ui/gfx/gfx.gyp:gfx_geometry',
+ '../ui/gl/gl.gyp:gl',
+ 'mojo_application',
+ 'mojo_cpp_bindings',
+ 'mojo_environment_chromium',
+ 'mojo_geometry_bindings',
+ 'mojo_gles2',
+ 'mojo_launcher_bindings',
+ 'mojo_navigation_bindings',
+ 'mojo_view_manager_lib',
+ 'mojo_window_manager_bindings',
+ 'mojo_system_impl',
+ 'mojo_utility',
+ ],
+ 'sources': [
+ 'examples/window_manager/window_manager.cc',
+ 'public/cpp/application/lib/mojo_main_chromium.cc',
+ ],
+ },
+ {
+ 'target_name': 'mojo_embedded_app',
+ 'type': 'shared_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../ui/gfx/gfx.gyp:gfx_geometry',
+ '../ui/gl/gl.gyp:gl',
+ '../url/url.gyp:url_lib',
+ 'mojo_application',
+ 'mojo_cpp_bindings',
+ 'mojo_environment_chromium',
+ 'mojo_geometry_bindings',
+ 'mojo_gles2',
+ 'mojo_navigation_bindings',
+ 'mojo_view_manager_lib',
+ 'mojo_window_manager_bindings',
+ 'mojo_system_impl',
+ 'mojo_utility',
+ ],
+ 'sources': [
+ 'examples/embedded_app/embedded_app.cc',
+ 'public/cpp/application/lib/mojo_main_chromium.cc',
+ ],
+ },
+ {
+ 'target_name': 'mojo_nesting_app',
+ 'type': 'shared_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../ui/gfx/gfx.gyp:gfx_geometry',
+ '../ui/gl/gl.gyp:gl',
+ '../url/url.gyp:url_lib',
+ 'mojo_application',
+ 'mojo_cpp_bindings',
+ 'mojo_environment_chromium',
+ 'mojo_geometry_bindings',
+ 'mojo_gles2',
+ 'mojo_navigation_bindings',
+ 'mojo_view_manager_lib',
+ 'mojo_window_manager_bindings',
+ 'mojo_system_impl',
+ 'mojo_utility',
+ ],
+ 'sources': [
+ 'examples/nesting_app/nesting_app.cc',
+ 'public/cpp/application/lib/mojo_main_chromium.cc',
+ ],
+ },
+ ],
+ }],
+ ['OS=="linux"', {
+ 'targets': [
+ {
+ 'target_name': 'mojo_dbus_echo',
+ 'type': 'shared_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'mojo_application',
+ 'mojo_cpp_bindings',
+ 'mojo_environment_standalone',
+ 'mojo_echo_bindings',
+ 'mojo_system',
+ 'mojo_utility',
+ ],
+ 'sources': [
+ 'examples/dbus_echo/dbus_echo_app.cc',
+ 'public/cpp/application/lib/mojo_main_standalone.cc',
+ ],
+ },
+ ],
+ }],
+ ],
}
diff --git a/chromium/mojo/mojo_js_unittests.isolate b/chromium/mojo/mojo_js_unittests.isolate
new file mode 100644
index 00000000000..6ceaab17342
--- /dev/null
+++ b/chromium/mojo/mojo_js_unittests.isolate
@@ -0,0 +1,52 @@
+# Copyright 2014 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.
+{
+ 'includes': [
+ '../third_party/icu/icu.isolate',
+ ],
+ 'conditions': [
+ ['OS=="win" or OS=="mac" or OS=="linux"', {
+ 'variables': {
+ 'command': [
+ '../testing/test_env.py',
+ '<(PRODUCT_DIR)/mojo_js_unittests<(EXECUTABLE_SUFFIX)',
+ '--brave-new-test-launcher',
+ '--test-launcher-bot-mode',
+ ],
+ 'isolate_dependency_tracked': [
+ '../gin/test/expect.js',
+ '../testing/test_env.py',
+ '<(PRODUCT_DIR)/mojo_js_unittests<(EXECUTABLE_SUFFIX)',
+ ],
+ 'isolate_dependency_untracked': [
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/',
+ 'bindings/js/',
+ 'public/js/bindings/',
+ ],
+ },
+ }],
+ ['OS=="win"', {
+ 'variables': {
+ 'isolate_dependency_tracked': [
+ '<(PRODUCT_DIR)/mojo_system.dll',
+ '<(PRODUCT_DIR)/mojo_test_support.dll',
+ ],
+ },
+ }],
+ ['OS=="linux"', {
+ 'variables': {
+ 'isolate_dependency_tracked': [
+ '<(PRODUCT_DIR)/lib/libmojo_test_support.so',
+ ],
+ },
+ }],
+ ['OS=="mac"', {
+ 'variables': {
+ 'isolate_dependency_tracked': [
+ '<(PRODUCT_DIR)/libmojo_test_support.dylib',
+ ],
+ },
+ }],
+ ],
+}
diff --git a/chromium/mojo/mojo_public.gypi b/chromium/mojo/mojo_public.gypi
index f3b1cd855b0..2690c7c773b 100644
--- a/chromium/mojo/mojo_public.gypi
+++ b/chromium/mojo/mojo_public.gypi
@@ -2,7 +2,7 @@
'targets': [
{
'target_name': 'mojo_system',
- 'type': 'shared_library',
+ 'type': 'static_library',
'defines': [
'MOJO_SYSTEM_IMPLEMENTATION',
],
@@ -14,13 +14,28 @@
'..',
],
},
+ 'all_dependent_settings': {
+ 'conditions': [
+ # We need to be able to call the MojoSetSystemThunks() function in
+ # system_thunks.cc
+ ['OS=="android"', {
+ 'ldflags!': [
+ '-Wl,--exclude-libs=ALL',
+ ],
+ }],
+ ],
+ },
'sources': [
- 'public/system/core.h',
- 'public/system/core_cpp.h',
- 'public/system/core_private.cc',
- 'public/system/core_private.h',
- 'public/system/macros.h',
- 'public/system/system_export.h',
+ 'public/c/system/buffer.h',
+ 'public/c/system/core.h',
+ 'public/c/system/data_pipe.h',
+ 'public/c/system/functions.h',
+ 'public/c/system/macros.h',
+ 'public/c/system/message_pipe.h',
+ 'public/c/system/system_export.h',
+ 'public/c/system/types.h',
+ 'public/platform/native/system_thunks.cc',
+ 'public/platform/native/system_thunks.h',
],
},
{
@@ -28,165 +43,400 @@
'type': 'shared_library',
'defines': [
'MOJO_GLES2_IMPLEMENTATION',
+ 'GLES2_USE_MOJO',
],
'include_dirs': [
'..',
],
+ 'dependencies': [
+ '../third_party/khronos/khronos.gyp:khronos_headers'
+ ],
'direct_dependent_settings': {
'include_dirs': [
'..',
],
+ 'defines': [
+ 'GLES2_USE_MOJO',
+ ],
},
'sources': [
- 'public/gles2/gles2.h',
+ 'public/c/gles2/gles2.h',
+ 'public/c/gles2/gles2_export.h',
'public/gles2/gles2_private.cc',
'public/gles2/gles2_private.h',
],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'xcode_settings': {
+ # Make it a run-path dependent library.
+ 'DYLIB_INSTALL_NAME_BASE': '@loader_path',
+ },
+ }],
+ ],
},
{
- 'target_name': 'mojo_public_test_support',
+ 'target_name': 'mojo_test_support',
+ 'type': 'shared_library',
+ 'defines': [
+ 'MOJO_TEST_SUPPORT_IMPLEMENTATION',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ 'sources': [
+ 'public/c/test_support/test_support.h',
+ 'public/c/test_support/test_support_export.h',
+ 'public/tests/test_support_private.cc',
+ 'public/tests/test_support_private.h',
+ ],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'xcode_settings': {
+ # Make it a run-path dependent library.
+ 'DYLIB_INSTALL_NAME_BASE': '@loader_path',
+ },
+ }],
+ ],
+ },
+ {
+ 'target_name': 'mojo_public_test_utils',
'type': 'static_library',
'dependencies': [
'../base/base.gyp:base',
'../testing/gtest.gyp:gtest',
- 'mojo_system',
+ 'mojo_test_support',
+ ],
+ 'sources': [
+ 'public/cpp/test_support/lib/test_support.cc',
+ 'public/cpp/test_support/lib/test_utils.cc',
+ 'public/cpp/test_support/test_utils.h',
+ ],
+ },
+ # TODO(vtl): Reorganize the mojo_public_*_unittests.
+ {
+ 'target_name': 'mojo_public_bindings_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../testing/gtest.gyp:gtest',
+ 'mojo_cpp_bindings',
+ 'mojo_environment_standalone',
+ 'mojo_public_test_utils',
+ 'mojo_run_all_unittests',
+ 'mojo_public_test_interfaces',
+ 'mojo_utility',
+ ],
+ 'sources': [
+ 'public/cpp/bindings/tests/array_unittest.cc',
+ 'public/cpp/bindings/tests/bounds_checker_unittest.cc',
+ 'public/cpp/bindings/tests/buffer_unittest.cc',
+ 'public/cpp/bindings/tests/connector_unittest.cc',
+ 'public/cpp/bindings/tests/handle_passing_unittest.cc',
+ 'public/cpp/bindings/tests/interface_ptr_unittest.cc',
+ 'public/cpp/bindings/tests/request_response_unittest.cc',
+ 'public/cpp/bindings/tests/router_unittest.cc',
+ 'public/cpp/bindings/tests/sample_service_unittest.cc',
+ 'public/cpp/bindings/tests/string_unittest.cc',
+ 'public/cpp/bindings/tests/struct_unittest.cc',
+ 'public/cpp/bindings/tests/type_conversion_unittest.cc',
+ 'public/cpp/bindings/tests/validation_test_input_parser.cc',
+ 'public/cpp/bindings/tests/validation_test_input_parser.h',
+ 'public/cpp/bindings/tests/validation_unittest.cc',
+ ],
+ },
+ {
+ 'target_name': 'mojo_public_environment_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../testing/gtest.gyp:gtest',
+ 'mojo_environment_standalone',
+ 'mojo_public_test_utils',
+ 'mojo_run_all_unittests',
+ 'mojo_utility',
+ ],
+ 'sources': [
+ 'public/cpp/environment/tests/async_waiter_unittest.cc',
+ 'public/cpp/environment/tests/logger_unittest.cc',
+ 'public/cpp/environment/tests/logging_unittest.cc',
+ ],
+ },
+ {
+ 'target_name': 'mojo_public_system_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../testing/gtest.gyp:gtest',
+ 'mojo_cpp_bindings',
+ 'mojo_public_test_utils',
+ 'mojo_run_all_unittests',
],
'sources': [
- 'public/tests/simple_bindings_support.cc',
- 'public/tests/simple_bindings_support.h',
- 'public/tests/test_support.cc',
- 'public/tests/test_support.h',
+ 'public/c/system/tests/core_unittest.cc',
+ 'public/c/system/tests/core_unittest_pure_c.c',
+ 'public/c/system/tests/macros_unittest.cc',
+ 'public/cpp/system/tests/core_unittest.cc',
+ 'public/cpp/system/tests/macros_unittest.cc',
],
},
{
- 'target_name': 'mojo_public_unittests',
+ 'target_name': 'mojo_public_utility_unittests',
'type': 'executable',
'dependencies': [
+ '../base/base.gyp:base',
'../testing/gtest.gyp:gtest',
- 'mojo_bindings',
- 'mojo_public_test_support',
+ 'mojo_cpp_bindings',
+ 'mojo_public_test_utils',
'mojo_run_all_unittests',
- 'mojo_system',
+ 'mojo_utility',
],
'sources': [
- 'public/tests/bindings_array_unittest.cc',
- 'public/tests/bindings_connector_unittest.cc',
- 'public/tests/bindings_handle_passing_unittest.cc',
- 'public/tests/bindings_remote_ptr_unittest.cc',
- 'public/tests/bindings_type_conversion_unittest.cc',
- 'public/tests/buffer_unittest.cc',
- 'public/tests/math_calculator.mojom',
- 'public/tests/sample_factory.mojom',
- 'public/tests/system_core_cpp_unittest.cc',
- 'public/tests/system_core_unittest.cc',
- 'public/tests/test_structs.mojom',
+ 'public/cpp/utility/tests/mutex_unittest.cc',
+ 'public/cpp/utility/tests/run_loop_unittest.cc',
+ 'public/cpp/utility/tests/thread_unittest.cc',
+ ],
+ 'conditions': [
+ # See crbug.com/342893:
+ ['OS=="win"', {
+ 'sources!': [
+ 'public/cpp/utility/tests/mutex_unittest.cc',
+ 'public/cpp/utility/tests/thread_unittest.cc',
+ ],
+ }],
],
- 'includes': [ 'public/bindings/mojom_bindings_generator.gypi' ],
},
{
- 'target_name': 'mojo_public_perftests',
+ 'target_name': 'mojo_public_system_perftests',
'type': 'executable',
'dependencies': [
'../base/base.gyp:base',
'../testing/gtest.gyp:gtest',
- 'mojo_public_test_support',
+ 'mojo_public_test_utils',
'mojo_run_all_perftests',
- 'mojo_system',
+ 'mojo_utility',
],
'sources': [
- 'public/tests/system_core_perftest.cc',
+ 'public/c/system/tests/core_perftest.cc',
],
},
{
- 'target_name': 'mojo_bindings',
+ # GN version: //mojo/public/cpp/bindings
+ 'target_name': 'mojo_cpp_bindings',
'type': 'static_library',
'include_dirs': [
'..'
],
'sources': [
- 'public/bindings/lib/array.cc',
- 'public/bindings/lib/array.h',
- 'public/bindings/lib/array_internal.h',
- 'public/bindings/lib/array_internal.cc',
- 'public/bindings/lib/bindings.h',
- 'public/bindings/lib/bindings_internal.h',
- 'public/bindings/lib/bindings_serialization.cc',
- 'public/bindings/lib/bindings_serialization.h',
- 'public/bindings/lib/bindings_support.cc',
- 'public/bindings/lib/bindings_support.h',
- 'public/bindings/lib/buffer.cc',
- 'public/bindings/lib/buffer.h',
- 'public/bindings/lib/connector.cc',
- 'public/bindings/lib/connector.h',
- 'public/bindings/lib/message.cc',
- 'public/bindings/lib/message.h',
- 'public/bindings/lib/message_builder.cc',
- 'public/bindings/lib/message_builder.h',
- 'public/bindings/lib/message_queue.cc',
- 'public/bindings/lib/message_queue.h',
- ],
- },
- {
- 'target_name': 'mojo_sample_service',
+ 'public/cpp/bindings/array.h',
+ 'public/cpp/bindings/callback.h',
+ 'public/cpp/bindings/error_handler.h',
+ 'public/cpp/bindings/interface_impl.h',
+ 'public/cpp/bindings/interface_ptr.h',
+ 'public/cpp/bindings/interface_request.h',
+ 'public/cpp/bindings/message.h',
+ 'public/cpp/bindings/message_filter.h',
+ 'public/cpp/bindings/no_interface.h',
+ 'public/cpp/bindings/string.h',
+ 'public/cpp/bindings/sync_dispatcher.h',
+ 'public/cpp/bindings/type_converter.h',
+ 'public/cpp/bindings/lib/array_internal.h',
+ 'public/cpp/bindings/lib/array_internal.cc',
+ 'public/cpp/bindings/lib/array_serialization.h',
+ 'public/cpp/bindings/lib/bindings_internal.h',
+ 'public/cpp/bindings/lib/bindings_serialization.cc',
+ 'public/cpp/bindings/lib/bindings_serialization.h',
+ 'public/cpp/bindings/lib/bounds_checker.cc',
+ 'public/cpp/bindings/lib/bounds_checker.h',
+ 'public/cpp/bindings/lib/buffer.h',
+ 'public/cpp/bindings/lib/callback_internal.h',
+ 'public/cpp/bindings/lib/connector.cc',
+ 'public/cpp/bindings/lib/connector.h',
+ 'public/cpp/bindings/lib/filter_chain.cc',
+ 'public/cpp/bindings/lib/filter_chain.h',
+ 'public/cpp/bindings/lib/fixed_buffer.cc',
+ 'public/cpp/bindings/lib/fixed_buffer.h',
+ 'public/cpp/bindings/lib/interface_impl_internal.h',
+ 'public/cpp/bindings/lib/interface_ptr_internal.h',
+ 'public/cpp/bindings/lib/message.cc',
+ 'public/cpp/bindings/lib/message_builder.cc',
+ 'public/cpp/bindings/lib/message_builder.h',
+ 'public/cpp/bindings/lib/message_filter.cc',
+ 'public/cpp/bindings/lib/message_header_validator.cc',
+ 'public/cpp/bindings/lib/message_header_validator.h',
+ 'public/cpp/bindings/lib/message_internal.h',
+ 'public/cpp/bindings/lib/message_queue.cc',
+ 'public/cpp/bindings/lib/message_queue.h',
+ 'public/cpp/bindings/lib/no_interface.cc',
+ 'public/cpp/bindings/lib/router.cc',
+ 'public/cpp/bindings/lib/router.h',
+ 'public/cpp/bindings/lib/shared_data.h',
+ 'public/cpp/bindings/lib/shared_ptr.h',
+ 'public/cpp/bindings/lib/string_serialization.h',
+ 'public/cpp/bindings/lib/string_serialization.cc',
+ 'public/cpp/bindings/lib/sync_dispatcher.cc',
+ 'public/cpp/bindings/lib/validation_errors.cc',
+ 'public/cpp/bindings/lib/validation_errors.h',
+ ],
+ },
+ {
+ # GN version: //mojo/public/js/bindings
+ 'target_name': 'mojo_js_bindings',
'type': 'static_library',
+ 'include_dirs': [
+ '..'
+ ],
'sources': [
- 'public/bindings/sample/sample_service.mojom',
+ 'public/js/bindings/constants.cc',
+ 'public/js/bindings/constants.h',
],
- 'includes': [ 'public/bindings/mojom_bindings_generator.gypi' ],
+ },
+ {
+ 'target_name': 'mojo_public_test_interfaces',
+ 'type': 'static_library',
+ 'sources': [
+ 'public/interfaces/bindings/tests/math_calculator.mojom',
+ 'public/interfaces/bindings/tests/sample_factory.mojom',
+ 'public/interfaces/bindings/tests/sample_import.mojom',
+ 'public/interfaces/bindings/tests/sample_import2.mojom',
+ 'public/interfaces/bindings/tests/sample_interfaces.mojom',
+ 'public/interfaces/bindings/tests/sample_service.mojom',
+ 'public/interfaces/bindings/tests/test_structs.mojom',
+ 'public/interfaces/bindings/tests/validation_test_interfaces.mojom',
+ ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
'export_dependent_settings': [
- 'mojo_bindings',
- 'mojo_system',
+ 'mojo_cpp_bindings',
+ ],
+ 'dependencies': [
+ 'mojo_cpp_bindings',
],
},
{
- 'target_name': 'mojo_bindings_unittests',
- 'type': 'executable',
+ 'target_name': 'mojo_environment_standalone',
+ 'type': 'static_library',
'sources': [
- 'public/bindings/sample/sample_service_unittests.cc',
+ 'public/c/environment/async_waiter.h',
+ 'public/c/environment/logger.h',
+ 'public/c/environment/logging.h',
+ 'public/cpp/environment/environment.h',
+ 'public/cpp/environment/lib/default_async_waiter.cc',
+ 'public/cpp/environment/lib/default_async_waiter.h',
+ 'public/cpp/environment/lib/default_logger.cc',
+ 'public/cpp/environment/lib/default_logger.h',
+ 'public/cpp/environment/lib/environment.cc',
+ 'public/cpp/environment/lib/logging.cc',
],
- 'dependencies': [
- '../testing/gtest.gyp:gtest',
- 'mojo_public_test_support',
- 'mojo_run_all_unittests',
- 'mojo_sample_service',
+ 'include_dirs': [
+ '..',
],
},
{
'target_name': 'mojo_utility',
'type': 'static_library',
'sources': [
- 'public/utility/bindings_support_impl.cc',
- 'public/utility/bindings_support_impl.h',
- 'public/utility/environment.cc',
- 'public/utility/environment.h',
- 'public/utility/run_loop.cc',
- 'public/utility/run_loop.h',
- 'public/utility/run_loop_handler.h',
- 'public/utility/thread_local.h',
- 'public/utility/thread_local_posix.cc',
- 'public/utility/thread_local_win.cc',
+ 'public/cpp/utility/mutex.h',
+ 'public/cpp/utility/run_loop.h',
+ 'public/cpp/utility/run_loop_handler.h',
+ 'public/cpp/utility/thread.h',
+ 'public/cpp/utility/lib/mutex.cc',
+ 'public/cpp/utility/lib/run_loop.cc',
+ 'public/cpp/utility/lib/thread.cc',
+ 'public/cpp/utility/lib/thread_local.h',
+ 'public/cpp/utility/lib/thread_local_posix.cc',
+ 'public/cpp/utility/lib/thread_local_win.cc',
+ ],
+ 'conditions': [
+ # See crbug.com/342893:
+ ['OS=="win"', {
+ 'sources!': [
+ 'public/cpp/utility/mutex.h',
+ 'public/cpp/utility/thread.h',
+ 'public/cpp/utility/lib/mutex.cc',
+ 'public/cpp/utility/lib/thread.cc',
+ ],
+ }],
],
'include_dirs': [
'..',
],
},
{
- 'target_name': 'mojo_utility_unittests',
- 'type': 'executable',
+ # GN version: //mojo/public/interfaces/interface_provider:interface_provider
+ 'target_name': 'mojo_interface_provider_bindings',
+ 'type': 'static_library',
+ 'sources': [
+ 'public/interfaces/interface_provider/interface_provider.mojom',
+ ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
'dependencies': [
- '../base/base.gyp:base',
- '../testing/gtest.gyp:gtest',
- 'mojo_bindings',
- 'mojo_public_test_support',
- 'mojo_run_all_unittests',
- 'mojo_system',
- 'mojo_utility',
+ 'mojo_cpp_bindings',
],
+ 'export_dependent_settings': [
+ 'mojo_cpp_bindings',
+ ],
+ },
+ {
+ # GN version: //mojo/public/interfaces/service_provider:service_provider
+ 'target_name': 'mojo_service_provider_bindings',
+ 'type': 'static_library',
'sources': [
- 'public/utility/bindings_support_impl_unittest.cc',
- 'public/utility/run_loop_unittest.cc',
- 'public/utility/thread_local_unittest.cc',
+ 'public/interfaces/service_provider/service_provider.mojom',
+ ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+ 'dependencies': [
+ 'mojo_cpp_bindings',
+ ],
+ 'export_dependent_settings': [
+ 'mojo_cpp_bindings',
],
},
+ {
+ 'target_name': 'mojo_application',
+ 'type': 'static_library',
+ 'sources': [
+ 'public/cpp/application/application.h',
+ 'public/cpp/application/connect.h',
+ 'public/cpp/application/lib/application.cc',
+ 'public/cpp/application/lib/service_connector.cc',
+ 'public/cpp/application/lib/service_connector.h',
+ 'public/cpp/application/lib/service_registry.cc',
+ 'public/cpp/application/lib/service_registry.h',
+ ],
+ 'dependencies': [
+ 'mojo_service_provider_bindings',
+ ],
+ 'export_dependent_settings': [
+ 'mojo_service_provider_bindings',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS == "android"', {
+ 'targets': [
+ {
+ 'target_name': 'mojo_public_java',
+ 'type': 'none',
+ 'variables': {
+ 'java_in_dir': 'public/java',
+ },
+ 'includes': [ '../build/java.gypi' ],
+ },
+ {
+ 'target_name': 'mojo_bindings_java',
+ 'type': 'none',
+ 'variables': {
+ 'java_in_dir': 'bindings/java',
+ },
+ 'dependencies': [
+ 'mojo_public_java',
+ ],
+ 'includes': [ '../build/java.gypi' ],
+ },
+ ],
+ }],
],
}
diff --git a/chromium/mojo/mojo_services.gypi b/chromium/mojo/mojo_services.gypi
index 983d69f1ec4..341a418af87 100644
--- a/chromium/mojo/mojo_services.gypi
+++ b/chromium/mojo/mojo_services.gypi
@@ -1,15 +1,104 @@
{
'targets': [
{
+ 'target_name': 'mojo_echo_bindings',
+ 'type': 'static_library',
+ 'sources': [
+ 'services/dbus_echo/echo.mojom',
+ ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+ 'export_dependent_settings': [
+ 'mojo_cpp_bindings',
+ ],
+ 'dependencies': [
+ 'mojo_cpp_bindings',
+ ],
+ },
+ {
+ 'target_name': 'mojo_input_events_lib',
+ 'type': '<(component)',
+ 'defines': [
+ 'MOJO_INPUT_EVENTS_IMPLEMENTATION',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../ui/events/events.gyp:events',
+ '../ui/gfx/gfx.gyp:gfx_geometry',
+ 'mojo_environment_chromium',
+ 'mojo_input_events_bindings',
+ 'mojo_geometry_bindings',
+ 'mojo_geometry_lib',
+ 'mojo_system_impl',
+ ],
+ 'sources': [
+ 'services/public/cpp/input_events/lib/input_events_type_converters.cc',
+ 'services/public/cpp/input_events/input_events_type_converters.h',
+ 'services/public/cpp/input_events/mojo_input_events_export.h',
+ ],
+ },
+ {
+ 'target_name': 'mojo_input_events_bindings',
+ 'type': 'static_library',
+ 'sources': [
+ 'services/public/interfaces/input_events/input_events.mojom',
+ ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+ 'export_dependent_settings': [
+ 'mojo_cpp_bindings',
+ ],
+ 'dependencies': [
+ 'mojo_cpp_bindings',
+ 'mojo_geometry_bindings',
+ ],
+ },
+ {
+ 'target_name': 'mojo_geometry_bindings',
+ 'type': 'static_library',
+ 'sources': [
+ 'services/public/interfaces/geometry/geometry.mojom',
+ ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+ 'dependencies': [
+ 'mojo_cpp_bindings',
+ ],
+ 'export_dependent_settings': [
+ 'mojo_cpp_bindings',
+ ],
+ },
+ {
+ 'target_name': 'mojo_geometry_lib',
+ 'type': '<(component)',
+ 'defines': [
+ 'MOJO_GEOMETRY_IMPLEMENTATION',
+ ],
+ 'dependencies': [
+ '../ui/gfx/gfx.gyp:gfx_geometry',
+ 'mojo_environment_chromium',
+ 'mojo_geometry_bindings',
+ 'mojo_system_impl',
+ ],
+ 'sources': [
+ 'services/public/cpp/geometry/lib/geometry_type_converters.cc',
+ 'services/public/cpp/geometry/geometry_type_converters.h',
+ ],
+ },
+ {
'target_name': 'mojo_gles2_bindings',
'type': 'static_library',
'sources': [
- 'services/gles2/gles2.mojom',
+ 'services/gles2/command_buffer.mojom',
+ 'services/gles2/command_buffer_type_conversions.cc',
+ 'services/gles2/command_buffer_type_conversions.h',
+ 'services/gles2/mojo_buffer_backing.cc',
+ 'services/gles2/mojo_buffer_backing.h',
],
- 'includes': [ 'public/bindings/mojom_bindings_generator.gypi' ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
'export_dependent_settings': [
- 'mojo_bindings',
- 'mojo_system',
+ 'mojo_cpp_bindings',
+ ],
+ 'dependencies': [
+ '../gpu/gpu.gyp:command_buffer_common',
+ 'mojo_cpp_bindings',
],
},
{
@@ -18,8 +107,8 @@
'dependencies': [
'../base/base.gyp:base',
'../gpu/gpu.gyp:command_buffer_service',
- '../gpu/gpu.gyp:gles2_implementation',
'../ui/gfx/gfx.gyp:gfx',
+ '../ui/gfx/gfx.gyp:gfx_geometry',
'../ui/gl/gl.gyp:gl',
'mojo_gles2_bindings',
],
@@ -27,41 +116,55 @@
'mojo_gles2_bindings',
],
'sources': [
- 'services/gles2/gles2_impl.cc',
- 'services/gles2/gles2_impl.h',
+ 'services/gles2/command_buffer_impl.cc',
+ 'services/gles2/command_buffer_impl.h',
],
},
{
'target_name': 'mojo_native_viewport_bindings',
'type': 'static_library',
'sources': [
- 'services/native_viewport/native_viewport.mojom',
+ 'services/public/interfaces/native_viewport/native_viewport.mojom',
],
- 'includes': [ 'public/bindings/mojom_bindings_generator.gypi' ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
'export_dependent_settings': [
- 'mojo_bindings',
- 'mojo_system',
+ 'mojo_cpp_bindings',
+ ],
+ 'dependencies': [
+ 'mojo_geometry_bindings',
+ 'mojo_gles2_bindings',
+ 'mojo_input_events_bindings',
+ 'mojo_cpp_bindings',
],
},
{
'target_name': 'mojo_native_viewport_service',
- 'type': 'static_library',
+ # This is linked directly into the embedder, so we make it a component.
+ 'type': '<(component)',
'dependencies': [
'../base/base.gyp:base',
'../ui/events/events.gyp:events',
'../ui/gfx/gfx.gyp:gfx',
+ '../ui/gfx/gfx.gyp:gfx_geometry',
+ 'mojo_application',
+ 'mojo_common_lib',
+ 'mojo_environment_chromium',
+ 'mojo_geometry_bindings',
+ 'mojo_geometry_lib',
'mojo_gles2_service',
+ 'mojo_input_events_lib',
'mojo_native_viewport_bindings',
+ 'mojo_system_impl',
],
- 'export_dependent_settings': [
- 'mojo_native_viewport_bindings',
+ 'defines': [
+ 'MOJO_NATIVE_VIEWPORT_IMPLEMENTATION',
],
'sources': [
'services/native_viewport/native_viewport.h',
'services/native_viewport/native_viewport_android.cc',
- 'services/native_viewport/native_viewport_impl.cc',
- 'services/native_viewport/native_viewport_impl.h',
'services/native_viewport/native_viewport_mac.mm',
+ 'services/native_viewport/native_viewport_service.cc',
+ 'services/native_viewport/native_viewport_service.h',
'services/native_viewport/native_viewport_stub.cc',
'services/native_viewport/native_viewport_win.cc',
'services/native_viewport/native_viewport_x11.cc',
@@ -79,5 +182,373 @@
}],
],
},
+ {
+ 'target_name': 'mojo_navigation_bindings',
+ 'type': 'static_library',
+ 'sources': [
+ 'services/public/interfaces/navigation/navigation.mojom',
+ ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+ 'export_dependent_settings': [
+ 'mojo_cpp_bindings',
+ ],
+ 'dependencies': [
+ 'mojo_cpp_bindings',
+ 'mojo_network_bindings',
+ ],
+ },
+ {
+ 'target_name': 'mojo_network_bindings',
+ 'type': 'static_library',
+ 'sources': [
+ 'services/public/interfaces/network/network_error.mojom',
+ 'services/public/interfaces/network/network_service.mojom',
+ 'services/public/interfaces/network/url_loader.mojom',
+ ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+ 'export_dependent_settings': [
+ 'mojo_cpp_bindings',
+ ],
+ 'dependencies': [
+ 'mojo_cpp_bindings',
+ ],
+ },
+ {
+ 'target_name': 'mojo_network_service',
+ 'type': 'shared_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../net/net.gyp:net',
+ '../url/url.gyp:url_lib',
+ 'mojo_application',
+ 'mojo_common_lib',
+ 'mojo_environment_chromium',
+ 'mojo_network_bindings',
+ 'mojo_system_impl',
+ ],
+ 'export_dependent_settings': [
+ 'mojo_network_bindings',
+ ],
+ 'sources': [
+ 'services/network/main.cc',
+ 'services/network/network_context.cc',
+ 'services/network/network_context.h',
+ 'services/network/network_service_impl.cc',
+ 'services/network/network_service_impl.h',
+ 'services/network/url_loader_impl.cc',
+ 'services/network/url_loader_impl.h',
+ ],
+ },
+ {
+ 'target_name': 'mojo_view_manager_common',
+ 'type': 'static_library',
+ 'sources': [
+ 'services/public/cpp/view_manager/types.h',
+ ],
+ },
+ {
+ 'target_name': 'mojo_launcher_bindings',
+ 'type': 'static_library',
+ 'sources': [
+ 'services/public/interfaces/launcher/launcher.mojom',
+ ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+ 'export_dependent_settings': [
+ 'mojo_cpp_bindings',
+ ],
+ 'dependencies': [
+ 'mojo_cpp_bindings',
+ 'mojo_navigation_bindings',
+ ],
+ },
+ {
+ 'target_name': 'mojo_launcher',
+ 'type': 'shared_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../url/url.gyp:url_lib',
+ 'mojo_application',
+ 'mojo_cpp_bindings',
+ 'mojo_environment_chromium',
+ 'mojo_launcher_bindings',
+ 'mojo_network_bindings',
+ 'mojo_system_impl',
+ 'mojo_utility',
+ ],
+ 'sources': [
+ 'services/launcher/launcher.cc',
+ 'public/cpp/application/lib/mojo_main_chromium.cc',
+ ],
+ },
+ {
+ 'target_name': 'mojo_view_manager_bindings',
+ 'type': 'static_library',
+ 'sources': [
+ 'services/public/interfaces/view_manager/view_manager.mojom',
+ 'services/public/interfaces/view_manager/view_manager_constants.mojom',
+ ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+ 'export_dependent_settings': [
+ 'mojo_cpp_bindings',
+ ],
+ 'dependencies': [
+ 'mojo_cpp_bindings',
+ 'mojo_geometry_bindings',
+ 'mojo_input_events_bindings',
+ ],
+ },
+ {
+ 'target_name': 'mojo_view_manager_lib',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../skia/skia.gyp:skia',
+ '../ui/gfx/gfx.gyp:gfx',
+ '../ui/gfx/gfx.gyp:gfx_geometry',
+ 'mojo_application',
+ 'mojo_geometry_bindings',
+ 'mojo_geometry_lib',
+ 'mojo_service_provider_bindings',
+ 'mojo_view_manager_bindings',
+ 'mojo_view_manager_common',
+ ],
+ 'sources': [
+ 'services/public/cpp/view_manager/lib/node.cc',
+ 'services/public/cpp/view_manager/lib/node_observer.cc',
+ 'services/public/cpp/view_manager/lib/node_private.cc',
+ 'services/public/cpp/view_manager/lib/node_private.h',
+ 'services/public/cpp/view_manager/lib/view.cc',
+ 'services/public/cpp/view_manager/lib/view_private.cc',
+ 'services/public/cpp/view_manager/lib/view_private.h',
+ 'services/public/cpp/view_manager/lib/view_manager_client_impl.cc',
+ 'services/public/cpp/view_manager/lib/view_manager_client_impl.h',
+ 'services/public/cpp/view_manager/node.h',
+ 'services/public/cpp/view_manager/node_observer.h',
+ 'services/public/cpp/view_manager/view.h',
+ 'services/public/cpp/view_manager/view_manager.h',
+ 'services/public/cpp/view_manager/view_manager_delegate.h',
+ 'services/public/cpp/view_manager/view_observer.h',
+ ],
+ },
+ {
+ 'target_name': 'mojo_view_manager_lib_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:test_support_base',
+ '../testing/gtest.gyp:gtest',
+ 'mojo_environment_chromium',
+ 'mojo_geometry_bindings',
+ 'mojo_geometry_lib',
+ 'mojo_shell_test_support',
+ 'mojo_view_manager_bindings',
+ 'mojo_view_manager_lib',
+ ],
+ 'sources': [
+ 'services/public/cpp/view_manager/tests/node_unittest.cc',
+ 'services/public/cpp/view_manager/tests/view_unittest.cc',
+ 'services/public/cpp/view_manager/tests/view_manager_unittest.cc',
+ ],
+ 'conditions': [
+ ['use_aura==1', {
+ 'dependencies': [
+ 'mojo_view_manager_run_unittests'
+ ],
+ }, { # use_aura==0
+ 'dependencies': [
+ 'mojo_run_all_unittests',
+ ],
+ }]
+ ],
+ },
+ {
+ 'target_name': 'mojo_surfaces_bindings',
+ 'type': 'static_library',
+ 'sources': [
+ 'services/public/interfaces/surfaces/surfaces.mojom',
+ 'services/public/interfaces/surfaces/surface_id.mojom',
+ 'services/public/interfaces/surfaces/quads.mojom',
+ ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+ 'export_dependent_settings': [
+ 'mojo_cpp_bindings',
+ ],
+ 'dependencies': [
+ 'mojo_cpp_bindings',
+ 'mojo_geometry_bindings',
+ ],
+ },
+ {
+ 'target_name': 'mojo_test_service_bindings',
+ 'type': 'static_library',
+ 'sources': [
+ 'services/test_service/test_service.mojom',
+ ],
+ 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ],
+ 'export_dependent_settings': [
+ 'mojo_cpp_bindings',
+ ],
+ 'dependencies': [
+ 'mojo_cpp_bindings',
+ ],
+ },
+ {
+ 'target_name': 'mojo_test_service',
+ 'type': 'shared_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'mojo_application',
+ 'mojo_environment_standalone',
+ 'mojo_test_service_bindings',
+ 'mojo_system',
+ 'mojo_utility',
+ ],
+ 'sources': [
+ 'public/cpp/application/lib/mojo_main_standalone.cc',
+ 'services/test_service/test_service_application.cc',
+ 'services/test_service/test_service_application.h',
+ 'services/test_service/test_service_impl.cc',
+ 'services/test_service/test_service_impl.h',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['use_aura==1', {
+ 'targets': [
+ {
+ 'target_name': 'mojo_view_manager',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../cc/cc.gyp:cc',
+ '../skia/skia.gyp:skia',
+ '../ui/aura/aura.gyp:aura',
+ '../ui/base/ui_base.gyp:ui_base',
+ '../ui/compositor/compositor.gyp:compositor',
+ '../ui/events/events.gyp:events',
+ '../ui/events/events.gyp:events_base',
+ '../ui/gfx/gfx.gyp:gfx',
+ '../ui/gfx/gfx.gyp:gfx_geometry',
+ '../ui/gl/gl.gyp:gl',
+ '../webkit/common/gpu/webkit_gpu.gyp:webkit_gpu',
+ 'mojo_application',
+ 'mojo_cc_support',
+ 'mojo_common_lib',
+ 'mojo_environment_chromium',
+ 'mojo_geometry_bindings',
+ 'mojo_geometry_lib',
+ 'mojo_gles2',
+ 'mojo_input_events_bindings',
+ 'mojo_input_events_lib',
+ 'mojo_native_viewport_bindings',
+ 'mojo_system_impl',
+ 'mojo_view_manager_bindings',
+ 'mojo_view_manager_common',
+ ],
+ 'sources': [
+ 'public/cpp/application/lib/mojo_main_chromium.cc',
+ 'services/view_manager/ids.h',
+ 'services/view_manager/main.cc',
+ 'services/view_manager/node.cc',
+ 'services/view_manager/node.h',
+ 'services/view_manager/node_delegate.h',
+ 'services/view_manager/root_node_manager.cc',
+ 'services/view_manager/root_node_manager.h',
+ 'services/view_manager/root_view_manager.cc',
+ 'services/view_manager/root_view_manager.h',
+ 'services/view_manager/root_view_manager_delegate.h',
+ 'services/view_manager/screen_impl.cc',
+ 'services/view_manager/screen_impl.h',
+ 'services/view_manager/view.cc',
+ 'services/view_manager/view.h',
+ 'services/view_manager/view_manager_export.h',
+ 'services/view_manager/view_manager_init_service_impl.cc',
+ 'services/view_manager/view_manager_init_service_impl.h',
+ 'services/view_manager/view_manager_service_impl.cc',
+ 'services/view_manager/view_manager_service_impl.h',
+ 'services/view_manager/context_factory_impl.cc',
+ 'services/view_manager/context_factory_impl.h',
+ 'services/view_manager/window_tree_host_impl.cc',
+ 'services/view_manager/window_tree_host_impl.h',
+ ],
+ 'defines': [
+ 'MOJO_VIEW_MANAGER_IMPLEMENTATION',
+ ],
+ },
+ {
+ 'target_name': 'mojo_view_manager_run_unittests',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:test_support_base',
+ '../ui/gl/gl.gyp:gl',
+ ],
+ 'sources': [
+ 'services/public/cpp/view_manager/lib/view_manager_test_suite.cc',
+ 'services/public/cpp/view_manager/lib/view_manager_test_suite.h',
+ 'services/public/cpp/view_manager/lib/view_manager_unittests.cc',
+ ],
+ },
+ {
+ 'target_name': 'mojo_view_manager_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:test_support_base',
+ '../skia/skia.gyp:skia',
+ '../testing/gtest.gyp:gtest',
+ '../ui/aura/aura.gyp:aura',
+ '../ui/gfx/gfx.gyp:gfx_geometry',
+ '../ui/gl/gl.gyp:gl',
+ 'mojo_application',
+ 'mojo_environment_chromium',
+ 'mojo_geometry_bindings',
+ 'mojo_geometry_lib',
+ 'mojo_input_events_bindings',
+ 'mojo_input_events_lib',
+ 'mojo_service_manager',
+ 'mojo_shell_test_support',
+ 'mojo_system_impl',
+ 'mojo_view_manager_bindings',
+ 'mojo_view_manager_common',
+ 'mojo_view_manager_run_unittests',
+ ],
+ 'sources': [
+ 'services/view_manager/test_change_tracker.cc',
+ 'services/view_manager/test_change_tracker.h',
+ 'services/view_manager/view_manager_unittest.cc',
+ ],
+ },
+ {
+ 'target_name': 'package_mojo_view_manager',
+ 'variables': {
+ 'app_name': 'mojo_view_manager',
+ },
+ 'includes': [ 'build/package_app.gypi' ],
+ },
+ ],
+ }],
+ ['OS=="linux"', {
+ 'targets': [
+ {
+ 'target_name': 'mojo_dbus_echo_service',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../build/linux/system.gyp:dbus',
+ '../dbus/dbus.gyp:dbus',
+ 'mojo_application',
+ 'mojo_common_lib',
+ 'mojo_dbus_service',
+ 'mojo_echo_bindings',
+ 'mojo_environment_chromium',
+ 'mojo_system_impl',
+ ],
+ 'sources': [
+ 'services/dbus_echo/dbus_echo_service.cc',
+ ],
+ },
+ ],
+ }],
],
}
diff --git a/chromium/mojo/public/DEPS b/chromium/mojo/public/DEPS
new file mode 100644
index 00000000000..0c679b9fd0f
--- /dev/null
+++ b/chromium/mojo/public/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "-base",
+ "-build",
+ "-mojo",
+ "+mojo/public",
+]
diff --git a/chromium/mojo/public/README.md b/chromium/mojo/public/README.md
new file mode 100644
index 00000000000..a31a8a8245e
--- /dev/null
+++ b/chromium/mojo/public/README.md
@@ -0,0 +1,43 @@
+Mojo Public API
+===============
+
+The Mojo Public API is a binary stable API to the Mojo system.
+
+It consists of support for a number of programming languages (with a directory
+for each support language), some "build" tools and build-time requirements, and
+interface definitions for Mojo services (specified using an IDL).
+
+Note that there are various subdirectories named tests/. These contain tests of
+the code in the enclosing directory, and are not meant for use by Mojo
+applications.
+
+C/CPP/JS
+--------
+
+The c/, cpp/, js/ subdirectories define the API for C, C++, and JavaScript,
+respectively.
+
+The basic principle for these directories is that they consist of the source
+files that one needs at build/deployment/run time (as appropriate for the
+language), organized in a natural way for the particular language.
+
+Interfaces
+----------
+
+The interfaces/ subdirectory contains Mojo IDL (a.k.a. .mojom) descriptions of
+standard Mojo services.
+
+Platform
+--------
+
+The platform/ subdirectory contains any build-time requirements (e.g., static
+libraries) that may be needed to produce a Mojo application for certain
+platforms, such as a native shared library or as a NaCl binary.
+
+Tools
+-----
+
+The tools/ subdirectory contains tools that are useful/necessary at
+build/deployment time. These tools may be needed (as a practical necessity) to
+use the API in any given language, e.g., to generate bindings from Mojo IDL
+files.
diff --git a/chromium/mojo/public/bindings/mojom_bindings_generator.gypi b/chromium/mojo/public/bindings/mojom_bindings_generator.gypi
deleted file mode 100644
index 45efe768d1b..00000000000
--- a/chromium/mojo/public/bindings/mojom_bindings_generator.gypi
+++ /dev/null
@@ -1,82 +0,0 @@
-# Copyright (c) 2013 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.
-
-{
- 'variables': {
- 'output_dir': '<(SHARED_INTERMEDIATE_DIR)/mojom',
- },
- 'rules': [
- {
- 'rule_name': 'Generate C++ source files from mojom files',
- 'extension': 'mojom',
- 'variables': {
- 'mojom_bindings_generator':
- '<(DEPTH)/mojo/public/bindings/mojom_bindings_generator.py',
- },
- 'inputs': [
- '<(mojom_bindings_generator)',
- '<(DEPTH)/mojo/public/bindings/parse/mojo_parser.py',
- '<(DEPTH)/mojo/public/bindings/parse/mojo_translate.py',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/interface_declaration',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/interface_definition',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/interface_proxy_declaration',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/interface_stub_case',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/interface_stub_declaration',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/interface_stub_definition',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/module.cc-template',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/module.h-template',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/module_internal.h-template',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/params_definition',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/params_serialization',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/proxy_implementation',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/struct_builder_definition',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/struct_declaration',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/struct_destructor',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/struct_definition',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/struct_serialization',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/struct_serialization_definition',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/struct_serialization_traits',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/template_declaration',
- '<(DEPTH)/mojo/public/bindings/generators/cpp_templates/wrapper_class_declaration',
- '<(DEPTH)/mojo/public/bindings/generators/js_templates/module.js.tmpl',
- '<(DEPTH)/mojo/public/bindings/generators/mojom.py',
- '<(DEPTH)/mojo/public/bindings/generators/mojom_cpp_generator.py',
- '<(DEPTH)/mojo/public/bindings/generators/mojom_data.py',
- '<(DEPTH)/mojo/public/bindings/generators/mojom_generator.py',
- '<(DEPTH)/mojo/public/bindings/generators/mojom_js_generator.py',
- '<(DEPTH)/mojo/public/bindings/generators/mojom_pack.py',
- '<(DEPTH)/mojo/public/bindings/generators/template_expander.py',
- ],
- 'outputs': [
- '<(output_dir)/<(RULE_INPUT_ROOT).cc',
- '<(output_dir)/<(RULE_INPUT_ROOT).h',
- '<(output_dir)/<(RULE_INPUT_ROOT).js',
- '<(output_dir)/<(RULE_INPUT_ROOT)_internal.h',
- ],
- 'action': [
- 'python', '<@(mojom_bindings_generator)',
- '<(RULE_INPUT_PATH)',
- '-i', 'mojom',
- '-o', '<(output_dir)',
- ],
- 'message': 'Generating C++ from mojom <(RULE_INPUT_PATH)',
- 'process_outputs_as_sources': 1,
- }
- ],
- 'dependencies': [
- 'mojo_bindings',
- 'mojo_system',
- ],
- 'include_dirs': [
- '<(DEPTH)',
- '<(SHARED_INTERMEDIATE_DIR)',
- ],
- 'direct_dependent_settings': {
- 'include_dirs': [
- '<(DEPTH)',
- '<(SHARED_INTERMEDIATE_DIR)',
- ],
- },
- 'hard_dependency': 1,
-}
diff --git a/chromium/mojo/public/c/DEPS b/chromium/mojo/public/c/DEPS
new file mode 100644
index 00000000000..52727706c38
--- /dev/null
+++ b/chromium/mojo/public/c/DEPS
@@ -0,0 +1,16 @@
+include_rules = [
+ # Require explicit dependencies in each directory.
+ "-mojo/public",
+ # But everyone can depend on the C system headers.
+ "+mojo/public/c/system",
+]
+
+specific_include_rules = {
+ r".*_(unit|perf)test\.cc": [
+ "+testing",
+ # Our test harness is C++, so allow the use of C++:
+ "+mojo/public/cpp/system",
+ "+mojo/public/cpp/test_support",
+ "+mojo/public/cpp/utility",
+ ],
+}
diff --git a/chromium/mojo/public/c/README.md b/chromium/mojo/public/c/README.md
new file mode 100644
index 00000000000..8e11545deb0
--- /dev/null
+++ b/chromium/mojo/public/c/README.md
@@ -0,0 +1,45 @@
+Mojo Public C API
+=================
+
+This directory contains C language bindings for the Mojo Public API.
+
+Environment
+-----------
+
+The environment/ subdirectory defines some common things that, while not part of
+the system API, may be required for GLES2 (for example). These are things that a
+Mojo application may be required to provide to the GLES2 (for example) library
+in order to use it. (However, the Mojo application may implement these things as
+it sees fit.)
+
+GLES2
+-----
+
+The gles2/ subdirectory defines the GLES2 C API that's available to Mojo
+applications. To use GLES2, Mojo applications must link against a dynamic
+library (the exact mechanism being platform-dependent) and use the header files
+in this directory as well as the standard Khronos GLES2 header files.
+
+The reason for this, rather than providing GLES2 using the standard Mojo IPC
+mechanism, is performance: The protocol (and transport mechanisms) used to
+communicate with the Mojo GLES2 service is not stable nor "public" (mainly for
+performance reasons), and using the dynamic library shields the application from
+changes to the underlying system.
+
+System
+------
+
+The system/ subdirectory provides definitions of the basic low-level API used by
+all Mojo applications (whether directly or indirectly). These consist primarily
+of the IPC primitives used to communicate with Mojo services.
+
+Though the message protocol is stable, the implementation of the transport is
+not, and access to the IPC mechanisms must be via the primitives defined in this
+directory.
+
+Test Support
+------------
+
+This directory contains a C API for running tests. This API is only available
+under special, specific test conditions. It is not meant for general use by Mojo
+applications.
diff --git a/chromium/mojo/public/c/environment/async_waiter.h b/chromium/mojo/public/c/environment/async_waiter.h
new file mode 100644
index 00000000000..1eb06317d70
--- /dev/null
+++ b/chromium/mojo/public/c/environment/async_waiter.h
@@ -0,0 +1,30 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_C_ENVIRONMENT_ASYNC_WAITER_H_
+#define MOJO_PUBLIC_C_ENVIRONMENT_ASYNC_WAITER_H_
+
+#include "mojo/public/c/system/types.h"
+
+typedef uintptr_t MojoAsyncWaitID;
+
+typedef void (*MojoAsyncWaitCallback)(void* closure, MojoResult result);
+
+struct MojoAsyncWaiter {
+ // Asynchronously call MojoWait on a background thread, and pass the result
+ // of MojoWait to the given MojoAsyncWaitCallback on the current thread.
+ // Returns a non-zero MojoAsyncWaitID that can be used with CancelWait to
+ // stop waiting. This identifier becomes invalid once the callback runs.
+ MojoAsyncWaitID (*AsyncWait)(MojoHandle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline,
+ MojoAsyncWaitCallback callback,
+ void* closure);
+
+ // Cancel an existing call to AsyncWait with the given MojoAsyncWaitID. The
+ // corresponding MojoAsyncWaitCallback will not be called in this case.
+ void (*CancelWait)(MojoAsyncWaitID wait_id);
+};
+
+#endif // MOJO_PUBLIC_C_ENVIRONMENT_ASYNC_WAITER_H_
diff --git a/chromium/mojo/public/c/environment/logger.h b/chromium/mojo/public/c/environment/logger.h
new file mode 100644
index 00000000000..5e9067fb509
--- /dev/null
+++ b/chromium/mojo/public/c/environment/logger.h
@@ -0,0 +1,54 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_C_ENVIRONMENT_LOGGER_H_
+#define MOJO_PUBLIC_C_ENVIRONMENT_LOGGER_H_
+
+#include <stdint.h>
+
+// |MojoLogLevel|: Used to specify the type of log message. Values are ordered
+// by severity (i.e., higher numerical values are more severe).
+
+typedef int32_t MojoLogLevel;
+
+#ifdef __cplusplus
+const MojoLogLevel MOJO_LOG_LEVEL_VERBOSE = -1;
+const MojoLogLevel MOJO_LOG_LEVEL_INFO = 0;
+const MojoLogLevel MOJO_LOG_LEVEL_WARNING = 1;
+const MojoLogLevel MOJO_LOG_LEVEL_ERROR = 2;
+const MojoLogLevel MOJO_LOG_LEVEL_FATAL = 3;
+#else
+#define MOJO_LOG_LEVEL_VERBOSE ((MojoLogLevel) -1)
+#define MOJO_LOG_LEVEL_INFO ((MojoLogLevel) 0)
+#define MOJO_LOG_LEVEL_WARNING ((MojoLogLevel) 1)
+#define MOJO_LOG_LEVEL_ERROR ((MojoLogLevel) 2)
+#define MOJO_LOG_LEVEL_FATAL ((MojoLogLevel) 3)
+#endif
+
+// Structure with basic logging functions (on top of which more friendly logging
+// macros may be built). The functions are thread-safe, except for
+// |SetMinimumLogLevel()| (see below).
+struct MojoLogger {
+ // Logs |message| at level |log_level| if |log_level| is at least the current
+ // minimum log level. If |log_level| is |MOJO_LOG_LEVEL_FATAL| (or greater),
+ // aborts the application/process.
+ void (*LogMessage)(MojoLogLevel log_level, const char* message);
+
+ // Gets the minimum log level (see above), which will always be at most
+ // |MOJO_LOG_LEVEL_FATAL|. (Though |LogMessage()| will automatically avoid
+ // logging messages below the minimum log level, this may be used to avoid
+ // extra work.)
+ MojoLogLevel (*GetMinimumLogLevel)(void);
+
+ // Sets the minimum log level (see above) to the lesser of |minimum_log_level|
+ // and |MOJO_LOG_LEVEL_FATAL|.
+ //
+ // Warning: This function may not be thread-safe, and should not be called
+ // concurrently with other |MojoLogger| functions. (In some environments --
+ // such as Chromium -- that share a logger across applications, this may mean
+ // that it is almost never safe to call this.)
+ void (*SetMinimumLogLevel)(MojoLogLevel minimum_log_level);
+};
+
+#endif // MOJO_PUBLIC_C_ENVIRONMENT_LOGGER_H_
diff --git a/chromium/mojo/public/c/gles2/DEPS b/chromium/mojo/public/c/gles2/DEPS
new file mode 100644
index 00000000000..38874579404
--- /dev/null
+++ b/chromium/mojo/public/c/gles2/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+mojo/public/c/environment",
+]
diff --git a/chromium/mojo/public/c/gles2/gles2.h b/chromium/mojo/public/c/gles2/gles2.h
new file mode 100644
index 00000000000..44880afc1ed
--- /dev/null
+++ b/chromium/mojo/public/c/gles2/gles2.h
@@ -0,0 +1,51 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_PUBLIC_C_GLES2_GLES2_H_
+#define MOJO_PUBLIC_C_GLES2_GLES2_H_
+
+// Note: This header should be compilable as C.
+
+#include <stdint.h>
+#include <GLES2/gl2.h>
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/c/gles2/gles2_export.h"
+#include "mojo/public/c/gles2/gles2_types.h"
+#include "mojo/public/c/system/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MOJO_GLES2_EXPORT void MojoGLES2Initialize(const MojoAsyncWaiter* async_waiter);
+MOJO_GLES2_EXPORT void MojoGLES2Terminate(void);
+MOJO_GLES2_EXPORT MojoGLES2Context MojoGLES2CreateContext(
+ MojoHandle handle,
+ MojoGLES2ContextLost lost_callback,
+ MojoGLES2DrawAnimationFrame animation_callback,
+ void* closure);
+MOJO_GLES2_EXPORT void MojoGLES2DestroyContext(MojoGLES2Context context);
+MOJO_GLES2_EXPORT void MojoGLES2MakeCurrent(MojoGLES2Context context);
+MOJO_GLES2_EXPORT void MojoGLES2SwapBuffers(void);
+// TODO(piman): this doesn't belong here.
+MOJO_GLES2_EXPORT void MojoGLES2RequestAnimationFrames(
+ MojoGLES2Context context);
+MOJO_GLES2_EXPORT void MojoGLES2CancelAnimationFrames(MojoGLES2Context context);
+
+// TODO(piman): We shouldn't have to leak those 2 interfaces, especially in a
+// type-unsafe way.
+MOJO_GLES2_EXPORT void* MojoGLES2GetGLES2Interface(MojoGLES2Context context);
+MOJO_GLES2_EXPORT void* MojoGLES2GetContextSupport(MojoGLES2Context context);
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+ MOJO_GLES2_EXPORT ReturnType GL_APIENTRY gl##Function PARAMETERS;
+#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h"
+#undef VISIT_GL_CALL
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_GLES2_GLES2_H_
diff --git a/chromium/mojo/public/c/gles2/gles2_call_visitor_autogen.h b/chromium/mojo/public/c/gles2/gles2_call_visitor_autogen.h
new file mode 100644
index 00000000000..72494c5416c
--- /dev/null
+++ b/chromium/mojo/public/c/gles2/gles2_call_visitor_autogen.h
@@ -0,0 +1,544 @@
+// Copyright 2014 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.
+
+// This file is auto-generated from
+// gpu/command_buffer/build_gles2_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+// clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+VISIT_GL_CALL(ActiveTexture, void, (GLenum texture), (texture))
+VISIT_GL_CALL(AttachShader,
+ void,
+ (GLuint program, GLuint shader),
+ (program, shader))
+VISIT_GL_CALL(BindAttribLocation,
+ void,
+ (GLuint program, GLuint index, const char* name),
+ (program, index, name))
+VISIT_GL_CALL(BindBuffer,
+ void,
+ (GLenum target, GLuint buffer),
+ (target, buffer))
+VISIT_GL_CALL(BindFramebuffer,
+ void,
+ (GLenum target, GLuint framebuffer),
+ (target, framebuffer))
+VISIT_GL_CALL(BindRenderbuffer,
+ void,
+ (GLenum target, GLuint renderbuffer),
+ (target, renderbuffer))
+VISIT_GL_CALL(BindTexture,
+ void,
+ (GLenum target, GLuint texture),
+ (target, texture))
+VISIT_GL_CALL(BlendColor,
+ void,
+ (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha),
+ (red, green, blue, alpha))
+VISIT_GL_CALL(BlendEquation, void, (GLenum mode), (mode))
+VISIT_GL_CALL(BlendEquationSeparate,
+ void,
+ (GLenum modeRGB, GLenum modeAlpha),
+ (modeRGB, modeAlpha))
+VISIT_GL_CALL(BlendFunc,
+ void,
+ (GLenum sfactor, GLenum dfactor),
+ (sfactor, dfactor))
+VISIT_GL_CALL(BlendFuncSeparate,
+ void,
+ (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha),
+ (srcRGB, dstRGB, srcAlpha, dstAlpha))
+VISIT_GL_CALL(BufferData,
+ void,
+ (GLenum target, GLsizeiptr size, const void* data, GLenum usage),
+ (target, size, data, usage))
+VISIT_GL_CALL(
+ BufferSubData,
+ void,
+ (GLenum target, GLintptr offset, GLsizeiptr size, const void* data),
+ (target, offset, size, data))
+VISIT_GL_CALL(CheckFramebufferStatus, GLenum, (GLenum target), (target))
+VISIT_GL_CALL(Clear, void, (GLbitfield mask), (mask))
+VISIT_GL_CALL(ClearColor,
+ void,
+ (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha),
+ (red, green, blue, alpha))
+VISIT_GL_CALL(ClearDepthf, void, (GLclampf depth), (depth))
+VISIT_GL_CALL(ClearStencil, void, (GLint s), (s))
+VISIT_GL_CALL(ColorMask,
+ void,
+ (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha),
+ (red, green, blue, alpha))
+VISIT_GL_CALL(CompileShader, void, (GLuint shader), (shader))
+VISIT_GL_CALL(
+ CompressedTexImage2D,
+ void,
+ (GLenum target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLsizei imageSize,
+ const void* data),
+ (target, level, internalformat, width, height, border, imageSize, data))
+VISIT_GL_CALL(
+ CompressedTexSubImage2D,
+ void,
+ (GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLsizei imageSize,
+ const void* data),
+ (target, level, xoffset, yoffset, width, height, format, imageSize, data))
+VISIT_GL_CALL(CopyTexImage2D,
+ void,
+ (GLenum target,
+ GLint level,
+ GLenum internalformat,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLint border),
+ (target, level, internalformat, x, y, width, height, border))
+VISIT_GL_CALL(CopyTexSubImage2D,
+ void,
+ (GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height),
+ (target, level, xoffset, yoffset, x, y, width, height))
+VISIT_GL_CALL(CreateProgram, GLuint, (), ())
+VISIT_GL_CALL(CreateShader, GLuint, (GLenum type), (type))
+VISIT_GL_CALL(CullFace, void, (GLenum mode), (mode))
+VISIT_GL_CALL(DeleteBuffers,
+ void,
+ (GLsizei n, const GLuint* buffers),
+ (n, buffers))
+VISIT_GL_CALL(DeleteFramebuffers,
+ void,
+ (GLsizei n, const GLuint* framebuffers),
+ (n, framebuffers))
+VISIT_GL_CALL(DeleteProgram, void, (GLuint program), (program))
+VISIT_GL_CALL(DeleteRenderbuffers,
+ void,
+ (GLsizei n, const GLuint* renderbuffers),
+ (n, renderbuffers))
+VISIT_GL_CALL(DeleteShader, void, (GLuint shader), (shader))
+VISIT_GL_CALL(DeleteTextures,
+ void,
+ (GLsizei n, const GLuint* textures),
+ (n, textures))
+VISIT_GL_CALL(DepthFunc, void, (GLenum func), (func))
+VISIT_GL_CALL(DepthMask, void, (GLboolean flag), (flag))
+VISIT_GL_CALL(DepthRangef, void, (GLclampf zNear, GLclampf zFar), (zNear, zFar))
+VISIT_GL_CALL(DetachShader,
+ void,
+ (GLuint program, GLuint shader),
+ (program, shader))
+VISIT_GL_CALL(Disable, void, (GLenum cap), (cap))
+VISIT_GL_CALL(DisableVertexAttribArray, void, (GLuint index), (index))
+VISIT_GL_CALL(DrawArrays,
+ void,
+ (GLenum mode, GLint first, GLsizei count),
+ (mode, first, count))
+VISIT_GL_CALL(DrawElements,
+ void,
+ (GLenum mode, GLsizei count, GLenum type, const void* indices),
+ (mode, count, type, indices))
+VISIT_GL_CALL(Enable, void, (GLenum cap), (cap))
+VISIT_GL_CALL(EnableVertexAttribArray, void, (GLuint index), (index))
+VISIT_GL_CALL(Finish, void, (), ())
+VISIT_GL_CALL(Flush, void, (), ())
+VISIT_GL_CALL(FramebufferRenderbuffer,
+ void,
+ (GLenum target,
+ GLenum attachment,
+ GLenum renderbuffertarget,
+ GLuint renderbuffer),
+ (target, attachment, renderbuffertarget, renderbuffer))
+VISIT_GL_CALL(FramebufferTexture2D,
+ void,
+ (GLenum target,
+ GLenum attachment,
+ GLenum textarget,
+ GLuint texture,
+ GLint level),
+ (target, attachment, textarget, texture, level))
+VISIT_GL_CALL(FrontFace, void, (GLenum mode), (mode))
+VISIT_GL_CALL(GenBuffers, void, (GLsizei n, GLuint * buffers), (n, buffers))
+VISIT_GL_CALL(GenerateMipmap, void, (GLenum target), (target))
+VISIT_GL_CALL(GenFramebuffers,
+ void,
+ (GLsizei n, GLuint * framebuffers),
+ (n, framebuffers))
+VISIT_GL_CALL(GenRenderbuffers,
+ void,
+ (GLsizei n, GLuint * renderbuffers),
+ (n, renderbuffers))
+VISIT_GL_CALL(GenTextures, void, (GLsizei n, GLuint * textures), (n, textures))
+VISIT_GL_CALL(GetActiveAttrib,
+ void,
+ (GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei * length,
+ GLint * size,
+ GLenum * type,
+ char* name),
+ (program, index, bufsize, length, size, type, name))
+VISIT_GL_CALL(GetActiveUniform,
+ void,
+ (GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei * length,
+ GLint * size,
+ GLenum * type,
+ char* name),
+ (program, index, bufsize, length, size, type, name))
+VISIT_GL_CALL(
+ GetAttachedShaders,
+ void,
+ (GLuint program, GLsizei maxcount, GLsizei * count, GLuint * shaders),
+ (program, maxcount, count, shaders))
+VISIT_GL_CALL(GetAttribLocation,
+ GLint,
+ (GLuint program, const char* name),
+ (program, name))
+VISIT_GL_CALL(GetBooleanv,
+ void,
+ (GLenum pname, GLboolean * params),
+ (pname, params))
+VISIT_GL_CALL(GetBufferParameteriv,
+ void,
+ (GLenum target, GLenum pname, GLint * params),
+ (target, pname, params))
+VISIT_GL_CALL(GetError, GLenum, (), ())
+VISIT_GL_CALL(GetFloatv,
+ void,
+ (GLenum pname, GLfloat * params),
+ (pname, params))
+VISIT_GL_CALL(GetFramebufferAttachmentParameteriv,
+ void,
+ (GLenum target, GLenum attachment, GLenum pname, GLint * params),
+ (target, attachment, pname, params))
+VISIT_GL_CALL(GetIntegerv,
+ void,
+ (GLenum pname, GLint * params),
+ (pname, params))
+VISIT_GL_CALL(GetProgramiv,
+ void,
+ (GLuint program, GLenum pname, GLint * params),
+ (program, pname, params))
+VISIT_GL_CALL(
+ GetProgramInfoLog,
+ void,
+ (GLuint program, GLsizei bufsize, GLsizei * length, char* infolog),
+ (program, bufsize, length, infolog))
+VISIT_GL_CALL(GetRenderbufferParameteriv,
+ void,
+ (GLenum target, GLenum pname, GLint * params),
+ (target, pname, params))
+VISIT_GL_CALL(GetShaderiv,
+ void,
+ (GLuint shader, GLenum pname, GLint * params),
+ (shader, pname, params))
+VISIT_GL_CALL(GetShaderInfoLog,
+ void,
+ (GLuint shader, GLsizei bufsize, GLsizei * length, char* infolog),
+ (shader, bufsize, length, infolog))
+VISIT_GL_CALL(
+ GetShaderPrecisionFormat,
+ void,
+ (GLenum shadertype, GLenum precisiontype, GLint * range, GLint * precision),
+ (shadertype, precisiontype, range, precision))
+VISIT_GL_CALL(GetShaderSource,
+ void,
+ (GLuint shader, GLsizei bufsize, GLsizei * length, char* source),
+ (shader, bufsize, length, source))
+VISIT_GL_CALL(GetString, const GLubyte*, (GLenum name), (name))
+VISIT_GL_CALL(GetTexParameterfv,
+ void,
+ (GLenum target, GLenum pname, GLfloat * params),
+ (target, pname, params))
+VISIT_GL_CALL(GetTexParameteriv,
+ void,
+ (GLenum target, GLenum pname, GLint * params),
+ (target, pname, params))
+VISIT_GL_CALL(GetUniformfv,
+ void,
+ (GLuint program, GLint location, GLfloat * params),
+ (program, location, params))
+VISIT_GL_CALL(GetUniformiv,
+ void,
+ (GLuint program, GLint location, GLint * params),
+ (program, location, params))
+VISIT_GL_CALL(GetUniformLocation,
+ GLint,
+ (GLuint program, const char* name),
+ (program, name))
+VISIT_GL_CALL(GetVertexAttribfv,
+ void,
+ (GLuint index, GLenum pname, GLfloat * params),
+ (index, pname, params))
+VISIT_GL_CALL(GetVertexAttribiv,
+ void,
+ (GLuint index, GLenum pname, GLint * params),
+ (index, pname, params))
+VISIT_GL_CALL(GetVertexAttribPointerv,
+ void,
+ (GLuint index, GLenum pname, void** pointer),
+ (index, pname, pointer))
+VISIT_GL_CALL(Hint, void, (GLenum target, GLenum mode), (target, mode))
+VISIT_GL_CALL(IsBuffer, GLboolean, (GLuint buffer), (buffer))
+VISIT_GL_CALL(IsEnabled, GLboolean, (GLenum cap), (cap))
+VISIT_GL_CALL(IsFramebuffer, GLboolean, (GLuint framebuffer), (framebuffer))
+VISIT_GL_CALL(IsProgram, GLboolean, (GLuint program), (program))
+VISIT_GL_CALL(IsRenderbuffer, GLboolean, (GLuint renderbuffer), (renderbuffer))
+VISIT_GL_CALL(IsShader, GLboolean, (GLuint shader), (shader))
+VISIT_GL_CALL(IsTexture, GLboolean, (GLuint texture), (texture))
+VISIT_GL_CALL(LineWidth, void, (GLfloat width), (width))
+VISIT_GL_CALL(LinkProgram, void, (GLuint program), (program))
+VISIT_GL_CALL(PixelStorei, void, (GLenum pname, GLint param), (pname, param))
+VISIT_GL_CALL(PolygonOffset,
+ void,
+ (GLfloat factor, GLfloat units),
+ (factor, units))
+VISIT_GL_CALL(ReadPixels,
+ void,
+ (GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ void* pixels),
+ (x, y, width, height, format, type, pixels))
+VISIT_GL_CALL(ReleaseShaderCompiler, void, (), ())
+VISIT_GL_CALL(
+ RenderbufferStorage,
+ void,
+ (GLenum target, GLenum internalformat, GLsizei width, GLsizei height),
+ (target, internalformat, width, height))
+VISIT_GL_CALL(SampleCoverage,
+ void,
+ (GLclampf value, GLboolean invert),
+ (value, invert))
+VISIT_GL_CALL(Scissor,
+ void,
+ (GLint x, GLint y, GLsizei width, GLsizei height),
+ (x, y, width, height))
+VISIT_GL_CALL(ShaderBinary,
+ void,
+ (GLsizei n,
+ const GLuint* shaders,
+ GLenum binaryformat,
+ const void* binary,
+ GLsizei length),
+ (n, shaders, binaryformat, binary, length))
+VISIT_GL_CALL(ShaderSource,
+ void,
+ (GLuint shader,
+ GLsizei count,
+ const GLchar* const* str,
+ const GLint* length),
+ (shader, count, str, length))
+VISIT_GL_CALL(StencilFunc,
+ void,
+ (GLenum func, GLint ref, GLuint mask),
+ (func, ref, mask))
+VISIT_GL_CALL(StencilFuncSeparate,
+ void,
+ (GLenum face, GLenum func, GLint ref, GLuint mask),
+ (face, func, ref, mask))
+VISIT_GL_CALL(StencilMask, void, (GLuint mask), (mask))
+VISIT_GL_CALL(StencilMaskSeparate,
+ void,
+ (GLenum face, GLuint mask),
+ (face, mask))
+VISIT_GL_CALL(StencilOp,
+ void,
+ (GLenum fail, GLenum zfail, GLenum zpass),
+ (fail, zfail, zpass))
+VISIT_GL_CALL(StencilOpSeparate,
+ void,
+ (GLenum face, GLenum fail, GLenum zfail, GLenum zpass),
+ (face, fail, zfail, zpass))
+VISIT_GL_CALL(TexImage2D,
+ void,
+ (GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const void* pixels),
+ (target,
+ level,
+ internalformat,
+ width,
+ height,
+ border,
+ format,
+ type,
+ pixels))
+VISIT_GL_CALL(TexParameterf,
+ void,
+ (GLenum target, GLenum pname, GLfloat param),
+ (target, pname, param))
+VISIT_GL_CALL(TexParameterfv,
+ void,
+ (GLenum target, GLenum pname, const GLfloat* params),
+ (target, pname, params))
+VISIT_GL_CALL(TexParameteri,
+ void,
+ (GLenum target, GLenum pname, GLint param),
+ (target, pname, param))
+VISIT_GL_CALL(TexParameteriv,
+ void,
+ (GLenum target, GLenum pname, const GLint* params),
+ (target, pname, params))
+VISIT_GL_CALL(
+ TexSubImage2D,
+ void,
+ (GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ const void* pixels),
+ (target, level, xoffset, yoffset, width, height, format, type, pixels))
+VISIT_GL_CALL(Uniform1f, void, (GLint location, GLfloat x), (location, x))
+VISIT_GL_CALL(Uniform1fv,
+ void,
+ (GLint location, GLsizei count, const GLfloat* v),
+ (location, count, v))
+VISIT_GL_CALL(Uniform1i, void, (GLint location, GLint x), (location, x))
+VISIT_GL_CALL(Uniform1iv,
+ void,
+ (GLint location, GLsizei count, const GLint* v),
+ (location, count, v))
+VISIT_GL_CALL(Uniform2f,
+ void,
+ (GLint location, GLfloat x, GLfloat y),
+ (location, x, y))
+VISIT_GL_CALL(Uniform2fv,
+ void,
+ (GLint location, GLsizei count, const GLfloat* v),
+ (location, count, v))
+VISIT_GL_CALL(Uniform2i,
+ void,
+ (GLint location, GLint x, GLint y),
+ (location, x, y))
+VISIT_GL_CALL(Uniform2iv,
+ void,
+ (GLint location, GLsizei count, const GLint* v),
+ (location, count, v))
+VISIT_GL_CALL(Uniform3f,
+ void,
+ (GLint location, GLfloat x, GLfloat y, GLfloat z),
+ (location, x, y, z))
+VISIT_GL_CALL(Uniform3fv,
+ void,
+ (GLint location, GLsizei count, const GLfloat* v),
+ (location, count, v))
+VISIT_GL_CALL(Uniform3i,
+ void,
+ (GLint location, GLint x, GLint y, GLint z),
+ (location, x, y, z))
+VISIT_GL_CALL(Uniform3iv,
+ void,
+ (GLint location, GLsizei count, const GLint* v),
+ (location, count, v))
+VISIT_GL_CALL(Uniform4f,
+ void,
+ (GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w),
+ (location, x, y, z, w))
+VISIT_GL_CALL(Uniform4fv,
+ void,
+ (GLint location, GLsizei count, const GLfloat* v),
+ (location, count, v))
+VISIT_GL_CALL(Uniform4i,
+ void,
+ (GLint location, GLint x, GLint y, GLint z, GLint w),
+ (location, x, y, z, w))
+VISIT_GL_CALL(Uniform4iv,
+ void,
+ (GLint location, GLsizei count, const GLint* v),
+ (location, count, v))
+VISIT_GL_CALL(
+ UniformMatrix2fv,
+ void,
+ (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value),
+ (location, count, transpose, value))
+VISIT_GL_CALL(
+ UniformMatrix3fv,
+ void,
+ (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value),
+ (location, count, transpose, value))
+VISIT_GL_CALL(
+ UniformMatrix4fv,
+ void,
+ (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value),
+ (location, count, transpose, value))
+VISIT_GL_CALL(UseProgram, void, (GLuint program), (program))
+VISIT_GL_CALL(ValidateProgram, void, (GLuint program), (program))
+VISIT_GL_CALL(VertexAttrib1f, void, (GLuint indx, GLfloat x), (indx, x))
+VISIT_GL_CALL(VertexAttrib1fv,
+ void,
+ (GLuint indx, const GLfloat* values),
+ (indx, values))
+VISIT_GL_CALL(VertexAttrib2f,
+ void,
+ (GLuint indx, GLfloat x, GLfloat y),
+ (indx, x, y))
+VISIT_GL_CALL(VertexAttrib2fv,
+ void,
+ (GLuint indx, const GLfloat* values),
+ (indx, values))
+VISIT_GL_CALL(VertexAttrib3f,
+ void,
+ (GLuint indx, GLfloat x, GLfloat y, GLfloat z),
+ (indx, x, y, z))
+VISIT_GL_CALL(VertexAttrib3fv,
+ void,
+ (GLuint indx, const GLfloat* values),
+ (indx, values))
+VISIT_GL_CALL(VertexAttrib4f,
+ void,
+ (GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w),
+ (indx, x, y, z, w))
+VISIT_GL_CALL(VertexAttrib4fv,
+ void,
+ (GLuint indx, const GLfloat* values),
+ (indx, values))
+VISIT_GL_CALL(VertexAttribPointer,
+ void,
+ (GLuint indx,
+ GLint size,
+ GLenum type,
+ GLboolean normalized,
+ GLsizei stride,
+ const void* ptr),
+ (indx, size, type, normalized, stride, ptr))
+VISIT_GL_CALL(Viewport,
+ void,
+ (GLint x, GLint y, GLsizei width, GLsizei height),
+ (x, y, width, height))
diff --git a/chromium/mojo/public/c/gles2/gles2_export.h b/chromium/mojo/public/c/gles2/gles2_export.h
new file mode 100644
index 00000000000..4f8796da069
--- /dev/null
+++ b/chromium/mojo/public/c/gles2/gles2_export.h
@@ -0,0 +1,26 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_C_GLES2_GLES2_EXPORT_H_
+#define MOJO_PUBLIC_C_GLES2_GLES2_EXPORT_H_
+
+#if defined(WIN32)
+
+#if defined(MOJO_GLES2_IMPLEMENTATION)
+#define MOJO_GLES2_EXPORT __declspec(dllexport)
+#else
+#define MOJO_GLES2_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_GLES2_IMPLEMENTATION)
+#define MOJO_GLES2_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_GLES2_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#endif // MOJO_PUBLIC_C_GLES2_GLES2_EXPORT_H_
diff --git a/chromium/mojo/public/c/gles2/gles2_types.h b/chromium/mojo/public/c/gles2/gles2_types.h
new file mode 100644
index 00000000000..dd2a21314cc
--- /dev/null
+++ b/chromium/mojo/public/c/gles2/gles2_types.h
@@ -0,0 +1,26 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_C_GLES2_GLES2_TYPES_H_
+#define MOJO_PUBLIC_C_GLES2_GLES2_TYPES_H_
+
+// Note: This header should be compilable as C.
+
+#include <stdint.h>
+
+#include "mojo/public/c/gles2/gles2_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct MojoGLES2ContextPrivate* MojoGLES2Context;
+typedef void (*MojoGLES2ContextLost)(void* closure);
+typedef void (*MojoGLES2DrawAnimationFrame)(void* closure);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_GLES2_GLES2_TYPES_H_
diff --git a/chromium/mojo/public/c/system/buffer.h b/chromium/mojo/public/c/system/buffer.h
new file mode 100644
index 00000000000..86cf8223ee0
--- /dev/null
+++ b/chromium/mojo/public/c/system/buffer.h
@@ -0,0 +1,187 @@
+// Copyright 2014 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.
+
+// This file contains types/constants and functions specific to buffers (and in
+// particular shared buffers).
+// TODO(vtl): Reorganize this file (etc.) to separate general buffer functions
+// from (shared) buffer creation.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_BUFFER_H_
+#define MOJO_PUBLIC_C_SYSTEM_BUFFER_H_
+
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+// |MojoCreateSharedBufferOptions|: Used to specify creation parameters for a
+// shared buffer to |MojoCreateSharedBuffer()|.
+// |uint32_t struct_size|: Set to the size of the
+// |MojoCreateSharedBufferOptions| struct. (Used to allow for future
+// extensions.)
+// |MojoCreateSharedBufferOptionsFlags flags|: Reserved for future use.
+// |MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE|: No flags; default mode.
+//
+// TODO(vtl): Maybe add a flag to indicate whether the memory should be
+// executable or not?
+// TODO(vtl): Also a flag for discardable (ashmem-style) buffers.
+
+typedef uint32_t MojoCreateSharedBufferOptionsFlags;
+
+#ifdef __cplusplus
+const MojoCreateSharedBufferOptionsFlags
+ MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE = 0;
+#else
+#define MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE \
+ ((MojoCreateSharedBufferOptionsFlags) 0)
+#endif
+
+MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int64_t) == 8, int64_t_has_weird_alignment);
+struct MOJO_ALIGNAS(8) MojoCreateSharedBufferOptions {
+ uint32_t struct_size;
+ MojoCreateSharedBufferOptionsFlags flags;
+};
+MOJO_COMPILE_ASSERT(sizeof(MojoCreateSharedBufferOptions) == 8,
+ MojoCreateSharedBufferOptions_has_wrong_size);
+
+// |MojoDuplicateBufferHandleOptions|: Used to specify parameters in duplicating
+// access to a shared buffer to |MojoDuplicateBufferHandle()|.
+// |uint32_t struct_size|: Set to the size of the
+// |MojoDuplicateBufferHandleOptions| struct. (Used to allow for future
+// extensions.)
+// |MojoDuplicateBufferHandleOptionsFlags flags|: Reserved for future use.
+// |MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE|: No flags; default
+// mode.
+//
+// TODO(vtl): Add flags to remove writability (and executability)? Also, COW?
+
+typedef uint32_t MojoDuplicateBufferHandleOptionsFlags;
+
+#ifdef __cplusplus
+const MojoDuplicateBufferHandleOptionsFlags
+ MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE = 0;
+#else
+#define MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE \
+ ((MojoDuplicateBufferHandleOptionsFlags) 0)
+#endif
+
+struct MojoDuplicateBufferHandleOptions {
+ uint32_t struct_size;
+ MojoDuplicateBufferHandleOptionsFlags flags;
+};
+MOJO_COMPILE_ASSERT(sizeof(MojoDuplicateBufferHandleOptions) == 8,
+ MojoDuplicateBufferHandleOptions_has_wrong_size);
+
+// |MojoMapBufferFlags|: Used to specify different modes to |MojoMapBuffer()|.
+// |MOJO_MAP_BUFFER_FLAG_NONE| - No flags; default mode.
+
+typedef uint32_t MojoMapBufferFlags;
+
+#ifdef __cplusplus
+const MojoMapBufferFlags MOJO_MAP_BUFFER_FLAG_NONE = 0;
+#else
+#define MOJO_MAP_BUFFER_FLAG_NONE ((MojoMapBufferFlags) 0)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Note: See the comment in functions.h about the meaning of the "optional"
+// label for pointer parameters.
+
+// Creates a buffer of size |num_bytes| bytes that can be shared between
+// applications (by duplicating the handle -- see |MojoDuplicateBufferHandle()|
+// -- and passing it over a message pipe). To access the buffer, one must call
+// |MojoMapBuffer()|.
+//
+// |options| may be set to null for a shared buffer with the default options.
+//
+// On success, |*shared_buffer_handle| will be set to the handle for the shared
+// buffer. (On failure, it is not modified.)
+//
+// Note: While more than |num_bytes| bytes may apparently be
+// available/visible/readable/writable, trying to use those extra bytes is
+// undefined behavior.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., if
+// |*options| is invalid or |shared_buffer_handle| looks invalid).
+// |MOJO_RESULT_RESOURCE_EXHAUSTED| if a process/system/quota/etc. limit has
+// been reached (e.g., if the requested size was too large, or if the
+// maximum number of handles was exceeded).
+// |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|.
+MOJO_SYSTEM_EXPORT MojoResult MojoCreateSharedBuffer(
+ const struct MojoCreateSharedBufferOptions* options, // Optional.
+ uint64_t num_bytes, // In.
+ MojoHandle* shared_buffer_handle); // Out.
+
+// Duplicates the handle |buffer_handle| to a buffer. This creates another
+// handle (returned in |*new_buffer_handle| on success), which can then be sent
+// to another application over a message pipe, while retaining access to the
+// |buffer_handle| (and any mappings that it may have).
+//
+// |options| may be set to null to duplicate the buffer handle with the default
+// options.
+//
+// On success, |*shared_buffer_handle| will be set to the handle for the new
+// buffer handle. (On failure, it is not modified.)
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., if
+// |buffer_handle| is not a valid buffer handle, |*options| is invalid, or
+// |new_buffer_handle| looks invalid).
+// |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|.
+MOJO_SYSTEM_EXPORT MojoResult MojoDuplicateBufferHandle(
+ MojoHandle buffer_handle,
+ const struct MojoDuplicateBufferHandleOptions* options, // Optional.
+ MojoHandle* new_buffer_handle); // Out.
+
+// Maps the part (at offset |offset| of length |num_bytes|) of the buffer given
+// by |buffer_handle| into memory, with options specified by |flags|. |offset +
+// num_bytes| must be less than or equal to the size of the buffer. On success,
+// |*buffer| points to memory with the requested part of the buffer. (On
+// failure, it is not modified.)
+//
+// A single buffer handle may have multiple active mappings (possibly depending
+// on the buffer type). The permissions (e.g., writable or executable) of the
+// returned memory may depend on the properties of the buffer and properties
+// attached to the buffer handle as well as |flags|.
+//
+// Note: Though data outside the specified range may apparently be
+// available/visible/readable/writable, trying to use those extra bytes is
+// undefined behavior.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., if
+// |buffer_handle| is not a valid buffer handle, the range specified by
+// |offset| and |num_bytes| is not valid, or |buffer| looks invalid).
+// |MOJO_RESULT_RESOURCE_EXHAUSTED| if the mapping operation itself failed
+// (e.g., due to not having appropriate address space available).
+MOJO_SYSTEM_EXPORT MojoResult MojoMapBuffer(MojoHandle buffer_handle,
+ uint64_t offset,
+ uint64_t num_bytes,
+ void** buffer, // Out.
+ MojoMapBufferFlags flags);
+
+// Unmaps a buffer pointer that was mapped by |MojoMapBuffer()|. |buffer| must
+// have been the result of |MojoMapBuffer()| (not some pointer strictly inside
+// the mapped memory), and the entire mapping will be removed (partial unmapping
+// is not supported). A mapping may only be unmapped exactly once.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if |buffer| is invalid (e.g., if it is not
+// the result of |MojoMapBuffer()| or it has already been unmapped).
+MOJO_SYSTEM_EXPORT MojoResult MojoUnmapBuffer(void* buffer); // In.
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_SYSTEM_BUFFER_H_
diff --git a/chromium/mojo/public/c/system/core.h b/chromium/mojo/public/c/system/core.h
new file mode 100644
index 00000000000..5ea03fc5a5e
--- /dev/null
+++ b/chromium/mojo/public/c/system/core.h
@@ -0,0 +1,20 @@
+// Copyright 2014 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.
+
+// This is a catch-all header that includes everything.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_CORE_H_
+#define MOJO_PUBLIC_C_SYSTEM_CORE_H_
+
+#include "mojo/public/c/system/buffer.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/c/system/functions.h"
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+#endif // MOJO_PUBLIC_C_SYSTEM_CORE_H_
diff --git a/chromium/mojo/public/c/system/data_pipe.h b/chromium/mojo/public/c/system/data_pipe.h
new file mode 100644
index 00000000000..e28cf6eff11
--- /dev/null
+++ b/chromium/mojo/public/c/system/data_pipe.h
@@ -0,0 +1,367 @@
+// Copyright 2014 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.
+
+// This file contains types/constants and functions specific to data pipes.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_DATA_PIPE_H_
+#define MOJO_PUBLIC_C_SYSTEM_DATA_PIPE_H_
+
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+// |MojoCreateDataPipeOptions|: Used to specify creation parameters for a data
+// pipe to |MojoCreateDataPipe()|.
+// |uint32_t struct_size|: Set to the size of the |MojoCreateDataPipeOptions|
+// struct. (Used to allow for future extensions.)
+// |MojoCreateDataPipeOptionsFlags flags|: Used to specify different modes of
+// operation.
+// |MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE|: No flags; default mode.
+// |MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD|: May discard data for
+// whatever reason; best-effort delivery. In particular, if the capacity
+// is reached, old data may be discard to make room for new data.
+// |uint32_t element_num_bytes|: The size of an element, in bytes. All
+// transactions and buffers will consist of an integral number of
+// elements. Must be nonzero.
+// |uint32_t capacity_num_bytes|: The capacity of the data pipe, in number of
+// bytes; must be a multiple of |element_num_bytes|. The data pipe will
+// always be able to queue AT LEAST this much data. Set to zero to opt for
+// a system-dependent automatically-calculated capacity (which will always
+// be at least one element).
+
+typedef uint32_t MojoCreateDataPipeOptionsFlags;
+
+#ifdef __cplusplus
+const MojoCreateDataPipeOptionsFlags
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE = 0;
+const MojoCreateDataPipeOptionsFlags
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD = 1 << 0;
+#else
+#define MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE \
+ ((MojoCreateDataPipeOptionsFlags) 0)
+#define MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD \
+ ((MojoCreateDataPipeOptionsFlags) 1 << 0)
+#endif
+
+MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int64_t) == 8, int64_t_has_weird_alignment);
+struct MOJO_ALIGNAS(8) MojoCreateDataPipeOptions {
+ uint32_t struct_size;
+ MojoCreateDataPipeOptionsFlags flags;
+ uint32_t element_num_bytes;
+ uint32_t capacity_num_bytes;
+};
+MOJO_COMPILE_ASSERT(sizeof(MojoCreateDataPipeOptions) == 16,
+ MojoCreateDataPipeOptions_has_wrong_size);
+
+// |MojoWriteDataFlags|: Used to specify different modes to |MojoWriteData()|
+// and |MojoBeginWriteData()|.
+// |MOJO_WRITE_DATA_FLAG_NONE| - No flags; default mode.
+// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| - Write either all the elements
+// requested or none of them.
+
+typedef uint32_t MojoWriteDataFlags;
+
+#ifdef __cplusplus
+const MojoWriteDataFlags MOJO_WRITE_DATA_FLAG_NONE = 0;
+const MojoWriteDataFlags MOJO_WRITE_DATA_FLAG_ALL_OR_NONE = 1 << 0;
+#else
+#define MOJO_WRITE_DATA_FLAG_NONE ((MojoWriteDataFlags) 0)
+#define MOJO_WRITE_DATA_FLAG_ALL_OR_NONE ((MojoWriteDataFlags) 1 << 0)
+#endif
+
+// |MojoReadDataFlags|: Used to specify different modes to |MojoReadData()| and
+// |MojoBeginReadData()|.
+// |MOJO_READ_DATA_FLAG_NONE| - No flags; default mode.
+// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| - Read (or discard) either the requested
+// number of elements or none.
+// |MOJO_READ_DATA_FLAG_DISCARD| - Discard (up to) the requested number of
+// elements.
+// |MOJO_READ_DATA_FLAG_QUERY| - Query the number of elements available to
+// read. For use with |MojoReadData()| only. Mutually exclusive with
+// |MOJO_READ_DATA_FLAG_DISCARD| and |MOJO_READ_DATA_FLAG_ALL_OR_NONE| is
+// ignored if this flag is set.
+
+typedef uint32_t MojoReadDataFlags;
+
+#ifdef __cplusplus
+const MojoReadDataFlags MOJO_READ_DATA_FLAG_NONE = 0;
+const MojoReadDataFlags MOJO_READ_DATA_FLAG_ALL_OR_NONE = 1 << 0;
+const MojoReadDataFlags MOJO_READ_DATA_FLAG_DISCARD = 1 << 1;
+const MojoReadDataFlags MOJO_READ_DATA_FLAG_QUERY = 1 << 2;
+#else
+#define MOJO_READ_DATA_FLAG_NONE ((MojoReadDataFlags) 0)
+#define MOJO_READ_DATA_FLAG_ALL_OR_NONE ((MojoReadDataFlags) 1 << 0)
+#define MOJO_READ_DATA_FLAG_DISCARD ((MojoReadDataFlags) 1 << 1)
+#define MOJO_READ_DATA_FLAG_QUERY ((MojoReadDataFlags) 1 << 2)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Note: See the comment in functions.h about the meaning of the "optional"
+// label for pointer parameters.
+
+// Creates a data pipe, which is a unidirectional communication channel for
+// unframed data, with the given options. Data is unframed, but must come as
+// (multiples of) discrete elements, of the size given in |options|. See
+// |MojoCreateDataPipeOptions| for a description of the different options
+// available for data pipes.
+//
+// |options| may be set to null for a data pipe with the default options (which
+// will have an element size of one byte and have some system-dependent
+// capacity).
+//
+// On success, |*data_pipe_producer_handle| will be set to the handle for the
+// producer and |*data_pipe_consumer_handle| will be set to the handle for the
+// consumer. (On failure, they are not modified.)
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., if
+// |*options| is invalid or one of the handle pointers looks invalid).
+// |MOJO_RESULT_RESOURCE_EXHAUSTED| if a process/system/quota/etc. limit has
+// been reached (e.g., if the requested capacity was too large, or if the
+// maximum number of handles was exceeded).
+// |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|.
+MOJO_SYSTEM_EXPORT MojoResult MojoCreateDataPipe(
+ const struct MojoCreateDataPipeOptions* options, // Optional.
+ MojoHandle* data_pipe_producer_handle, // Out.
+ MojoHandle* data_pipe_consumer_handle); // Out.
+
+// Writes the given data to the data pipe producer given by
+// |data_pipe_producer_handle|. |elements| points to data of size |*num_bytes|;
+// |*num_bytes| should be a multiple of the data pipe's element size. If
+// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| is set in |flags|, either all the data
+// will be written or none is.
+//
+// On success, |*num_bytes| is set to the amount of data that was actually
+// written.
+//
+// Note: If the data pipe has the "may discard" option flag (specified on
+// creation), this will discard as much data as required to write the given
+// data, starting with the earliest written data that has not been consumed.
+// However, even with "may discard", if |*num_bytes| is greater than the data
+// pipe's capacity (and |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| is not set), this
+// will write the maximum amount possible (namely, the data pipe's capacity) and
+// set |*num_bytes| to that amount. It will *not* discard data from |elements|.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., if
+// |data_pipe_producer_dispatcher| is not a handle to a data pipe
+// producer, |elements| does not look like a valid pointer, or
+// |*num_bytes| is not a multiple of the data pipe's element size).
+// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe consumer handle has been
+// closed.
+// |MOJO_RESULT_OUT_OF_RANGE| if |flags| has
+// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set and the required amount of data
+// (specified by |*num_bytes|) could not be written.
+// |MOJO_RESULT_BUSY| if there is a two-phase write ongoing with
+// |data_pipe_producer_handle| (i.e., |MojoBeginWriteData()| has been
+// called, but not yet the matching |MojoEndWriteData()|).
+// |MOJO_RESULT_SHOULD_WAIT| if no data can currently be written (and the
+// consumer is still open) and |flags| does *not* have
+// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set.
+//
+// TODO(vtl): Should there be a way of querying how much data can be written?
+MOJO_SYSTEM_EXPORT MojoResult MojoWriteData(
+ MojoHandle data_pipe_producer_handle,
+ const void* elements,
+ uint32_t* num_bytes, // In/out.
+ MojoWriteDataFlags flags);
+
+// Begins a two-phase write to the data pipe producer given by
+// |data_pipe_producer_handle|. On success, |*buffer| will be a pointer to which
+// the caller can write |*buffer_num_bytes| bytes of data. If flags has
+// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set, then the output value
+// |*buffer_num_bytes| will be at least as large as its input value, which must
+// also be a multiple of the element size (if |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE|
+// is not set, the input value of |*buffer_num_bytes| is ignored).
+//
+// During a two-phase write, |data_pipe_producer_handle| is *not* writable.
+// E.g., if another thread tries to write to it, it will get |MOJO_RESULT_BUSY|;
+// that thread can then wait for |data_pipe_producer_handle| to become writable
+// again.
+//
+// Once the caller has finished writing data to |*buffer|, it should call
+// |MojoEndWriteData()| to specify the amount written and to complete the
+// two-phase write.
+//
+// Note: If the data pipe has the "may discard" option flag (specified on
+// creation) and |flags| has |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set, this may
+// discard some data.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., if
+// |data_pipe_producer_handle| is not a handle to a data pipe producer,
+// |buffer| or |buffer_num_bytes| does not look like a valid pointer, or
+// flags has |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set and
+// |*buffer_num_bytes| is not a multiple of the element size).
+// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe consumer handle has been
+// closed.
+// |MOJO_RESULT_OUT_OF_RANGE| if |flags| has
+// |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set and the required amount of data
+// (specified by |*buffer_num_bytes|) cannot be written contiguously at
+// this time. (Note that there may be space available for the required
+// amount of data, but the "next" write position may not be large enough.)
+// |MOJO_RESULT_BUSY| if there is already a two-phase write ongoing with
+// |data_pipe_producer_handle| (i.e., |MojoBeginWriteData()| has been
+// called, but not yet the matching |MojoEndWriteData()|).
+// |MOJO_RESULT_SHOULD_WAIT| if no data can currently be written (and the
+// consumer is still open).
+MOJO_SYSTEM_EXPORT MojoResult MojoBeginWriteData(
+ MojoHandle data_pipe_producer_handle,
+ void** buffer, // Out.
+ uint32_t* buffer_num_bytes, // In/out.
+ MojoWriteDataFlags flags);
+
+// Ends a two-phase write to the data pipe producer given by
+// |data_pipe_producer_handle| that was begun by a call to
+// |MojoBeginWriteData()| on the same handle. |num_bytes_written| should
+// indicate the amount of data actually written; it must be less than or equal
+// to the value of |*buffer_num_bytes| output by |MojoBeginWriteData()| and must
+// be a multiple of the element size. The buffer given by |*buffer| from
+// |MojoBeginWriteData()| must have been filled with exactly |num_bytes_written|
+// bytes of data.
+//
+// On failure, the two-phase write (if any) is ended (so the handle may become
+// writable again, if there's space available) but no data written to |*buffer|
+// is "put into" the data pipe.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if |data_pipe_producer_handle| is not a
+// handle to a data pipe producer or |num_bytes_written| is invalid
+// (greater than the maximum value provided by |MojoBeginWriteData()| or
+// not a multiple of the element size).
+// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe producer is not in a
+// two-phase write (e.g., |MojoBeginWriteData()| was not called or
+// |MojoEndWriteData()| has already been called).
+MOJO_SYSTEM_EXPORT MojoResult MojoEndWriteData(
+ MojoHandle data_pipe_producer_handle,
+ uint32_t num_bytes_written);
+
+// Reads data from the data pipe consumer given by |data_pipe_consumer_handle|.
+// May also be used to discard data or query the amount of data available.
+//
+// If |flags| has neither |MOJO_READ_DATA_FLAG_DISCARD| nor
+// |MOJO_READ_DATA_FLAG_QUERY| set, this tries to read up to |*num_bytes| (which
+// must be a multiple of the data pipe's element size) bytes of data to
+// |elements| and set |*num_bytes| to the amount actually read. If flags has
+// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set, it will either read exactly
+// |*num_bytes| bytes of data or none.
+//
+// If flags has |MOJO_READ_DATA_FLAG_DISCARD| set, it discards up to
+// |*num_bytes| (which again be a multiple of the element size) bytes of data,
+// setting |*num_bytes| to the amount actually discarded. If flags has
+// |MOJO_READ_DATA_FLAG_ALL_OR_NONE|, it will either discard exactly
+// |*num_bytes| bytes of data or none. In this case, |MOJO_READ_DATA_FLAG_QUERY|
+// must not be set, and |elements| is ignored (and should typically be set to
+// null).
+//
+// If flags has |MOJO_READ_DATA_FLAG_QUERY| set, it queries the amount of data
+// available, setting |*num_bytes| to the number of bytes available. In this
+// case, |MOJO_READ_DATA_FLAG_DISCARD| must not be set, and
+// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| is ignored, as are |elements| and the input
+// value of |*num_bytes|.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success (see above for a description of the different
+// operations).
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g.,
+// |data_pipe_consumer_handle| is invalid, the combination of flags in
+// |flags| is invalid, etc.).
+// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe producer handle has been
+// closed and data (or the required amount of data) was not available to
+// be read or discarded.
+// |MOJO_RESULT_OUT_OF_RANGE| if |flags| has |MOJO_READ_DATA_FLAG_ALL_OR_NONE|
+// set and the required amount of data is not available to be read or
+// discarded (and the producer is still open).
+// |MOJO_RESULT_BUSY| if there is a two-phase read ongoing with
+// |data_pipe_consumer_handle| (i.e., |MojoBeginReadData()| has been
+// called, but not yet the matching |MojoEndReadData()|).
+// |MOJO_RESULT_SHOULD_WAIT| if there is no data to be read or discarded (and
+// the producer is still open) and |flags| does *not* have
+// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set.
+MOJO_SYSTEM_EXPORT MojoResult MojoReadData(
+ MojoHandle data_pipe_consumer_handle,
+ void* elements, // Out.
+ uint32_t* num_bytes, // In/out.
+ MojoReadDataFlags flags);
+
+// Begins a two-phase read from the data pipe consumer given by
+// |data_pipe_consumer_handle|. On success, |*buffer| will be a pointer from
+// which the caller can read |*buffer_num_bytes| bytes of data. If flags has
+// |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set, then the output value
+// |*buffer_num_bytes| will be at least as large as its input value, which must
+// also be a multiple of the element size (if |MOJO_READ_DATA_FLAG_ALL_OR_NONE|
+// is not set, the input value of |*buffer_num_bytes| is ignored). |flags| must
+// not have |MOJO_READ_DATA_FLAG_DISCARD| or |MOJO_READ_DATA_FLAG_QUERY| set.
+//
+// During a two-phase read, |data_pipe_consumer_handle| is *not* readable.
+// E.g., if another thread tries to read from it, it will get
+// |MOJO_RESULT_BUSY|; that thread can then wait for |data_pipe_consumer_handle|
+// to become readable again.
+//
+// Once the caller has finished reading data from |*buffer|, it should call
+// |MojoEndReadData()| to specify the amount read and to complete the two-phase
+// read.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., if
+// |data_pipe_consumer_handle| is not a handle to a data pipe consumer,
+// |buffer| or |buffer_num_bytes| does not look like a valid pointer,
+// |flags| has |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set and
+// |*buffer_num_bytes| is not a multiple of the element size, or |flags|
+// has invalid flags set).
+// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe producer handle has been
+// closed.
+// |MOJO_RESULT_OUT_OF_RANGE| if |flags| has |MOJO_READ_DATA_FLAG_ALL_OR_NONE|
+// set and the required amount of data (specified by |*buffer_num_bytes|)
+// cannot be read from a contiguous buffer at this time. (Note that there
+// may be the required amount of data, but it may not be contiguous.)
+// |MOJO_RESULT_BUSY| if there is already a two-phase read ongoing with
+// |data_pipe_consumer_handle| (i.e., |MojoBeginReadData()| has been
+// called, but not yet the matching |MojoEndReadData()|).
+// |MOJO_RESULT_SHOULD_WAIT| if no data can currently be read (and the
+// producer is still open).
+MOJO_SYSTEM_EXPORT MojoResult MojoBeginReadData(
+ MojoHandle data_pipe_consumer_handle,
+ const void** buffer, // Out.
+ uint32_t* buffer_num_bytes, // In/out.
+ MojoReadDataFlags flags);
+
+// Ends a two-phase read from the data pipe consumer given by
+// |data_pipe_consumer_handle| that was begun by a call to |MojoBeginReadData()|
+// on the same handle. |num_bytes_read| should indicate the amount of data
+// actually read; it must be less than or equal to the value of
+// |*buffer_num_bytes| output by |MojoBeginReadData()| and must be a multiple of
+// the element size.
+//
+// On failure, the two-phase read (if any) is ended (so the handle may become
+// readable again) but no data is "removed" from the data pipe.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if |data_pipe_consumer_handle| is not a
+// handle to a data pipe consumer or |num_bytes_written| is invalid
+// (greater than the maximum value provided by |MojoBeginReadData()| or
+// not a multiple of the element size).
+// |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe consumer is not in a
+// two-phase read (e.g., |MojoBeginReadData()| was not called or
+// |MojoEndReadData()| has already been called).
+MOJO_SYSTEM_EXPORT MojoResult MojoEndReadData(
+ MojoHandle data_pipe_consumer_handle,
+ uint32_t num_bytes_read);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_SYSTEM_DATA_PIPE_H_
diff --git a/chromium/mojo/public/c/system/functions.h b/chromium/mojo/public/c/system/functions.h
new file mode 100644
index 00000000000..9d2ef6ca800
--- /dev/null
+++ b/chromium/mojo/public/c/system/functions.h
@@ -0,0 +1,87 @@
+// Copyright 2014 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.
+
+// This file contains basic functions common to different Mojo system APIs.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_FUNCTIONS_H_
+#define MOJO_PUBLIC_C_SYSTEM_FUNCTIONS_H_
+
+// Note: This header should be compilable as C.
+
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Note: Pointer parameters that are labelled "optional" may be null (at least
+// under some circumstances). Non-const pointer parameters are also labeled
+// "in", "out", or "in/out", to indicate how they are used. (Note that how/if
+// such a parameter is used may depend on other parameters or the requested
+// operation's success/failure. E.g., a separate |flags| parameter may control
+// whether a given "in/out" parameter is used for input, output, or both.)
+
+// Platform-dependent monotonically increasing tick count representing "right
+// now." The resolution of this clock is ~1-15ms. Resolution varies depending
+// on hardware/operating system configuration.
+MOJO_SYSTEM_EXPORT MojoTimeTicks MojoGetTimeTicksNow(void);
+
+// Closes the given |handle|.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not a valid handle.
+//
+// Concurrent operations on |handle| may succeed (or fail as usual) if they
+// happen before the close, be cancelled with result |MOJO_RESULT_CANCELLED| if
+// they properly overlap (this is likely the case with |MojoWait()|, etc.), or
+// fail with |MOJO_RESULT_INVALID_ARGUMENT| if they happen after.
+MOJO_SYSTEM_EXPORT MojoResult MojoClose(MojoHandle handle);
+
+// Waits on the given handle until a signal indicated by |signals| is satisfied
+// or until |deadline| has passed.
+//
+// Returns:
+// |MOJO_RESULT_OK| if some signal in |signals| was satisfied (or is already
+// satisfied).
+// |MOJO_RESULT_INVALID_ARGUMENT| if |handle| is not a valid handle (e.g., if
+// it has already been closed).
+// |MOJO_RESULT_DEADLINE_EXCEEDED| if the deadline has passed without any of
+// the signals being satisfied.
+// |MOJO_RESULT_FAILED_PRECONDITION| if it is or becomes impossible that any
+// signal in |signals| will ever be satisfied.
+//
+// If there are multiple waiters (on different threads, obviously) waiting on
+// the same handle and signal, and that signal becomes is satisfied, all waiters
+// will be awoken.
+MOJO_SYSTEM_EXPORT MojoResult MojoWait(MojoHandle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline);
+
+// Waits on |handles[0]|, ..., |handles[num_handles-1]| for at least one of them
+// to satisfy a signal indicated by |signals[0]|, ..., |signals[num_handles-1]|,
+// respectively, or until |deadline| has passed.
+//
+// Returns:
+// The index |i| (from 0 to |num_handles-1|) if |handle[i]| satisfies a signal
+// from |signals[i]|.
+// |MOJO_RESULT_INVALID_ARGUMENT| if some |handle[i]| is not a valid handle
+// (e.g., if it has already been closed).
+// |MOJO_RESULT_DEADLINE_EXCEEDED| if the deadline has passed without any of
+// handles satisfying any of its signals.
+// |MOJO_RESULT_FAILED_PRECONDITION| if it is or becomes impossible that SOME
+// |handle[i]| will ever satisfy any of the signals in |signals[i]|.
+MOJO_SYSTEM_EXPORT MojoResult MojoWaitMany(const MojoHandle* handles,
+ const MojoHandleSignals* signals,
+ uint32_t num_handles,
+ MojoDeadline deadline);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_SYSTEM_FUNCTIONS_H_
diff --git a/chromium/mojo/public/c/system/macros.h b/chromium/mojo/public/c/system/macros.h
new file mode 100644
index 00000000000..f1e3c7d6831
--- /dev/null
+++ b/chromium/mojo/public/c/system/macros.h
@@ -0,0 +1,72 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_MACROS_H_
+#define MOJO_PUBLIC_C_SYSTEM_MACROS_H_
+
+#include <stddef.h>
+
+// Annotate a variable indicating it's okay if it's unused.
+// Use like:
+// int x MOJO_ALLOW_UNUSED = ...;
+#if defined(__GNUC__)
+#define MOJO_ALLOW_UNUSED __attribute__((unused))
+#else
+#define MOJO_ALLOW_UNUSED
+#endif
+
+// Annotate a function indicating that the caller must examine the return value.
+// Use like:
+// int foo() MOJO_WARN_UNUSED_RESULT;
+// Note that it can only be used on the prototype, and not the definition.
+#if defined(__GNUC__)
+#define MOJO_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define MOJO_WARN_UNUSED_RESULT
+#endif
+
+// Assert things at compile time. (|msg| should be a valid identifier name.)
+// This macro is currently C++-only, but we want to use it in the C core.h.
+// Use like:
+// MOJO_COMPILE_ASSERT(sizeof(Foo) == 12, Foo_has_invalid_size);
+#if __cplusplus >= 201103L
+#define MOJO_COMPILE_ASSERT(expr, msg) static_assert(expr, #msg)
+#elif defined(__cplusplus)
+namespace mojo { template <bool> struct CompileAssert {}; }
+#define MOJO_COMPILE_ASSERT(expr, msg) \
+ typedef ::mojo::CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
+#else
+#define MOJO_COMPILE_ASSERT(expr, msg)
+#endif
+
+// Like the C++11 |alignof| operator.
+#if __cplusplus >= 201103L
+#define MOJO_ALIGNOF(type) alignof(type)
+#elif defined(__GNUC__)
+#define MOJO_ALIGNOF(type) __alignof__(type)
+#elif defined(_MSC_VER)
+// The use of |sizeof| is to work around a bug in MSVC 2010 (see
+// http://goo.gl/isH0C; supposedly fixed since then).
+#define MOJO_ALIGNOF(type) (sizeof(type) - sizeof(type) + __alignof(type))
+#else
+#error "Please define MOJO_ALIGNOF() for your compiler."
+#endif
+
+// Specify the alignment of a |struct|, etc.
+// Use like:
+// struct MOJO_ALIGNAS(8) Foo { ... };
+// Unlike the C++11 |alignas()|, |alignment| must be an integer. It may not be a
+// type, nor can it be an expression like |MOJO_ALIGNOF(type)| (due to the
+// non-C++11 MSVS version).
+#if __cplusplus >= 201103L
+#define MOJO_ALIGNAS(alignment) alignas(alignment)
+#elif defined(__GNUC__)
+#define MOJO_ALIGNAS(alignment) __attribute__((aligned(alignment)))
+#elif defined(_MSC_VER)
+#define MOJO_ALIGNAS(alignment) __declspec(align(alignment))
+#else
+#error "Please define MOJO_ALIGNAS() for your compiler."
+#endif
+
+#endif // MOJO_PUBLIC_C_SYSTEM_MACROS_H_
diff --git a/chromium/mojo/public/c/system/message_pipe.h b/chromium/mojo/public/c/system/message_pipe.h
new file mode 100644
index 00000000000..0d0b61b7c5b
--- /dev/null
+++ b/chromium/mojo/public/c/system/message_pipe.h
@@ -0,0 +1,178 @@
+// Copyright 2014 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.
+
+// This file contains types/constants and functions specific to message pipes.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_MESSAGE_PIPE_H_
+#define MOJO_PUBLIC_C_SYSTEM_MESSAGE_PIPE_H_
+
+#include "mojo/public/c/system/macros.h"
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/c/system/types.h"
+
+// |MojoCreateMessagePipeOptions|: Used to specify creation parameters for a
+// message pipe to |MojoCreateMessagePipe()|.
+// |uint32_t struct_size|: Set to the size of the
+// |MojoCreateMessagePipeOptions| struct. (Used to allow for future
+// extensions.)
+// |MojoCreateMessagePipeOptionsFlags flags|: Reserved for future use.
+// |MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE|: No flags; default mode.
+
+typedef uint32_t MojoCreateMessagePipeOptionsFlags;
+
+#ifdef __cplusplus
+const MojoCreateMessagePipeOptionsFlags
+ MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE = 0;
+#else
+#define MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE \
+ ((MojoCreateMessagePipeOptionsFlags) 0)
+#endif
+
+MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int64_t) == 8, int64_t_has_weird_alignment);
+struct MOJO_ALIGNAS(8) MojoCreateMessagePipeOptions {
+ uint32_t struct_size;
+ MojoCreateMessagePipeOptionsFlags flags;
+};
+MOJO_COMPILE_ASSERT(sizeof(MojoCreateMessagePipeOptions) == 8,
+ MojoCreateMessagePipeOptions_has_wrong_size);
+
+// |MojoWriteMessageFlags|: Used to specify different modes to
+// |MojoWriteMessage()|.
+// |MOJO_WRITE_MESSAGE_FLAG_NONE| - No flags; default mode.
+
+typedef uint32_t MojoWriteMessageFlags;
+
+#ifdef __cplusplus
+const MojoWriteMessageFlags MOJO_WRITE_MESSAGE_FLAG_NONE = 0;
+#else
+#define MOJO_WRITE_MESSAGE_FLAG_NONE ((MojoWriteMessageFlags) 0)
+#endif
+
+// |MojoReadMessageFlags|: Used to specify different modes to
+// |MojoReadMessage()|.
+// |MOJO_READ_MESSAGE_FLAG_NONE| - No flags; default mode.
+// |MOJO_READ_MESSAGE_FLAG_MAY_DISCARD| - If the message is unable to be read
+// for whatever reason (e.g., the caller-supplied buffer is too small),
+// discard the message (i.e., simply dequeue it).
+
+typedef uint32_t MojoReadMessageFlags;
+
+#ifdef __cplusplus
+const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_NONE = 0;
+const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_MAY_DISCARD = 1 << 0;
+#else
+#define MOJO_READ_MESSAGE_FLAG_NONE ((MojoReadMessageFlags) 0)
+#define MOJO_READ_MESSAGE_FLAG_MAY_DISCARD ((MojoReadMessageFlags) 1 << 0)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Note: See the comment in functions.h about the meaning of the "optional"
+// label for pointer parameters.
+
+// Creates a message pipe, which is a bidirectional communication channel for
+// framed data (i.e., messages). Messages can contain plain data and/or Mojo
+// handles.
+//
+// |options| may be set to null for a message pipe with the default options.
+//
+// On success, |*message_pipe_handle0| and |*message_pipe_handle1| are set to
+// handles for the two endpoints (ports) for the message pipe.
+//
+// Returns:
+// |MOJO_RESULT_OK| on success.
+// |MOJO_RESULT_INVALID_ARGUMENT| if |message_pipe_handle0| and/or
+// |message_pipe_handle1| do not appear to be valid pointers.
+// |MOJO_RESULT_RESOURCE_EXHAUSTED| if a process/system/quota/etc. limit has
+// been reached.
+//
+// TODO(vtl): Add an options struct pointer argument.
+MOJO_SYSTEM_EXPORT MojoResult MojoCreateMessagePipe(
+ const struct MojoCreateMessagePipeOptions* options, // Optional.
+ MojoHandle* message_pipe_handle0, // Out.
+ MojoHandle* message_pipe_handle1); // Out.
+
+// Writes a message to the message pipe endpoint given by |message_pipe_handle|,
+// with message data specified by |bytes| of size |num_bytes| and attached
+// handles specified by |handles| of count |num_handles|, and options specified
+// by |flags|. If there is no message data, |bytes| may be null, in which case
+// |num_bytes| must be zero. If there are no attached handles, |handles| may be
+// null, in which case |num_handles| must be zero.
+//
+// If handles are attached, on success the handles will no longer be valid (the
+// receiver will receive equivalent, but logically different, handles). Handles
+// to be sent should not be in simultaneous use (e.g., on another thread).
+//
+// Returns:
+// |MOJO_RESULT_OK| on success (i.e., the message was enqueued).
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid (e.g., if
+// |message_pipe_handle| is not a valid handle, or some of the
+// requirements above are not satisfied).
+// |MOJO_RESULT_RESOURCE_EXHAUSTED| if some system limit has been reached, or
+// the number of handles to send is too large (TODO(vtl): reconsider the
+// latter case).
+// |MOJO_RESULT_FAILED_PRECONDITION| if the other endpoint has been closed.
+// Note that closing an endpoint is not necessarily synchronous (e.g.,
+// across processes), so this function may be succeed even if the other
+// endpoint has been closed (in which case the message would be dropped).
+// |MOJO_RESULT_BUSY| if some handle to be sent is currently in use.
+//
+// TODO(vtl): Add a notion of capacity for message pipes, and return
+// |MOJO_RESULT_SHOULD_WAIT| if the message pipe is full.
+MOJO_SYSTEM_EXPORT MojoResult MojoWriteMessage(
+ MojoHandle message_pipe_handle,
+ const void* bytes, // Optional.
+ uint32_t num_bytes,
+ const MojoHandle* handles, // Optional.
+ uint32_t num_handles,
+ MojoWriteMessageFlags flags);
+
+// Reads a message from the message pipe endpoint given by
+// |message_pipe_handle|; also usable to query the size of the next message or
+// discard the next message. |bytes|/|*num_bytes| indicate the buffer/buffer
+// size to receive the message data (if any) and |handles|/|*num_handles|
+// indicate the buffer/maximum handle count to receive the attached handles (if
+// any).
+//
+// |num_bytes| and |num_handles| are optional "in-out" parameters. If non-null,
+// on return |*num_bytes| and |*num_handles| will usually indicate the number
+// of bytes and number of attached handles in the "next" message, respectively,
+// whether that message was read or not. (If null, the number of bytes/handles
+// is treated as zero.)
+//
+// If |bytes| is null, then |*num_bytes| must be zero, and similarly for
+// |handles| and |*num_handles|.
+//
+// Partial reads are NEVER done. Either a full read is done and |MOJO_RESULT_OK|
+// returned, or the read is NOT done and |MOJO_RESULT_RESOURCE_EXHAUSTED| is
+// returned (if |MOJO_READ_MESSAGE_FLAG_MAY_DISCARD| was set, the message is
+// also discarded in this case).
+//
+// Returns:
+// |MOJO_RESULT_OK| on success (i.e., a message was actually read).
+// |MOJO_RESULT_INVALID_ARGUMENT| if some argument was invalid.
+// |MOJO_RESULT_FAILED_PRECONDITION| if the other endpoint has been closed.
+// |MOJO_RESULT_RESOURCE_EXHAUSTED| if one of the buffers to receive the
+// message/attached handles (|bytes|/|*num_bytes| or
+// |handles|/|*num_handles|) was too small. (TODO(vtl): Reconsider this
+// error code; should distinguish this from the hitting-system-limits
+// case.)
+// |MOJO_RESULT_SHOULD_WAIT| if no message was available to be read.
+MOJO_SYSTEM_EXPORT MojoResult MojoReadMessage(
+ MojoHandle message_pipe_handle,
+ void* bytes, // Optional out.
+ uint32_t* num_bytes, // Optional in/out.
+ MojoHandle* handles, // Optional out.
+ uint32_t* num_handles, // Optional in/out.
+ MojoReadMessageFlags flags);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_SYSTEM_MESSAGE_PIPE_H_
diff --git a/chromium/mojo/public/c/system/system_export.h b/chromium/mojo/public/c/system/system_export.h
new file mode 100644
index 00000000000..bc3b459843f
--- /dev/null
+++ b/chromium/mojo/public/c/system/system_export.h
@@ -0,0 +1,33 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_SYSTEM_EXPORT_H_
+#define MOJO_PUBLIC_C_SYSTEM_SYSTEM_EXPORT_H_
+
+#if defined(COMPONENT_BUILD) && defined(MOJO_USE_SYSTEM_IMPL)
+#if defined(WIN32)
+
+#if defined(MOJO_SYSTEM_IMPLEMENTATION)
+#define MOJO_SYSTEM_EXPORT __declspec(dllexport)
+#else
+#define MOJO_SYSTEM_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_SYSTEM_IMPLEMENTATION)
+#define MOJO_SYSTEM_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_SYSTEM_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#else // !defined(COMPONENT_BUILD) || !defined(MOJO_USE_SYSTEM_IMPL)
+
+#define MOJO_SYSTEM_EXPORT
+
+#endif // defined(COMPONENT_BUILD) && defined(MOJO_USE_SYSTEM_IMPL)
+
+#endif // MOJO_PUBLIC_C_SYSTEM_SYSTEM_EXPORT_H_
diff --git a/chromium/mojo/public/c/system/types.h b/chromium/mojo/public/c/system/types.h
new file mode 100644
index 00000000000..e992d110e3a
--- /dev/null
+++ b/chromium/mojo/public/c/system/types.h
@@ -0,0 +1,176 @@
+// Copyright 2014 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.
+
+// This file contains types and constants/macros common to different Mojo system
+// APIs.
+//
+// Note: This header should be compilable as C.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_TYPES_H_
+#define MOJO_PUBLIC_C_SYSTEM_TYPES_H_
+
+#include <stdint.h>
+
+#include "mojo/public/c/system/macros.h"
+
+// TODO(vtl): Notes: Use of undefined flags will lead to undefined behavior
+// (typically they'll be ignored), not necessarily an error.
+
+// |MojoTimeTicks|: Used to specify time ticks. Value is in microseconds.
+
+typedef int64_t MojoTimeTicks;
+
+// |MojoHandle|: Handles to Mojo objects.
+// |MOJO_HANDLE_INVALID| - A value that is never a valid handle.
+
+typedef uint32_t MojoHandle;
+
+#ifdef __cplusplus
+const MojoHandle MOJO_HANDLE_INVALID = 0;
+#else
+#define MOJO_HANDLE_INVALID ((MojoHandle) 0)
+#endif
+
+// |MojoResult|: Result codes for Mojo operations. Non-negative values are
+// success codes; negative values are error/failure codes.
+// |MOJO_RESULT_OK| - Not an error; returned on success. Note that positive
+// |MojoResult|s may also be used to indicate success.
+// |MOJO_RESULT_CANCELLED| - Operation was cancelled, typically by the caller.
+// |MOJO_RESULT_UNKNOWN| - Unknown error (e.g., if not enough information is
+// available for a more specific error).
+// |MOJO_RESULT_INVALID_ARGUMENT| - Caller specified an invalid argument. This
+// differs from |MOJO_RESULT_FAILED_PRECONDITION| in that the former
+// indicates arguments that are invalid regardless of the state of the
+// system.
+// |MOJO_RESULT_DEADLINE_EXCEEDED| - Deadline expired before the operation
+// could complete.
+// |MOJO_RESULT_NOT_FOUND| - Some requested entity was not found (i.e., does
+// not exist).
+// |MOJO_RESULT_ALREADY_EXISTS| - Some entity or condition that we attempted
+// to create already exists.
+// |MOJO_RESULT_PERMISSION_DENIED| - The caller does not have permission to
+// for the operation (use |MOJO_RESULT_RESOURCE_EXHAUSTED| for rejections
+// caused by exhausting some resource instead).
+// |MOJO_RESULT_RESOURCE_EXHAUSTED| - Some resource required for the call
+// (possibly some quota) has been exhausted.
+// |MOJO_RESULT_FAILED_PRECONDITION| - The system is not in a state required
+// for the operation (use this if the caller must do something to rectify
+// the state before retrying).
+// |MOJO_RESULT_ABORTED| - The operation was aborted by the system, possibly
+// due to a concurrency issue (use this if the caller may retry at a
+// higher level).
+// |MOJO_RESULT_OUT_OF_RANGE| - The operation was attempted past the valid
+// range. Unlike |MOJO_RESULT_INVALID_ARGUMENT|, this indicates that the
+// operation may be/become valid depending on the system state. (This
+// error is similar to |MOJO_RESULT_FAILED_PRECONDITION|, but is more
+// specific.)
+// |MOJO_RESULT_UNIMPLEMENTED| - The operation is not implemented, supported,
+// or enabled.
+// |MOJO_RESULT_INTERNAL| - Internal error: this should never happen and
+// indicates that some invariant expected by the system has been broken.
+// |MOJO_RESULT_UNAVAILABLE| - The operation is (temporarily) currently
+// unavailable. The caller may simply retry the operation (possibly with a
+// backoff).
+// |MOJO_RESULT_DATA_LOSS| - Unrecoverable data loss or corruption.
+// |MOJO_RESULT_BUSY| - One of the resources involved is currently being used
+// (possibly on another thread) in a way that prevents the current
+// operation from proceeding, e.g., if the other operation may result in
+// the resource being invalidated.
+// |MOJO_RESULT_SHOULD_WAIT| - The request cannot currently be completed
+// (e.g., if the data requested is not yet available). The caller should
+// wait for it to be feasible using |MojoWait()| or |MojoWaitMany()|.
+//
+// Note that positive values are also available as success codes.
+//
+// The codes from |MOJO_RESULT_OK| to |MOJO_RESULT_DATA_LOSS| come from
+// Google3's canonical error codes.
+//
+// TODO(vtl): Add a |MOJO_RESULT_UNSATISFIABLE|?
+
+typedef int32_t MojoResult;
+
+#ifdef __cplusplus
+const MojoResult MOJO_RESULT_OK = 0;
+const MojoResult MOJO_RESULT_CANCELLED = -1;
+const MojoResult MOJO_RESULT_UNKNOWN = -2;
+const MojoResult MOJO_RESULT_INVALID_ARGUMENT = -3;
+const MojoResult MOJO_RESULT_DEADLINE_EXCEEDED = -4;
+const MojoResult MOJO_RESULT_NOT_FOUND = -5;
+const MojoResult MOJO_RESULT_ALREADY_EXISTS = -6;
+const MojoResult MOJO_RESULT_PERMISSION_DENIED = -7;
+const MojoResult MOJO_RESULT_RESOURCE_EXHAUSTED = -8;
+const MojoResult MOJO_RESULT_FAILED_PRECONDITION = -9;
+const MojoResult MOJO_RESULT_ABORTED = -10;
+const MojoResult MOJO_RESULT_OUT_OF_RANGE = -11;
+const MojoResult MOJO_RESULT_UNIMPLEMENTED = -12;
+const MojoResult MOJO_RESULT_INTERNAL = -13;
+const MojoResult MOJO_RESULT_UNAVAILABLE = -14;
+const MojoResult MOJO_RESULT_DATA_LOSS = -15;
+const MojoResult MOJO_RESULT_BUSY = -16;
+const MojoResult MOJO_RESULT_SHOULD_WAIT = -17;
+#else
+#define MOJO_RESULT_OK ((MojoResult) 0)
+#define MOJO_RESULT_CANCELLED ((MojoResult) -1)
+#define MOJO_RESULT_UNKNOWN ((MojoResult) -2)
+#define MOJO_RESULT_INVALID_ARGUMENT ((MojoResult) -3)
+#define MOJO_RESULT_DEADLINE_EXCEEDED ((MojoResult) -4)
+#define MOJO_RESULT_NOT_FOUND ((MojoResult) -5)
+#define MOJO_RESULT_ALREADY_EXISTS ((MojoResult) -6)
+#define MOJO_RESULT_PERMISSION_DENIED ((MojoResult) -7)
+#define MOJO_RESULT_RESOURCE_EXHAUSTED ((MojoResult) -8)
+#define MOJO_RESULT_FAILED_PRECONDITION ((MojoResult) -9)
+#define MOJO_RESULT_ABORTED ((MojoResult) -10)
+#define MOJO_RESULT_OUT_OF_RANGE ((MojoResult) -11)
+#define MOJO_RESULT_UNIMPLEMENTED ((MojoResult) -12)
+#define MOJO_RESULT_INTERNAL ((MojoResult) -13)
+#define MOJO_RESULT_UNAVAILABLE ((MojoResult) -14)
+#define MOJO_RESULT_DATA_LOSS ((MojoResult) -15)
+#define MOJO_RESULT_BUSY ((MojoResult) -16)
+#define MOJO_RESULT_SHOULD_WAIT ((MojoResult) -17)
+#endif
+
+// |MojoDeadline|: Used to specify deadlines (timeouts), in microseconds (except
+// for |MOJO_DEADLINE_INDEFINITE|).
+// |MOJO_DEADLINE_INDEFINITE| - Used to indicate "forever".
+
+typedef uint64_t MojoDeadline;
+
+#ifdef __cplusplus
+const MojoDeadline MOJO_DEADLINE_INDEFINITE = static_cast<MojoDeadline>(-1);
+#else
+#define MOJO_DEADLINE_INDEFINITE ((MojoDeadline) -1)
+#endif
+
+// |MojoHandleSignals|: Used to specify signals that can be waited on for a
+// handle (and which can be triggered), e.g., the ability to read or write to
+// the handle.
+// |MOJO_HANDLE_SIGNAL_NONE| - No flags. |MojoWait()|, etc. will return
+// |MOJO_RESULT_FAILED_PRECONDITION| if you attempt to wait on this.
+// |MOJO_HANDLE_SIGNAL_READABLE| - Can read (e.g., a message) from the handle.
+// |MOJO_HANDLE_SIGNAL_WRITABLE| - Can write (e.g., a message) to the handle.
+
+typedef uint32_t MojoHandleSignals;
+
+#ifdef __cplusplus
+const MojoHandleSignals MOJO_HANDLE_SIGNAL_NONE = 0;
+const MojoHandleSignals MOJO_HANDLE_SIGNAL_READABLE = 1 << 0;
+const MojoHandleSignals MOJO_HANDLE_SIGNAL_WRITABLE = 1 << 1;
+#else
+#define MOJO_HANDLE_SIGNAL_NONE ((MojoHandleSignals) 0)
+#define MOJO_HANDLE_SIGNAL_READABLE ((MojoHandleSignals) 1 << 0)
+#define MOJO_HANDLE_SIGNAL_WRITABLE ((MojoHandleSignals) 1 << 1)
+#endif
+
+// TODO(vtl): Add out parameters with this to MojoWait/MojoWaitMany.
+// Note: This struct is not extensible (and only has 32-bit quantities), so it's
+// 32-bit-aligned.
+MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int32_t) == 4, int32_t_has_weird_alignment);
+struct MOJO_ALIGNAS(4) MojoHandleSignalsState {
+ MojoHandleSignals satisfied_signals;
+ MojoHandleSignals satisfiable_signals;
+};
+MOJO_COMPILE_ASSERT(sizeof(MojoHandleSignalsState) == 8,
+ MojoHandleSignalsState_has_wrong_size);
+
+#endif // MOJO_PUBLIC_C_SYSTEM_TYPES_H_
diff --git a/chromium/mojo/public/c/test_support/test_support.h b/chromium/mojo/public/c/test_support/test_support.h
new file mode 100644
index 00000000000..2b686b272eb
--- /dev/null
+++ b/chromium/mojo/public/c/test_support/test_support.h
@@ -0,0 +1,48 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_H_
+#define MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_H_
+
+// Note: This header should be compilable as C.
+
+#include <stdio.h>
+
+#include "mojo/public/c/test_support/test_support_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+MOJO_TEST_SUPPORT_EXPORT void MojoTestSupportLogPerfResult(
+ const char* test_name,
+ double value,
+ const char* units);
+
+// Opens a "/"-delimited file path relative to the source root.
+MOJO_TEST_SUPPORT_EXPORT FILE* MojoTestSupportOpenSourceRootRelativeFile(
+ const char* source_root_relative_path);
+
+// Enumerates a "/"-delimited directory path relative to the source root.
+// Returns only regular files. The return value is a heap-allocated array of
+// heap-allocated strings. Each must be free'd separately.
+//
+// The return value is built like so:
+//
+// char** rv = (char**) calloc(N + 1, sizeof(char*));
+// rv[0] = strdup("a");
+// rv[1] = strdup("b");
+// rv[2] = strdup("c");
+// ...
+// rv[N] = NULL;
+//
+MOJO_TEST_SUPPORT_EXPORT
+char** MojoTestSupportEnumerateSourceRootRelativeDirectory(
+ const char* source_root_relative_path);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_H_
diff --git a/chromium/mojo/public/c/test_support/test_support_export.h b/chromium/mojo/public/c/test_support/test_support_export.h
new file mode 100644
index 00000000000..e22a9e30144
--- /dev/null
+++ b/chromium/mojo/public/c/test_support/test_support_export.h
@@ -0,0 +1,26 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_EXPORT_H_
+#define MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_EXPORT_H_
+
+#if defined(WIN32)
+
+#if defined(MOJO_TEST_SUPPORT_IMPLEMENTATION)
+#define MOJO_TEST_SUPPORT_EXPORT __declspec(dllexport)
+#else
+#define MOJO_TEST_SUPPORT_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_TEST_SUPPORT_IMPLEMENTATION)
+#define MOJO_TEST_SUPPORT_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_TEST_SUPPORT_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#endif // MOJO_PUBLIC_C_TEST_SUPPORT_TEST_SUPPORT_EXPORT_H_
diff --git a/chromium/mojo/public/cpp/DEPS b/chromium/mojo/public/cpp/DEPS
new file mode 100644
index 00000000000..74acd7c7ed3
--- /dev/null
+++ b/chromium/mojo/public/cpp/DEPS
@@ -0,0 +1,18 @@
+include_rules = [
+ # Require explicit dependencies in each directory.
+ "-mojo/public",
+ # But everyone can depend on the C and C++ system headers.
+ "+mojo/public/c/system",
+ "+mojo/public/cpp/system",
+ # Ditto for the C environment headers (but not the C++ environment, since it
+ # has dependencies of its own).
+ "+mojo/public/c/environment",
+]
+
+specific_include_rules = {
+ r".*_(unit|perf)test\.cc": [
+ "+testing",
+ "+mojo/public/cpp/test_support",
+ "+mojo/public/cpp/utility",
+ ],
+}
diff --git a/chromium/mojo/public/cpp/README.md b/chromium/mojo/public/cpp/README.md
new file mode 100644
index 00000000000..8f03d984b9a
--- /dev/null
+++ b/chromium/mojo/public/cpp/README.md
@@ -0,0 +1,71 @@
+Mojo Public C++ API
+===================
+
+This directory contains C++ language bindings for the Mojo Public API.
+
+A number of subdirectories provide wrappers for the lower-level C APIs (in
+subdirectories of the same name, under mojo/public/c/). Typically, these
+wrappers provide increased convenience and/or type-safety.
+
+Other subdirectories provide support (static) libraries of various sorts. In
+this case, the organization is to have the public interface for the library in
+defined in header files in the subdirectory itself and the implementation of the
+library at a lower level, under a lib (sub)subdirectory. A developer should be
+able to substitute their own implementation of any such support library, and
+expect other support libraries, which may depend on that library, to work
+properly.
+
+Bindings
+--------
+
+The bindings/ subdirectory contains a support (static) library needed by the
+code generated by the bindings generator tool (in mojo/public/tools/bindings/),
+which translates Mojo IDL (.mojom) files into idiomatic C++ (among other
+languages).
+
+This library depends on the Environment library.
+
+Environment
+-----------
+
+The environment/ subdirectory contains a support (static) library that
+represents shared state needed to support the Bindings and GLES2 libraries.
+
+This library depends on the Utility library.
+
+
+GLES2
+-----
+
+The gles2/ subdirectory contains C++ wrappers (and some additional helpers) of
+the API defined in mojo/public/c/gles2/ (which provides access to GLES2).
+
+These wrappers depend on the Environment library.
+
+Shell
+-----
+
+The shell/ subdirectory contains a support (static) library that aids in writing
+Mojo applications and interacting with the Shell service.
+
+System
+------
+
+The system/ subdirectory contains C++ wrappers (and some additional helpers) of
+the API defined in mojo/public/c/system/, which defines the basic, "core" API,
+especially used to communicate with Mojo services.
+
+Test Support
+------------
+
+The test_support/ subdirectory contains C++ wrappers of the test-only API
+defined in mojo/public/c/test_support/. It is not meant for general use by Mojo
+applications.
+
+Utility
+-------
+
+The utility/ subdirectory contains a support (static) library that provides
+various basic functionality. Most notably, it provides an implementation of a
+RunLoop based on MojoWaitMany() that applications may use as the basis for
+asynchronous message processing.
diff --git a/chromium/mojo/public/cpp/application/DEPS b/chromium/mojo/public/cpp/application/DEPS
new file mode 100644
index 00000000000..e808b79707e
--- /dev/null
+++ b/chromium/mojo/public/cpp/application/DEPS
@@ -0,0 +1,14 @@
+include_rules = [
+ "+mojo/public/cpp/bindings",
+ "+mojo/public/interfaces/service_provider",
+]
+
+specific_include_rules = {
+ "mojo_main_chromium.cc": [
+ "+base",
+ "+mojo/public/cpp"
+ ],
+ "mojo_main_standalone.cc": [
+ "+mojo/public/cpp"
+ ],
+}
diff --git a/chromium/mojo/public/cpp/application/application.h b/chromium/mojo/public/cpp/application/application.h
new file mode 100644
index 00000000000..da7a0ed7c61
--- /dev/null
+++ b/chromium/mojo/public/cpp/application/application.h
@@ -0,0 +1,121 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_APPLICATION_APPLICATION_H_
+#define MOJO_PUBLIC_APPLICATION_APPLICATION_H_
+#include <vector>
+
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/cpp/application/lib/service_connector.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+
+#if defined(WIN32)
+#if !defined(CDECL)
+#define CDECL __cdecl
+#endif
+#define APPLICATION_EXPORT __declspec(dllexport)
+#else
+#define CDECL
+#define APPLICATION_EXPORT __attribute__((visibility("default")))
+#endif
+
+// DSOs can either implement MojoMain directly or include
+// mojo_main_{standalone|chromium}.cc in their project and implement
+// Application::Create();
+// TODO(davemoore): Establish this as part of our SDK for third party mojo
+// application writers.
+extern "C" APPLICATION_EXPORT MojoResult CDECL MojoMain(
+ MojoHandle service_provider_handle);
+
+namespace mojo {
+
+// Utility class for creating ServiceProviders that vend service instances.
+// To use define a class that implements your specific server api, e.g. FooImpl
+// to implement a service named Foo.
+// That class must subclass an InterfaceImpl specialization.
+//
+// If there is context that is to be shared amongst all instances, define a
+// constructor with that class as its only argument, otherwise define an empty
+// constructor.
+//
+// class FooImpl : public InterfaceImpl<Foo> {
+// public:
+// FooImpl() {}
+// };
+//
+// or
+//
+// class BarImpl : public InterfaceImpl<Bar> {
+// public:
+// // context will remain valid for the lifetime of BarImpl.
+// BarImpl(BarContext* context) : context_(context) {}
+// private:
+// BarContext* context;
+// };
+//
+// Create an Application instance that collects any service implementations.
+//
+// Application app(service_provider_handle);
+// app.AddService<FooImpl>();
+//
+// BarContext context;
+// app.AddService<BarImpl>(&context);
+//
+//
+class Application {
+ public:
+ Application();
+ explicit Application(ScopedMessagePipeHandle service_provider_handle);
+ explicit Application(MojoHandle service_provider_handle);
+ virtual ~Application();
+
+ // Override this method to control what urls are allowed to connect to a
+ // service.
+ virtual bool AllowIncomingConnection(const mojo::String& service_name,
+ const mojo::String& requestor_url);
+
+ template <typename Impl, typename Context>
+ void AddService(Context* context) {
+ service_registry_.AddServiceConnector(
+ new internal::ServiceConnector<Impl, Context>(Impl::Name_, context));
+ }
+
+ template <typename Impl>
+ void AddService() {
+ service_registry_.AddServiceConnector(
+ new internal::ServiceConnector<Impl, void>(Impl::Name_, NULL));
+ }
+
+ template <typename Interface>
+ void ConnectTo(const std::string& url, InterfacePtr<Interface>* ptr) {
+ mojo::ConnectToService(service_provider(), url, ptr);
+ }
+
+ ServiceProvider* service_provider() {
+ return service_registry_.remote_service_provider();
+ }
+
+ void BindServiceProvider(ScopedMessagePipeHandle service_provider_handle);
+
+ protected:
+ // Override this to do any necessary initialization. There's no need to call
+ // Application's implementation.
+ // The service_provider will be bound to its pipe before this is called.
+ virtual void Initialize();
+
+ private:
+ friend MojoResult (::MojoMain)(MojoHandle);
+
+ // Implement this method to create the specific subclass of Application.
+ static Application* Create();
+
+ internal::ServiceRegistry service_registry_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Application);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_APPLICATION_APPLICATION_H_
diff --git a/chromium/mojo/public/cpp/application/connect.h b/chromium/mojo/public/cpp/application/connect.h
new file mode 100644
index 00000000000..e4ba641e2ed
--- /dev/null
+++ b/chromium/mojo/public/cpp/application/connect.h
@@ -0,0 +1,24 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_APPLICATION_CONNECT_H_
+#define MOJO_PUBLIC_CPP_APPLICATION_CONNECT_H_
+
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+
+namespace mojo {
+
+template <typename Interface>
+inline void ConnectToService(ServiceProvider* service_provider,
+ const std::string& url,
+ InterfacePtr<Interface>* ptr) {
+ MessagePipe pipe;
+ ptr->Bind(pipe.handle0.Pass());
+ service_provider->ConnectToService(
+ url, Interface::Name_, pipe.handle1.Pass(), std::string());
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_APPLICATION_CONNECT_H_
diff --git a/chromium/mojo/public/cpp/application/lib/application.cc b/chromium/mojo/public/cpp/application/lib/application.cc
new file mode 100644
index 00000000000..78f5a8bbd0e
--- /dev/null
+++ b/chromium/mojo/public/cpp/application/lib/application.cc
@@ -0,0 +1,34 @@
+// Copyright 2014 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 "mojo/public/cpp/application/application.h"
+
+namespace mojo {
+
+Application::Application() : service_registry_(this) {}
+
+Application::Application(ScopedMessagePipeHandle service_provider_handle)
+ : service_registry_(this, service_provider_handle.Pass()) {}
+
+Application::Application(MojoHandle service_provider_handle)
+ : service_registry_(
+ this,
+ mojo::MakeScopedHandle(
+ MessagePipeHandle(service_provider_handle)).Pass()) {}
+
+Application::~Application() {}
+
+bool Application::AllowIncomingConnection(const mojo::String& service_name,
+ const mojo::String& requestor_url) {
+ return true;
+}
+
+void Application::BindServiceProvider(
+ ScopedMessagePipeHandle service_provider_handle) {
+ service_registry_.BindRemoteServiceProvider(service_provider_handle.Pass());
+}
+
+void Application::Initialize() {}
+
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/application/lib/mojo_main_chromium.cc b/chromium/mojo/public/cpp/application/lib/mojo_main_chromium.cc
new file mode 100644
index 00000000000..cda7cd02d18
--- /dev/null
+++ b/chromium/mojo/public/cpp/application/lib/mojo_main_chromium.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 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 "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/public/cpp/application/application.h"
+
+extern "C" APPLICATION_EXPORT MojoResult CDECL MojoMain(
+ MojoHandle service_provider_handle) {
+ base::CommandLine::Init(0, NULL);
+ base::AtExitManager at_exit;
+ base::MessageLoop loop;
+
+ scoped_ptr<mojo::Application> app(mojo::Application::Create());
+ app->BindServiceProvider(
+ mojo::MakeScopedHandle(mojo::MessagePipeHandle(service_provider_handle)));
+ app->Initialize();
+ loop.Run();
+
+ return MOJO_RESULT_OK;
+}
diff --git a/chromium/mojo/public/cpp/application/lib/mojo_main_standalone.cc b/chromium/mojo/public/cpp/application/lib/mojo_main_standalone.cc
new file mode 100644
index 00000000000..05825aa3319
--- /dev/null
+++ b/chromium/mojo/public/cpp/application/lib/mojo_main_standalone.cc
@@ -0,0 +1,22 @@
+// Copyright 2014 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 "mojo/public/cpp/application/application.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+
+extern "C" APPLICATION_EXPORT MojoResult CDECL MojoMain(
+ MojoHandle service_provider_handle) {
+ mojo::Environment env;
+ mojo::RunLoop loop;
+
+ mojo::Application* app = mojo::Application::Create();
+ app->BindServiceProvider(
+ mojo::MakeScopedHandle(mojo::MessagePipeHandle(service_provider_handle)));
+ app->Initialize();
+ loop.Run();
+ delete app;
+
+ return MOJO_RESULT_OK;
+}
diff --git a/chromium/mojo/public/cpp/application/lib/service_connector.cc b/chromium/mojo/public/cpp/application/lib/service_connector.cc
new file mode 100644
index 00000000000..5cc4421b7aa
--- /dev/null
+++ b/chromium/mojo/public/cpp/application/lib/service_connector.cc
@@ -0,0 +1,18 @@
+// Copyright 2014 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 "mojo/public/cpp/application/lib/service_connector.h"
+
+namespace mojo {
+namespace internal {
+
+ServiceConnectorBase::ServiceConnectorBase(const std::string& name)
+ : name_(name),
+ registry_(NULL) {
+}
+
+ServiceConnectorBase::~ServiceConnectorBase() {}
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/application/lib/service_connector.h b/chromium/mojo/public/cpp/application/lib/service_connector.h
new file mode 100644
index 00000000000..30786adb55d
--- /dev/null
+++ b/chromium/mojo/public/cpp/application/lib/service_connector.h
@@ -0,0 +1,133 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_APPLICATION_LIB_SERVICE_CONNECTOR_H_
+#define MOJO_PUBLIC_CPP_APPLICATION_LIB_SERVICE_CONNECTOR_H_
+
+#include <assert.h>
+
+#include <vector>
+
+#include "mojo/public/cpp/application/lib/service_registry.h"
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+
+namespace mojo {
+namespace internal {
+
+template <class ServiceImpl, typename Context>
+class ServiceConnector;
+
+// Specialization of ServiceConnection.
+// ServiceImpl: Subclass of InterfaceImpl<...>.
+// Context: Type of shared context.
+template <class ServiceImpl, typename Context>
+class ServiceConnection : public ServiceImpl {
+ public:
+ ServiceConnection() : ServiceImpl() {}
+ ServiceConnection(Context* context) : ServiceImpl(context) {}
+
+ virtual void OnConnectionError() MOJO_OVERRIDE {
+ service_connector_->RemoveConnection(static_cast<ServiceImpl*>(this));
+ ServiceImpl::OnConnectionError();
+ }
+
+private:
+ friend class ServiceConnector<ServiceImpl, Context>;
+
+ // Called shortly after this class is instantiated.
+ void set_service_connector(
+ ServiceConnector<ServiceImpl, Context>* connector) {
+ service_connector_ = connector;
+ }
+
+ ServiceConnector<ServiceImpl, Context>* service_connector_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceConnection);
+};
+
+template <typename ServiceImpl, typename Context>
+struct ServiceConstructor {
+ static ServiceConnection<ServiceImpl, Context>* New(Context* context) {
+ return new ServiceConnection<ServiceImpl, Context>(context);
+ }
+};
+
+template <typename ServiceImpl>
+struct ServiceConstructor<ServiceImpl, void> {
+ public:
+ static ServiceConnection<ServiceImpl, void>* New(void* context) {
+ return new ServiceConnection<ServiceImpl, void>();
+ }
+};
+
+class ServiceConnectorBase {
+ public:
+ ServiceConnectorBase(const std::string& name);
+ virtual ~ServiceConnectorBase();
+ virtual void ConnectToService(const std::string& url,
+ const std::string& name,
+ ScopedMessagePipeHandle client_handle) = 0;
+ std::string name() const { return name_; }
+ void set_registry(ServiceRegistry* registry) { registry_ = registry; }
+
+ protected:
+ std::string name_;
+ ServiceRegistry* registry_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceConnectorBase);
+};
+
+template <class ServiceImpl, typename Context=void>
+class ServiceConnector : public internal::ServiceConnectorBase {
+ public:
+ ServiceConnector(const std::string& name, Context* context = NULL)
+ : ServiceConnectorBase(name), context_(context) {}
+
+ virtual ~ServiceConnector() {
+ ConnectionList doomed;
+ doomed.swap(connections_);
+ for (typename ConnectionList::iterator it = doomed.begin();
+ it != doomed.end(); ++it) {
+ delete *it;
+ }
+ assert(connections_.empty()); // No one should have added more!
+ }
+
+ virtual void ConnectToService(const std::string& url,
+ const std::string& name,
+ ScopedMessagePipeHandle handle) MOJO_OVERRIDE {
+ ServiceConnection<ServiceImpl, Context>* impl =
+ ServiceConstructor<ServiceImpl, Context>::New(context_);
+ impl->set_service_connector(this);
+ BindToPipe(impl, handle.Pass());
+
+ connections_.push_back(impl);
+ }
+
+ void RemoveConnection(ServiceImpl* impl) {
+ // Called from ~ServiceImpl, in response to a connection error.
+ for (typename ConnectionList::iterator it = connections_.begin();
+ it != connections_.end(); ++it) {
+ if (*it == impl) {
+ delete impl;
+ connections_.erase(it);
+ return;
+ }
+ }
+ }
+
+ Context* context() const { return context_; }
+
+ private:
+ typedef std::vector<ServiceImpl*> ConnectionList;
+ ConnectionList connections_;
+ Context* context_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceConnector);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_APPLICATION_LIB_SERVICE_CONNECTOR_H_
diff --git a/chromium/mojo/public/cpp/application/lib/service_registry.cc b/chromium/mojo/public/cpp/application/lib/service_registry.cc
new file mode 100644
index 00000000000..bc901dfb924
--- /dev/null
+++ b/chromium/mojo/public/cpp/application/lib/service_registry.cc
@@ -0,0 +1,78 @@
+// Copyright 2014 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 "mojo/public/cpp/application/lib/service_registry.h"
+
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/public/cpp/application/lib/service_connector.h"
+
+namespace mojo {
+namespace internal {
+
+ServiceRegistry::ServiceRegistry(Application* application)
+ : application_(application) {
+}
+
+ServiceRegistry::ServiceRegistry(
+ Application* application,
+ ScopedMessagePipeHandle service_provider_handle)
+ : application_(application) {
+ remote_service_provider_.Bind(service_provider_handle.Pass());
+ remote_service_provider_.set_client(this);
+}
+
+ServiceRegistry::~ServiceRegistry() {
+ for (NameToServiceConnectorMap::iterator i =
+ name_to_service_connector_.begin();
+ i != name_to_service_connector_.end(); ++i) {
+ delete i->second;
+ }
+ name_to_service_connector_.clear();
+}
+
+void ServiceRegistry::AddServiceConnector(
+ ServiceConnectorBase* service_connector) {
+ name_to_service_connector_[service_connector->name()] = service_connector;
+ service_connector->set_registry(this);
+}
+
+void ServiceRegistry::RemoveServiceConnector(
+ ServiceConnectorBase* service_connector) {
+ NameToServiceConnectorMap::iterator it =
+ name_to_service_connector_.find(service_connector->name());
+ assert(it != name_to_service_connector_.end());
+ delete it->second;
+ name_to_service_connector_.erase(it);
+ if (name_to_service_connector_.empty())
+ remote_service_provider_.reset();
+}
+
+void ServiceRegistry::BindRemoteServiceProvider(
+ ScopedMessagePipeHandle service_provider_handle) {
+ remote_service_provider_.Bind(service_provider_handle.Pass());
+ remote_service_provider_.set_client(this);
+}
+
+void ServiceRegistry::ConnectToService(const mojo::String& service_url,
+ const mojo::String& service_name,
+ ScopedMessagePipeHandle client_handle,
+ const mojo::String& requestor_url) {
+ if (name_to_service_connector_.find(service_name) ==
+ name_to_service_connector_.end() ||
+ !application_->AllowIncomingConnection(service_name, requestor_url)) {
+ client_handle.reset();
+ return;
+ }
+
+ internal::ServiceConnectorBase* service_connector =
+ name_to_service_connector_[service_name];
+ assert(service_connector);
+ // requestor_url is ignored because the service_connector stores the url
+ // of the requestor safely.
+ return service_connector->ConnectToService(
+ service_url, service_name, client_handle.Pass());
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/application/lib/service_registry.h b/chromium/mojo/public/cpp/application/lib/service_registry.h
new file mode 100644
index 00000000000..478cd055ef2
--- /dev/null
+++ b/chromium/mojo/public/cpp/application/lib/service_registry.h
@@ -0,0 +1,55 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_APPLICATION_LIB_SERVICE_REGISTRY_H_
+#define MOJO_PUBLIC_CPP_APPLICATION_LIB_SERVICE_REGISTRY_H_
+
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+
+namespace mojo {
+
+class Application;
+
+namespace internal {
+
+class ServiceConnectorBase;
+
+class ServiceRegistry : public ServiceProvider {
+ public:
+ ServiceRegistry(Application* application);
+ ServiceRegistry(Application* application,
+ ScopedMessagePipeHandle service_provider_handle);
+ virtual ~ServiceRegistry();
+
+ void AddServiceConnector(ServiceConnectorBase* service_connector);
+ void RemoveServiceConnector(ServiceConnectorBase* service_connector);
+
+ ServiceProvider* remote_service_provider() {
+ return remote_service_provider_.get();
+ }
+
+ void BindRemoteServiceProvider(
+ ScopedMessagePipeHandle service_provider_handle);
+
+ // ServiceProvider method.
+ virtual void ConnectToService(const mojo::String& service_url,
+ const mojo::String& service_name,
+ ScopedMessagePipeHandle client_handle,
+ const mojo::String& requestor_url)
+ MOJO_OVERRIDE;
+
+ private:
+ Application* application_;
+ typedef std::map<std::string, ServiceConnectorBase*>
+ NameToServiceConnectorMap;
+ NameToServiceConnectorMap name_to_service_connector_;
+ ServiceProviderPtr remote_service_provider_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ServiceRegistry);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_APPLICATION_LIB_SERVICE_REGISTRY_H_
diff --git a/chromium/mojo/public/cpp/bindings/BUILD.gn b/chromium/mojo/public/cpp/bindings/BUILD.gn
new file mode 100644
index 00000000000..2cc35aa52ff
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/BUILD.gn
@@ -0,0 +1,53 @@
+# Copyright 2014 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.
+
+source_set("bindings") {
+ sources = [
+ "array.h",
+ "callback.h",
+ "error_handler.h",
+ "interface_ptr.h",
+ "message.h",
+ "message_filter.h",
+ "no_interface.h",
+ "string.h",
+ "struct_ptr.h",
+ "sync_dispatcher.h",
+ "type_converter.h",
+ "lib/array_internal.cc",
+ "lib/array_internal.h",
+ "lib/array_serialization.h",
+ "lib/bindings_internal.h",
+ "lib/bindings_serialization.cc",
+ "lib/bindings_serialization.h",
+ "lib/bounds_checker.cc",
+ "lib/bounds_checker.h",
+ "lib/buffer.h",
+ "lib/callback_internal.h",
+ "lib/connector.cc",
+ "lib/connector.h",
+ "lib/filter_chain.cc",
+ "lib/filter_chain.h",
+ "lib/fixed_buffer.cc",
+ "lib/fixed_buffer.h",
+ "lib/message.cc",
+ "lib/message_builder.cc",
+ "lib/message_builder.h",
+ "lib/message_filter.cc",
+ "lib/message_internal.h",
+ "lib/message_queue.cc",
+ "lib/message_queue.h",
+ "lib/no_interface.cc",
+ "lib/router.cc",
+ "lib/router.h",
+ "lib/shared_data.h",
+ "lib/shared_ptr.h",
+ "lib/string_serialization.cc",
+ "lib/string_serialization.h",
+ "lib/sync_dispatcher.cc",
+ "lib/template_util.h",
+ "lib/validation_errors.cc",
+ "lib/validation_errors.h",
+ ]
+}
diff --git a/chromium/mojo/public/cpp/bindings/DEPS b/chromium/mojo/public/cpp/bindings/DEPS
new file mode 100644
index 00000000000..2a0496e9d9c
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+mojo/public/cpp/environment",
+]
diff --git a/chromium/mojo/public/cpp/bindings/array.h b/chromium/mojo/public/cpp/bindings/array.h
new file mode 100644
index 00000000000..daf7125bb3c
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/array.h
@@ -0,0 +1,143 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_
+
+#include <string.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+namespace mojo {
+
+// Provides read-only access to array data.
+template <typename T>
+class Array {
+ MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(Array, RValue)
+ public:
+ typedef internal::ArrayTraits<T, internal::IsMoveOnlyType<T>::value>
+ Traits;
+ typedef typename Traits::ConstRefType ConstRefType;
+ typedef typename Traits::RefType RefType;
+ typedef typename Traits::StorageType StorageType;
+ typedef typename Traits::ForwardType ForwardType;
+
+ typedef internal::Array_Data<typename internal::WrapperTraits<T>::DataType>
+ Data_;
+
+ Array() : is_null_(true) {}
+ explicit Array(size_t size) : vec_(size), is_null_(false) {
+ Traits::Initialize(&vec_);
+ }
+ ~Array() { Traits::Finalize(&vec_); }
+
+ Array(RValue other) : is_null_(true) { Take(other.object); }
+ Array& operator=(RValue other) {
+ Take(other.object);
+ return *this;
+ }
+
+ static Array New(size_t size) {
+ return Array(size).Pass();
+ }
+
+ template <typename U>
+ static Array From(const U& other) {
+ return TypeConverter<Array, U>::ConvertFrom(other);
+ }
+
+ template <typename U>
+ U To() const {
+ return TypeConverter<Array, U>::ConvertTo(*this);
+ }
+
+ void reset() {
+ if (!vec_.empty()) {
+ Traits::Finalize(&vec_);
+ vec_.clear();
+ }
+ is_null_ = true;
+ }
+
+ bool is_null() const { return is_null_; }
+
+ size_t size() const { return vec_.size(); }
+
+ ConstRefType at(size_t offset) const { return Traits::at(&vec_, offset); }
+ ConstRefType operator[](size_t offset) const { return at(offset); }
+
+ RefType at(size_t offset) { return Traits::at(&vec_, offset); }
+ RefType operator[](size_t offset) { return at(offset); }
+
+ void push_back(ForwardType value) {
+ is_null_ = false;
+ Traits::PushBack(&vec_, value);
+ }
+
+ void resize(size_t size) {
+ is_null_ = false;
+ Traits::Resize(&vec_, size);
+ }
+
+ const std::vector<StorageType>& storage() const {
+ return vec_;
+ }
+ operator const std::vector<StorageType>&() const {
+ return vec_;
+ }
+
+ void Swap(Array* other) {
+ std::swap(is_null_, other->is_null_);
+ vec_.swap(other->vec_);
+ }
+ void Swap(std::vector<StorageType>* other) {
+ is_null_ = false;
+ vec_.swap(*other);
+ }
+
+ private:
+ typedef std::vector<StorageType> Array::*Testable;
+
+ public:
+ operator Testable() const { return is_null_ ? 0 : &Array::vec_; }
+
+ private:
+ void Take(Array* other) {
+ reset();
+ Swap(other);
+ }
+
+ std::vector<StorageType> vec_;
+ bool is_null_;
+};
+
+template <typename T, typename E>
+class TypeConverter<Array<T>, std::vector<E> > {
+ public:
+ static Array<T> ConvertFrom(const std::vector<E>& input) {
+ Array<T> result(input.size());
+ for (size_t i = 0; i < input.size(); ++i)
+ result[i] = TypeConverter<T, E>::ConvertFrom(input[i]);
+ return result.Pass();
+ }
+ static std::vector<E> ConvertTo(const Array<T>& input) {
+ std::vector<E> result;
+ if (!input.is_null()) {
+ result.resize(input.size());
+ for (size_t i = 0; i < input.size(); ++i)
+ result[i] = TypeConverter<T, E>::ConvertTo(input[i]);
+ }
+ return result;
+ }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_
diff --git a/chromium/mojo/public/cpp/bindings/callback.h b/chromium/mojo/public/cpp/bindings/callback.h
new file mode 100644
index 00000000000..d8ff4718232
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/callback.h
@@ -0,0 +1,462 @@
+// This file was GENERATED by command:
+// pump.py callback.h.pump
+// DO NOT EDIT BY HAND!!!
+
+
+
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_H_
+
+#include "mojo/public/cpp/bindings/lib/callback_internal.h"
+#include "mojo/public/cpp/bindings/lib/shared_ptr.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+
+namespace mojo {
+
+template <typename Sig>
+class Callback;
+
+template <>
+class Callback<void()> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run() const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run() const {
+ if (sink_.get())
+ sink_->Run();
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run() const MOJO_OVERRIDE {
+ sink.Run();
+ }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1>
+class Callback<void(A1)> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1) const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1) const {
+ if (sink_.get())
+ sink_->Run(
+ internal::Forward(a1));
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1) const
+ MOJO_OVERRIDE {
+ sink.Run(
+ internal::Forward(a1));
+ }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2>
+class Callback<void(A1, A2)> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2) const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2) const {
+ if (sink_.get())
+ sink_->Run(
+ internal::Forward(a1),
+ internal::Forward(a2));
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2) const
+ MOJO_OVERRIDE {
+ sink.Run(
+ internal::Forward(a1),
+ internal::Forward(a2));
+ }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2, typename A3>
+class Callback<void(A1, A2, A3)> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3) const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3) const {
+ if (sink_.get())
+ sink_->Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3));
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3) const
+ MOJO_OVERRIDE {
+ sink.Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3));
+ }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2, typename A3, typename A4>
+class Callback<void(A1, A2, A3, A4)> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4) const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4) const {
+ if (sink_.get())
+ sink_->Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3),
+ internal::Forward(a4));
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4) const
+ MOJO_OVERRIDE {
+ sink.Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3),
+ internal::Forward(a4));
+ }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5>
+class Callback<void(A1, A2, A3, A4, A5)> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5) const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5) const {
+ if (sink_.get())
+ sink_->Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3),
+ internal::Forward(a4),
+ internal::Forward(a5));
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5) const
+ MOJO_OVERRIDE {
+ sink.Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3),
+ internal::Forward(a4),
+ internal::Forward(a5));
+ }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6>
+class Callback<void(A1, A2, A3, A4, A5, A6)> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+ typename internal::Callback_ParamTraits<A6>::ForwardType a6) const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+ typename internal::Callback_ParamTraits<A6>::ForwardType a6) const {
+ if (sink_.get())
+ sink_->Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3),
+ internal::Forward(a4),
+ internal::Forward(a5),
+ internal::Forward(a6));
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+ typename internal::Callback_ParamTraits<A6>::ForwardType a6) const
+ MOJO_OVERRIDE {
+ sink.Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3),
+ internal::Forward(a4),
+ internal::Forward(a5),
+ internal::Forward(a6));
+ }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7>
+class Callback<void(A1, A2, A3, A4, A5, A6, A7)> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+ typename internal::Callback_ParamTraits<A6>::ForwardType a6,
+ typename internal::Callback_ParamTraits<A7>::ForwardType a7) const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+ typename internal::Callback_ParamTraits<A6>::ForwardType a6,
+ typename internal::Callback_ParamTraits<A7>::ForwardType a7) const {
+ if (sink_.get())
+ sink_->Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3),
+ internal::Forward(a4),
+ internal::Forward(a5),
+ internal::Forward(a6),
+ internal::Forward(a7));
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run(
+ typename internal::Callback_ParamTraits<A1>::ForwardType a1,
+ typename internal::Callback_ParamTraits<A2>::ForwardType a2,
+ typename internal::Callback_ParamTraits<A3>::ForwardType a3,
+ typename internal::Callback_ParamTraits<A4>::ForwardType a4,
+ typename internal::Callback_ParamTraits<A5>::ForwardType a5,
+ typename internal::Callback_ParamTraits<A6>::ForwardType a6,
+ typename internal::Callback_ParamTraits<A7>::ForwardType a7) const
+ MOJO_OVERRIDE {
+ sink.Run(
+ internal::Forward(a1),
+ internal::Forward(a2),
+ internal::Forward(a3),
+ internal::Forward(a4),
+ internal::Forward(a5),
+ internal::Forward(a6),
+ internal::Forward(a7));
+ }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_H_
diff --git a/chromium/mojo/public/cpp/bindings/callback.h.pump b/chromium/mojo/public/cpp/bindings/callback.h.pump
new file mode 100644
index 00000000000..a0f23d5f77e
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/callback.h.pump
@@ -0,0 +1,80 @@
+$$ This is a pump file for generating file templates. Pump is a python
+$$ script that is part of the Google Test suite of utilities. Description
+$$ can be found here:
+$$
+$$ http://code.google.com/p/googletest/wiki/PumpManual
+$$
+
+$var MAX_ARITY = 7
+
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_H_
+
+#include "mojo/public/cpp/bindings/lib/callback_internal.h"
+#include "mojo/public/cpp/bindings/lib/shared_ptr.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+
+namespace mojo {
+
+template <typename Sig>
+class Callback;
+
+$range ARITY 0..MAX_ARITY
+$for ARITY [[
+$range ARG 1..ARITY
+
+template <$for ARG , [[typename A$(ARG)]]>
+class Callback<void($for ARG , [[A$(ARG)]])> {
+ public:
+ struct Runnable {
+ virtual ~Runnable() {}
+ virtual void Run(
+ $for ARG ,
+ [[typename internal::Callback_ParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) const = 0;
+ };
+
+ Callback() {}
+
+ // The Callback assumes ownership of |runnable|.
+ explicit Callback(Runnable* runnable) : sink_(runnable) {}
+
+ // Any class that is copy-constructable and has a compatible Run method may
+ // be adapted to a Callback using this constructor.
+ template <typename Sink>
+ Callback(const Sink& sink) : sink_(new Adapter<Sink>(sink)) {}
+
+ void Run(
+ $for ARG ,
+ [[typename internal::Callback_ParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) const {
+ if (sink_.get())
+ sink_->Run(
+ $for ARG ,
+ [[internal::Forward(a$(ARG))]]);
+ }
+
+ private:
+ template <typename Sink>
+ struct Adapter : public Runnable {
+ explicit Adapter(const Sink& sink) : sink(sink) {}
+ virtual void Run(
+ $for ARG ,
+ [[typename internal::Callback_ParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) const MOJO_OVERRIDE {
+ sink.Run(
+ $for ARG ,
+ [[internal::Forward(a$(ARG))]]);
+ }
+ Sink sink;
+ };
+
+ internal::SharedPtr<Runnable> sink_;
+};
+
+]] $$ for ARITY
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_CALLBACK_H_
diff --git a/chromium/mojo/public/cpp/bindings/error_handler.h b/chromium/mojo/public/cpp/bindings/error_handler.h
new file mode 100644
index 00000000000..8ce1af27264
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/error_handler.h
@@ -0,0 +1,19 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_ERROR_HANDLER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ERROR_HANDLER_H_
+
+namespace mojo {
+
+// This interface is used to report connection errors.
+class ErrorHandler {
+ public:
+ virtual ~ErrorHandler() {}
+ virtual void OnConnectionError() = 0;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_ERROR_HANDLER_H_
diff --git a/chromium/mojo/public/cpp/bindings/interface_impl.h b/chromium/mojo/public/cpp/bindings/interface_impl.h
new file mode 100644
index 00000000000..8ba5e7ffa4a
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/interface_impl.h
@@ -0,0 +1,109 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_IMPL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_IMPL_H_
+
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/lib/interface_impl_internal.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// InterfaceImpl<..> is designed to be the base class of an interface
+// implementation. It may be bound to a pipe or a proxy, see BindToPipe and
+// BindToProxy.
+template <typename Interface>
+class InterfaceImpl : public internal::InterfaceImplBase<Interface> {
+ public:
+ typedef typename Interface::Client Client;
+
+ InterfaceImpl() : internal_state_(this) {}
+ virtual ~InterfaceImpl() {}
+
+ // Returns a proxy to the client interface. This is null upon construction,
+ // and becomes non-null after OnClientConnected. NOTE: It remains non-null
+ // until this instance is deleted.
+ Client* client() { return internal_state_.client(); }
+
+ // Called when the client has connected to this instance.
+ virtual void OnConnectionEstablished() {}
+
+ // Called when the client is no longer connected to this instance. NOTE: The
+ // client() method continues to return a non-null pointer after this method
+ // is called. After this method is called, any method calls made on client()
+ // will be silently ignored.
+ virtual void OnConnectionError() {}
+
+ // DO NOT USE. Exposed only for internal use and for testing.
+ internal::InterfaceImplState<Interface>* internal_state() {
+ return &internal_state_;
+ }
+
+ private:
+ internal::InterfaceImplState<Interface> internal_state_;
+ MOJO_DISALLOW_COPY_AND_ASSIGN(InterfaceImpl);
+};
+
+// Takes an instance of an InterfaceImpl<..> subclass and binds it to the given
+// MessagePipe. The instance is returned for convenience in member initializer
+// lists, etc. If the pipe is closed, the instance's OnConnectionError method
+// will be called.
+//
+// The instance is also bound to the current thread. Its methods will only be
+// called on the current thread, and if the current thread exits, then it will
+// also be deleted, and along with it, its end point of the pipe will be closed.
+//
+// Before returning, the instance's OnConnectionEstablished method is called.
+template <typename Impl>
+Impl* BindToPipe(
+ Impl* instance,
+ ScopedMessagePipeHandle handle,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ instance->internal_state()->Bind(handle.Pass(), waiter);
+ return instance;
+}
+
+// Takes an instance of an InterfaceImpl<..> subclass and binds it to the given
+// InterfacePtr<..>. The instance is returned for convenience in member
+// initializer lists, etc. If the pipe is closed, the instance's
+// OnConnectionError method will be called.
+//
+// The instance is also bound to the current thread. Its methods will only be
+// called on the current thread, and if the current thread exits, then it will
+// also be deleted, and along with it, its end point of the pipe will be closed.
+//
+// Before returning, the instance's OnConnectionEstablished method is called.
+template <typename Impl, typename Interface>
+Impl* BindToProxy(
+ Impl* instance,
+ InterfacePtr<Interface>* ptr,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ instance->internal_state()->BindProxy(ptr, waiter);
+ return instance;
+}
+
+// Takes an instance of an InterfaceImpl<..> subclass and binds it to the given
+// InterfaceRequest<..>. The instance is returned for convenience in member
+// initializer lists, etc. If the pipe is closed, the instance's
+// OnConnectionError method will be called.
+//
+// The instance is also bound to the current thread. Its methods will only be
+// called on the current thread, and if the current thread exits, then it will
+// also be deleted, and along with it, its end point of the pipe will be closed.
+//
+// Before returning, the instance will receive a SetClient call, providing it
+// with a proxy to the client on the other end of the pipe.
+template <typename Impl, typename Interface>
+Impl* BindToRequest(
+ Impl* instance,
+ InterfaceRequest<Interface>* request,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ return BindToPipe(instance, request->PassMessagePipe(), waiter);
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_IMPL_H_
diff --git a/chromium/mojo/public/cpp/bindings/interface_ptr.h b/chromium/mojo/public/cpp/bindings/interface_ptr.h
new file mode 100644
index 00000000000..726896d9cce
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/interface_ptr.h
@@ -0,0 +1,124 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_
+
+#include <assert.h>
+
+#include <algorithm>
+
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/public/cpp/bindings/lib/interface_ptr_internal.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+class ErrorHandler;
+
+// InterfacePtr represents a proxy to a remote instance of an interface.
+template <typename Interface>
+class InterfacePtr {
+ MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(InterfacePtr, RValue)
+ public:
+ InterfacePtr() {}
+
+ InterfacePtr(RValue other) {
+ internal_state_.Swap(&other.object->internal_state_);
+ }
+ InterfacePtr& operator=(RValue other) {
+ reset();
+ internal_state_.Swap(&other.object->internal_state_);
+ return *this;
+ }
+
+ ~InterfacePtr() {}
+
+ Interface* get() const {
+ return internal_state_.instance();
+ }
+ Interface* operator->() const { return get(); }
+ Interface& operator*() const { return *get(); }
+
+ void reset() {
+ State doomed;
+ internal_state_.Swap(&doomed);
+ }
+
+ // This method configures the InterfacePtr<..> to be a proxy to a remote
+ // object on the other end of the given pipe.
+ //
+ // The proxy is bound to the current thread, which means its methods may
+ // only be called on the current thread.
+ //
+ // To move a bound InterfacePtr<..> to another thread, call
+ // ResetAndReturnMessagePipe. Then create a new InterfacePtr<..> on another
+ // thread, and bind the new InterfacePtr<..> to the message pipe on that
+ // thread.
+ void Bind(
+ ScopedMessagePipeHandle handle,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ reset();
+ internal_state_.ConfigureProxy(handle.Pass(), waiter);
+ }
+
+ // The client interface may only be set after this InterfacePtr<..> is bound.
+ void set_client(typename Interface::Client* client) {
+ internal_state_.set_client(client);
+ }
+
+ // This method may be called to query if the underlying pipe has encountered
+ // an error. If true, this means method calls made on this interface will be
+ // dropped (and may have already been dropped) on the floor.
+ bool encountered_error() const {
+ assert(internal_state_.router());
+ return internal_state_.router()->encountered_error();
+ }
+
+ // This method may be called to register an ErrorHandler to observe a
+ // connection error on the underlying pipe. The callback runs asynchronously
+ // from the current message loop.
+ void set_error_handler(ErrorHandler* error_handler) {
+ assert(internal_state_.router());
+ internal_state_.router()->set_error_handler(error_handler);
+ }
+
+ // Returns the underlying message pipe handle (if any) and resets the
+ // InterfacePtr<..> to its uninitialized state. This method is helpful if you
+ // need to move a proxy to another thread. See related notes for Bind.
+ ScopedMessagePipeHandle PassMessagePipe() {
+ State state;
+ internal_state_.Swap(&state);
+ return state.router() ?
+ state.router()->PassMessagePipe() : ScopedMessagePipeHandle();
+ }
+
+ // DO NOT USE. Exposed only for internal use and for testing.
+ internal::InterfacePtrState<Interface>* internal_state() {
+ return &internal_state_;
+ }
+
+ private:
+ typedef internal::InterfacePtrState<Interface> State;
+ State internal_state_;
+};
+
+// Takes a handle to the proxy end-point of a pipe. On the other end is
+// presumed to be an interface implementation of type |Interface|. Returns a
+// generated proxy to that interface, which may be used on the current thread.
+// It is valid to call set_client on the returned InterfacePtr<..> to set an
+// instance of Interface::Client.
+template <typename Interface>
+InterfacePtr<Interface> MakeProxy(
+ ScopedMessagePipeHandle handle,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ InterfacePtr<Interface> ptr;
+ if (handle.is_valid())
+ ptr.Bind(handle.Pass(), waiter);
+ return ptr.Pass();
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_
diff --git a/chromium/mojo/public/cpp/bindings/interface_request.h b/chromium/mojo/public/cpp/bindings/interface_request.h
new file mode 100644
index 00000000000..6b7d3033c9d
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/interface_request.h
@@ -0,0 +1,76 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
+
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+
+namespace mojo {
+
+// Used in methods that return instances of remote objects.
+template <typename Interface>
+class InterfaceRequest {
+ MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(InterfaceRequest, RValue)
+ public:
+ InterfaceRequest() {}
+
+ InterfaceRequest(RValue other) {
+ handle_ = other.object->handle_.Pass();
+ }
+ InterfaceRequest& operator=(RValue other) {
+ handle_ = other.object->handle_.Pass();
+ return *this;
+ }
+
+ // Returns true if the request has yet to be completed.
+ bool is_pending() const { return handle_.is_valid(); }
+
+ void Bind(ScopedMessagePipeHandle handle) {
+ handle_ = handle.Pass();
+ }
+
+ ScopedMessagePipeHandle PassMessagePipe() {
+ return handle_.Pass();
+ }
+
+ private:
+ ScopedMessagePipeHandle handle_;
+};
+
+template <typename Interface>
+InterfaceRequest<Interface> MakeRequest(ScopedMessagePipeHandle handle) {
+ InterfaceRequest<Interface> request;
+ request.Bind(handle.Pass());
+ return request.Pass();
+}
+
+// Used to construct a request that synchronously binds an InterfacePtr<..>,
+// making it immediately usable upon return. The resulting request object may
+// then be later bound to an InterfaceImpl<..> via BindToRequest.
+//
+// Given the following interface:
+//
+// interface Foo {
+// CreateBar(Bar& bar);
+// }
+//
+// The caller of CreateBar would have code similar to the following:
+//
+// InterfacePtr<Foo> foo = ...;
+// InterfacePtr<Bar> bar;
+// foo->CreateBar(Get(&bar));
+//
+// Upon return from CreateBar, |bar| is ready to have methods called on it.
+//
+template <typename Interface>
+InterfaceRequest<Interface> Get(InterfacePtr<Interface>* ptr) {
+ MessagePipe pipe;
+ ptr->Bind(pipe.handle0.Pass());
+ return MakeRequest<Interface>(pipe.handle1.Pass());
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/DEPS b/chromium/mojo/public/cpp/bindings/lib/DEPS
new file mode 100644
index 00000000000..b809b58d337
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+mojo/public/cpp/bindings",
+ "+mojo/public/cpp/environment",
+ "+mojo/public/cpp/system",
+]
diff --git a/chromium/mojo/public/cpp/bindings/lib/TODO b/chromium/mojo/public/cpp/bindings/lib/TODO
new file mode 100644
index 00000000000..21bcb6fe7a2
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/TODO
@@ -0,0 +1,6 @@
+TODOs:
+ - Ensure validation checks are solid
+ - Add tests of validation logic
+ - Optimize Buffer classes?
+ - Add compile-time asserts to verify object packing and padding.
+ - Investigate making arrays of objects not be arrays of pointers.
diff --git a/chromium/mojo/public/cpp/bindings/lib/array_internal.cc b/chromium/mojo/public/cpp/bindings/lib/array_internal.cc
new file mode 100644
index 00000000000..3f7f1ce0b16
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/array_internal.cc
@@ -0,0 +1,70 @@
+// Copyright 2013 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 "mojo/public/cpp/bindings/lib/array_internal.h"
+
+namespace mojo {
+namespace internal {
+
+ArrayDataTraits<bool>::BitRef::~BitRef() {
+}
+
+ArrayDataTraits<bool>::BitRef::BitRef(uint8_t* storage, uint8_t mask)
+ : storage_(storage),
+ mask_(mask) {
+}
+
+ArrayDataTraits<bool>::BitRef&
+ArrayDataTraits<bool>::BitRef::operator=(bool value) {
+ if (value) {
+ *storage_ |= mask_;
+ } else {
+ *storage_ &= ~mask_;
+ }
+ return *this;
+}
+
+ArrayDataTraits<bool>::BitRef&
+ArrayDataTraits<bool>::BitRef::operator=(const BitRef& value) {
+ return (*this) = static_cast<bool>(value);
+}
+
+ArrayDataTraits<bool>::BitRef::operator bool() const {
+ return (*storage_ & mask_) != 0;
+}
+
+// static
+void ArraySerializationHelper<Handle, true>::EncodePointersAndHandles(
+ const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles) {
+ for (uint32_t i = 0; i < header->num_elements; ++i)
+ EncodeHandle(&elements[i], handles);
+}
+
+// static
+void ArraySerializationHelper<Handle, true>::DecodePointersAndHandles(
+ const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles) {
+ for (uint32_t i = 0; i < header->num_elements; ++i)
+ DecodeHandle(&elements[i], handles);
+}
+
+// static
+bool ArraySerializationHelper<Handle, true>::ValidateElements(
+ const ArrayHeader* header,
+ const ElementType* elements,
+ BoundsChecker* bounds_checker) {
+ for (uint32_t i = 0; i < header->num_elements; ++i) {
+ if (!bounds_checker->ClaimHandle(elements[i])) {
+ ReportValidationError(VALIDATION_ERROR_ILLEGAL_HANDLE);
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/bindings/lib/array_internal.h b/chromium/mojo/public/cpp/bindings/lib/array_internal.h
new file mode 100644
index 00000000000..8ea00991251
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/array_internal.h
@@ -0,0 +1,384 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_
+
+#include <new>
+#include <vector>
+
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
+#include "mojo/public/cpp/bindings/lib/buffer.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+
+namespace mojo {
+template <typename T> class Array;
+class String;
+
+namespace internal {
+
+template <typename T>
+struct ArrayDataTraits {
+ typedef T StorageType;
+ typedef T& Ref;
+ typedef T const& ConstRef;
+
+ static size_t GetStorageSize(size_t num_elements) {
+ return sizeof(StorageType) * num_elements;
+ }
+ static Ref ToRef(StorageType* storage, size_t offset) {
+ return storage[offset];
+ }
+ static ConstRef ToConstRef(const StorageType* storage, size_t offset) {
+ return storage[offset];
+ }
+};
+
+template <typename P>
+struct ArrayDataTraits<P*> {
+ typedef StructPointer<P> StorageType;
+ typedef P*& Ref;
+ typedef P* const& ConstRef;
+
+ static size_t GetStorageSize(size_t num_elements) {
+ return sizeof(StorageType) * num_elements;
+ }
+ static Ref ToRef(StorageType* storage, size_t offset) {
+ return storage[offset].ptr;
+ }
+ static ConstRef ToConstRef(const StorageType* storage, size_t offset) {
+ return storage[offset].ptr;
+ }
+};
+
+template <typename T>
+struct ArrayDataTraits<Array_Data<T>*> {
+ typedef ArrayPointer<T> StorageType;
+ typedef Array_Data<T>*& Ref;
+ typedef Array_Data<T>* const& ConstRef;
+
+ static size_t GetStorageSize(size_t num_elements) {
+ return sizeof(StorageType) * num_elements;
+ }
+ static Ref ToRef(StorageType* storage, size_t offset) {
+ return storage[offset].ptr;
+ }
+ static ConstRef ToConstRef(const StorageType* storage, size_t offset) {
+ return storage[offset].ptr;
+ }
+};
+
+// Specialization of Arrays for bools, optimized for space. It has the
+// following differences from a generalized Array:
+// * Each element takes up a single bit of memory.
+// * Accessing a non-const single element uses a helper class |BitRef|, which
+// emulates a reference to a bool.
+template <>
+struct ArrayDataTraits<bool> {
+ // Helper class to emulate a reference to a bool, used for direct element
+ // access.
+ class BitRef {
+ public:
+ ~BitRef();
+ BitRef& operator=(bool value);
+ BitRef& operator=(const BitRef& value);
+ operator bool() const;
+ private:
+ friend struct ArrayDataTraits<bool>;
+ BitRef(uint8_t* storage, uint8_t mask);
+ BitRef();
+ uint8_t* storage_;
+ uint8_t mask_;
+ };
+
+ typedef uint8_t StorageType;
+ typedef BitRef Ref;
+ typedef bool ConstRef;
+
+ static size_t GetStorageSize(size_t num_elements) {
+ return ((num_elements + 7) / 8);
+ }
+ static BitRef ToRef(StorageType* storage, size_t offset) {
+ return BitRef(&storage[offset / 8], 1 << (offset % 8));
+ }
+ static bool ToConstRef(const StorageType* storage, size_t offset) {
+ return (storage[offset / 8] & (1 << (offset % 8))) != 0;
+ }
+};
+
+// What follows is code to support the serialization of Array_Data<T>. There
+// are two interesting cases: arrays of primitives and arrays of objects.
+// Arrays of objects are represented as arrays of pointers to objects.
+
+template <typename T, bool kIsHandle> struct ArraySerializationHelper;
+
+template <typename T>
+struct ArraySerializationHelper<T, false> {
+ typedef typename ArrayDataTraits<T>::StorageType ElementType;
+
+ static void EncodePointersAndHandles(const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles) {
+ }
+
+ static void DecodePointersAndHandles(const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles) {
+ }
+
+ static bool ValidateElements(const ArrayHeader* header,
+ const ElementType* elements,
+ BoundsChecker* bounds_checker) {
+ return true;
+ }
+};
+
+template <>
+struct ArraySerializationHelper<Handle, true> {
+ typedef ArrayDataTraits<Handle>::StorageType ElementType;
+
+ static void EncodePointersAndHandles(const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles);
+
+ static void DecodePointersAndHandles(const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles);
+
+ static bool ValidateElements(const ArrayHeader* header,
+ const ElementType* elements,
+ BoundsChecker* bounds_checker);
+};
+
+template <typename H>
+struct ArraySerializationHelper<H, true> {
+ typedef typename ArrayDataTraits<H>::StorageType ElementType;
+
+ static void EncodePointersAndHandles(const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles) {
+ ArraySerializationHelper<Handle, true>::EncodePointersAndHandles(
+ header, elements, handles);
+ }
+
+ static void DecodePointersAndHandles(const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles) {
+ ArraySerializationHelper<Handle, true>::DecodePointersAndHandles(
+ header, elements, handles);
+ }
+
+ static bool ValidateElements(const ArrayHeader* header,
+ const ElementType* elements,
+ BoundsChecker* bounds_checker) {
+ return ArraySerializationHelper<Handle, true>::ValidateElements(
+ header, elements, bounds_checker);
+ }
+};
+
+template <typename P>
+struct ArraySerializationHelper<P*, false> {
+ typedef typename ArrayDataTraits<P*>::StorageType ElementType;
+
+ static void EncodePointersAndHandles(const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles) {
+ for (uint32_t i = 0; i < header->num_elements; ++i)
+ Encode(&elements[i], handles);
+ }
+
+ static void DecodePointersAndHandles(const ArrayHeader* header,
+ ElementType* elements,
+ std::vector<Handle>* handles) {
+ for (uint32_t i = 0; i < header->num_elements; ++i)
+ Decode(&elements[i], handles);
+ }
+
+ static bool ValidateElements(const ArrayHeader* header,
+ const ElementType* elements,
+ BoundsChecker* bounds_checker) {
+ for (uint32_t i = 0; i < header->num_elements; ++i) {
+ if (!ValidateEncodedPointer(&elements[i].offset)) {
+ ReportValidationError(VALIDATION_ERROR_ILLEGAL_POINTER);
+ return false;
+ }
+ if (!P::Validate(DecodePointerRaw(&elements[i].offset), bounds_checker))
+ return false;
+ }
+ return true;
+ }
+};
+
+template <typename T>
+class Array_Data {
+ public:
+ typedef ArrayDataTraits<T> Traits;
+ typedef typename Traits::StorageType StorageType;
+ typedef typename Traits::Ref Ref;
+ typedef typename Traits::ConstRef ConstRef;
+ typedef ArraySerializationHelper<T, IsHandle<T>::value> Helper;
+
+ static Array_Data<T>* New(size_t num_elements, Buffer* buf) {
+ size_t num_bytes = sizeof(Array_Data<T>) +
+ Traits::GetStorageSize(num_elements);
+ return new (buf->Allocate(num_bytes)) Array_Data<T>(num_bytes,
+ num_elements);
+ }
+
+ static bool Validate(const void* data, BoundsChecker* bounds_checker) {
+ if (!data)
+ return true;
+ if (!IsAligned(data)) {
+ ReportValidationError(VALIDATION_ERROR_MISALIGNED_OBJECT);
+ return false;
+ }
+ if (!bounds_checker->IsValidRange(data, sizeof(ArrayHeader))) {
+ ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
+ return false;
+ }
+ const ArrayHeader* header = static_cast<const ArrayHeader*>(data);
+ if (header->num_bytes < (sizeof(Array_Data<T>) +
+ Traits::GetStorageSize(header->num_elements))) {
+ ReportValidationError(VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER);
+ return false;
+ }
+ if (!bounds_checker->ClaimMemory(data, header->num_bytes)) {
+ ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
+ return false;
+ }
+
+ const Array_Data<T>* object = static_cast<const Array_Data<T>*>(data);
+ return Helper::ValidateElements(&object->header_, object->storage(),
+ bounds_checker);
+ }
+
+ size_t size() const { return header_.num_elements; }
+
+ Ref at(size_t offset) {
+ assert(offset < static_cast<size_t>(header_.num_elements));
+ return Traits::ToRef(storage(), offset);
+ }
+
+ ConstRef at(size_t offset) const {
+ assert(offset < static_cast<size_t>(header_.num_elements));
+ return Traits::ToConstRef(storage(), offset);
+ }
+
+ StorageType* storage() {
+ return reinterpret_cast<StorageType*>(
+ reinterpret_cast<char*>(this) + sizeof(*this));
+ }
+
+ const StorageType* storage() const {
+ return reinterpret_cast<const StorageType*>(
+ reinterpret_cast<const char*>(this) + sizeof(*this));
+ }
+
+ void EncodePointersAndHandles(std::vector<Handle>* handles) {
+ Helper::EncodePointersAndHandles(&header_, storage(), handles);
+ }
+
+ void DecodePointersAndHandles(std::vector<Handle>* handles) {
+ Helper::DecodePointersAndHandles(&header_, storage(), handles);
+ }
+
+ private:
+ Array_Data(size_t num_bytes, size_t num_elements) {
+ header_.num_bytes = static_cast<uint32_t>(num_bytes);
+ header_.num_elements = static_cast<uint32_t>(num_elements);
+ }
+ ~Array_Data() {}
+
+ internal::ArrayHeader header_;
+
+ // Elements of type internal::ArrayDataTraits<T>::StorageType follow.
+};
+MOJO_COMPILE_ASSERT(sizeof(Array_Data<char>) == 8, bad_sizeof_Array_Data);
+
+// UTF-8 encoded
+typedef Array_Data<char> String_Data;
+
+template <typename T, bool kIsMoveOnlyType> struct ArrayTraits {};
+
+template <typename T> struct ArrayTraits<T, false> {
+ typedef T StorageType;
+ typedef typename std::vector<T>::reference RefType;
+ typedef typename std::vector<T>::const_reference ConstRefType;
+ typedef ConstRefType ForwardType;
+ static inline void Initialize(std::vector<T>* vec) {
+ }
+ static inline void Finalize(std::vector<T>* vec) {
+ }
+ static inline ConstRefType at(const std::vector<T>* vec, size_t offset) {
+ return vec->at(offset);
+ }
+ static inline RefType at(std::vector<T>* vec, size_t offset) {
+ return vec->at(offset);
+ }
+ static inline void Resize(std::vector<T>* vec, size_t size) {
+ vec->resize(size);
+ }
+ static inline void PushBack(std::vector<T>* vec, ForwardType value) {
+ vec->push_back(value);
+ }
+};
+
+template <typename T> struct ArrayTraits<T, true> {
+ struct StorageType {
+ char buf[sizeof(T) + (8 - (sizeof(T) % 8)) % 8]; // Make 8-byte aligned.
+ };
+ typedef T& RefType;
+ typedef const T& ConstRefType;
+ typedef T ForwardType;
+ static inline void Initialize(std::vector<StorageType>* vec) {
+ for (size_t i = 0; i < vec->size(); ++i)
+ new (vec->at(i).buf) T();
+ }
+ static inline void Finalize(std::vector<StorageType>* vec) {
+ for (size_t i = 0; i < vec->size(); ++i)
+ reinterpret_cast<T*>(vec->at(i).buf)->~T();
+ }
+ static inline ConstRefType at(const std::vector<StorageType>* vec,
+ size_t offset) {
+ return *reinterpret_cast<const T*>(vec->at(offset).buf);
+ }
+ static inline RefType at(std::vector<StorageType>* vec, size_t offset) {
+ return *reinterpret_cast<T*>(vec->at(offset).buf);
+ }
+ static inline void Resize(std::vector<StorageType>* vec, size_t size) {
+ size_t old_size = vec->size();
+ for (size_t i = size; i < old_size; i++)
+ reinterpret_cast<T*>(vec->at(i).buf)->~T();
+ ResizeStorage(vec, size);
+ for (size_t i = old_size; i < vec->size(); i++)
+ new (vec->at(i).buf) T();
+ }
+ static inline void PushBack(std::vector<StorageType>* vec, RefType value) {
+ size_t old_size = vec->size();
+ ResizeStorage(vec, old_size + 1);
+ new (vec->at(old_size).buf) T(value.Pass());
+ }
+ static inline void ResizeStorage(std::vector<StorageType>* vec, size_t size) {
+ if (size <= vec->capacity()) {
+ vec->resize(size);
+ return;
+ }
+ std::vector<StorageType> new_storage(size);
+ for (size_t i = 0; i < vec->size(); i++)
+ new (new_storage.at(i).buf) T(at(vec, i).Pass());
+ vec->swap(new_storage);
+ Finalize(&new_storage);
+ }
+};
+
+template <> struct WrapperTraits<String, false> {
+ typedef String_Data* DataType;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_INTERNAL_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/array_serialization.h b/chromium/mojo/public/cpp/bindings/lib/array_serialization.h
new file mode 100644
index 00000000000..83403d52502
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/array_serialization.h
@@ -0,0 +1,179 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_
+
+#include <string>
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/string_serialization.h"
+
+namespace mojo {
+
+template <typename E>
+inline size_t GetSerializedSize_(const Array<E>& input);
+
+template <typename E, typename F>
+inline void Serialize_(Array<E> input, internal::Buffer* buf,
+ internal::Array_Data<F>** output);
+template <typename E, typename F>
+inline void Deserialize_(internal::Array_Data<F>* data, Array<E>* output);
+
+namespace internal {
+
+template <typename E, typename F, bool move_only = IsMoveOnlyType<E>::value>
+struct ArraySerializer;
+
+template <typename E, typename F> struct ArraySerializer<E, F, false> {
+ MOJO_COMPILE_ASSERT(sizeof(E) == sizeof(F), wrong_array_serializer);
+ static size_t GetSerializedSize(const Array<E>& input) {
+ return sizeof(Array_Data<F>) + Align(input.size() * sizeof(E));
+ }
+ static void SerializeElements(
+ Array<E> input, Buffer* buf, Array_Data<F>* output) {
+ memcpy(output->storage(), &input.storage()[0], input.size() * sizeof(E));
+ }
+ static void DeserializeElements(
+ Array_Data<F>* input, Array<E>* output) {
+ std::vector<E> result(input->size());
+ memcpy(&result[0], input->storage(), input->size() * sizeof(E));
+ output->Swap(&result);
+ }
+};
+
+template <> struct ArraySerializer<bool, bool, false> {
+ static size_t GetSerializedSize(const Array<bool>& input) {
+ return sizeof(Array_Data<bool>) + Align((input.size() + 7) / 8);
+ }
+ static void SerializeElements(
+ Array<bool> input, Buffer* buf, Array_Data<bool>* output) {
+ // TODO(darin): Can this be a memcpy somehow instead of a bit-by-bit copy?
+ for (size_t i = 0; i < input.size(); ++i)
+ output->at(i) = input[i];
+ }
+ static void DeserializeElements(
+ Array_Data<bool>* input, Array<bool>* output) {
+ Array<bool> result(input->size());
+ // TODO(darin): Can this be a memcpy somehow instead of a bit-by-bit copy?
+ for (size_t i = 0; i < input->size(); ++i)
+ result.at(i) = input->at(i);
+ output->Swap(&result);
+ }
+};
+
+template <typename H> struct ArraySerializer<ScopedHandleBase<H>, H, true> {
+ static size_t GetSerializedSize(const Array<ScopedHandleBase<H> >& input) {
+ return sizeof(Array_Data<H>) + Align(input.size() * sizeof(H));
+ }
+ static void SerializeElements(
+ Array<ScopedHandleBase<H> > input,
+ Buffer* buf,
+ Array_Data<H>* output) {
+ for (size_t i = 0; i < input.size(); ++i)
+ output->at(i) = input[i].release(); // Transfer ownership of the handle.
+ }
+ static void DeserializeElements(
+ Array_Data<H>* input, Array<ScopedHandleBase<H> >* output) {
+ Array<ScopedHandleBase<H> > result(input->size());
+ for (size_t i = 0; i < input->size(); ++i)
+ result.at(i) = MakeScopedHandle(FetchAndReset(&input->at(i)));
+ output->Swap(&result);
+ }
+};
+
+template <typename S> struct ArraySerializer<S, typename S::Data_*, true> {
+ static size_t GetSerializedSize(const Array<S>& input) {
+ size_t size = sizeof(Array_Data<typename S::Data_*>) +
+ input.size() * sizeof(internal::StructPointer<typename S::Data_>);
+ for (size_t i = 0; i < input.size(); ++i)
+ size += GetSerializedSize_(input[i]);
+ return size;
+ }
+ static void SerializeElements(
+ Array<S> input,
+ Buffer* buf,
+ Array_Data<typename S::Data_*>* output) {
+ for (size_t i = 0; i < input.size(); ++i) {
+ typename S::Data_* element;
+ Serialize_(input[i].Pass(), buf, &element);
+ output->at(i) = element;
+ }
+ }
+ static void DeserializeElements(
+ Array_Data<typename S::Data_*>* input, Array<S>* output) {
+ Array<S> result(input->size());
+ for (size_t i = 0; i < input->size(); ++i) {
+ S element;
+ Deserialize_(input->at(i), &element);
+ result[i] = element.Pass();
+ }
+ output->Swap(&result);
+ }
+};
+
+template <> struct ArraySerializer<String, String_Data*, false> {
+ static size_t GetSerializedSize(const Array<String>& input) {
+ size_t size = sizeof(Array_Data<String_Data*>) +
+ input.size() * sizeof(internal::StringPointer);
+ for (size_t i = 0; i < input.size(); ++i)
+ size += GetSerializedSize_(input[i]);
+ return size;
+ }
+ static void SerializeElements(
+ Array<String> input,
+ Buffer* buf,
+ Array_Data<String_Data*>* output) {
+ for (size_t i = 0; i < input.size(); ++i) {
+ String_Data* element;
+ Serialize_(input[i], buf, &element);
+ output->at(i) = element;
+ }
+ }
+ static void DeserializeElements(
+ Array_Data<String_Data*>* input, Array<String>* output) {
+ Array<String> result(input->size());
+ for (size_t i = 0; i < input->size(); ++i)
+ Deserialize_(input->at(i), &result[i]);
+ output->Swap(&result);
+ }
+};
+
+} // namespace internal
+
+template <typename E>
+inline size_t GetSerializedSize_(const Array<E>& input) {
+ if (!input)
+ return 0;
+ typedef typename internal::WrapperTraits<E>::DataType F;
+ return internal::ArraySerializer<E, F>::GetSerializedSize(input);
+}
+
+template <typename E, typename F>
+inline void Serialize_(Array<E> input, internal::Buffer* buf,
+ internal::Array_Data<F>** output) {
+ if (input) {
+ internal::Array_Data<F>* result =
+ internal::Array_Data<F>::New(input.size(), buf);
+ internal::ArraySerializer<E, F>::SerializeElements(
+ internal::Forward(input), buf, result);
+ *output = result;
+ } else {
+ *output = NULL;
+ }
+}
+
+template <typename E, typename F>
+inline void Deserialize_(internal::Array_Data<F>* input,
+ Array<E>* output) {
+ if (input) {
+ internal::ArraySerializer<E, F>::DeserializeElements(input, output);
+ } else {
+ output->reset();
+ }
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ARRAY_SERIALIZATION_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/bindings_internal.h b/chromium/mojo/public/cpp/bindings/lib/bindings_internal.h
new file mode 100644
index 00000000000..6e87cbd1fb5
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/bindings_internal.h
@@ -0,0 +1,86 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_INTERNAL_H_
+
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+class String;
+
+namespace internal {
+template <typename T> class Array_Data;
+
+#pragma pack(push, 1)
+
+struct StructHeader {
+ uint32_t num_bytes;
+ uint32_t num_fields;
+};
+MOJO_COMPILE_ASSERT(sizeof(StructHeader) == 8, bad_sizeof_StructHeader);
+
+struct ArrayHeader {
+ uint32_t num_bytes;
+ uint32_t num_elements;
+};
+MOJO_COMPILE_ASSERT(sizeof(ArrayHeader) == 8, bad_sizeof_ArrayHeader);
+
+template <typename T>
+union StructPointer {
+ uint64_t offset;
+ T* ptr;
+};
+MOJO_COMPILE_ASSERT(sizeof(StructPointer<char>) == 8, bad_sizeof_StructPointer);
+
+template <typename T>
+union ArrayPointer {
+ uint64_t offset;
+ Array_Data<T>* ptr;
+};
+MOJO_COMPILE_ASSERT(sizeof(ArrayPointer<char>) == 8, bad_sizeof_ArrayPointer);
+
+union StringPointer {
+ uint64_t offset;
+ Array_Data<char>* ptr;
+};
+MOJO_COMPILE_ASSERT(sizeof(StringPointer) == 8, bad_sizeof_StringPointer);
+
+#pragma pack(pop)
+
+template <typename T>
+void ResetIfNonNull(T* ptr) {
+ if (ptr)
+ *ptr = T();
+}
+
+template <typename T>
+T FetchAndReset(T* ptr) {
+ T temp = *ptr;
+ *ptr = T();
+ return temp;
+}
+
+template <typename H> struct IsHandle {
+ static const bool value = IsBaseOf<Handle, H>::value;
+};
+
+template <typename T, bool move_only = IsMoveOnlyType<T>::value>
+struct WrapperTraits;
+
+template <typename T> struct WrapperTraits<T, false> {
+ typedef T DataType;
+};
+template <typename H> struct WrapperTraits<ScopedHandleBase<H>, true> {
+ typedef H DataType;
+};
+template <typename S> struct WrapperTraits<S, true> {
+ typedef typename S::Data_* DataType;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_INTERNAL_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/bindings_serialization.cc b/chromium/mojo/public/cpp/bindings/lib/bindings_serialization.cc
new file mode 100644
index 00000000000..d5f77853684
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/bindings_serialization.cc
@@ -0,0 +1,118 @@
+// Copyright 2013 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 "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+
+#include <assert.h>
+
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+
+namespace mojo {
+namespace internal {
+
+namespace {
+
+const size_t kAlignment = 8;
+
+template<typename T>
+T AlignImpl(T t) {
+ return t + (kAlignment - (t % kAlignment)) % kAlignment;
+}
+
+} // namespace
+
+size_t Align(size_t size) {
+ return AlignImpl(size);
+}
+
+char* AlignPointer(char* ptr) {
+ return reinterpret_cast<char*>(AlignImpl(reinterpret_cast<uintptr_t>(ptr)));
+}
+
+bool IsAligned(const void* ptr) {
+ return !(reinterpret_cast<uintptr_t>(ptr) % kAlignment);
+}
+
+void EncodePointer(const void* ptr, uint64_t* offset) {
+ if (!ptr) {
+ *offset = 0;
+ return;
+ }
+
+ const char* p_obj = reinterpret_cast<const char*>(ptr);
+ const char* p_slot = reinterpret_cast<const char*>(offset);
+ assert(p_obj > p_slot);
+
+ *offset = static_cast<uint64_t>(p_obj - p_slot);
+}
+
+const void* DecodePointerRaw(const uint64_t* offset) {
+ if (!*offset)
+ return NULL;
+ return reinterpret_cast<const char*>(offset) + *offset;
+}
+
+bool ValidateEncodedPointer(const uint64_t* offset) {
+ // Cast to uintptr_t so overflow behavior is well defined.
+ return reinterpret_cast<uintptr_t>(offset) + *offset >=
+ reinterpret_cast<uintptr_t>(offset);
+}
+
+void EncodeHandle(Handle* handle, std::vector<Handle>* handles) {
+ if (handle->is_valid()) {
+ handles->push_back(*handle);
+ handle->set_value(static_cast<MojoHandle>(handles->size() - 1));
+ } else {
+ handle->set_value(kEncodedInvalidHandleValue);
+ }
+}
+
+void DecodeHandle(Handle* handle, std::vector<Handle>* handles) {
+ if (handle->value() == kEncodedInvalidHandleValue) {
+ *handle = Handle();
+ return;
+ }
+ assert(handle->value() < handles->size());
+ // Just leave holes in the vector so we don't screw up other indices.
+ *handle = FetchAndReset(&handles->at(handle->value()));
+}
+
+bool ValidateStructHeader(const void* data,
+ uint32_t min_num_bytes,
+ uint32_t min_num_fields,
+ BoundsChecker* bounds_checker) {
+ assert(min_num_bytes >= sizeof(StructHeader));
+
+ if (!IsAligned(data)) {
+ ReportValidationError(VALIDATION_ERROR_MISALIGNED_OBJECT);
+ return false;
+ }
+ if (!bounds_checker->IsValidRange(data, sizeof(StructHeader))) {
+ ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
+ return false;
+ }
+
+ const StructHeader* header = static_cast<const StructHeader*>(data);
+
+ // TODO(yzshen): Currently our binding code cannot handle structs of smaller
+ // size or with fewer fields than the version that it sees. That needs to be
+ // changed in order to provide backward compatibility.
+ if (header->num_bytes < min_num_bytes ||
+ header->num_fields < min_num_fields) {
+ ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+ return false;
+ }
+
+ if (!bounds_checker->ClaimMemory(data, header->num_bytes)) {
+ ReportValidationError(VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/bindings/lib/bindings_serialization.h b/chromium/mojo/public/cpp/bindings/lib/bindings_serialization.h
new file mode 100644
index 00000000000..6bebf90bf95
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/bindings_serialization.h
@@ -0,0 +1,83 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_SERIALIZATION_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_SERIALIZATION_H_
+
+#include <vector>
+
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace internal {
+
+class BoundsChecker;
+
+// Please note that this is a different value than |mojo::kInvalidHandleValue|,
+// which is the "decoded" invalid handle.
+const MojoHandle kEncodedInvalidHandleValue = static_cast<MojoHandle>(-1);
+
+size_t Align(size_t size);
+char* AlignPointer(char* ptr);
+
+bool IsAligned(const void* ptr);
+
+// Pointers are encoded as relative offsets. The offsets are relative to the
+// address of where the offset value is stored, such that the pointer may be
+// recovered with the expression:
+//
+// ptr = reinterpret_cast<char*>(offset) + *offset
+//
+// A null pointer is encoded as an offset value of 0.
+//
+void EncodePointer(const void* ptr, uint64_t* offset);
+// Note: This function doesn't validate the encoded pointer value.
+const void* DecodePointerRaw(const uint64_t* offset);
+
+// Note: This function doesn't validate the encoded pointer value.
+template <typename T>
+inline void DecodePointer(const uint64_t* offset, T** ptr) {
+ *ptr = reinterpret_cast<T*>(const_cast<void*>(DecodePointerRaw(offset)));
+}
+
+// Checks whether decoding the pointer will overflow and produce a pointer
+// smaller than |offset|.
+bool ValidateEncodedPointer(const uint64_t* offset);
+
+// Handles are encoded as indices into a vector of handles. These functions
+// manipulate the value of |handle|, mapping it to and from an index.
+void EncodeHandle(Handle* handle, std::vector<Handle>* handles);
+// Note: This function doesn't validate the encoded handle value.
+void DecodeHandle(Handle* handle, std::vector<Handle>* handles);
+
+// The following 2 functions are used to encode/decode all objects (structs and
+// arrays) in a consistent manner.
+
+template <typename T>
+inline void Encode(T* obj, std::vector<Handle>* handles) {
+ if (obj->ptr)
+ obj->ptr->EncodePointersAndHandles(handles);
+ EncodePointer(obj->ptr, &obj->offset);
+}
+
+// Note: This function doesn't validate the encoded pointer and handle values.
+template <typename T>
+inline void Decode(T* obj, std::vector<Handle>* handles) {
+ DecodePointer(&obj->offset, &obj->ptr);
+ if (obj->ptr)
+ obj->ptr->DecodePointersAndHandles(handles);
+}
+
+// If returns true, this function also claims the memory range of the size
+// specified in the struct header, starting from |data|.
+// Note: |min_num_bytes| must be no less than sizeof(StructHeader).
+bool ValidateStructHeader(const void* data,
+ uint32_t min_num_bytes,
+ uint32_t min_num_fields,
+ BoundsChecker* bounds_checker);
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDINGS_SERIALIZATION_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/bounds_checker.cc b/chromium/mojo/public/cpp/bindings/lib/bounds_checker.cc
new file mode 100644
index 00000000000..cf1f3238e03
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/bounds_checker.cc
@@ -0,0 +1,77 @@
+// Copyright 2014 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 "mojo/public/cpp/bindings/lib/bounds_checker.h"
+
+#include <assert.h>
+
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace internal {
+
+BoundsChecker::BoundsChecker(const void* data, uint32_t data_num_bytes,
+ size_t num_handles)
+ : data_begin_(reinterpret_cast<uintptr_t>(data)),
+ data_end_(data_begin_ + data_num_bytes),
+ handle_begin_(0),
+ handle_end_(static_cast<uint32_t>(num_handles)) {
+ if (data_end_ < data_begin_) {
+ // The calculation of |data_end_| overflowed.
+ // It shouldn't happen but if it does, set the range to empty so
+ // IsValidRange() and ClaimMemory() always fail.
+ assert(false); // Not reached.
+ data_end_ = data_begin_;
+ }
+ if (handle_end_ < num_handles) {
+ // Assigning |num_handles| to |handle_end_| overflowed.
+ // It shouldn't happen but if it does, set the handle index range to empty.
+ assert(false); // Not reached.
+ handle_end_ = 0;
+ }
+}
+
+BoundsChecker::~BoundsChecker() {
+}
+
+bool BoundsChecker::ClaimMemory(const void* position, uint32_t num_bytes) {
+ uintptr_t begin = reinterpret_cast<uintptr_t>(position);
+ uintptr_t end = begin + num_bytes;
+
+ if (!InternalIsValidRange(begin, end))
+ return false;
+
+ data_begin_ = end;
+ return true;
+}
+
+bool BoundsChecker::ClaimHandle(const Handle& encoded_handle) {
+ uint32_t index = encoded_handle.value();
+ if (index == kEncodedInvalidHandleValue)
+ return true;
+
+ if (index < handle_begin_ || index >= handle_end_)
+ return false;
+
+ // |index| + 1 shouldn't overflow, because |index| is not the max value of
+ // uint32_t (it is less than |handle_end_|).
+ handle_begin_ = index + 1;
+ return true;
+}
+
+bool BoundsChecker::IsValidRange(const void* position,
+ uint32_t num_bytes) const {
+ uintptr_t begin = reinterpret_cast<uintptr_t>(position);
+ uintptr_t end = begin + num_bytes;
+
+ return InternalIsValidRange(begin, end);
+}
+
+bool BoundsChecker::InternalIsValidRange(uintptr_t begin, uintptr_t end) const {
+ return end > begin && begin >= data_begin_ && end <= data_end_;
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/bindings/lib/bounds_checker.h b/chromium/mojo/public/cpp/bindings/lib/bounds_checker.h
new file mode 100644
index 00000000000..6c472309dde
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/bounds_checker.h
@@ -0,0 +1,64 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_BOUNDS_CHECKER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BOUNDS_CHECKER_H_
+
+#include <stdint.h>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+class Handle;
+
+namespace internal {
+
+// BoundsChecker is used to validate object sizes, pointers and handle indices
+// for payload of incoming messages.
+class BoundsChecker {
+ public:
+ // [data, data + data_num_bytes) specifies the initial valid memory range.
+ // [0, num_handles) specifies the initial valid range of handle indices.
+ BoundsChecker(const void* data, uint32_t data_num_bytes,
+ size_t num_handles);
+
+ ~BoundsChecker();
+
+ // Claims the specified memory range.
+ // The method succeeds if the range is valid to claim. (Please see
+ // the comments for IsValidRange().)
+ // On success, the valid memory range is shrinked to begin right after the end
+ // of the claimed range.
+ bool ClaimMemory(const void* position, uint32_t num_bytes);
+
+ // Claims the specified encoded handle (which is basically a handle index).
+ // The method succeeds if:
+ // - |encoded_handle|'s value is |kEncodedInvalidHandleValue|.
+ // - the handle is contained inside the valid range of handle indices. In this
+ // case, the valid range is shinked to begin right after the claimed handle.
+ bool ClaimHandle(const Handle& encoded_handle);
+
+ // Returns true if the specified range is not empty, and the range is
+ // contained inside the valid memory range.
+ bool IsValidRange(const void* position, uint32_t num_bytes) const;
+
+ private:
+ bool InternalIsValidRange(uintptr_t begin, uintptr_t end) const;
+
+ // [data_begin_, data_end_) is the valid memory range.
+ uintptr_t data_begin_;
+ uintptr_t data_end_;
+
+ // [handle_begin_, handle_end_) is the valid handle index range.
+ uint32_t handle_begin_;
+ uint32_t handle_end_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(BoundsChecker);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_BOUNDS_CHECKER_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/buffer.h b/chromium/mojo/public/cpp/bindings/lib/buffer.h
new file mode 100644
index 00000000000..c3b570e7767
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/buffer.h
@@ -0,0 +1,24 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_BUFFER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BUFFER_H_
+
+#include <stddef.h>
+
+namespace mojo {
+namespace internal {
+
+// Buffer provides a way to allocate memory. Allocations are 8-byte aligned and
+// zero-initialized. Allocations remain valid for the lifetime of the Buffer.
+class Buffer {
+ public:
+ virtual ~Buffer() {}
+ virtual void* Allocate(size_t num_bytes) = 0;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_BUFFER_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/callback_internal.h b/chromium/mojo/public/cpp/bindings/lib/callback_internal.h
new file mode 100644
index 00000000000..f76ebef59e6
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/callback_internal.h
@@ -0,0 +1,26 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_CALLBACK_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CALLBACK_INTERNAL_H_
+
+namespace mojo {
+class String;
+
+namespace internal {
+
+template <typename T>
+struct Callback_ParamTraits {
+ typedef T ForwardType;
+};
+
+template <>
+struct Callback_ParamTraits<String> {
+ typedef const String& ForwardType;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_CALLBACK_INTERNAL_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/connector.cc b/chromium/mojo/public/cpp/bindings/lib/connector.cc
new file mode 100644
index 00000000000..aa2c4d12334
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/connector.cc
@@ -0,0 +1,157 @@
+// Copyright 2013 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 "mojo/public/cpp/bindings/lib/connector.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "mojo/public/cpp/bindings/error_handler.h"
+
+namespace mojo {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+
+Connector::Connector(ScopedMessagePipeHandle message_pipe,
+ const MojoAsyncWaiter* waiter)
+ : error_handler_(NULL),
+ waiter_(waiter),
+ message_pipe_(message_pipe.Pass()),
+ incoming_receiver_(NULL),
+ async_wait_id_(0),
+ error_(false),
+ drop_writes_(false),
+ enforce_errors_from_incoming_receiver_(true),
+ destroyed_flag_(NULL) {
+ // Even though we don't have an incoming receiver, we still want to monitor
+ // the message pipe to know if is closed or encounters an error.
+ WaitToReadMore();
+}
+
+Connector::~Connector() {
+ if (destroyed_flag_)
+ *destroyed_flag_ = true;
+
+ if (async_wait_id_)
+ waiter_->CancelWait(async_wait_id_);
+}
+
+void Connector::CloseMessagePipe() {
+ Close(message_pipe_.Pass());
+}
+
+ScopedMessagePipeHandle Connector::PassMessagePipe() {
+ if (async_wait_id_) {
+ waiter_->CancelWait(async_wait_id_);
+ async_wait_id_ = 0;
+ }
+ return message_pipe_.Pass();
+}
+
+bool Connector::Accept(Message* message) {
+ assert(message_pipe_.is_valid());
+
+ if (error_)
+ return false;
+
+ if (drop_writes_)
+ return true;
+
+ MojoResult rv = WriteMessageRaw(
+ message_pipe_.get(),
+ message->data(),
+ message->data_num_bytes(),
+ message->mutable_handles()->empty() ? NULL :
+ reinterpret_cast<const MojoHandle*>(
+ &message->mutable_handles()->front()),
+ static_cast<uint32_t>(message->mutable_handles()->size()),
+ MOJO_WRITE_MESSAGE_FLAG_NONE);
+
+ switch (rv) {
+ case MOJO_RESULT_OK:
+ // The handles were successfully transferred, so we don't need the message
+ // to track their lifetime any longer.
+ message->mutable_handles()->clear();
+ break;
+ case MOJO_RESULT_FAILED_PRECONDITION:
+ // There's no point in continuing to write to this pipe since the other
+ // end is gone. Avoid writing any future messages. Hide write failures
+ // from the caller since we'd like them to continue consuming any backlog
+ // of incoming messages before regarding the message pipe as closed.
+ drop_writes_ = true;
+ break;
+ default:
+ // This particular write was rejected, presumably because of bad input.
+ // The pipe is not necessarily in a bad state.
+ return false;
+ }
+ return true;
+}
+
+// static
+void Connector::CallOnHandleReady(void* closure, MojoResult result) {
+ Connector* self = static_cast<Connector*>(closure);
+ self->OnHandleReady(result);
+}
+
+void Connector::OnHandleReady(MojoResult result) {
+ assert(async_wait_id_ != 0);
+ async_wait_id_ = 0;
+
+ if (result == MOJO_RESULT_OK) {
+ // Return immediately if |this| was destroyed. Do not touch any members!
+ if (!ReadMore())
+ return;
+ } else {
+ error_ = true;
+ }
+
+ if (error_ && error_handler_)
+ error_handler_->OnConnectionError();
+}
+
+void Connector::WaitToReadMore() {
+ async_wait_id_ = waiter_->AsyncWait(message_pipe_.get().value(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ &Connector::CallOnHandleReady,
+ this);
+}
+
+bool Connector::ReadMore() {
+ while (true) {
+ bool receiver_result = false;
+
+ // Detect if |this| was destroyed during message dispatch. Allow for the
+ // possibility of re-entering ReadMore() through message dispatch.
+ bool was_destroyed_during_dispatch = false;
+ bool* previous_destroyed_flag = destroyed_flag_;
+ destroyed_flag_ = &was_destroyed_during_dispatch;
+
+ MojoResult rv = ReadAndDispatchMessage(
+ message_pipe_.get(), incoming_receiver_, &receiver_result);
+
+ if (was_destroyed_during_dispatch) {
+ if (previous_destroyed_flag)
+ *previous_destroyed_flag = true; // Propagate flag.
+ return false;
+ }
+ destroyed_flag_ = previous_destroyed_flag;
+
+ if (rv == MOJO_RESULT_SHOULD_WAIT) {
+ WaitToReadMore();
+ break;
+ }
+ if (rv != MOJO_RESULT_OK ||
+ (enforce_errors_from_incoming_receiver_ && !receiver_result)) {
+ error_ = true;
+ break;
+ }
+ }
+ return true;
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/bindings/lib/connector.h b/chromium/mojo/public/cpp/bindings/lib/connector.h
new file mode 100644
index 00000000000..30cd65f4c42
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/connector.h
@@ -0,0 +1,100 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_CONNECTOR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CONNECTOR_H_
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/cpp/bindings/lib/message_queue.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+class ErrorHandler;
+
+namespace internal {
+
+// The Connector class is responsible for performing read/write operations on a
+// MessagePipe. It writes messages it receives through the MessageReceiver
+// interface that it subclasses, and it forwards messages it reads through the
+// MessageReceiver interface assigned as its incoming receiver.
+//
+// NOTE: MessagePipe I/O is non-blocking.
+//
+class Connector : public MessageReceiver {
+ public:
+ // The Connector takes ownership of |message_pipe|.
+ explicit Connector(
+ ScopedMessagePipeHandle message_pipe,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter());
+ virtual ~Connector();
+
+ // Sets the receiver to handle messages read from the message pipe. The
+ // Connector will read messages from the pipe regardless of whether or not an
+ // incoming receiver has been set.
+ void set_incoming_receiver(MessageReceiver* receiver) {
+ incoming_receiver_ = receiver;
+ }
+
+ // Errors from incoming receivers will force the connector into an error
+ // state, where no more messages will be processed. This method is used
+ // during testing to prevent that from happening.
+ void set_enforce_errors_from_incoming_receiver(bool enforce) {
+ enforce_errors_from_incoming_receiver_ = enforce;
+ }
+
+ // Sets the error handler to receive notifications when an error is
+ // encountered while reading from the pipe or waiting to read from the pipe.
+ void set_error_handler(ErrorHandler* error_handler) {
+ error_handler_ = error_handler;
+ }
+
+ // Returns true if an error was encountered while reading from the pipe or
+ // waiting to read from the pipe.
+ bool encountered_error() const { return error_; }
+
+ // Closes the pipe, triggering the error state. Connector is put into a
+ // quiescent state.
+ void CloseMessagePipe();
+
+ // Releases the pipe, not triggering the error state. Connector is put into
+ // a quiescent state.
+ ScopedMessagePipeHandle PassMessagePipe();
+
+ // MessageReceiver implementation:
+ virtual bool Accept(Message* message) MOJO_OVERRIDE;
+
+ private:
+ static void CallOnHandleReady(void* closure, MojoResult result);
+ void OnHandleReady(MojoResult result);
+
+ void WaitToReadMore();
+
+ // Returns false if |this| was destroyed during message dispatch.
+ MOJO_WARN_UNUSED_RESULT bool ReadMore();
+
+ ErrorHandler* error_handler_;
+ const MojoAsyncWaiter* waiter_;
+
+ ScopedMessagePipeHandle message_pipe_;
+ MessageReceiver* incoming_receiver_;
+
+ MojoAsyncWaitID async_wait_id_;
+ bool error_;
+ bool drop_writes_;
+ bool enforce_errors_from_incoming_receiver_;
+
+ // If non-null, this will be set to true when the Connector is destroyed. We
+ // use this flag to allow for the Connector to be destroyed as a side-effect
+ // of dispatching an incoming message.
+ bool* destroyed_flag_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Connector);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_CONNECTOR_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/filter_chain.cc b/chromium/mojo/public/cpp/bindings/lib/filter_chain.cc
new file mode 100644
index 00000000000..efd4ba19875
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/filter_chain.cc
@@ -0,0 +1,49 @@
+// Copyright 2014 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 "mojo/public/cpp/bindings/lib/filter_chain.h"
+
+#include <assert.h>
+
+#include <algorithm>
+
+namespace mojo {
+namespace internal {
+
+FilterChain::FilterChain(MessageReceiver* sink) : sink_(sink) {
+}
+
+FilterChain::FilterChain(RValue other) : sink_(other.object->sink_) {
+ other.object->sink_ = NULL;
+ filters_.swap(other.object->filters_);
+}
+
+FilterChain& FilterChain::operator=(RValue other) {
+ std::swap(sink_, other.object->sink_);
+ filters_.swap(other.object->filters_);
+ return *this;
+}
+
+FilterChain::~FilterChain() {
+ for (std::vector<MessageFilter*>::iterator iter = filters_.begin();
+ iter != filters_.end();
+ ++iter) {
+ delete *iter;
+ }
+}
+
+void FilterChain::SetSink(MessageReceiver* sink) {
+ assert(!sink_);
+ sink_ = sink;
+ if (!filters_.empty())
+ filters_.back()->set_sink(sink);
+}
+
+MessageReceiver* FilterChain::GetHead() {
+ assert(sink_);
+ return filters_.empty() ? sink_ : filters_.front();
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/bindings/lib/filter_chain.h b/chromium/mojo/public/cpp/bindings/lib/filter_chain.h
new file mode 100644
index 00000000000..8680f41aaee
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/filter_chain.h
@@ -0,0 +1,66 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_
+
+#include <vector>
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+class FilterChain {
+ MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(FilterChain, RValue)
+
+ public:
+ // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while
+ // this object is alive.
+ explicit FilterChain(MessageReceiver* sink = NULL);
+
+ // Move-only constructor and operator=.
+ FilterChain(RValue other);
+ FilterChain& operator=(RValue other);
+
+ ~FilterChain();
+
+ template <typename FilterType>
+ inline void Append();
+
+ // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while
+ // this object is alive.
+ void SetSink(MessageReceiver* sink);
+
+ // Returns a receiver to accept messages. Messages flow through all filters in
+ // the same order as they were appended to the chain. If all filters allow a
+ // message to pass, it will be forwarded to |sink_|.
+ // The returned value is invalidated when this object goes away.
+ MessageReceiver* GetHead();
+
+ private:
+ // Owned by this object.
+ std::vector<MessageFilter*> filters_;
+
+ MessageReceiver* sink_;
+};
+
+template <typename FilterType>
+inline void FilterChain::Append() {
+ FilterType* filter = new FilterType(sink_);
+ if (!filters_.empty())
+ filters_.back()->set_sink(filter);
+ filters_.push_back(filter);
+}
+
+template <>
+inline void FilterChain::Append<PassThroughFilter>() {
+}
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/fixed_buffer.cc b/chromium/mojo/public/cpp/bindings/lib/fixed_buffer.cc
new file mode 100644
index 00000000000..5226231725c
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/fixed_buffer.cc
@@ -0,0 +1,53 @@
+// Copyright 2014 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 "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+
+namespace mojo {
+namespace internal {
+
+FixedBuffer::FixedBuffer(size_t size)
+ : ptr_(NULL),
+ cursor_(0),
+ size_(internal::Align(size)) {
+ // calloc() required to zero memory and thus avoid info leaks.
+ ptr_ = static_cast<char*>(calloc(size_, 1));
+}
+
+FixedBuffer::~FixedBuffer() {
+ free(ptr_);
+}
+
+void* FixedBuffer::Allocate(size_t delta) {
+ delta = internal::Align(delta);
+
+ if (delta == 0 || delta > size_ - cursor_) {
+ assert(false);
+ return NULL;
+ }
+
+ char* result = ptr_ + cursor_;
+ cursor_ += delta;
+
+ return result;
+}
+
+void* FixedBuffer::Leak() {
+ char* ptr = ptr_;
+ ptr_ = NULL;
+ cursor_ = 0;
+ size_ = 0;
+ return ptr;
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/bindings/lib/fixed_buffer.h b/chromium/mojo/public/cpp/bindings/lib/fixed_buffer.h
new file mode 100644
index 00000000000..a3515a2ae86
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/fixed_buffer.h
@@ -0,0 +1,67 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_
+
+#include "mojo/public/cpp/bindings/lib/buffer.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+// FixedBuffer provides a simple way to allocate objects within a fixed chunk
+// of memory. Objects are allocated by calling the |Allocate| method, which
+// extends the buffer accordingly. Objects allocated in this way are not freed
+// explicitly. Instead, they remain valid so long as the FixedBuffer remains
+// valid. The Leak method may be used to steal the underlying memory from the
+// FixedBuffer.
+//
+// Typical usage:
+//
+// {
+// FixedBuffer buf(8 + 8);
+//
+// int* a = static_cast<int*>(buf->Allocate(sizeof(int)));
+// *a = 2;
+//
+// double* b = static_cast<double*>(buf->Allocate(sizeof(double)));
+// *b = 3.14f;
+//
+// void* data = buf.Leak();
+// Process(data);
+//
+// free(data);
+// }
+//
+class FixedBuffer : public Buffer {
+ public:
+ explicit FixedBuffer(size_t size);
+ virtual ~FixedBuffer();
+
+ // Grows the buffer by |num_bytes| and returns a pointer to the start of the
+ // addition. The resulting address is 8-byte aligned, and the content of the
+ // memory is zero-filled.
+ virtual void* Allocate(size_t num_bytes) MOJO_OVERRIDE;
+
+ size_t size() const { return size_; }
+
+ // Returns the internal memory owned by the Buffer to the caller. The Buffer
+ // relinquishes its pointer, effectively resetting the state of the Buffer
+ // and leaving the caller responsible for freeing the returned memory address
+ // when no longer needed.
+ void* Leak();
+
+ private:
+ char* ptr_;
+ size_t cursor_;
+ size_t size_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(FixedBuffer);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_FIXED_BUFFER_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/interface_impl_internal.h b/chromium/mojo/public/cpp/bindings/lib/interface_impl_internal.h
new file mode 100644
index 00000000000..f2cc4f0c5b6
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/interface_impl_internal.h
@@ -0,0 +1,94 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_IMPL_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_IMPL_INTERNAL_H_
+
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/message_header_validator.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename Interface>
+class InterfaceImplBase : public Interface {
+ public:
+ virtual ~InterfaceImplBase() {}
+ virtual void OnConnectionEstablished() = 0;
+ virtual void OnConnectionError() = 0;
+};
+
+template <typename Interface>
+class InterfaceImplState : public ErrorHandler {
+ public:
+ typedef typename Interface::Client Client;
+
+ explicit InterfaceImplState(InterfaceImplBase<Interface>* instance)
+ : router_(NULL),
+ proxy_(NULL) {
+ assert(instance);
+ stub_.set_sink(instance);
+ }
+
+ virtual ~InterfaceImplState() {
+ delete proxy_;
+ if (router_) {
+ router_->set_error_handler(NULL);
+ delete router_;
+ }
+ }
+
+ void BindProxy(
+ InterfacePtr<Interface>* ptr,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ MessagePipe pipe;
+ ptr->Bind(pipe.handle0.Pass(), waiter);
+ Bind(pipe.handle1.Pass(), waiter);
+ }
+
+ void Bind(ScopedMessagePipeHandle handle,
+ const MojoAsyncWaiter* waiter) {
+ assert(!router_);
+
+ FilterChain filters;
+ filters.Append<MessageHeaderValidator>();
+ filters.Append<typename Interface::RequestValidator_>();
+ filters.Append<typename Interface::Client::ResponseValidator_>();
+
+ router_ = new Router(handle.Pass(), filters.Pass(), waiter);
+ router_->set_incoming_receiver(&stub_);
+ router_->set_error_handler(this);
+
+ proxy_ = new typename Client::Proxy_(router_);
+
+ instance()->OnConnectionEstablished();
+ }
+
+ Router* router() { return router_; }
+ Client* client() { return proxy_; }
+
+ private:
+ InterfaceImplBase<Interface>* instance() {
+ return static_cast<InterfaceImplBase<Interface>*>(stub_.sink());
+ }
+
+ virtual void OnConnectionError() MOJO_OVERRIDE {
+ instance()->OnConnectionError();
+ }
+
+ Router* router_;
+ typename Client::Proxy_* proxy_;
+ typename Interface::Stub_ stub_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(InterfaceImplState);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_IMPL_INTERNAL_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/interface_ptr_internal.h b/chromium/mojo/public/cpp/bindings/lib/interface_ptr_internal.h
new file mode 100644
index 00000000000..a318d6185c5
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/interface_ptr_internal.h
@@ -0,0 +1,84 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_PTR_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_PTR_INTERNAL_H_
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/message_header_validator.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
+#include "mojo/public/cpp/environment/environment.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename Interface>
+class InterfacePtrState {
+ public:
+ InterfacePtrState() : proxy_(NULL), router_(NULL) {}
+
+ ~InterfacePtrState() {
+ // Destruction order matters here. We delete |proxy_| first, even though
+ // |router_| may have a reference to it, so that |~Interface| may have a
+ // shot at generating new outbound messages (ie, invoking client methods).
+ delete proxy_;
+ delete router_;
+ }
+
+ Interface* instance() const { return proxy_; }
+
+ Router* router() const { return router_; }
+
+ void Swap(InterfacePtrState* other) {
+ std::swap(other->proxy_, proxy_);
+ std::swap(other->router_, router_);
+ }
+
+ void ConfigureProxy(
+ ScopedMessagePipeHandle handle,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter()) {
+ assert(!proxy_);
+ assert(!router_);
+
+ FilterChain filters;
+ filters.Append<MessageHeaderValidator>();
+ filters.Append<typename Interface::Client::RequestValidator_>();
+ filters.Append<typename Interface::ResponseValidator_>();
+
+ router_ = new Router(handle.Pass(), filters.Pass(), waiter);
+ ProxyWithStub* proxy = new ProxyWithStub(router_);
+ router_->set_incoming_receiver(&proxy->stub);
+
+ proxy_ = proxy;
+ }
+
+ void set_client(typename Interface::Client* client) {
+ assert(proxy_);
+ proxy_->stub.set_sink(client);
+ }
+
+ private:
+ class ProxyWithStub : public Interface::Proxy_ {
+ public:
+ explicit ProxyWithStub(MessageReceiverWithResponder* receiver)
+ : Interface::Proxy_(receiver) {
+ }
+ typename Interface::Client::Stub_ stub;
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ProxyWithStub);
+ };
+
+ ProxyWithStub* proxy_;
+ Router* router_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(InterfacePtrState);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_INTERFACE_PTR_INTERNAL_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/message.cc b/chromium/mojo/public/cpp/bindings/lib/message.cc
new file mode 100644
index 00000000000..78017c00793
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/message.cc
@@ -0,0 +1,81 @@
+// Copyright 2013 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 "mojo/public/cpp/bindings/message.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <algorithm>
+
+namespace mojo {
+
+Message::Message()
+ : data_num_bytes_(0),
+ data_(NULL) {
+}
+
+Message::~Message() {
+ free(data_);
+
+ for (std::vector<Handle>::iterator it = handles_.begin();
+ it != handles_.end(); ++it) {
+ if (it->is_valid())
+ CloseRaw(*it);
+ }
+}
+
+void Message::AllocUninitializedData(uint32_t num_bytes) {
+ assert(!data_);
+ data_num_bytes_ = num_bytes;
+ data_ = static_cast<internal::MessageData*>(malloc(num_bytes));
+}
+
+void Message::AdoptData(uint32_t num_bytes, internal::MessageData* data) {
+ assert(!data_);
+ data_num_bytes_ = num_bytes;
+ data_ = data;
+}
+
+void Message::Swap(Message* other) {
+ std::swap(data_num_bytes_, other->data_num_bytes_);
+ std::swap(data_, other->data_);
+ std::swap(handles_, other->handles_);
+}
+
+MojoResult ReadAndDispatchMessage(MessagePipeHandle handle,
+ MessageReceiver* receiver,
+ bool* receiver_result) {
+ MojoResult rv;
+
+ uint32_t num_bytes = 0, num_handles = 0;
+ rv = ReadMessageRaw(handle,
+ NULL,
+ &num_bytes,
+ NULL,
+ &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+ if (rv != MOJO_RESULT_RESOURCE_EXHAUSTED)
+ return rv;
+
+ Message message;
+ message.AllocUninitializedData(num_bytes);
+ message.mutable_handles()->resize(num_handles);
+
+ rv = ReadMessageRaw(handle,
+ message.mutable_data(),
+ &num_bytes,
+ message.mutable_handles()->empty()
+ ? NULL
+ : reinterpret_cast<MojoHandle*>(
+ &message.mutable_handles()->front()),
+ &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+ if (receiver && rv == MOJO_RESULT_OK)
+ *receiver_result = receiver->Accept(&message);
+
+ return rv;
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/bindings/lib/message_builder.cc b/chromium/mojo/public/cpp/bindings/lib/message_builder.cc
new file mode 100644
index 00000000000..c7466444d04
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/message_builder.cc
@@ -0,0 +1,52 @@
+// Copyright 2013 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 "mojo/public/cpp/bindings/lib/message_builder.h"
+
+#include "mojo/public/cpp/bindings/message.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename Header>
+void Allocate(Buffer* buf, Header** header) {
+ *header = static_cast<Header*>(buf->Allocate(sizeof(Header)));
+ (*header)->num_bytes = sizeof(Header);
+}
+
+MessageBuilder::MessageBuilder(uint32_t name, size_t payload_size)
+ : buf_(sizeof(MessageHeader) + payload_size) {
+ MessageHeader* header;
+ Allocate(&buf_, &header);
+ header->num_fields = 2;
+ header->name = name;
+}
+
+MessageBuilder::~MessageBuilder() {
+}
+
+void MessageBuilder::Finish(Message* message) {
+ uint32_t num_bytes = static_cast<uint32_t>(buf_.size());
+ message->AdoptData(num_bytes, static_cast<MessageData*>(buf_.Leak()));
+}
+
+MessageBuilder::MessageBuilder(size_t size)
+ : buf_(size) {
+}
+
+MessageWithRequestIDBuilder::MessageWithRequestIDBuilder(uint32_t name,
+ size_t payload_size,
+ uint32_t flags,
+ uint64_t request_id)
+ : MessageBuilder(sizeof(MessageHeaderWithRequestID) + payload_size) {
+ MessageHeaderWithRequestID* header;
+ Allocate(&buf_, &header);
+ header->num_fields = 3;
+ header->name = name;
+ header->flags = flags;
+ header->request_id = request_id;
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/bindings/lib/message_builder.h b/chromium/mojo/public/cpp/bindings/lib/message_builder.h
new file mode 100644
index 00000000000..b4988ff9cb9
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/message_builder.h
@@ -0,0 +1,63 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_BUILDER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_BUILDER_H_
+
+#include <stdint.h>
+
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "mojo/public/cpp/bindings/lib/message_internal.h"
+
+namespace mojo {
+class Message;
+
+namespace internal {
+
+class MessageBuilder {
+ public:
+ MessageBuilder(uint32_t name, size_t payload_size);
+ ~MessageBuilder();
+
+ Buffer* buffer() { return &buf_; }
+
+ // Call Finish when done making allocations in |buffer()|. Upon return,
+ // |message| will contain the message data, and |buffer()| will no longer be
+ // valid to reference.
+ void Finish(Message* message);
+
+ protected:
+ explicit MessageBuilder(size_t size);
+ FixedBuffer buf_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(MessageBuilder);
+};
+
+class MessageWithRequestIDBuilder : public MessageBuilder {
+ public:
+ MessageWithRequestIDBuilder(uint32_t name, size_t payload_size,
+ uint32_t flags, uint64_t request_id);
+};
+
+class RequestMessageBuilder : public MessageWithRequestIDBuilder {
+ public:
+ RequestMessageBuilder(uint32_t name, size_t payload_size)
+ : MessageWithRequestIDBuilder(name, payload_size, kMessageExpectsResponse,
+ 0) {
+ }
+};
+
+class ResponseMessageBuilder : public MessageWithRequestIDBuilder {
+ public:
+ ResponseMessageBuilder(uint32_t name, size_t payload_size,
+ uint64_t request_id)
+ : MessageWithRequestIDBuilder(name, payload_size, kMessageIsResponse,
+ request_id) {
+ }
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_BUILDER_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/message_filter.cc b/chromium/mojo/public/cpp/bindings/lib/message_filter.cc
new file mode 100644
index 00000000000..b09f40d8c5e
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/message_filter.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 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 "mojo/public/cpp/bindings/message_filter.h"
+
+namespace mojo {
+
+MessageFilter::MessageFilter(MessageReceiver* sink) : sink_(sink) {
+}
+
+MessageFilter::~MessageFilter() {
+}
+
+PassThroughFilter::PassThroughFilter(MessageReceiver* sink)
+ : MessageFilter(sink) {
+}
+
+bool PassThroughFilter::Accept(Message* message) {
+ return sink_->Accept(message);
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/bindings/lib/message_header_validator.cc b/chromium/mojo/public/cpp/bindings/lib/message_header_validator.cc
new file mode 100644
index 00000000000..a55917afde5
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/message_header_validator.cc
@@ -0,0 +1,81 @@
+// Copyright 2014 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 "mojo/public/cpp/bindings/lib/message_header_validator.h"
+
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+
+namespace mojo {
+namespace internal {
+namespace {
+
+bool IsValidMessageHeader(const MessageHeader* header) {
+ // NOTE: Our goal is to preserve support for future extension of the message
+ // header. If we encounter fields we do not understand, we must ignore them.
+
+ // Extra validation of the struct header:
+ if (header->num_fields == 2) {
+ if (header->num_bytes != sizeof(MessageHeader)) {
+ ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+ return false;
+ }
+ } else if (header->num_fields == 3) {
+ if (header->num_bytes != sizeof(MessageHeaderWithRequestID)) {
+ ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+ return false;
+ }
+ } else if (header->num_fields > 3) {
+ if (header->num_bytes < sizeof(MessageHeaderWithRequestID)) {
+ ReportValidationError(VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+ return false;
+ }
+ }
+
+ // Validate flags (allow unknown bits):
+
+ // These flags require a RequestID.
+ if (header->num_fields < 3 &&
+ ((header->flags & kMessageExpectsResponse) ||
+ (header->flags & kMessageIsResponse))) {
+ ReportValidationError(VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID);
+ return false;
+ }
+
+ // These flags are mutually exclusive.
+ if ((header->flags & kMessageExpectsResponse) &&
+ (header->flags & kMessageIsResponse)) {
+ ReportValidationError(
+ VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION);
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+MessageHeaderValidator::MessageHeaderValidator(MessageReceiver* sink)
+ : MessageFilter(sink) {
+}
+
+bool MessageHeaderValidator::Accept(Message* message) {
+ // Pass 0 as number of handles because we don't expect any in the header, even
+ // if |message| contains handles.
+ BoundsChecker bounds_checker(message->data(), message->data_num_bytes(), 0);
+
+ if (!ValidateStructHeader(message->data(), sizeof(MessageHeader), 2,
+ &bounds_checker)) {
+ return false;
+ }
+
+ if (!IsValidMessageHeader(message->header()))
+ return false;
+
+ return sink_->Accept(message);
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/bindings/lib/message_header_validator.h b/chromium/mojo/public/cpp/bindings/lib/message_header_validator.h
new file mode 100644
index 00000000000..c3f43a07fbc
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/message_header_validator.h
@@ -0,0 +1,24 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_HEADER_VALIDATOR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_HEADER_VALIDATOR_H_
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+
+namespace mojo {
+namespace internal {
+
+class MessageHeaderValidator : public MessageFilter {
+ public:
+ explicit MessageHeaderValidator(MessageReceiver* sink = NULL);
+
+ virtual bool Accept(Message* message) MOJO_OVERRIDE;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_HEADER_VALIDATOR_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/message_internal.h b/chromium/mojo/public/cpp/bindings/lib/message_internal.h
new file mode 100644
index 00000000000..3c67902b7a8
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/message_internal.h
@@ -0,0 +1,44 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_INTERNAL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_INTERNAL_H_
+
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+
+namespace mojo {
+namespace internal {
+
+#pragma pack(push, 1)
+
+enum {
+ kMessageExpectsResponse = 1 << 0,
+ kMessageIsResponse = 1 << 1
+};
+
+struct MessageHeader : internal::StructHeader {
+ uint32_t name;
+ uint32_t flags;
+};
+MOJO_COMPILE_ASSERT(sizeof(MessageHeader) == 16, bad_sizeof_MessageHeader);
+
+struct MessageHeaderWithRequestID : MessageHeader {
+ uint64_t request_id;
+};
+MOJO_COMPILE_ASSERT(sizeof(MessageHeaderWithRequestID) == 24,
+ bad_sizeof_MessageHeaderWithRequestID);
+
+struct MessageData {
+ MessageHeader header;
+};
+
+MOJO_COMPILE_ASSERT(sizeof(MessageData) == sizeof(MessageHeader),
+ bad_sizeof_MessageData);
+
+#pragma pack(pop)
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_INTERNAL_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/message_queue.cc b/chromium/mojo/public/cpp/bindings/lib/message_queue.cc
new file mode 100644
index 00000000000..1982ccb14c2
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/message_queue.cc
@@ -0,0 +1,50 @@
+// Copyright 2013 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 "mojo/public/cpp/bindings/lib/message_queue.h"
+
+#include <assert.h>
+#include <stddef.h>
+
+#include "mojo/public/cpp/bindings/message.h"
+
+namespace mojo {
+namespace internal {
+
+MessageQueue::MessageQueue() {
+}
+
+MessageQueue::~MessageQueue() {
+ while (!queue_.empty())
+ Pop();
+}
+
+bool MessageQueue::IsEmpty() const {
+ return queue_.empty();
+}
+
+Message* MessageQueue::Peek() {
+ assert(!queue_.empty());
+ return queue_.front();
+}
+
+void MessageQueue::Push(Message* message) {
+ queue_.push(new Message());
+ queue_.back()->Swap(message);
+}
+
+void MessageQueue::Pop(Message* message) {
+ assert(!queue_.empty());
+ queue_.front()->Swap(message);
+ Pop();
+}
+
+void MessageQueue::Pop() {
+ assert(!queue_.empty());
+ delete queue_.front();
+ queue_.pop();
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/bindings/lib/message_queue.h b/chromium/mojo/public/cpp/bindings/lib/message_queue.h
new file mode 100644
index 00000000000..4e46b54a250
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/message_queue.h
@@ -0,0 +1,47 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_QUEUE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_QUEUE_H_
+
+#include <queue>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+class Message;
+
+namespace internal {
+
+// A queue for Message objects.
+class MessageQueue {
+ public:
+ MessageQueue();
+ ~MessageQueue();
+
+ bool IsEmpty() const;
+ Message* Peek();
+
+ // This method transfers ownership of |message->data| and |message->handles|
+ // to the message queue, resetting |message| in the process.
+ void Push(Message* message);
+
+ // Removes the next message from the queue, transferring ownership of its
+ // data and handles to the given |message|.
+ void Pop(Message* message);
+
+ // Removes the next message from the queue, discarding its data and handles.
+ // This is meant to be used in conjunction with |Peek|.
+ void Pop();
+
+ private:
+ std::queue<Message*> queue_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(MessageQueue);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_QUEUE_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/no_interface.cc b/chromium/mojo/public/cpp/bindings/lib/no_interface.cc
new file mode 100644
index 00000000000..9e0945c83b3
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/no_interface.cc
@@ -0,0 +1,20 @@
+// Copyright 2014 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 "mojo/public/cpp/bindings/no_interface.h"
+
+namespace mojo {
+
+const char* NoInterface::Name_ = "mojo::NoInterface";
+
+bool NoInterfaceStub::Accept(Message* message) {
+ return false;
+}
+
+bool NoInterfaceStub::AcceptWithResponder(Message* message,
+ MessageReceiver* responder) {
+ return false;
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/bindings/lib/router.cc b/chromium/mojo/public/cpp/bindings/lib/router.cc
new file mode 100644
index 00000000000..a8222203c47
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/router.cc
@@ -0,0 +1,140 @@
+// Copyright 2014 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 "mojo/public/cpp/bindings/lib/router.h"
+
+namespace mojo {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+
+class ResponderThunk : public MessageReceiver {
+ public:
+ explicit ResponderThunk(const SharedData<Router*>& router)
+ : router_(router) {
+ }
+ virtual ~ResponderThunk() {
+ }
+
+ // MessageReceiver implementation:
+ virtual bool Accept(Message* message) MOJO_OVERRIDE {
+ assert(message->has_flag(kMessageIsResponse));
+
+ bool result = false;
+
+ Router* router = router_.value();
+ if (router)
+ result = router->Accept(message);
+
+ return result;
+ }
+
+ private:
+ SharedData<Router*> router_;
+};
+
+// ----------------------------------------------------------------------------
+
+Router::HandleIncomingMessageThunk::HandleIncomingMessageThunk(Router* router)
+ : router_(router) {
+}
+
+Router::HandleIncomingMessageThunk::~HandleIncomingMessageThunk() {
+}
+
+bool Router::HandleIncomingMessageThunk::Accept(Message* message) {
+ return router_->HandleIncomingMessage(message);
+}
+
+// ----------------------------------------------------------------------------
+
+Router::Router(ScopedMessagePipeHandle message_pipe,
+ FilterChain filters,
+ const MojoAsyncWaiter* waiter)
+ : thunk_(this),
+ filters_(filters.Pass()),
+ connector_(message_pipe.Pass(), waiter),
+ weak_self_(this),
+ incoming_receiver_(NULL),
+ next_request_id_(0),
+ testing_mode_(false) {
+ filters_.SetSink(&thunk_);
+ connector_.set_incoming_receiver(filters_.GetHead());
+}
+
+Router::~Router() {
+ weak_self_.set_value(NULL);
+
+ for (ResponderMap::const_iterator i = responders_.begin();
+ i != responders_.end(); ++i) {
+ delete i->second;
+ }
+}
+
+bool Router::Accept(Message* message) {
+ assert(!message->has_flag(kMessageExpectsResponse));
+ return connector_.Accept(message);
+}
+
+bool Router::AcceptWithResponder(Message* message,
+ MessageReceiver* responder) {
+ assert(message->has_flag(kMessageExpectsResponse));
+
+ // Reserve 0 in case we want it to convey special meaning in the future.
+ uint64_t request_id = next_request_id_++;
+ if (request_id == 0)
+ request_id = next_request_id_++;
+
+ message->set_request_id(request_id);
+ if (!connector_.Accept(message))
+ return false;
+
+ // We assume ownership of |responder|.
+ responders_[request_id] = responder;
+ return true;
+}
+
+void Router::EnableTestingMode() {
+ testing_mode_ = true;
+ connector_.set_enforce_errors_from_incoming_receiver(false);
+}
+
+bool Router::HandleIncomingMessage(Message* message) {
+ if (message->has_flag(kMessageExpectsResponse)) {
+ if (incoming_receiver_) {
+ MessageReceiver* responder = new ResponderThunk(weak_self_);
+ bool ok = incoming_receiver_->AcceptWithResponder(message, responder);
+ if (!ok)
+ delete responder;
+ return ok;
+ }
+
+ // If we receive a request expecting a response when the client is not
+ // listening, then we have no choice but to tear down the pipe.
+ connector_.CloseMessagePipe();
+ } else if (message->has_flag(kMessageIsResponse)) {
+ uint64_t request_id = message->request_id();
+ ResponderMap::iterator it = responders_.find(request_id);
+ if (it == responders_.end()) {
+ assert(testing_mode_);
+ return false;
+ }
+ MessageReceiver* responder = it->second;
+ responders_.erase(it);
+ bool ok = responder->Accept(message);
+ delete responder;
+ return ok;
+ } else {
+ if (incoming_receiver_)
+ return incoming_receiver_->Accept(message);
+ // OK to drop message on the floor.
+ }
+
+ return false;
+}
+
+// ----------------------------------------------------------------------------
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/bindings/lib/router.h b/chromium/mojo/public/cpp/bindings/lib/router.h
new file mode 100644
index 00000000000..76bf1471e2e
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/router.h
@@ -0,0 +1,91 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_
+
+#include <map>
+
+#include "mojo/public/cpp/bindings/lib/connector.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/shared_data.h"
+#include "mojo/public/cpp/environment/environment.h"
+
+namespace mojo {
+namespace internal {
+
+class Router : public MessageReceiverWithResponder {
+ public:
+ Router(ScopedMessagePipeHandle message_pipe,
+ FilterChain filters,
+ const MojoAsyncWaiter* waiter = Environment::GetDefaultAsyncWaiter());
+ virtual ~Router();
+
+ // Sets the receiver to handle messages read from the message pipe that do
+ // not have the kMessageIsResponse flag set.
+ void set_incoming_receiver(MessageReceiverWithResponder* receiver) {
+ incoming_receiver_ = receiver;
+ }
+
+ // Sets the error handler to receive notifications when an error is
+ // encountered while reading from the pipe or waiting to read from the pipe.
+ void set_error_handler(ErrorHandler* error_handler) {
+ connector_.set_error_handler(error_handler);
+ }
+
+ // Returns true if an error was encountered while reading from the pipe or
+ // waiting to read from the pipe.
+ bool encountered_error() const { return connector_.encountered_error(); }
+
+ void CloseMessagePipe() {
+ connector_.CloseMessagePipe();
+ }
+
+ ScopedMessagePipeHandle PassMessagePipe() {
+ return connector_.PassMessagePipe();
+ }
+
+ // MessageReceiver implementation:
+ virtual bool Accept(Message* message) MOJO_OVERRIDE;
+ virtual bool AcceptWithResponder(Message* message, MessageReceiver* responder)
+ MOJO_OVERRIDE;
+
+ // Sets this object to testing mode.
+ // In testing mode:
+ // - the object is more tolerant of unrecognized response messages;
+ // - the connector continues working after seeing errors from its incoming
+ // receiver.
+ void EnableTestingMode();
+
+ private:
+ typedef std::map<uint64_t, MessageReceiver*> ResponderMap;
+
+ class HandleIncomingMessageThunk : public MessageReceiver {
+ public:
+ HandleIncomingMessageThunk(Router* router);
+ virtual ~HandleIncomingMessageThunk();
+
+ // MessageReceiver implementation:
+ virtual bool Accept(Message* message) MOJO_OVERRIDE;
+
+ private:
+ Router* router_;
+ };
+
+ bool HandleIncomingMessage(Message* message);
+
+ HandleIncomingMessageThunk thunk_;
+ FilterChain filters_;
+ Connector connector_;
+ SharedData<Router*> weak_self_;
+ MessageReceiverWithResponder* incoming_receiver_;
+ ResponderMap responders_;
+ uint64_t next_request_id_;
+ bool testing_mode_;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/shared_data.h b/chromium/mojo/public/cpp/bindings/lib/shared_data.h
new file mode 100644
index 00000000000..c7bd54f8d7c
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/shared_data.h
@@ -0,0 +1,84 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_DATA_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_DATA_H_
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+// Used to allocate an instance of T that can be shared via reference counting.
+template <typename T>
+class SharedData {
+ public:
+ ~SharedData() {
+ holder_->Release();
+ }
+
+ SharedData() : holder_(new Holder()) {
+ }
+
+ explicit SharedData(const T& value) : holder_(new Holder(value)) {
+ }
+
+ SharedData(const SharedData<T>& other) : holder_(other.holder_) {
+ holder_->Retain();
+ }
+
+ SharedData<T>& operator=(const SharedData<T>& other) {
+ if (other.holder_ == holder_)
+ return *this;
+ holder_->Release();
+ holder_ = other.holder_;
+ holder_->Retain();
+ return *this;
+ }
+
+ void reset() {
+ holder_->Release();
+ holder_ = new Holder();
+ }
+
+ void reset(const T& value) {
+ holder_->Release();
+ holder_ = new Holder(value);
+ }
+
+ void set_value(const T& value) {
+ holder_->value = value;
+ }
+ T* mutable_value() {
+ return &holder_->value;
+ }
+ const T& value() const {
+ return holder_->value;
+ }
+
+ private:
+ class Holder {
+ public:
+ Holder() : value(), ref_count_(1) {
+ }
+ Holder(const T& value) : value(value), ref_count_(1) {
+ }
+
+ void Retain() { ++ref_count_; }
+ void Release() { if (--ref_count_ == 0) delete this; }
+
+ T value;
+
+ private:
+ int ref_count_;
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Holder);
+ };
+
+ Holder* holder_;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_DATA_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/shared_ptr.h b/chromium/mojo/public/cpp/bindings/lib/shared_ptr.h
new file mode 100644
index 00000000000..6ae53a5681a
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/shared_ptr.h
@@ -0,0 +1,63 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_PTR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_PTR_H_
+
+#include "mojo/public/cpp/bindings/lib/shared_data.h"
+
+namespace mojo {
+namespace internal {
+
+// Used to manage a heap-allocated instance of P that can be shared via
+// reference counting. When the last reference is dropped, the instance is
+// deleted.
+template <typename P>
+class SharedPtr {
+ public:
+ SharedPtr() {}
+
+ explicit SharedPtr(P* ptr) {
+ impl_.mutable_value()->ptr = ptr;
+ }
+
+ // Default copy-constructor and assignment operator are OK.
+
+ P* get() {
+ return impl_.value().ptr;
+ }
+ const P* get() const {
+ return impl_.value().ptr;
+ }
+
+ P* operator->() { return get(); }
+ const P* operator->() const { return get(); }
+
+ private:
+ class Impl {
+ public:
+ ~Impl() {
+ if (ptr)
+ delete ptr;
+ }
+
+ Impl() : ptr(NULL) {
+ }
+
+ Impl(P* ptr) : ptr(ptr) {
+ }
+
+ P* ptr;
+
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Impl);
+ };
+
+ SharedData<Impl> impl_;
+};
+
+} // namespace mojo
+} // namespace internal
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_SHARED_PTR_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/string_serialization.cc b/chromium/mojo/public/cpp/bindings/lib/string_serialization.cc
new file mode 100644
index 00000000000..0044b1b9cff
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/string_serialization.cc
@@ -0,0 +1,38 @@
+// Copyright 2014 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 "mojo/public/cpp/bindings/lib/string_serialization.h"
+
+#include <string.h>
+
+namespace mojo {
+
+size_t GetSerializedSize_(const String& input) {
+ if (!input)
+ return 0;
+ return internal::Align(sizeof(internal::String_Data) + input.size());
+}
+
+void Serialize_(const String& input, internal::Buffer* buf,
+ internal::String_Data** output) {
+ if (input) {
+ internal::String_Data* result =
+ internal::String_Data::New(input.size(), buf);
+ memcpy(result->storage(), input.data(), input.size());
+ *output = result;
+ } else {
+ *output = NULL;
+ }
+}
+
+void Deserialize_(internal::String_Data* input, String* output) {
+ if (input) {
+ String result(input->storage(), input->size());
+ result.Swap(output);
+ } else {
+ output->reset();
+ }
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/bindings/lib/string_serialization.h b/chromium/mojo/public/cpp/bindings/lib/string_serialization.h
new file mode 100644
index 00000000000..bad2a0c2766
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/string_serialization.h
@@ -0,0 +1,20 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_STRING_SERIALIZATION_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_STRING_SERIALIZATION_H_
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/string.h"
+
+namespace mojo {
+
+size_t GetSerializedSize_(const String& input);
+void Serialize_(const String& input, internal::Buffer* buffer,
+ internal::String_Data** output);
+void Deserialize_(internal::String_Data* input, String* output);
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_STRING_SERIALIZATION_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/sync_dispatcher.cc b/chromium/mojo/public/cpp/bindings/lib/sync_dispatcher.cc
new file mode 100644
index 00000000000..2cde97f6ee4
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/sync_dispatcher.cc
@@ -0,0 +1,27 @@
+// Copyright 2014 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 "mojo/public/cpp/bindings/sync_dispatcher.h"
+
+#include <stdlib.h>
+
+#include "mojo/public/cpp/bindings/message.h"
+
+namespace mojo {
+
+bool WaitForMessageAndDispatch(MessagePipeHandle handle,
+ MessageReceiver* receiver) {
+ while (true) {
+ bool result;
+ MojoResult rv = ReadAndDispatchMessage(handle, receiver, &result);
+ if (rv == MOJO_RESULT_OK)
+ return result;
+ if (rv == MOJO_RESULT_SHOULD_WAIT)
+ rv = Wait(handle, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE);
+ if (rv != MOJO_RESULT_OK)
+ return false;
+ }
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/bindings/lib/template_util.h b/chromium/mojo/public/cpp/bindings/lib/template_util.h
new file mode 100644
index 00000000000..599126691ed
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/template_util.h
@@ -0,0 +1,89 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_TEMPLATE_UTIL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_TEMPLATE_UTIL_H_
+
+namespace mojo {
+namespace internal {
+
+template<class T, T v>
+struct IntegralConstant {
+ static const T value = v;
+};
+
+template <class T, T v> const T IntegralConstant<T, v>::value;
+
+typedef IntegralConstant<bool, true> TrueType;
+typedef IntegralConstant<bool, false> FalseType;
+
+template <class T> struct IsConst : FalseType {};
+template <class T> struct IsConst<const T> : TrueType {};
+
+template<bool B, typename T = void>
+struct EnableIf {};
+
+template<typename T>
+struct EnableIf<true, T> { typedef T type; };
+
+// Types YesType and NoType are guaranteed such that sizeof(YesType) <
+// sizeof(NoType).
+typedef char YesType;
+
+struct NoType {
+ YesType dummy[2];
+};
+
+// A helper template to determine if given type is non-const move-only-type,
+// i.e. if a value of the given type should be passed via .Pass() in a
+// destructive way.
+template <typename T> struct IsMoveOnlyType {
+ template <typename U>
+ static YesType Test(const typename U::MoveOnlyTypeForCPP03*);
+
+ template <typename U>
+ static NoType Test(...);
+
+ static const bool value = sizeof(Test<T>(0)) == sizeof(YesType) &&
+ !IsConst<T>::value;
+};
+
+template <typename T>
+typename EnableIf<!IsMoveOnlyType<T>::value, T>::type& Forward(T& t) {
+ return t;
+}
+
+template <typename T>
+typename EnableIf<IsMoveOnlyType<T>::value, T>::type Forward(T& t) {
+ return t.Pass();
+}
+
+// This goop is a trick used to implement a template that can be used to
+// determine if a given class is the base class of another given class.
+template<typename, typename> struct IsSame {
+ static bool const value = false;
+};
+template<typename A> struct IsSame<A, A> {
+ static bool const value = true;
+};
+template<typename Base, typename Derived> struct IsBaseOf {
+ private:
+ // This class doesn't work correctly with forward declarations.
+ // Because sizeof cannot be applied to incomplete types, this line prevents us
+ // from passing in forward declarations.
+ typedef char (*EnsureTypesAreComplete)[sizeof(Base) + sizeof(Derived)];
+
+ static Derived* CreateDerived();
+ static char (&Check(Base*))[1];
+ static char (&Check(...))[2];
+
+ public:
+ static bool const value = sizeof Check(CreateDerived()) == 1 &&
+ !IsSame<Base const, void const>::value;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_TEMPLATE_UTIL_H_
diff --git a/chromium/mojo/public/cpp/bindings/lib/validation_errors.cc b/chromium/mojo/public/cpp/bindings/lib/validation_errors.cc
new file mode 100644
index 00000000000..ed5904d3963
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/validation_errors.cc
@@ -0,0 +1,64 @@
+// Copyright 2014 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 "mojo/public/cpp/bindings/lib/validation_errors.h"
+
+#include <assert.h>
+#include <stdio.h>
+
+namespace mojo {
+namespace internal {
+namespace {
+
+ValidationErrorObserverForTesting* g_validation_error_observer = NULL;
+
+} // namespace
+
+const char* ValidationErrorToString(ValidationError error) {
+ switch (error) {
+ case VALIDATION_ERROR_NONE:
+ return "VALIDATION_ERROR_NONE";
+ case VALIDATION_ERROR_MISALIGNED_OBJECT:
+ return "VALIDATION_ERROR_MISALIGNED_OBJECT";
+ case VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE:
+ return "VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE";
+ case VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER:
+ return "VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER";
+ case VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER:
+ return "VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER";
+ case VALIDATION_ERROR_ILLEGAL_HANDLE:
+ return "VALIDATION_ERROR_ILLEGAL_HANDLE";
+ case VALIDATION_ERROR_ILLEGAL_POINTER:
+ return "VALIDATION_ERROR_ILLEGAL_POINTER";
+ case VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION:
+ return "VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION";
+ case VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID:
+ return "VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID";
+ }
+
+ return "Unknown error";
+}
+
+void ReportValidationError(ValidationError error) {
+ if (g_validation_error_observer) {
+ g_validation_error_observer->set_last_error(error);
+ } else {
+ // TODO(yzshen): Consider adding better logging support.
+ fprintf(stderr, "Invalid message: %s\n", ValidationErrorToString(error));
+ }
+}
+
+ValidationErrorObserverForTesting::ValidationErrorObserverForTesting()
+ : last_error_(VALIDATION_ERROR_NONE) {
+ assert(!g_validation_error_observer);
+ g_validation_error_observer = this;
+}
+
+ValidationErrorObserverForTesting::~ValidationErrorObserverForTesting() {
+ assert(g_validation_error_observer == this);
+ g_validation_error_observer = NULL;
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/bindings/lib/validation_errors.h b/chromium/mojo/public/cpp/bindings/lib/validation_errors.h
new file mode 100644
index 00000000000..9b443c371e5
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/lib/validation_errors.h
@@ -0,0 +1,66 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_ERRORS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_ERRORS_H_
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+enum ValidationError {
+ // There is no validation error.
+ VALIDATION_ERROR_NONE,
+ // An object (struct or array) is not 8-byte aligned.
+ VALIDATION_ERROR_MISALIGNED_OBJECT,
+ // An object is not contained inside the message data, or it overlaps other
+ // objects.
+ VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE,
+ // A struct header doesn't make sense, for example:
+ // - |num_bytes| is smaller than the size of the oldest version that we
+ // support.
+ // - |num_fields| is smaller than the field number of the oldest version that
+ // we support.
+ // - |num_bytes| and |num_fields| don't match.
+ VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER,
+ // An array header doesn't make sense, for example:
+ // - |num_bytes| is smaller than the size of the header plus the size required
+ // to store |num_elements| elements.
+ VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
+ // An encoded handle is illegal.
+ VALIDATION_ERROR_ILLEGAL_HANDLE,
+ // An encoded pointer is illegal.
+ VALIDATION_ERROR_ILLEGAL_POINTER,
+ // |flags| in the message header is an invalid flag combination.
+ VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION,
+ // |flags| in the message header indicates that a request ID is required but
+ // there isn't one.
+ VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID,
+};
+
+const char* ValidationErrorToString(ValidationError error);
+
+void ReportValidationError(ValidationError error);
+
+// Only used by validation tests and when there is only one thread doing message
+// validation.
+class ValidationErrorObserverForTesting {
+ public:
+ ValidationErrorObserverForTesting();
+ ~ValidationErrorObserverForTesting();
+
+ ValidationError last_error() const { return last_error_; }
+ void set_last_error(ValidationError error) { last_error_ = error; }
+
+ private:
+ ValidationError last_error_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ValidationErrorObserverForTesting);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_VALIDATION_ERRORS_H_
diff --git a/chromium/mojo/public/cpp/bindings/message.h b/chromium/mojo/public/cpp/bindings/message.h
new file mode 100644
index 00000000000..beb1cd9036f
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/message.h
@@ -0,0 +1,123 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_H_
+
+#include <assert.h>
+
+#include <vector>
+
+#include "mojo/public/cpp/bindings/lib/message_internal.h"
+
+namespace mojo {
+
+// Message is a holder for the data and handles to be sent over a MessagePipe.
+// Message owns its data and handles, but a consumer of Message is free to
+// mutate the data and handles. The message's data is comprised of a header
+// followed by payload.
+class Message {
+ public:
+ Message();
+ ~Message();
+
+ // These may only be called on a newly created Message object.
+ void AllocUninitializedData(uint32_t num_bytes);
+ void AdoptData(uint32_t num_bytes, internal::MessageData* data);
+
+ // Swaps data and handles between this Message and another.
+ void Swap(Message* other);
+
+ uint32_t data_num_bytes() const { return data_num_bytes_; }
+
+ // Access the raw bytes of the message.
+ const uint8_t* data() const { return
+ reinterpret_cast<const uint8_t*>(data_);
+ }
+ uint8_t* mutable_data() { return reinterpret_cast<uint8_t*>(data_); }
+
+ // Access the header.
+ const internal::MessageHeader* header() const { return &data_->header; }
+
+ uint32_t name() const { return data_->header.name; }
+ bool has_flag(uint32_t flag) const { return !!(data_->header.flags & flag); }
+
+ // Access the request_id field (if present).
+ bool has_request_id() const { return data_->header.num_fields >= 3; }
+ uint64_t request_id() const {
+ assert(has_request_id());
+ return static_cast<const internal::MessageHeaderWithRequestID*>(
+ &data_->header)->request_id;
+ }
+ void set_request_id(uint64_t request_id) {
+ assert(has_request_id());
+ static_cast<internal::MessageHeaderWithRequestID*>(&data_->header)->
+ request_id = request_id;
+ }
+
+ // Access the payload.
+ const uint8_t* payload() const {
+ return reinterpret_cast<const uint8_t*>(data_) + data_->header.num_bytes;
+ }
+ uint8_t* mutable_payload() {
+ return reinterpret_cast<uint8_t*>(data_) + data_->header.num_bytes;
+ }
+ uint32_t payload_num_bytes() const {
+ assert(data_num_bytes_ >= data_->header.num_bytes);
+ return data_num_bytes_ - data_->header.num_bytes;
+ }
+
+ // Access the handles.
+ const std::vector<Handle>* handles() const { return &handles_; }
+ std::vector<Handle>* mutable_handles() { return &handles_; }
+
+ private:
+ uint32_t data_num_bytes_;
+ internal::MessageData* data_; // Heap-allocated using malloc.
+ std::vector<Handle> handles_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Message);
+};
+
+class MessageReceiver {
+ public:
+ virtual ~MessageReceiver() {}
+
+ // The receiver may mutate the given message. Returns true if the message
+ // was accepted and false otherwise, indicating that the message was invalid
+ // or malformed.
+ virtual bool Accept(Message* message) MOJO_WARN_UNUSED_RESULT = 0;
+};
+
+class MessageReceiverWithResponder : public MessageReceiver {
+ public:
+ virtual ~MessageReceiverWithResponder() {}
+
+ // A variant on Accept that registers a MessageReceiver (known as the
+ // responder) to handle the response message generated from the given
+ // message. The responder's Accept method may be called during
+ // AcceptWithResponder or some time after its return.
+ //
+ // NOTE: Upon returning true, AcceptWithResponder assumes ownership of
+ // |responder| and will delete it after calling |responder->Accept| or upon
+ // its own destruction.
+ //
+ virtual bool AcceptWithResponder(
+ Message* message, MessageReceiver* responder) MOJO_WARN_UNUSED_RESULT = 0;
+};
+
+// Read a single message from the pipe and dispatch to the given receiver. The
+// receiver may be null, in which case the message is simply discarded.
+// Returns MOJO_RESULT_SHOULD_WAIT if the caller should wait on the handle to
+// become readable. Returns MOJO_RESULT_OK if a message was dispatched and
+// otherwise returns an error code if something went wrong.
+//
+// NOTE: The message hasn't been validated and may be malformed!
+MojoResult ReadAndDispatchMessage(MessagePipeHandle handle,
+ MessageReceiver* receiver,
+ bool* receiver_result);
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_H_
diff --git a/chromium/mojo/public/cpp/bindings/message_filter.h b/chromium/mojo/public/cpp/bindings/message_filter.h
new file mode 100644
index 00000000000..6d716733ecc
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/message_filter.h
@@ -0,0 +1,39 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// This class is the base class for message filters. Subclasses should
+// implement the pure virtual method Accept() inherited from MessageReceiver to
+// process messages and/or forward them to |sink_|.
+class MessageFilter : public MessageReceiver {
+ public:
+ // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while
+ // this object is alive.
+ explicit MessageFilter(MessageReceiver* sink = NULL);
+ virtual ~MessageFilter();
+
+ void set_sink(MessageReceiver* sink) { sink_ = sink; }
+
+ protected:
+ MessageReceiver* sink_;
+};
+
+// A trivial filter that simply forwards every message it receives to |sink_|.
+class PassThroughFilter : public MessageFilter {
+ public:
+ explicit PassThroughFilter(MessageReceiver* sink = NULL);
+
+ virtual bool Accept(Message* message) MOJO_OVERRIDE;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_FILTER_H_
diff --git a/chromium/mojo/public/cpp/bindings/no_interface.h b/chromium/mojo/public/cpp/bindings/no_interface.h
new file mode 100644
index 00000000000..83b0eff5000
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/no_interface.h
@@ -0,0 +1,56 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_NO_INTERFACE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_NO_INTERFACE_H_
+
+#include <assert.h>
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+// NoInterface is for use in cases when a non-existent or empty interface is
+// needed (e.g., when the Mojom "Peer" attribute is not present).
+
+class NoInterfaceProxy;
+class NoInterfaceStub;
+
+class NoInterface {
+ public:
+ static const char* Name_;
+ typedef NoInterfaceProxy Proxy_;
+ typedef NoInterfaceStub Stub_;
+ typedef PassThroughFilter RequestValidator_;
+ typedef PassThroughFilter ResponseValidator_;
+ typedef NoInterface Client;
+ virtual ~NoInterface() {}
+};
+
+class NoInterfaceProxy : public NoInterface {
+ public:
+ explicit NoInterfaceProxy(MessageReceiver* receiver) {}
+};
+
+class NoInterfaceStub : public MessageReceiverWithResponder {
+ public:
+ NoInterfaceStub() {}
+ void set_sink(NoInterface* sink) {}
+ NoInterface* sink() { return NULL; }
+ virtual bool Accept(Message* message) MOJO_OVERRIDE;
+ virtual bool AcceptWithResponder(Message* message, MessageReceiver* responder)
+ MOJO_OVERRIDE;
+};
+
+
+// AnyInterface is for use in cases where any interface would do (e.g., see the
+// Shell::Connect method).
+
+typedef NoInterface AnyInterface;
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_NO_INTERFACE_H_
diff --git a/chromium/mojo/public/cpp/bindings/string.h b/chromium/mojo/public/cpp/bindings/string.h
new file mode 100644
index 00000000000..a2427844975
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/string.h
@@ -0,0 +1,154 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRING_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRING_H_
+
+#include <assert.h>
+
+#include <string>
+
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+class String {
+ public:
+ typedef internal::String_Data Data_;
+
+ String() : is_null_(true) {}
+ String(const std::string& str) : value_(str), is_null_(false) {}
+ String(const char* chars) : is_null_(!chars) {
+ if (chars)
+ value_ = chars;
+ }
+ String(const char* chars, size_t num_chars)
+ : value_(chars, num_chars),
+ is_null_(false) {
+ }
+ template <size_t N>
+ String(const char chars[N]) : value_(chars, N-1), is_null_(false) {}
+
+ template <typename U>
+ static String From(const U& other) {
+ return TypeConverter<String, U>::ConvertFrom(other);
+ }
+
+ template <typename U>
+ U To() const {
+ return TypeConverter<String, U>::ConvertTo(*this);
+ }
+
+ String& operator=(const std::string& str) {
+ value_ = str;
+ is_null_ = false;
+ return *this;
+ }
+ String& operator=(const char* chars) {
+ is_null_ = !chars;
+ if (chars) {
+ value_ = chars;
+ } else {
+ value_.clear();
+ }
+ return *this;
+ }
+
+ void reset() {
+ value_.clear();
+ is_null_ = true;
+ }
+
+ bool is_null() const { return is_null_; }
+
+ size_t size() const { return value_.size(); }
+
+ const char* data() const { return value_.data(); }
+
+ const char& at(size_t offset) const { return value_.at(offset); }
+ const char& operator[](size_t offset) const { return value_[offset]; }
+
+ const std::string& get() const { return value_; }
+ operator const std::string&() const { return value_; }
+
+ void Swap(String* other) {
+ std::swap(is_null_, other->is_null_);
+ value_.swap(other->value_);
+ }
+
+ void Swap(std::string* other) {
+ is_null_ = false;
+ value_.swap(*other);
+ }
+
+ private:
+ typedef std::string String::*Testable;
+
+ public:
+ operator Testable() const { return is_null_ ? 0 : &String::value_; }
+
+ private:
+ std::string value_;
+ bool is_null_;
+};
+
+inline bool operator==(const String& a, const String& b) {
+ return a.is_null() == b.is_null() && a.get() == b.get();
+}
+inline bool operator==(const char* a, const String& b) {
+ return !b.is_null() && a == b.get();
+}
+inline bool operator==(const String& a, const char* b) {
+ return !a.is_null() && a.get() == b;
+}
+inline bool operator!=(const String& a, const String& b) { return !(a == b); }
+inline bool operator!=(const char* a, const String& b) { return !(a == b); }
+inline bool operator!=(const String& a, const char* b) { return !(a == b); }
+
+// TODO(darin): Add similar variants of operator<,<=,>,>=
+
+template <>
+class TypeConverter<String, std::string> {
+ public:
+ static String ConvertFrom(const std::string& input) {
+ return String(input);
+ }
+ static std::string ConvertTo(const String& input) {
+ return input;
+ }
+};
+
+template <size_t N>
+class TypeConverter<String, char[N]> {
+ public:
+ static String ConvertFrom(const char input[N]) {
+ assert(input);
+ return String(input, N-1);
+ }
+};
+
+// Appease MSVC.
+template <size_t N>
+class TypeConverter<String, const char[N]> {
+ public:
+ static String ConvertFrom(const char input[N]) {
+ assert(input);
+ return String(input, N-1);
+ }
+};
+
+template <>
+class TypeConverter<String, const char*> {
+ public:
+ // |input| may be null, in which case a null String will be returned.
+ static String ConvertFrom(const char* input) {
+ return String(input);
+ }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_STRING_H_
diff --git a/chromium/mojo/public/cpp/bindings/struct_ptr.h b/chromium/mojo/public/cpp/bindings/struct_ptr.h
new file mode 100644
index 00000000000..a4e55e758f4
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/struct_ptr.h
@@ -0,0 +1,154 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
+
+#include <assert.h>
+
+#include <new>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename Struct>
+class StructHelper {
+ public:
+ template <typename Ptr>
+ static void Initialize(Ptr* ptr) { ptr->Initialize(); }
+};
+
+} // namespace internal
+
+template <typename Struct>
+class StructPtr {
+ MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(StructPtr, RValue);
+ public:
+ typedef typename Struct::Data_ Data_;
+
+ StructPtr() : ptr_(NULL) {}
+ ~StructPtr() {
+ delete ptr_;
+ }
+
+ StructPtr(RValue other) : ptr_(NULL) { Take(other.object); }
+ StructPtr& operator=(RValue other) {
+ Take(other.object);
+ return *this;
+ }
+
+ template <typename U>
+ U To() const {
+ return TypeConverter<StructPtr, U>::ConvertTo(*this);
+ }
+
+ void reset() {
+ if (ptr_) {
+ delete ptr_;
+ ptr_ = NULL;
+ }
+ }
+
+ bool is_null() const { return ptr_ == NULL; }
+
+ Struct& operator*() const {
+ assert(ptr_);
+ return *ptr_;
+ }
+ Struct* operator->() const {
+ assert(ptr_);
+ return ptr_;
+ }
+ Struct* get() const { return ptr_; }
+
+ void Swap(StructPtr* other) {
+ std::swap(ptr_, other->ptr_);
+ }
+
+ private:
+ typedef Struct* StructPtr::*Testable;
+
+ public:
+ operator Testable() const { return ptr_ ? &StructPtr::ptr_ : 0; }
+
+ private:
+ friend class internal::StructHelper<Struct>;
+ void Initialize() { assert(!ptr_); ptr_ = new Struct(); }
+
+ void Take(StructPtr* other) {
+ reset();
+ Swap(other);
+ }
+
+ Struct* ptr_;
+};
+
+// Designed to be used when Struct is small and copyable.
+template <typename Struct>
+class InlinedStructPtr {
+ MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(InlinedStructPtr, RValue);
+ public:
+ typedef typename Struct::Data_ Data_;
+
+ InlinedStructPtr() : is_null_(true) {}
+ ~InlinedStructPtr() {}
+
+ InlinedStructPtr(RValue other) : is_null_(true) { Take(other.object); }
+ InlinedStructPtr& operator=(RValue other) {
+ Take(other.object);
+ return *this;
+ }
+
+ template <typename U>
+ U To() const {
+ return TypeConverter<InlinedStructPtr, U>::ConvertTo(*this);
+ }
+
+ void reset() {
+ is_null_ = true;
+ value_.~Struct();
+ new (&value_) Struct();
+ }
+
+ bool is_null() const { return is_null_; }
+
+ Struct& operator*() const {
+ assert(!is_null_);
+ return value_;
+ }
+ Struct* operator->() const {
+ assert(!is_null_);
+ return &value_;
+ }
+ Struct* get() const { return &value_; }
+
+ void Swap(InlinedStructPtr* other) {
+ std::swap(value_, other->value_);
+ std::swap(is_null_, other->is_null_);
+ }
+
+ private:
+ typedef Struct InlinedStructPtr::*Testable;
+
+ public:
+ operator Testable() const { return is_null_ ? 0 : &InlinedStructPtr::value_; }
+
+ private:
+ friend class internal::StructHelper<Struct>;
+ void Initialize() { is_null_ = false; }
+
+ void Take(InlinedStructPtr* other) {
+ reset();
+ Swap(other);
+ }
+
+ mutable Struct value_;
+ bool is_null_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
diff --git a/chromium/mojo/public/cpp/bindings/sync_dispatcher.h b/chromium/mojo/public/cpp/bindings/sync_dispatcher.h
new file mode 100644
index 00000000000..9f825bf8a6a
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/sync_dispatcher.h
@@ -0,0 +1,47 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_SYNC_DISPATCHER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_SYNC_DISPATCHER_H_
+
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/message_header_validator.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+class MessageReceiver;
+
+// Waits for one message to arrive on the message pipe, and dispatch it to the
+// receiver. Returns true on success, false on failure.
+//
+// NOTE: The message hasn't been validated and may be malformed!
+bool WaitForMessageAndDispatch(MessagePipeHandle handle,
+ mojo::MessageReceiver* receiver);
+
+template<typename Interface> class SyncDispatcher {
+ public:
+ SyncDispatcher(ScopedMessagePipeHandle message_pipe, Interface* sink)
+ : message_pipe_(message_pipe.Pass()) {
+ stub_.set_sink(sink);
+
+ filters_.Append<internal::MessageHeaderValidator>();
+ filters_.Append<typename Interface::RequestValidator_>();
+ filters_.SetSink(&stub_);
+ }
+
+ bool WaitAndDispatchOneMessage() {
+ return WaitForMessageAndDispatch(message_pipe_.get(),
+ filters_.GetHead());
+ }
+
+ private:
+ ScopedMessagePipeHandle message_pipe_;
+ typename Interface::Stub_ stub_;
+ internal::FilterChain filters_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_SYNC_DISPATCHER_H_
diff --git a/chromium/mojo/public/cpp/bindings/type_converter.h b/chromium/mojo/public/cpp/bindings/type_converter.h
new file mode 100644
index 00000000000..0ac5f6d4760
--- /dev/null
+++ b/chromium/mojo/public/cpp/bindings/type_converter.h
@@ -0,0 +1,82 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_TYPE_CONVERTER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TYPE_CONVERTER_H_
+
+namespace mojo {
+
+// Specialize the following class:
+// template <typename T, typename U> class TypeConverter;
+// to perform type conversion for Mojom-defined structs and arrays. Here, T is
+// the Mojom-defined struct or array, and U is some other non-Mojom
+// struct or array type.
+//
+// Specializations should implement the following interface:
+// namespace mojo {
+// template <>
+// class TypeConverter<T, U> {
+// public:
+// static T ConvertFrom(const U& input);
+// static U ConvertTo(const T& input);
+// };
+// }
+//
+// EXAMPLE:
+//
+// Suppose you have the following Mojom-defined struct:
+//
+// module geometry {
+// struct Point {
+// int32 x;
+// int32 y;
+// };
+// }
+//
+// Now, imagine you wanted to write a TypeConverter specialization for
+// gfx::Point. It might look like this:
+//
+// namespace mojo {
+// template <>
+// class TypeConverter<geometry::PointPtr, gfx::Point> {
+// public:
+// static geometry::PointPtr ConvertFrom(const gfx::Point& input) {
+// geometry::PointPtr result;
+// result->x = input.x();
+// result->y = input.y();
+// return result.Pass();
+// }
+// static gfx::Point ConvertTo(const geometry::PointPtr& input) {
+// return input ? gfx::Point(input->x, input->y) : gfx::Point();
+// }
+// };
+// }
+//
+// With the above TypeConverter defined, it is possible to write code like this:
+//
+// void AcceptPoint(const geometry::PointPtr& input) {
+// // With an explicit cast using the .To<> method.
+// gfx::Point pt = input.To<gfx::Point>();
+//
+// // With an explicit cast using the static From() method.
+// geometry::PointPtr output = geometry::Point::From(pt);
+// }
+//
+template <typename T, typename U> class TypeConverter;
+
+// The following specialization is useful when you are converting between
+// Array<POD> and std::vector<POD>.
+template <typename T> class TypeConverter<T, T> {
+ public:
+ static T ConvertFrom(const T& obj) {
+ return obj;
+ }
+ static T ConvertTo(const T& obj) {
+ return obj;
+ }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_TYPE_CONVERTER_H_
diff --git a/chromium/mojo/public/cpp/environment/environment.h b/chromium/mojo/public/cpp/environment/environment.h
new file mode 100644
index 00000000000..48f4c26eb72
--- /dev/null
+++ b/chromium/mojo/public/cpp/environment/environment.h
@@ -0,0 +1,41 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_ENVIRONMENT_ENVIRONMENT_H_
+#define MOJO_PUBLIC_CPP_ENVIRONMENT_ENVIRONMENT_H_
+
+#include "mojo/public/cpp/system/macros.h"
+
+struct MojoAsyncWaiter;
+struct MojoLogger;
+
+namespace mojo {
+
+// Other parts of the Mojo C++ APIs use the *static* methods of this class.
+//
+// The "standalone" implementation of this class requires that this class (in
+// the lib/ subdirectory) be instantiated (and remain so) while using the Mojo
+// C++ APIs. I.e., the static methods depend on things set up by the constructor
+// and torn down by the destructor.
+//
+// Other implementations may not have this requirement.
+class Environment {
+ public:
+ Environment();
+ // This constructor allows the standard implementations to be overridden (set
+ // a parameter to null to get the standard implementation).
+ Environment(const MojoAsyncWaiter* default_async_waiter,
+ const MojoLogger* default_logger);
+ ~Environment();
+
+ static const MojoAsyncWaiter* GetDefaultAsyncWaiter();
+ static const MojoLogger* GetDefaultLogger();
+
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Environment);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_ENVIRONMENT_H_
diff --git a/chromium/mojo/public/cpp/environment/lib/DEPS b/chromium/mojo/public/cpp/environment/lib/DEPS
new file mode 100644
index 00000000000..1889e1fb75f
--- /dev/null
+++ b/chromium/mojo/public/cpp/environment/lib/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+mojo/public/cpp/environment",
+ "+mojo/public/cpp/utility",
+]
diff --git a/chromium/mojo/public/cpp/environment/lib/default_async_waiter.cc b/chromium/mojo/public/cpp/environment/lib/default_async_waiter.cc
new file mode 100644
index 00000000000..257f1c8d797
--- /dev/null
+++ b/chromium/mojo/public/cpp/environment/lib/default_async_waiter.cc
@@ -0,0 +1,94 @@
+// Copyright 2014 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 "mojo/public/cpp/environment/lib/default_async_waiter.h"
+
+#include <assert.h>
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/public/cpp/utility/run_loop_handler.h"
+
+namespace mojo {
+
+namespace {
+
+// RunLoopHandler implementation used for a request to AsyncWait(). There are
+// two ways RunLoopHandlerImpl is deleted:
+// . when the handle is ready (or errored).
+// . when CancelWait() is invoked.
+class RunLoopHandlerImpl : public RunLoopHandler {
+ public:
+ RunLoopHandlerImpl(const Handle& handle,
+ MojoAsyncWaitCallback callback,
+ void* closure)
+ : handle_(handle),
+ callback_(callback),
+ closure_(closure) {
+ }
+
+ virtual ~RunLoopHandlerImpl() {
+ RunLoop::current()->RemoveHandler(handle_);
+ }
+
+ // RunLoopHandler:
+ virtual void OnHandleReady(const Handle& handle) MOJO_OVERRIDE {
+ NotifyCallback(MOJO_RESULT_OK);
+ }
+
+ virtual void OnHandleError(const Handle& handle,
+ MojoResult result) MOJO_OVERRIDE {
+ NotifyCallback(result);
+ }
+
+ private:
+ void NotifyCallback(MojoResult result) {
+ // Delete this to unregister the handle. That way if the callback
+ // reregisters everything is ok.
+ MojoAsyncWaitCallback callback = callback_;
+ void* closure = closure_;
+ delete this;
+
+ callback(closure, result);
+ }
+
+ const Handle handle_;
+ MojoAsyncWaitCallback callback_;
+ void* closure_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(RunLoopHandlerImpl);
+};
+
+MojoAsyncWaitID AsyncWait(MojoHandle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline,
+ MojoAsyncWaitCallback callback,
+ void* closure) {
+ RunLoop* run_loop = RunLoop::current();
+ assert(run_loop);
+
+ // |run_loop_handler| is destroyed either when the handle is ready or if
+ // CancelWait is invoked.
+ RunLoopHandlerImpl* run_loop_handler =
+ new RunLoopHandlerImpl(Handle(handle), callback, closure);
+ run_loop->AddHandler(run_loop_handler, Handle(handle), signals, deadline);
+ return reinterpret_cast<MojoAsyncWaitID>(run_loop_handler);
+}
+
+void CancelWait(MojoAsyncWaitID wait_id) {
+ delete reinterpret_cast<RunLoopHandlerImpl*>(wait_id);
+}
+
+} // namespace
+
+namespace internal {
+
+const MojoAsyncWaiter kDefaultAsyncWaiter = {
+ AsyncWait,
+ CancelWait
+};
+
+} // namespace internal
+
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/environment/lib/default_async_waiter.h b/chromium/mojo/public/cpp/environment/lib/default_async_waiter.h
new file mode 100644
index 00000000000..49ce233490c
--- /dev/null
+++ b/chromium/mojo/public/cpp/environment/lib/default_async_waiter.h
@@ -0,0 +1,18 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_ASYNC_WAITER_H_
+#define MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_ASYNC_WAITER_H_
+
+struct MojoAsyncWaiter;
+
+namespace mojo {
+namespace internal {
+
+extern const MojoAsyncWaiter kDefaultAsyncWaiter;
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_ASYNC_WAITER_H_
diff --git a/chromium/mojo/public/cpp/environment/lib/default_logger.cc b/chromium/mojo/public/cpp/environment/lib/default_logger.cc
new file mode 100644
index 00000000000..af4a62866b0
--- /dev/null
+++ b/chromium/mojo/public/cpp/environment/lib/default_logger.cc
@@ -0,0 +1,71 @@
+// Copyright 2014 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 "mojo/public/cpp/environment/lib/default_logger.h"
+
+#include <stdio.h>
+#include <stdlib.h> // For |abort()|.
+
+#include <algorithm>
+
+#include "mojo/public/c/environment/logger.h"
+
+namespace mojo {
+
+namespace {
+
+MojoLogLevel g_minimum_log_level = MOJO_LOG_LEVEL_INFO;
+
+const char* GetLogLevelString(MojoLogLevel log_level) {
+ if (log_level <= MOJO_LOG_LEVEL_VERBOSE-3)
+ return "VERBOSE4+";
+ switch (log_level) {
+ case MOJO_LOG_LEVEL_VERBOSE-2:
+ return "VERBOSE3";
+ case MOJO_LOG_LEVEL_VERBOSE-1:
+ return "VERBOSE2";
+ case MOJO_LOG_LEVEL_VERBOSE:
+ return "VERBOSE1";
+ case MOJO_LOG_LEVEL_INFO:
+ return "INFO";
+ case MOJO_LOG_LEVEL_WARNING:
+ return "WARNING";
+ case MOJO_LOG_LEVEL_ERROR:
+ return "ERROR";
+ }
+ // Consider everything higher to be fatal.
+ return "FATAL";
+}
+
+void LogMessage(MojoLogLevel log_level, const char* message) {
+ if (log_level < g_minimum_log_level)
+ return;
+
+ // TODO(vtl): Add timestamp also?
+ fprintf(stderr, "%s: %s\n", GetLogLevelString(log_level), message);
+ if (log_level >= MOJO_LOG_LEVEL_FATAL)
+ abort();
+}
+
+MojoLogLevel GetMinimumLogLevel() {
+ return g_minimum_log_level;
+}
+
+void SetMinimumLogLevel(MojoLogLevel minimum_log_level) {
+ g_minimum_log_level = std::min(minimum_log_level, MOJO_LOG_LEVEL_FATAL);
+}
+
+} // namespace
+
+namespace internal {
+
+const MojoLogger kDefaultLogger = {
+ LogMessage,
+ GetMinimumLogLevel,
+ SetMinimumLogLevel
+};
+
+} // namespace internal
+
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/environment/lib/default_logger.h b/chromium/mojo/public/cpp/environment/lib/default_logger.h
new file mode 100644
index 00000000000..4db32336811
--- /dev/null
+++ b/chromium/mojo/public/cpp/environment/lib/default_logger.h
@@ -0,0 +1,18 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_LOGGER_H_
+#define MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_LOGGER_H_
+
+struct MojoLogger;
+
+namespace mojo {
+namespace internal {
+
+extern const MojoLogger kDefaultLogger;
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_LIB_DEFAULT_LOGGER_H_
diff --git a/chromium/mojo/public/cpp/environment/lib/environment.cc b/chromium/mojo/public/cpp/environment/lib/environment.cc
new file mode 100644
index 00000000000..58e270e9ae4
--- /dev/null
+++ b/chromium/mojo/public/cpp/environment/lib/environment.cc
@@ -0,0 +1,60 @@
+// Copyright 2014 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 "mojo/public/cpp/environment/environment.h"
+
+#include <stddef.h>
+
+#include "mojo/public/c/environment/logger.h"
+#include "mojo/public/cpp/environment/lib/default_async_waiter.h"
+#include "mojo/public/cpp/environment/lib/default_logger.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+
+namespace mojo {
+
+namespace {
+
+const MojoAsyncWaiter* g_default_async_waiter = NULL;
+const MojoLogger* g_default_logger = NULL;
+
+void Init(const MojoAsyncWaiter* default_async_waiter,
+ const MojoLogger* default_logger) {
+ g_default_async_waiter =
+ default_async_waiter ? default_async_waiter :
+ &internal::kDefaultAsyncWaiter;
+ g_default_logger = default_logger ? default_logger :
+ &internal::kDefaultLogger;
+
+ RunLoop::SetUp();
+}
+
+} // namespace
+
+Environment::Environment() {
+ Init(NULL, NULL);
+}
+
+Environment::Environment(const MojoAsyncWaiter* default_async_waiter,
+ const MojoLogger* default_logger) {
+ Init(default_async_waiter, default_logger);
+}
+
+Environment::~Environment() {
+ RunLoop::TearDown();
+
+ // TODO(vtl): Maybe we should allow nesting, and restore previous default
+ // async waiters and loggers?
+}
+
+// static
+const MojoAsyncWaiter* Environment::GetDefaultAsyncWaiter() {
+ return g_default_async_waiter;
+}
+
+// static
+const MojoLogger* Environment::GetDefaultLogger() {
+ return g_default_logger;
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/environment/lib/logging.cc b/chromium/mojo/public/cpp/environment/lib/logging.cc
new file mode 100644
index 00000000000..990626df736
--- /dev/null
+++ b/chromium/mojo/public/cpp/environment/lib/logging.cc
@@ -0,0 +1,45 @@
+// Copyright 2014 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 "mojo/public/cpp/environment/logging.h"
+
+#include "mojo/public/cpp/environment/environment.h"
+
+namespace mojo {
+namespace internal {
+
+namespace {
+
+// Gets a pointer to the filename portion of |s|. Assumes that the filename
+// follows the last slash or backslash in |s|, or is |s| if no slash or
+// backslash is present.
+//
+// E.g., a pointer to "foo.cc" is returned for the following inputs: "foo.cc",
+// "./foo.cc", ".\foo.cc", "/absolute/path/to/foo.cc",
+// "relative/path/to/foo.cc", "C:\absolute\path\to\foo.cc", etc.
+const char* GetFilename(const char* s) {
+ const char* rv = s;
+ while (*s) {
+ if (*s == '/' || *s == '\\')
+ rv = s + 1;
+ s++;
+ }
+ return rv;
+}
+
+} // namespace
+
+LogMessage::LogMessage(const char* file, int line, MojoLogLevel log_level)
+ : log_level_(log_level) {
+ // Note: Don't include the log level in the message, since that's passed on.
+ stream_ << GetFilename(file) << '(' << line << "): ";
+}
+
+LogMessage::~LogMessage() {
+ Environment::GetDefaultLogger()->LogMessage(log_level_,
+ stream_.str().c_str());
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/environment/logging.h b/chromium/mojo/public/cpp/environment/logging.h
new file mode 100644
index 00000000000..a3e2cef4293
--- /dev/null
+++ b/chromium/mojo/public/cpp/environment/logging.h
@@ -0,0 +1,87 @@
+// Copyright 2014 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.
+
+// Logging macros, similar to Chromium's base/logging.h, except with |MOJO_|
+// prefixes and missing some features (notably |CHECK_EQ()|, etc.).
+
+// TODO(vtl): It's weird that this is in the environment directory, since its
+// implementation (in environment/lib) is meant to be used by any implementation
+// of the environment.
+
+#ifndef MOJO_PUBLIC_CPP_ENVIRONMENT_LOGGING_H_
+#define MOJO_PUBLIC_CPP_ENVIRONMENT_LOGGING_H_
+
+#include <sstream>
+
+#include "mojo/public/c/environment/logger.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/public/cpp/system/macros.h"
+
+#define MOJO_LOG_STREAM(level) \
+ ::mojo::internal::LogMessage(__FILE__, __LINE__, \
+ MOJO_LOG_LEVEL_ ## level).stream()
+
+#define MOJO_LAZY_LOG_STREAM(level, condition) \
+ !(condition) ? \
+ (void) 0 : \
+ ::mojo::internal::VoidifyOstream() & MOJO_LOG_STREAM(level)
+
+#define MOJO_SHOULD_LOG(level) \
+ (MOJO_LOG_LEVEL_ ## level >= \
+ ::mojo::Environment::GetDefaultLogger()->GetMinimumLogLevel())
+
+#define MOJO_LOG(level) \
+ MOJO_LAZY_LOG_STREAM(level, MOJO_SHOULD_LOG(level))
+
+#define MOJO_LOG_IF(level, condition) \
+ MOJO_LAZY_LOG_STREAM(level, MOJO_SHOULD_LOG(level) && (condition))
+
+#define MOJO_CHECK(condition) \
+ MOJO_LAZY_LOG_STREAM(FATAL, !(condition)) \
+ << "Check failed: " #condition ". "
+
+// Note: For non-debug builds, |MOJO_DLOG_IF()| *eliminates* (i.e., doesn't
+// compile) the condition, whereas |MOJO_DCHECK()| "neuters" the condition
+// (i.e., compiles, but doesn't evaluate).
+#ifdef NDEBUG
+
+#define MOJO_DLOG(level) MOJO_LAZY_LOG_STREAM(level, false)
+#define MOJO_DLOG_IF(level, condition) MOJO_LAZY_LOG_STREAM(level, false)
+#define MOJO_DCHECK(condition) MOJO_LAZY_LOG_STREAM(FATAL, false && (condition))
+
+#else
+
+#define MOJO_DLOG(level) MOJO_LOG(level)
+#define MOJO_DLOG_IF(level, condition) MOJO_LOG_IF(level, condition)
+#define MOJO_DCHECK(condition) MOJO_CHECK(condition)
+
+#endif // NDEBUG
+
+namespace mojo {
+namespace internal {
+
+class LogMessage {
+ public:
+ LogMessage(const char* file, int line, MojoLogLevel log_level);
+ ~LogMessage();
+
+ std::ostream& stream() { return stream_; }
+
+ private:
+ const MojoLogLevel log_level_;
+ std::ostringstream stream_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+// Used to ignore a stream.
+struct VoidifyOstream {
+ // Use & since it has precedence lower than << but higher than ?:.
+ void operator&(std::ostream&) {}
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_ENVIRONMENT_LOGGING_H_
diff --git a/chromium/mojo/public/cpp/gles2/DEPS b/chromium/mojo/public/cpp/gles2/DEPS
new file mode 100644
index 00000000000..3c48be99eb8
--- /dev/null
+++ b/chromium/mojo/public/cpp/gles2/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+mojo/public/c/gles2",
+ "+mojo/public/cpp/environment",
+]
diff --git a/chromium/mojo/public/cpp/gles2/gles2.h b/chromium/mojo/public/cpp/gles2/gles2.h
new file mode 100644
index 00000000000..a408fc701eb
--- /dev/null
+++ b/chromium/mojo/public/cpp/gles2/gles2.h
@@ -0,0 +1,24 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_GLES2_GLES2_H_
+#define MOJO_PUBLIC_CPP_GLES2_GLES2_H_
+
+#include "mojo/public/c/gles2/gles2.h"
+#include "mojo/public/cpp/environment/environment.h"
+
+namespace mojo {
+
+class GLES2Initializer {
+ public:
+ explicit GLES2Initializer(const MojoAsyncWaiter* async_waiter =
+ Environment::GetDefaultAsyncWaiter()) {
+ MojoGLES2Initialize(async_waiter);
+ }
+ ~GLES2Initializer() { MojoGLES2Terminate(); }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_GLES2_GLES2_H_
diff --git a/chromium/mojo/public/cpp/system/core.h b/chromium/mojo/public/cpp/system/core.h
new file mode 100644
index 00000000000..d73bb296514
--- /dev/null
+++ b/chromium/mojo/public/cpp/system/core.h
@@ -0,0 +1,555 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_CORE_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_CORE_H_
+
+#include <assert.h>
+#include <stddef.h>
+
+#include <limits>
+
+#include "mojo/public/c/system/core.h"
+#include "mojo/public/c/system/system_export.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// OVERVIEW
+//
+// |Handle| and |...Handle|:
+//
+// |Handle| is a simple, copyable wrapper for the C type |MojoHandle| (which is
+// just an integer). Its purpose is to increase type-safety, not provide
+// lifetime management. For the same purpose, we have trivial *subclasses* of
+// |Handle|, e.g., |MessagePipeHandle| and |DataPipeProducerHandle|. |Handle|
+// and its subclasses impose *no* extra overhead over using |MojoHandle|s
+// directly.
+//
+// Note that though we provide constructors for |Handle|/|...Handle| from a
+// |MojoHandle|, we do not provide, e.g., a constructor for |MessagePipeHandle|
+// from a |Handle|. This is for type safety: If we did, you'd then be able to
+// construct a |MessagePipeHandle| from, e.g., a |DataPipeProducerHandle| (since
+// it's a |Handle|).
+//
+// |ScopedHandleBase| and |Scoped...Handle|:
+//
+// |ScopedHandleBase<HandleType>| is a templated scoped wrapper, for the handle
+// types above (in the same sense that a C++11 |unique_ptr<T>| is a scoped
+// wrapper for a |T*|). It provides lifetime management, closing its owned
+// handle on destruction. It also provides (emulated) move semantics, again
+// along the lines of C++11's |unique_ptr| (and exactly like Chromium's
+// |scoped_ptr|).
+//
+// |ScopedHandle| is just (a typedef of) a |ScopedHandleBase<Handle>|.
+// Similarly, |ScopedMessagePipeHandle| is just a
+// |ScopedHandleBase<MessagePipeHandle>|. Etc. Note that a
+// |ScopedMessagePipeHandle| is *not* a (subclass of) |ScopedHandle|.
+//
+// Wrapper functions:
+//
+// We provide simple wrappers for the |Mojo...()| functions (in
+// mojo/public/c/system/core.h -- see that file for details on individual
+// functions).
+//
+// The general guideline is functions that imply ownership transfer of a handle
+// should take (or produce) an appropriate |Scoped...Handle|, while those that
+// don't take a |...Handle|. For example, |CreateMessagePipe()| has two
+// |ScopedMessagePipe| "out" parameters, whereas |Wait()| and |WaitMany()| take
+// |Handle| parameters. Some, have both: e.g., |DuplicatedBuffer()| takes a
+// suitable (unscoped) handle (e.g., |SharedBufferHandle|) "in" parameter and
+// produces a suitable scoped handle (e.g., |ScopedSharedBufferHandle| a.k.a.
+// |ScopedHandleBase<SharedBufferHandle>|) as an "out" parameter.
+//
+// An exception are some of the |...Raw()| functions. E.g., |CloseRaw()| takes a
+// |Handle|, leaving the user to discard the handle.
+//
+// More significantly, |WriteMessageRaw()| exposes the full API complexity of
+// |MojoWriteMessage()| (but doesn't require any extra overhead). It takes a raw
+// array of |Handle|s as input, and takes ownership of them (i.e., invalidates
+// them) on *success* (but not on failure). There are a number of reasons for
+// this. First, C++03 |std::vector|s cannot contain the move-only
+// |Scoped...Handle|s. Second, |std::vector|s impose extra overhead
+// (necessitating heap-allocation of the buffer). Third, |std::vector|s wouldn't
+// provide the desired level of flexibility/safety: a vector of handles would
+// have to be all of the same type (probably |Handle|/|ScopedHandle|). Fourth,
+// it's expected to not be used directly, but instead be used by generated
+// bindings.
+//
+// Other |...Raw()| functions expose similar rough edges, e.g., dealing with raw
+// pointers (and lengths) instead of taking |std::vector|s or similar.
+
+// Standalone functions --------------------------------------------------------
+
+inline MojoTimeTicks GetTimeTicksNow() {
+ return MojoGetTimeTicksNow();
+}
+
+// ScopedHandleBase ------------------------------------------------------------
+
+// Scoper for the actual handle types defined further below. It's move-only,
+// like the C++11 |unique_ptr|.
+template <class HandleType>
+class ScopedHandleBase {
+ MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(ScopedHandleBase, RValue)
+
+ public:
+ ScopedHandleBase() {}
+ explicit ScopedHandleBase(HandleType handle) : handle_(handle) {}
+ ~ScopedHandleBase() { CloseIfNecessary(); }
+
+ template <class CompatibleHandleType>
+ explicit ScopedHandleBase(ScopedHandleBase<CompatibleHandleType> other)
+ : handle_(other.release()) {
+ }
+
+ // Move-only constructor and operator=.
+ ScopedHandleBase(RValue other) : handle_(other.object->release()) {}
+ ScopedHandleBase& operator=(RValue other) {
+ if (other.object != this) {
+ CloseIfNecessary();
+ handle_ = other.object->release();
+ }
+ return *this;
+ }
+
+ const HandleType& get() const { return handle_; }
+
+ template <typename PassedHandleType>
+ static ScopedHandleBase<HandleType> From(
+ ScopedHandleBase<PassedHandleType> other) {
+ MOJO_COMPILE_ASSERT(
+ sizeof(static_cast<PassedHandleType*>(static_cast<HandleType*>(0))),
+ HandleType_is_not_a_subtype_of_PassedHandleType);
+ return ScopedHandleBase<HandleType>(
+ static_cast<HandleType>(other.release().value()));
+ }
+
+ void swap(ScopedHandleBase& other) {
+ handle_.swap(other.handle_);
+ }
+
+ HandleType release() MOJO_WARN_UNUSED_RESULT {
+ HandleType rv;
+ rv.swap(handle_);
+ return rv;
+ }
+
+ void reset(HandleType handle = HandleType()) {
+ CloseIfNecessary();
+ handle_ = handle;
+ }
+
+ bool is_valid() const {
+ return handle_.is_valid();
+ }
+
+ private:
+ void CloseIfNecessary() {
+ if (!handle_.is_valid())
+ return;
+ MojoResult result MOJO_ALLOW_UNUSED = MojoClose(handle_.value());
+ assert(result == MOJO_RESULT_OK);
+ }
+
+ HandleType handle_;
+};
+
+template <typename HandleType>
+inline ScopedHandleBase<HandleType> MakeScopedHandle(HandleType handle) {
+ return ScopedHandleBase<HandleType>(handle);
+}
+
+// Handle ----------------------------------------------------------------------
+
+const MojoHandle kInvalidHandleValue = MOJO_HANDLE_INVALID;
+
+// Wrapper base class for |MojoHandle|.
+class Handle {
+ public:
+ Handle() : value_(kInvalidHandleValue) {}
+ explicit Handle(MojoHandle value) : value_(value) {}
+ ~Handle() {}
+
+ void swap(Handle& other) {
+ MojoHandle temp = value_;
+ value_ = other.value_;
+ other.value_ = temp;
+ }
+
+ bool is_valid() const {
+ return value_ != kInvalidHandleValue;
+ }
+
+ MojoHandle value() const { return value_; }
+ MojoHandle* mutable_value() { return &value_; }
+ void set_value(MojoHandle value) { value_ = value; }
+
+ private:
+ MojoHandle value_;
+
+ // Copying and assignment allowed.
+};
+
+// Should have zero overhead.
+MOJO_COMPILE_ASSERT(sizeof(Handle) == sizeof(MojoHandle),
+ bad_size_for_cpp_Handle);
+
+// The scoper should also impose no more overhead.
+typedef ScopedHandleBase<Handle> ScopedHandle;
+MOJO_COMPILE_ASSERT(sizeof(ScopedHandle) == sizeof(Handle),
+ bad_size_for_cpp_ScopedHandle);
+
+inline MojoResult Wait(const Handle& handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline) {
+ return MojoWait(handle.value(), signals, deadline);
+}
+
+// |HandleVectorType| and |FlagsVectorType| should be similar enough to
+// |std::vector<Handle>| and |std::vector<MojoHandleSignals>|, respectively:
+// - They should have a (const) |size()| method that returns an unsigned type.
+// - They must provide contiguous storage, with access via (const) reference to
+// that storage provided by a (const) |operator[]()| (by reference).
+template <class HandleVectorType, class FlagsVectorType>
+inline MojoResult WaitMany(const HandleVectorType& handles,
+ const FlagsVectorType& signals,
+ MojoDeadline deadline) {
+ if (signals.size() != handles.size())
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (handles.size() > std::numeric_limits<uint32_t>::max())
+ return MOJO_RESULT_OUT_OF_RANGE;
+
+ if (handles.size() == 0)
+ return MojoWaitMany(NULL, NULL, 0, deadline);
+
+ const Handle& first_handle = handles[0];
+ const MojoHandleSignals& first_signals = signals[0];
+ return MojoWaitMany(
+ reinterpret_cast<const MojoHandle*>(&first_handle),
+ reinterpret_cast<const MojoHandleSignals*>(&first_signals),
+ static_cast<uint32_t>(handles.size()),
+ deadline);
+}
+
+// |Close()| takes ownership of the handle, since it'll invalidate it.
+// Note: There's nothing to do, since the argument will be destroyed when it
+// goes out of scope.
+template <class HandleType>
+inline void Close(ScopedHandleBase<HandleType> /*handle*/) {}
+
+// Most users should typically use |Close()| (above) instead.
+inline MojoResult CloseRaw(Handle handle) {
+ return MojoClose(handle.value());
+}
+
+// Strict weak ordering, so that |Handle|s can be used as keys in |std::map|s,
+// etc.
+inline bool operator<(const Handle& a, const Handle& b) {
+ return a.value() < b.value();
+}
+
+// MessagePipeHandle -----------------------------------------------------------
+
+class MessagePipeHandle : public Handle {
+ public:
+ MessagePipeHandle() {}
+ explicit MessagePipeHandle(MojoHandle value) : Handle(value) {}
+
+ // Copying and assignment allowed.
+};
+
+MOJO_COMPILE_ASSERT(sizeof(MessagePipeHandle) == sizeof(Handle),
+ bad_size_for_cpp_MessagePipeHandle);
+
+typedef ScopedHandleBase<MessagePipeHandle> ScopedMessagePipeHandle;
+MOJO_COMPILE_ASSERT(sizeof(ScopedMessagePipeHandle) ==
+ sizeof(MessagePipeHandle),
+ bad_size_for_cpp_ScopedMessagePipeHandle);
+
+inline MojoResult CreateMessagePipe(const MojoCreateMessagePipeOptions* options,
+ ScopedMessagePipeHandle* message_pipe0,
+ ScopedMessagePipeHandle* message_pipe1) {
+ assert(message_pipe0);
+ assert(message_pipe1);
+ MessagePipeHandle handle0;
+ MessagePipeHandle handle1;
+ MojoResult rv = MojoCreateMessagePipe(options,
+ handle0.mutable_value(),
+ handle1.mutable_value());
+ // Reset even on failure (reduces the chances that a "stale"/incorrect handle
+ // will be used).
+ message_pipe0->reset(handle0);
+ message_pipe1->reset(handle1);
+ return rv;
+}
+
+// These "raw" versions fully expose the underlying API, but don't help with
+// ownership of handles (especially when writing messages).
+// TODO(vtl): Write "baked" versions.
+inline MojoResult WriteMessageRaw(MessagePipeHandle message_pipe,
+ const void* bytes,
+ uint32_t num_bytes,
+ const MojoHandle* handles,
+ uint32_t num_handles,
+ MojoWriteMessageFlags flags) {
+ return MojoWriteMessage(message_pipe.value(), bytes, num_bytes, handles,
+ num_handles, flags);
+}
+
+inline MojoResult ReadMessageRaw(MessagePipeHandle message_pipe,
+ void* bytes,
+ uint32_t* num_bytes,
+ MojoHandle* handles,
+ uint32_t* num_handles,
+ MojoReadMessageFlags flags) {
+ return MojoReadMessage(message_pipe.value(), bytes, num_bytes, handles,
+ num_handles, flags);
+}
+
+// A wrapper class that automatically creates a message pipe and owns both
+// handles.
+class MessagePipe {
+ public:
+ MessagePipe();
+ explicit MessagePipe(const MojoCreateMessagePipeOptions& options);
+ ~MessagePipe();
+
+ ScopedMessagePipeHandle handle0;
+ ScopedMessagePipeHandle handle1;
+};
+
+inline MessagePipe::MessagePipe() {
+ MojoResult result MOJO_ALLOW_UNUSED =
+ CreateMessagePipe(NULL, &handle0, &handle1);
+ assert(result == MOJO_RESULT_OK);
+}
+
+inline MessagePipe::MessagePipe(const MojoCreateMessagePipeOptions& options) {
+ MojoResult result MOJO_ALLOW_UNUSED =
+ CreateMessagePipe(&options, &handle0, &handle1);
+ assert(result == MOJO_RESULT_OK);
+}
+
+inline MessagePipe::~MessagePipe() {
+}
+
+// DataPipeProducerHandle and DataPipeConsumerHandle ---------------------------
+
+class DataPipeProducerHandle : public Handle {
+ public:
+ DataPipeProducerHandle() {}
+ explicit DataPipeProducerHandle(MojoHandle value) : Handle(value) {}
+
+ // Copying and assignment allowed.
+};
+
+MOJO_COMPILE_ASSERT(sizeof(DataPipeProducerHandle) == sizeof(Handle),
+ bad_size_for_cpp_DataPipeProducerHandle);
+
+typedef ScopedHandleBase<DataPipeProducerHandle> ScopedDataPipeProducerHandle;
+MOJO_COMPILE_ASSERT(sizeof(ScopedDataPipeProducerHandle) ==
+ sizeof(DataPipeProducerHandle),
+ bad_size_for_cpp_ScopedDataPipeProducerHandle);
+
+class DataPipeConsumerHandle : public Handle {
+ public:
+ DataPipeConsumerHandle() {}
+ explicit DataPipeConsumerHandle(MojoHandle value) : Handle(value) {}
+
+ // Copying and assignment allowed.
+};
+
+MOJO_COMPILE_ASSERT(sizeof(DataPipeConsumerHandle) == sizeof(Handle),
+ bad_size_for_cpp_DataPipeConsumerHandle);
+
+typedef ScopedHandleBase<DataPipeConsumerHandle> ScopedDataPipeConsumerHandle;
+MOJO_COMPILE_ASSERT(sizeof(ScopedDataPipeConsumerHandle) ==
+ sizeof(DataPipeConsumerHandle),
+ bad_size_for_cpp_ScopedDataPipeConsumerHandle);
+
+inline MojoResult CreateDataPipe(
+ const MojoCreateDataPipeOptions* options,
+ ScopedDataPipeProducerHandle* data_pipe_producer,
+ ScopedDataPipeConsumerHandle* data_pipe_consumer) {
+ assert(data_pipe_producer);
+ assert(data_pipe_consumer);
+ DataPipeProducerHandle producer_handle;
+ DataPipeConsumerHandle consumer_handle;
+ MojoResult rv = MojoCreateDataPipe(options, producer_handle.mutable_value(),
+ consumer_handle.mutable_value());
+ // Reset even on failure (reduces the chances that a "stale"/incorrect handle
+ // will be used).
+ data_pipe_producer->reset(producer_handle);
+ data_pipe_consumer->reset(consumer_handle);
+ return rv;
+}
+
+inline MojoResult WriteDataRaw(DataPipeProducerHandle data_pipe_producer,
+ const void* elements,
+ uint32_t* num_bytes,
+ MojoWriteDataFlags flags) {
+ return MojoWriteData(data_pipe_producer.value(), elements, num_bytes, flags);
+}
+
+inline MojoResult BeginWriteDataRaw(DataPipeProducerHandle data_pipe_producer,
+ void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoWriteDataFlags flags) {
+ return MojoBeginWriteData(data_pipe_producer.value(), buffer,
+ buffer_num_bytes, flags);
+}
+
+inline MojoResult EndWriteDataRaw(DataPipeProducerHandle data_pipe_producer,
+ uint32_t num_bytes_written) {
+ return MojoEndWriteData(data_pipe_producer.value(), num_bytes_written);
+}
+
+inline MojoResult ReadDataRaw(DataPipeConsumerHandle data_pipe_consumer,
+ void* elements,
+ uint32_t* num_bytes,
+ MojoReadDataFlags flags) {
+ return MojoReadData(data_pipe_consumer.value(), elements, num_bytes, flags);
+}
+
+inline MojoResult BeginReadDataRaw(DataPipeConsumerHandle data_pipe_consumer,
+ const void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoReadDataFlags flags) {
+ return MojoBeginReadData(data_pipe_consumer.value(), buffer, buffer_num_bytes,
+ flags);
+}
+
+inline MojoResult EndReadDataRaw(DataPipeConsumerHandle data_pipe_consumer,
+ uint32_t num_bytes_read) {
+ return MojoEndReadData(data_pipe_consumer.value(), num_bytes_read);
+}
+
+// A wrapper class that automatically creates a data pipe and owns both handles.
+// TODO(vtl): Make an even more friendly version? (Maybe templatized for a
+// particular type instead of some "element"? Maybe functions that take
+// vectors?)
+class DataPipe {
+ public:
+ DataPipe();
+ explicit DataPipe(const MojoCreateDataPipeOptions& options);
+ ~DataPipe();
+
+ ScopedDataPipeProducerHandle producer_handle;
+ ScopedDataPipeConsumerHandle consumer_handle;
+};
+
+inline DataPipe::DataPipe() {
+ MojoResult result MOJO_ALLOW_UNUSED =
+ CreateDataPipe(NULL, &producer_handle, &consumer_handle);
+ assert(result == MOJO_RESULT_OK);
+}
+
+inline DataPipe::DataPipe(const MojoCreateDataPipeOptions& options) {
+ MojoResult result MOJO_ALLOW_UNUSED =
+ CreateDataPipe(&options, &producer_handle, &consumer_handle);
+ assert(result == MOJO_RESULT_OK);
+}
+
+inline DataPipe::~DataPipe() {
+}
+
+// SharedBufferHandle ----------------------------------------------------------
+
+class SharedBufferHandle : public Handle {
+ public:
+ SharedBufferHandle() {}
+ explicit SharedBufferHandle(MojoHandle value) : Handle(value) {}
+
+ // Copying and assignment allowed.
+};
+
+MOJO_COMPILE_ASSERT(sizeof(SharedBufferHandle) == sizeof(Handle),
+ bad_size_for_cpp_SharedBufferHandle);
+
+typedef ScopedHandleBase<SharedBufferHandle> ScopedSharedBufferHandle;
+MOJO_COMPILE_ASSERT(sizeof(ScopedSharedBufferHandle) ==
+ sizeof(SharedBufferHandle),
+ bad_size_for_cpp_ScopedSharedBufferHandle);
+
+inline MojoResult CreateSharedBuffer(
+ const MojoCreateSharedBufferOptions* options,
+ uint64_t num_bytes,
+ ScopedSharedBufferHandle* shared_buffer) {
+ assert(shared_buffer);
+ SharedBufferHandle handle;
+ MojoResult rv = MojoCreateSharedBuffer(options, num_bytes,
+ handle.mutable_value());
+ // Reset even on failure (reduces the chances that a "stale"/incorrect handle
+ // will be used).
+ shared_buffer->reset(handle);
+ return rv;
+}
+
+// TODO(vtl): This (and also the functions below) are templatized to allow for
+// future/other buffer types. A bit "safer" would be to overload this function
+// manually. (The template enforces that the in and out handles to be of the
+// same type.)
+template <class BufferHandleType>
+inline MojoResult DuplicateBuffer(
+ BufferHandleType buffer,
+ const MojoDuplicateBufferHandleOptions* options,
+ ScopedHandleBase<BufferHandleType>* new_buffer) {
+ assert(new_buffer);
+ BufferHandleType handle;
+ MojoResult rv = MojoDuplicateBufferHandle(
+ buffer.value(), options, handle.mutable_value());
+ // Reset even on failure (reduces the chances that a "stale"/incorrect handle
+ // will be used).
+ new_buffer->reset(handle);
+ return rv;
+}
+
+template <class BufferHandleType>
+inline MojoResult MapBuffer(BufferHandleType buffer,
+ uint64_t offset,
+ uint64_t num_bytes,
+ void** pointer,
+ MojoMapBufferFlags flags) {
+ assert(buffer.is_valid());
+ return MojoMapBuffer(buffer.value(), offset, num_bytes, pointer, flags);
+}
+
+inline MojoResult UnmapBuffer(void* pointer) {
+ assert(pointer);
+ return MojoUnmapBuffer(pointer);
+}
+
+// A wrapper class that automatically creates a shared buffer and owns the
+// handle.
+class SharedBuffer {
+ public:
+ explicit SharedBuffer(uint64_t num_bytes);
+ SharedBuffer(uint64_t num_bytes,
+ const MojoCreateSharedBufferOptions& options);
+ ~SharedBuffer();
+
+ ScopedSharedBufferHandle handle;
+};
+
+inline SharedBuffer::SharedBuffer(uint64_t num_bytes) {
+ MojoResult result MOJO_ALLOW_UNUSED =
+ CreateSharedBuffer(NULL, num_bytes, &handle);
+ assert(result == MOJO_RESULT_OK);
+}
+
+inline SharedBuffer::SharedBuffer(
+ uint64_t num_bytes,
+ const MojoCreateSharedBufferOptions& options) {
+ MojoResult result MOJO_ALLOW_UNUSED =
+ CreateSharedBuffer(&options, num_bytes, &handle);
+ assert(result == MOJO_RESULT_OK);
+}
+
+inline SharedBuffer::~SharedBuffer() {
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_CORE_H_
diff --git a/chromium/mojo/public/cpp/system/macros.h b/chromium/mojo/public/cpp/system/macros.h
new file mode 100644
index 00000000000..ac4f3dbc84c
--- /dev/null
+++ b/chromium/mojo/public/cpp/system/macros.h
@@ -0,0 +1,53 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_PUBLIC_CPP_SYSTEM_MACROS_H_
+#define MOJO_PUBLIC_CPP_SYSTEM_MACROS_H_
+
+#include "mojo/public/c/system/macros.h"
+
+// Annotate a virtual method indicating it must be overriding a virtual method
+// in the parent class. Use like:
+// virtual void foo() OVERRIDE;
+#if defined(_MSC_VER) || defined(__clang__)
+#define MOJO_OVERRIDE override
+#else
+#define MOJO_OVERRIDE
+#endif
+
+// A macro to disallow the copy constructor and operator= functions.
+// This should be used in the private: declarations for a class.
+#define MOJO_DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+// Used to calculate the number of elements in an array.
+// (See |arraysize()| in Chromium's base/basictypes.h for more details.)
+namespace mojo {
+template <typename T, size_t N>
+char (&ArraySizeHelper(T (&array)[N]))[N];
+#if !defined(_MSC_VER)
+template <typename T, size_t N>
+char (&ArraySizeHelper(const T (&array)[N]))[N];
+#endif
+} // namespace mojo
+#define MOJO_ARRAYSIZE(array) (sizeof(::mojo::ArraySizeHelper(array)))
+
+// Used to make a type move-only in C++03. See Chromium's base/move.h for more
+// details.
+#define MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(type, rvalue_type) \
+ private: \
+ struct rvalue_type { \
+ explicit rvalue_type(type* object) : object(object) {} \
+ type* object; \
+ }; \
+ type(type&); \
+ void operator=(type&); \
+ public: \
+ operator rvalue_type() { return rvalue_type(this); } \
+ type Pass() { return type(rvalue_type(this)); } \
+ typedef void MoveOnlyTypeForCPP03; \
+ private:
+
+#endif // MOJO_PUBLIC_CPP_SYSTEM_MACROS_H_
diff --git a/chromium/mojo/public/cpp/test_support/DEPS b/chromium/mojo/public/cpp/test_support/DEPS
new file mode 100644
index 00000000000..6dc53942e16
--- /dev/null
+++ b/chromium/mojo/public/cpp/test_support/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+mojo/public/c/test_support",
+]
diff --git a/chromium/mojo/public/cpp/test_support/lib/test_support.cc b/chromium/mojo/public/cpp/test_support/lib/test_support.cc
new file mode 100644
index 00000000000..55a3dcc7024
--- /dev/null
+++ b/chromium/mojo/public/cpp/test_support/lib/test_support.cc
@@ -0,0 +1,26 @@
+// Copyright 2014 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 "mojo/public/cpp/test_support/test_support.h"
+
+#include <stdlib.h>
+
+namespace mojo {
+namespace test {
+
+std::vector<std::string> EnumerateSourceRootRelativeDirectory(
+ const std::string& relative_path) {
+ char** names = MojoTestSupportEnumerateSourceRootRelativeDirectory(
+ relative_path.c_str());
+ std::vector<std::string> results;
+ for (char** ptr = names; *ptr != NULL; ++ptr) {
+ results.push_back(*ptr);
+ free(*ptr);
+ }
+ free(names);
+ return results;
+}
+
+} // namespace test
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/test_support/lib/test_utils.cc b/chromium/mojo/public/cpp/test_support/lib/test_utils.cc
new file mode 100644
index 00000000000..b47ea209020
--- /dev/null
+++ b/chromium/mojo/public/cpp/test_support/lib/test_utils.cc
@@ -0,0 +1,91 @@
+// Copyright 2013 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 "mojo/public/cpp/test_support/test_utils.h"
+
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/test_support/test_support.h"
+
+namespace mojo {
+namespace test {
+
+bool WriteTextMessage(const MessagePipeHandle& handle,
+ const std::string& text) {
+ MojoResult rv = WriteMessageRaw(handle,
+ text.data(),
+ static_cast<uint32_t>(text.size()),
+ NULL,
+ 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE);
+ return rv == MOJO_RESULT_OK;
+}
+
+bool ReadTextMessage(const MessagePipeHandle& handle, std::string* text) {
+ MojoResult rv;
+ bool did_wait = false;
+
+ uint32_t num_bytes = 0, num_handles = 0;
+ for (;;) {
+ rv = ReadMessageRaw(handle,
+ NULL,
+ &num_bytes,
+ NULL,
+ &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+ if (rv == MOJO_RESULT_SHOULD_WAIT) {
+ if (did_wait) {
+ assert(false); // Looping endlessly!?
+ return false;
+ }
+ rv = Wait(handle, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE);
+ if (rv != MOJO_RESULT_OK)
+ return false;
+ did_wait = true;
+ } else {
+ assert(!num_handles);
+ break;
+ }
+ }
+
+ text->resize(num_bytes);
+ rv = ReadMessageRaw(handle,
+ &text->at(0),
+ &num_bytes,
+ NULL,
+ &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+ return rv == MOJO_RESULT_OK;
+}
+
+bool DiscardMessage(const MessagePipeHandle& handle) {
+ MojoResult rv = ReadMessageRaw(handle, NULL, NULL, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
+ return rv == MOJO_RESULT_OK;
+}
+
+void IterateAndReportPerf(const char* test_name,
+ PerfTestSingleIteration single_iteration,
+ void* closure) {
+ // TODO(vtl): These should be specifiable using command-line flags.
+ static const size_t kGranularity = 100;
+ static const MojoTimeTicks kPerftestTimeMicroseconds = 3 * 1000000;
+
+ const MojoTimeTicks start_time = GetTimeTicksNow();
+ MojoTimeTicks end_time;
+ size_t iterations = 0;
+ do {
+ for (size_t i = 0; i < kGranularity; i++)
+ (*single_iteration)(closure);
+ iterations += kGranularity;
+
+ end_time = GetTimeTicksNow();
+ } while (end_time - start_time < kPerftestTimeMicroseconds);
+
+ MojoTestSupportLogPerfResult(test_name,
+ 1000000.0 * iterations / (end_time - start_time),
+ "iterations/second");
+}
+
+} // namespace test
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/test_support/test_support.h b/chromium/mojo/public/cpp/test_support/test_support.h
new file mode 100644
index 00000000000..eb4d4be7116
--- /dev/null
+++ b/chromium/mojo/public/cpp/test_support/test_support.h
@@ -0,0 +1,34 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_SUPPORT_H_
+#define MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_SUPPORT_H_
+
+#include <string>
+#include <vector>
+
+#include "mojo/public/c/test_support/test_support.h"
+
+namespace mojo {
+namespace test {
+
+inline void LogPerfResult(const char* test_name,
+ double value,
+ const char* units) {
+ MojoTestSupportLogPerfResult(test_name, value, units);
+}
+
+// Opens text file relative to the source root for reading.
+inline FILE* OpenSourceRootRelativeFile(const std::string& relative_path) {
+ return MojoTestSupportOpenSourceRootRelativeFile(relative_path.c_str());
+}
+
+// Returns the list of regular files in a directory relative to the source root.
+std::vector<std::string> EnumerateSourceRootRelativeDirectory(
+ const std::string& relative_path);
+
+} // namespace test
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_SUPPORT_H_
diff --git a/chromium/mojo/public/cpp/test_support/test_utils.h b/chromium/mojo/public/cpp/test_support/test_utils.h
new file mode 100644
index 00000000000..43a3ea99821
--- /dev/null
+++ b/chromium/mojo/public/cpp/test_support/test_utils.h
@@ -0,0 +1,39 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_UTILS_H_
+#define MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_UTILS_H_
+
+#include <string>
+
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace test {
+
+// Writes a message to |handle| with message data |text|. Returns true on
+// success.
+bool WriteTextMessage(const MessagePipeHandle& handle, const std::string& text);
+
+// Reads a message from |handle|, putting its contents into |*text|. Returns
+// true on success. (This blocks if necessary and will call |MojoReadMessage()|
+// multiple times, e.g., to query the size of the message.)
+bool ReadTextMessage(const MessagePipeHandle& handle, std::string* text);
+
+// Discards a message from |handle|. Returns true on success. (This does not
+// block. It will fail if no message is available to discard.)
+bool DiscardMessage(const MessagePipeHandle& handle);
+
+// Run |single_iteration| an appropriate number of times and report its
+// performance appropriately. (This actually runs |single_iteration| for a fixed
+// amount of time and reports the number of iterations per unit time.)
+typedef void (*PerfTestSingleIteration)(void* closure);
+void IterateAndReportPerf(const char* test_name,
+ PerfTestSingleIteration single_iteration,
+ void* closure);
+
+} // namespace test
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_TEST_SUPPORT_TEST_UTILS_H_
diff --git a/chromium/mojo/public/cpp/utility/lib/mutex.cc b/chromium/mojo/public/cpp/utility/lib/mutex.cc
new file mode 100644
index 00000000000..23370e1c023
--- /dev/null
+++ b/chromium/mojo/public/cpp/utility/lib/mutex.cc
@@ -0,0 +1,52 @@
+// Copyright 2014 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 "mojo/public/cpp/utility/mutex.h"
+
+#include <assert.h>
+#include <errno.h>
+
+namespace mojo {
+
+// Release builds have inlined (non-error-checking) definitions in the header.
+#if !defined(NDEBUG)
+Mutex::Mutex() {
+ pthread_mutexattr_t mutexattr;
+ int rv = pthread_mutexattr_init(&mutexattr);
+ assert(rv == 0);
+ rv = pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ERRORCHECK);
+ assert(rv == 0);
+ rv = pthread_mutex_init(&mutex_, &mutexattr);
+ assert(rv == 0);
+ rv = pthread_mutexattr_destroy(&mutexattr);
+ assert(rv == 0);
+}
+
+Mutex::~Mutex() {
+ int rv = pthread_mutex_destroy(&mutex_);
+ assert(rv == 0);
+}
+
+void Mutex::Lock() {
+ int rv = pthread_mutex_lock(&mutex_);
+ assert(rv == 0);
+}
+
+void Mutex::Unlock() {
+ int rv = pthread_mutex_unlock(&mutex_);
+ assert(rv == 0);
+}
+
+bool Mutex::TryLock() {
+ int rv = pthread_mutex_trylock(&mutex_);
+ assert(rv == 0 || rv == EBUSY);
+ return rv == 0;
+}
+
+void Mutex::AssertHeld() {
+ assert(pthread_mutex_lock(&mutex_) == EDEADLK);
+}
+#endif // !defined(NDEBUG)
+
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/utility/lib/run_loop.cc b/chromium/mojo/public/cpp/utility/lib/run_loop.cc
new file mode 100644
index 00000000000..f523eaeaead
--- /dev/null
+++ b/chromium/mojo/public/cpp/utility/lib/run_loop.cc
@@ -0,0 +1,221 @@
+// Copyright 2014 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 "mojo/public/cpp/utility/run_loop.h"
+
+#include <assert.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "mojo/public/cpp/utility/lib/thread_local.h"
+#include "mojo/public/cpp/utility/run_loop_handler.h"
+
+namespace mojo {
+namespace {
+
+internal::ThreadLocalPointer<RunLoop> current_run_loop;
+
+const MojoTimeTicks kInvalidTimeTicks = static_cast<MojoTimeTicks>(0);
+
+} // namespace
+
+// State needed for one iteration of WaitMany().
+struct RunLoop::WaitState {
+ WaitState() : deadline(MOJO_DEADLINE_INDEFINITE) {}
+
+ std::vector<Handle> handles;
+ std::vector<MojoHandleSignals> handle_signals;
+ MojoDeadline deadline;
+};
+
+struct RunLoop::RunState {
+ RunState() : should_quit(false) {}
+
+ bool should_quit;
+};
+
+RunLoop::RunLoop() : run_state_(NULL), next_handler_id_(0) {
+ assert(!current());
+ current_run_loop.Set(this);
+}
+
+RunLoop::~RunLoop() {
+ assert(current() == this);
+ current_run_loop.Set(NULL);
+}
+
+// static
+void RunLoop::SetUp() {
+ current_run_loop.Allocate();
+}
+
+// static
+void RunLoop::TearDown() {
+ assert(!current());
+ current_run_loop.Free();
+}
+
+// static
+RunLoop* RunLoop::current() {
+ return current_run_loop.Get();
+}
+
+void RunLoop::AddHandler(RunLoopHandler* handler,
+ const Handle& handle,
+ MojoHandleSignals handle_signals,
+ MojoDeadline deadline) {
+ assert(current() == this);
+ assert(handler);
+ assert(handle.is_valid());
+ // Assume it's an error if someone tries to reregister an existing handle.
+ assert(0u == handler_data_.count(handle));
+ HandlerData handler_data;
+ handler_data.handler = handler;
+ handler_data.handle_signals = handle_signals;
+ handler_data.deadline = (deadline == MOJO_DEADLINE_INDEFINITE) ?
+ kInvalidTimeTicks :
+ GetTimeTicksNow() + static_cast<MojoTimeTicks>(deadline);
+ handler_data.id = next_handler_id_++;
+ handler_data_[handle] = handler_data;
+}
+
+void RunLoop::RemoveHandler(const Handle& handle) {
+ assert(current() == this);
+ handler_data_.erase(handle);
+}
+
+bool RunLoop::HasHandler(const Handle& handle) const {
+ return handler_data_.find(handle) != handler_data_.end();
+}
+
+void RunLoop::Run() {
+ assert(current() == this);
+ RunState* old_state = run_state_;
+ RunState run_state;
+ run_state_ = &run_state;
+ while (!run_state.should_quit)
+ Wait(false);
+ run_state_ = old_state;
+}
+
+void RunLoop::RunUntilIdle() {
+ assert(current() == this);
+ RunState* old_state = run_state_;
+ RunState run_state;
+ run_state_ = &run_state;
+ while (!run_state.should_quit) {
+ if (!Wait(true))
+ break;
+ }
+ run_state_ = old_state;
+}
+
+void RunLoop::Quit() {
+ assert(current() == this);
+ if (run_state_)
+ run_state_->should_quit = true;
+}
+
+bool RunLoop::Wait(bool non_blocking) {
+ const WaitState wait_state = GetWaitState(non_blocking);
+ if (wait_state.handles.empty()) {
+ Quit();
+ return false;
+ }
+
+ const MojoResult result = WaitMany(wait_state.handles,
+ wait_state.handle_signals,
+ wait_state.deadline);
+ if (result >= 0) {
+ const size_t index = static_cast<size_t>(result);
+ assert(handler_data_.find(wait_state.handles[index]) !=
+ handler_data_.end());
+ handler_data_[wait_state.handles[index]].handler->OnHandleReady(
+ wait_state.handles[index]);
+ return true;
+ }
+
+ switch (result) {
+ case MOJO_RESULT_INVALID_ARGUMENT:
+ case MOJO_RESULT_FAILED_PRECONDITION:
+ return RemoveFirstInvalidHandle(wait_state);
+ case MOJO_RESULT_DEADLINE_EXCEEDED:
+ return NotifyDeadlineExceeded();
+ }
+
+ assert(false);
+ return false;
+}
+
+bool RunLoop::NotifyDeadlineExceeded() {
+ bool notified = false;
+
+ // Make a copy in case someone tries to add/remove new handlers as part of
+ // notifying.
+ const HandleToHandlerData cloned_handlers(handler_data_);
+ const MojoTimeTicks now(GetTimeTicksNow());
+ for (HandleToHandlerData::const_iterator i = cloned_handlers.begin();
+ i != cloned_handlers.end(); ++i) {
+ // Since we're iterating over a clone of the handlers, verify the handler is
+ // still valid before notifying.
+ if (i->second.deadline != kInvalidTimeTicks &&
+ i->second.deadline < now &&
+ handler_data_.find(i->first) != handler_data_.end() &&
+ handler_data_[i->first].id == i->second.id) {
+ handler_data_.erase(i->first);
+ i->second.handler->OnHandleError(i->first, MOJO_RESULT_DEADLINE_EXCEEDED);
+ notified = true;
+ }
+ }
+
+ return notified;
+}
+
+bool RunLoop::RemoveFirstInvalidHandle(const WaitState& wait_state) {
+ for (size_t i = 0; i < wait_state.handles.size(); ++i) {
+ const MojoResult result =
+ mojo::Wait(wait_state.handles[i], wait_state.handle_signals[i],
+ static_cast<MojoDeadline>(0));
+ if (result == MOJO_RESULT_INVALID_ARGUMENT ||
+ result == MOJO_RESULT_FAILED_PRECONDITION) {
+ // Remove the handle first, this way if OnHandleError() tries to remove
+ // the handle our iterator isn't invalidated.
+ assert(handler_data_.find(wait_state.handles[i]) != handler_data_.end());
+ RunLoopHandler* handler =
+ handler_data_[wait_state.handles[i]].handler;
+ handler_data_.erase(wait_state.handles[i]);
+ handler->OnHandleError(wait_state.handles[i], result);
+ return true;
+ }
+ assert(MOJO_RESULT_DEADLINE_EXCEEDED == result);
+ }
+ return false;
+}
+
+RunLoop::WaitState RunLoop::GetWaitState(bool non_blocking) const {
+ WaitState wait_state;
+ MojoTimeTicks min_time = kInvalidTimeTicks;
+ for (HandleToHandlerData::const_iterator i = handler_data_.begin();
+ i != handler_data_.end(); ++i) {
+ wait_state.handles.push_back(i->first);
+ wait_state.handle_signals.push_back(i->second.handle_signals);
+ if (!non_blocking && i->second.deadline != kInvalidTimeTicks &&
+ (min_time == kInvalidTimeTicks || i->second.deadline < min_time)) {
+ min_time = i->second.deadline;
+ }
+ }
+ if (non_blocking) {
+ wait_state.deadline = static_cast<MojoDeadline>(0);
+ } else if (min_time != kInvalidTimeTicks) {
+ const MojoTimeTicks now = GetTimeTicksNow();
+ if (min_time < now)
+ wait_state.deadline = static_cast<MojoDeadline>(0);
+ else
+ wait_state.deadline = static_cast<MojoDeadline>(min_time - now);
+ }
+ return wait_state;
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/utility/lib/thread.cc b/chromium/mojo/public/cpp/utility/lib/thread.cc
new file mode 100644
index 00000000000..da33497df42
--- /dev/null
+++ b/chromium/mojo/public/cpp/utility/lib/thread.cc
@@ -0,0 +1,69 @@
+// Copyright 2014 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 "mojo/public/cpp/utility/thread.h"
+
+#include <assert.h>
+
+namespace mojo {
+
+Thread::Thread()
+ : options_(),
+ thread_(),
+ started_(false),
+ joined_(false) {
+}
+
+Thread::Thread(const Options& options)
+ : options_(options),
+ thread_(),
+ started_(false),
+ joined_(false) {
+}
+
+Thread::~Thread() {
+ // If it was started, it must have been joined.
+ assert(!started_ || joined_);
+}
+
+void Thread::Start() {
+ assert(!started_);
+ assert(!joined_);
+
+ pthread_attr_t attr;
+ int rv MOJO_ALLOW_UNUSED = pthread_attr_init(&attr);
+ assert(rv == 0);
+
+ // Non-default stack size?
+ if (options_.stack_size() != 0) {
+ rv = pthread_attr_setstacksize(&attr, options_.stack_size());
+ assert(rv == 0);
+ }
+
+ started_ = true;
+ rv = pthread_create(&thread_, &attr, &ThreadRunTrampoline, this);
+ assert(rv == 0);
+
+ rv = pthread_attr_destroy(&attr);
+ assert(rv == 0);
+}
+
+void Thread::Join() {
+ // Must have been started but not yet joined.
+ assert(started_);
+ assert(!joined_);
+
+ joined_ = true;
+ int rv MOJO_ALLOW_UNUSED = pthread_join(thread_, NULL);
+ assert(rv == 0);
+}
+
+// static
+void* Thread::ThreadRunTrampoline(void* arg) {
+ Thread* self = static_cast<Thread*>(arg);
+ self->Run();
+ return NULL;
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/utility/lib/thread_local.h b/chromium/mojo/public/cpp/utility/lib/thread_local.h
new file mode 100644
index 00000000000..4c3625d00d8
--- /dev/null
+++ b/chromium/mojo/public/cpp/utility/lib/thread_local.h
@@ -0,0 +1,61 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_UTILITY_LIB_THREAD_LOCAL_H_
+#define MOJO_PUBLIC_CPP_UTILITY_LIB_THREAD_LOCAL_H_
+
+#ifndef _WIN32
+#include <pthread.h>
+#endif
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace internal {
+
+// Helper functions that abstract the cross-platform APIs.
+struct ThreadLocalPlatform {
+#ifdef _WIN32
+ typedef unsigned long SlotType;
+#else
+ typedef pthread_key_t SlotType;
+#endif
+
+ static void AllocateSlot(SlotType* slot);
+ static void FreeSlot(SlotType slot);
+ static void* GetValueFromSlot(SlotType slot);
+ static void SetValueInSlot(SlotType slot, void* value);
+};
+
+// This class is intended to be statically allocated.
+template <typename P>
+class ThreadLocalPointer {
+ public:
+ ThreadLocalPointer() : slot_() {
+ }
+
+ void Allocate() {
+ ThreadLocalPlatform::AllocateSlot(&slot_);
+ }
+
+ void Free() {
+ ThreadLocalPlatform::FreeSlot(slot_);
+ }
+
+ P* Get() {
+ return static_cast<P*>(ThreadLocalPlatform::GetValueFromSlot(slot_));
+ }
+
+ void Set(P* value) {
+ ThreadLocalPlatform::SetValueInSlot(slot_, value);
+ }
+
+ private:
+ ThreadLocalPlatform::SlotType slot_;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_UTILITY_LIB_THREAD_LOCAL_H_
diff --git a/chromium/mojo/public/cpp/utility/lib/thread_local_posix.cc b/chromium/mojo/public/cpp/utility/lib/thread_local_posix.cc
new file mode 100644
index 00000000000..b33dfc61297
--- /dev/null
+++ b/chromium/mojo/public/cpp/utility/lib/thread_local_posix.cc
@@ -0,0 +1,39 @@
+// Copyright 2014 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 "mojo/public/cpp/utility/lib/thread_local.h"
+
+#include <assert.h>
+
+namespace mojo {
+namespace internal {
+
+// static
+void ThreadLocalPlatform::AllocateSlot(SlotType* slot) {
+ if (pthread_key_create(slot, NULL) != 0) {
+ assert(false);
+ }
+}
+
+// static
+void ThreadLocalPlatform::FreeSlot(SlotType slot) {
+ if (pthread_key_delete(slot) != 0) {
+ assert(false);
+ }
+}
+
+// static
+void* ThreadLocalPlatform::GetValueFromSlot(SlotType slot) {
+ return pthread_getspecific(slot);
+}
+
+// static
+void ThreadLocalPlatform::SetValueInSlot(SlotType slot, void* value) {
+ if (pthread_setspecific(slot, value) != 0) {
+ assert(false);
+ }
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/utility/lib/thread_local_win.cc b/chromium/mojo/public/cpp/utility/lib/thread_local_win.cc
new file mode 100644
index 00000000000..98841f7273a
--- /dev/null
+++ b/chromium/mojo/public/cpp/utility/lib/thread_local_win.cc
@@ -0,0 +1,39 @@
+// Copyright 2014 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 "mojo/public/cpp/utility/lib/thread_local.h"
+
+#include <assert.h>
+#include <windows.h>
+
+namespace mojo {
+namespace internal {
+
+// static
+void ThreadLocalPlatform::AllocateSlot(SlotType* slot) {
+ *slot = TlsAlloc();
+ assert(*slot != TLS_OUT_OF_INDEXES);
+}
+
+// static
+void ThreadLocalPlatform::FreeSlot(SlotType slot) {
+ if (!TlsFree(slot)) {
+ assert(false);
+ }
+}
+
+// static
+void* ThreadLocalPlatform::GetValueFromSlot(SlotType slot) {
+ return TlsGetValue(slot);
+}
+
+// static
+void ThreadLocalPlatform::SetValueInSlot(SlotType slot, void* value) {
+ if (!TlsSetValue(slot, value)) {
+ assert(false);
+ }
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/chromium/mojo/public/cpp/utility/mutex.h b/chromium/mojo/public/cpp/utility/mutex.h
new file mode 100644
index 00000000000..35611c2ba9a
--- /dev/null
+++ b/chromium/mojo/public/cpp/utility/mutex.h
@@ -0,0 +1,70 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_UTILITY_MUTEX_H_
+#define MOJO_PUBLIC_CPP_UTILITY_MUTEX_H_
+
+#ifdef _WIN32
+#error "Not implemented: See crbug.com/342893."
+#endif
+
+#include <pthread.h>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+#ifdef NDEBUG
+// Note: Make a C++ constant for |PTHREAD_MUTEX_INITIALIZER|. (We can't directly
+// use the C macro in an initializer list, since it might expand to |{ ... }|.)
+namespace internal {
+const pthread_mutex_t kPthreadMutexInitializer = PTHREAD_MUTEX_INITIALIZER;
+}
+#endif
+
+class Mutex {
+ public:
+#ifdef NDEBUG
+ Mutex() : mutex_(internal::kPthreadMutexInitializer) {}
+ ~Mutex() { pthread_mutex_destroy(&mutex_); }
+
+ void Lock() { pthread_mutex_lock(&mutex_); }
+ void Unlock() { pthread_mutex_unlock(&mutex_); }
+ bool TryLock() { return pthread_mutex_trylock(&mutex_) == 0; }
+
+ void AssertHeld() {}
+#else
+ Mutex();
+ ~Mutex();
+
+ void Lock();
+ void Unlock();
+ bool TryLock();
+
+ void AssertHeld();
+#endif
+
+ private:
+ pthread_mutex_t mutex_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Mutex);
+};
+
+class MutexLock {
+ public:
+ explicit MutexLock(Mutex* mutex) : mutex_(mutex) { mutex_->Lock(); }
+ ~MutexLock() { mutex_->Unlock(); }
+
+ private:
+ Mutex* const mutex_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(MutexLock);
+};
+
+// Catch bug where variable name is omitted (e.g., |MutexLock (&mu)|).
+#define MutexLock(x) MOJO_COMPILE_ASSERT(0, mutex_lock_missing_variable_name);
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_UTILITY_MUTEX_H_
diff --git a/chromium/mojo/public/cpp/utility/run_loop.h b/chromium/mojo/public/cpp/utility/run_loop.h
new file mode 100644
index 00000000000..74f870abc57
--- /dev/null
+++ b/chromium/mojo/public/cpp/utility/run_loop.h
@@ -0,0 +1,108 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_H_
+#define MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_H_
+
+#include <map>
+
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+class RunLoopHandler;
+
+class RunLoop {
+ public:
+ RunLoop();
+ ~RunLoop();
+
+ // Sets up state needed for RunLoop. This must be invoked before creating a
+ // RunLoop.
+ static void SetUp();
+
+ // Cleans state created by Setup().
+ static void TearDown();
+
+ // Returns the RunLoop for the current thread. Returns NULL if not yet
+ // created.
+ static RunLoop* current();
+
+ // Registers a RunLoopHandler for the specified handle. Only one handler can
+ // be registered for a specified handle.
+ void AddHandler(RunLoopHandler* handler,
+ const Handle& handle,
+ MojoHandleSignals handle_signals,
+ MojoDeadline deadline);
+ void RemoveHandler(const Handle& handle);
+ bool HasHandler(const Handle& handle) const;
+
+ // Runs the loop servicing handles as they are ready. This returns when Quit()
+ // is invoked, or there no more handles.
+ void Run();
+
+ // Runs the loop servicing any handles that are ready. Does not wait for
+ // handles to become ready before returning. Returns early if Quit() is
+ // invoked.
+ void RunUntilIdle();
+
+ void Quit();
+
+ private:
+ struct RunState;
+ struct WaitState;
+
+ // Contains the data needed to track a request to AddHandler().
+ struct HandlerData {
+ HandlerData()
+ : handler(NULL),
+ handle_signals(MOJO_HANDLE_SIGNAL_NONE),
+ deadline(0),
+ id(0) {}
+
+ RunLoopHandler* handler;
+ MojoHandleSignals handle_signals;
+ MojoTimeTicks deadline;
+ // See description of |RunLoop::next_handler_id_| for details.
+ int id;
+ };
+
+ typedef std::map<Handle, HandlerData> HandleToHandlerData;
+
+ // Waits for a handle to be ready. Returns after servicing at least one
+ // handle (or there are no more handles) unless |non_blocking| is true,
+ // in which case it will also return if servicing at least one handle
+ // would require blocking. Returns true if a RunLoopHandler was notified.
+ bool Wait(bool non_blocking);
+
+ // Notifies any handlers whose deadline has expired. Returns true if a
+ // RunLoopHandler was notified.
+ bool NotifyDeadlineExceeded();
+
+ // Removes the first invalid handle. This is called if MojoWaitMany() finds an
+ // invalid handle. Returns true if a RunLoopHandler was notified.
+ bool RemoveFirstInvalidHandle(const WaitState& wait_state);
+
+ // Returns the state needed to pass to WaitMany().
+ WaitState GetWaitState(bool non_blocking) const;
+
+ HandleToHandlerData handler_data_;
+
+ // If non-NULL we're running (inside Run()). Member references a value on the
+ // stack.
+ RunState* run_state_;
+
+ // An ever increasing value assigned to each HandlerData::id. Used to detect
+ // uniqueness while notifying. That is, while notifying expired timers we copy
+ // |handler_data_| and only notify handlers whose id match. If the id does not
+ // match it means the handler was removed then added so that we shouldn't
+ // notify it.
+ int next_handler_id_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(RunLoop);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_H_
diff --git a/chromium/mojo/public/cpp/utility/run_loop_handler.h b/chromium/mojo/public/cpp/utility/run_loop_handler.h
new file mode 100644
index 00000000000..69838d5e57c
--- /dev/null
+++ b/chromium/mojo/public/cpp/utility/run_loop_handler.h
@@ -0,0 +1,25 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_HANDLER_H_
+#define MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_HANDLER_H_
+
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+// Used by RunLoop to notify when a handle is either ready or has become
+// invalid.
+class RunLoopHandler {
+ public:
+ virtual void OnHandleReady(const Handle& handle) = 0;
+ virtual void OnHandleError(const Handle& handle, MojoResult result) = 0;
+
+ protected:
+ virtual ~RunLoopHandler() {}
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_UTILITY_RUN_LOOP_HANDLER_H_
diff --git a/chromium/mojo/public/cpp/utility/thread.h b/chromium/mojo/public/cpp/utility/thread.h
new file mode 100644
index 00000000000..b7d10ee7037
--- /dev/null
+++ b/chromium/mojo/public/cpp/utility/thread.h
@@ -0,0 +1,62 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_CPP_UTILITY_THREAD_H_
+#define MOJO_PUBLIC_CPP_UTILITY_THREAD_H_
+
+#ifdef _WIN32
+#error "Not implemented: See crbug.com/342893."
+#endif
+
+#include <pthread.h>
+#include <stddef.h>
+
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+
+// This class is thread-friendly, not thread-safe (e.g., you mustn't call
+// |Join()| from multiple threads and/or simultaneously try to destroy the
+// object).
+class Thread {
+ public:
+ // TODO(vtl): Support non-joinable? priority?
+ class Options {
+ public:
+ Options() : stack_size_(0) {}
+
+ // A stack size of 0 means the default.
+ size_t stack_size() const { return stack_size_; }
+ void set_stack_size(size_t stack_size) { stack_size_ = stack_size; }
+
+ private:
+ size_t stack_size_;
+
+ // Copy and assign allowed.
+ };
+
+ // TODO(vtl): Add name or name prefix?
+ Thread();
+ explicit Thread(const Options& options);
+ virtual ~Thread();
+
+ void Start();
+ void Join();
+
+ virtual void Run() = 0;
+
+ private:
+ static void* ThreadRunTrampoline(void* arg);
+
+ const Options options_;
+ pthread_t thread_;
+ bool started_;
+ bool joined_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(Thread);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_UTILITY_THREAD_H_
diff --git a/chromium/mojo/public/gles2/gles2_interface.h b/chromium/mojo/public/gles2/gles2_interface.h
new file mode 100644
index 00000000000..19b958ec112
--- /dev/null
+++ b/chromium/mojo/public/gles2/gles2_interface.h
@@ -0,0 +1,23 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_GLES2_GLES2_INTERFACE_H_
+#define MOJO_PUBLIC_GLES2_GLES2_INTERFACE_H_
+
+#include <GLES2/gl2.h>
+
+namespace mojo {
+
+class GLES2Interface {
+ public:
+ virtual ~GLES2Interface() {}
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+ virtual ReturnType Function PARAMETERS = 0;
+#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h"
+#undef VISIT_GL_CALL
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_GLES2_GLES2_INTERFACE_H_
diff --git a/chromium/mojo/public/gles2/gles2_private.cc b/chromium/mojo/public/gles2/gles2_private.cc
new file mode 100644
index 00000000000..52b47d36c3f
--- /dev/null
+++ b/chromium/mojo/public/gles2/gles2_private.cc
@@ -0,0 +1,92 @@
+// Copyright 2014 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 "mojo/public/gles2/gles2_private.h"
+
+#include <assert.h>
+#include <stddef.h>
+
+#include "mojo/public/c/gles2/gles2.h"
+#include "mojo/public/gles2/gles2_interface.h"
+
+static mojo::GLES2Support* g_gles2_support = NULL;
+static mojo::GLES2Interface* g_gles2_interface = NULL;
+
+extern "C" {
+
+void MojoGLES2Initialize(const MojoAsyncWaiter* async_waiter) {
+ assert(g_gles2_support);
+ return g_gles2_support->Initialize(async_waiter);
+}
+
+void MojoGLES2Terminate() {
+ assert(g_gles2_support);
+ return g_gles2_support->Terminate();
+}
+
+MojoGLES2Context MojoGLES2CreateContext(
+ MojoHandle handle,
+ MojoGLES2ContextLost lost_callback,
+ MojoGLES2DrawAnimationFrame animation_callback,
+ void* closure) {
+ return g_gles2_support->CreateContext(mojo::MessagePipeHandle(handle),
+ lost_callback,
+ animation_callback,
+ closure);
+}
+
+void MojoGLES2DestroyContext(MojoGLES2Context context) {
+ return g_gles2_support->DestroyContext(context);
+}
+
+void MojoGLES2MakeCurrent(MojoGLES2Context context) {
+ assert(g_gles2_support);
+ g_gles2_support->MakeCurrent(context);
+ g_gles2_interface = g_gles2_support->GetGLES2InterfaceForCurrentContext();
+}
+
+void MojoGLES2SwapBuffers() {
+ assert(g_gles2_support);
+ return g_gles2_support->SwapBuffers();
+}
+
+void MojoGLES2RequestAnimationFrames(MojoGLES2Context context) {
+ assert(g_gles2_support);
+ return g_gles2_support->RequestAnimationFrames(context);
+}
+
+void MojoGLES2CancelAnimationFrames(MojoGLES2Context context) {
+ assert(g_gles2_support);
+ return g_gles2_support->CancelAnimationFrames(context);
+}
+
+void* MojoGLES2GetGLES2Interface(MojoGLES2Context context) {
+ assert(g_gles2_support);
+ return g_gles2_support->GetGLES2Interface(context);
+}
+
+void* MojoGLES2GetContextSupport(MojoGLES2Context context) {
+ assert(g_gles2_support);
+ return g_gles2_support->GetContextSupport(context);
+}
+
+#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \
+ ReturnType gl##Function PARAMETERS { \
+ return g_gles2_interface->Function ARGUMENTS; \
+ }
+#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h"
+#undef VISIT_GL_CALL
+
+} // extern "C"
+
+namespace mojo {
+
+GLES2Support::~GLES2Support() {}
+
+void GLES2Support::Init(GLES2Support* gles2_support) {
+ assert(!g_gles2_support);
+ g_gles2_support = gles2_support;
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/public/gles2/gles2_private.h b/chromium/mojo/public/gles2/gles2_private.h
new file mode 100644
index 00000000000..f0a336d60e6
--- /dev/null
+++ b/chromium/mojo/public/gles2/gles2_private.h
@@ -0,0 +1,46 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_GLES2_GLES2_PRIVATE_H_
+#define MOJO_PUBLIC_GLES2_GLES2_PRIVATE_H_
+
+#include <stdint.h>
+
+#include "mojo/public/c/environment/async_waiter.h"
+#include "mojo/public/c/gles2/gles2_export.h"
+#include "mojo/public/c/gles2/gles2_types.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+class GLES2Interface;
+
+// Implementors of the GLES2 APIs can use this interface to install their
+// implementation into the mojo_gles2 dynamic library. Mojo clients should not
+// call these functions directly.
+class MOJO_GLES2_EXPORT GLES2Support {
+ public:
+ virtual ~GLES2Support();
+
+ static void Init(GLES2Support* gles2_support);
+
+ virtual void Initialize(const MojoAsyncWaiter* async_waiter) = 0;
+ virtual void Terminate() = 0;
+ virtual MojoGLES2Context CreateContext(
+ MessagePipeHandle handle,
+ MojoGLES2ContextLost lost_callback,
+ MojoGLES2DrawAnimationFrame animation_callback,
+ void* closure) = 0;
+ virtual void DestroyContext(MojoGLES2Context context) = 0;
+ virtual void MakeCurrent(MojoGLES2Context context) = 0;
+ virtual void SwapBuffers() = 0;
+ virtual void RequestAnimationFrames(MojoGLES2Context context) = 0;
+ virtual void CancelAnimationFrames(MojoGLES2Context context) = 0;
+ virtual void* GetGLES2Interface(MojoGLES2Context context) = 0;
+ virtual void* GetContextSupport(MojoGLES2Context context) = 0;
+ virtual GLES2Interface* GetGLES2InterfaceForCurrentContext() = 0;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_GLES2_GLES2_PRIVATE_H_
diff --git a/chromium/mojo/public/interfaces/interface_provider/BUILD.gn b/chromium/mojo/public/interfaces/interface_provider/BUILD.gn
new file mode 100644
index 00000000000..55d5b0a37da
--- /dev/null
+++ b/chromium/mojo/public/interfaces/interface_provider/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2014 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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("interface_provider") {
+ sources = [
+ "interface_provider.mojom",
+ ]
+}
diff --git a/chromium/mojo/public/interfaces/interface_provider/interface_provider.mojom b/chromium/mojo/public/interfaces/interface_provider/interface_provider.mojom
new file mode 100644
index 00000000000..91e32813da6
--- /dev/null
+++ b/chromium/mojo/public/interfaces/interface_provider/interface_provider.mojom
@@ -0,0 +1,12 @@
+// Copyright 2014 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.
+
+module mojo {
+
+[Client=IInterfaceProvider]
+interface IInterfaceProvider {
+ GetInterface(string name, handle<message_pipe> client_handle);
+};
+
+}
diff --git a/chromium/mojo/public/interfaces/service_provider/BUILD.gn b/chromium/mojo/public/interfaces/service_provider/BUILD.gn
new file mode 100644
index 00000000000..990d3e9b626
--- /dev/null
+++ b/chromium/mojo/public/interfaces/service_provider/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2014 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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("service_provider") {
+ sources = [
+ "service_provider.mojom",
+ ]
+}
diff --git a/chromium/mojo/public/interfaces/service_provider/service_provider.mojom b/chromium/mojo/public/interfaces/service_provider/service_provider.mojom
new file mode 100644
index 00000000000..190c85c2d78
--- /dev/null
+++ b/chromium/mojo/public/interfaces/service_provider/service_provider.mojom
@@ -0,0 +1,19 @@
+// Copyright 2013 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.
+
+module mojo {
+
+[Client=ServiceProvider]
+interface ServiceProvider {
+ // Loads url. mojo:{service} will result in the user of the value of the
+ // --origin flag to the shell being used.
+ ConnectToService(string service_url,
+ string service_name,
+ handle<message_pipe> client_handle,
+ // ignored for client making request, filled in by system for
+ // implementor.
+ string requestor_url);
+};
+
+}
diff --git a/chromium/mojo/public/interfaces/shell/BUILD.gn b/chromium/mojo/public/interfaces/shell/BUILD.gn
new file mode 100644
index 00000000000..c11ae70a311
--- /dev/null
+++ b/chromium/mojo/public/interfaces/shell/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2014 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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("shell") {
+ sources = [
+ "shell.mojom",
+ ]
+}
diff --git a/chromium/mojo/public/js/bindings/BUILD.gn b/chromium/mojo/public/js/bindings/BUILD.gn
new file mode 100644
index 00000000000..0f305e924ec
--- /dev/null
+++ b/chromium/mojo/public/js/bindings/BUILD.gn
@@ -0,0 +1,10 @@
+# Copyright 2014 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.
+
+source_set("bindings") {
+ sources = [
+ "constants.cc",
+ "constants.h",
+ ]
+}
diff --git a/chromium/mojo/public/js/bindings/codec.js b/chromium/mojo/public/js/bindings/codec.js
new file mode 100644
index 00000000000..84c86c12991
--- /dev/null
+++ b/chromium/mojo/public/js/bindings/codec.js
@@ -0,0 +1,739 @@
+// Copyright 2014 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.
+
+define("mojo/public/js/bindings/codec", [
+ "mojo/public/js/bindings/unicode"
+], function(unicode) {
+
+ var kErrorUnsigned = "Passing negative value to unsigned";
+
+ // Memory -------------------------------------------------------------------
+
+ var kAlignment = 8;
+ var kHighWordMultiplier = 0x100000000;
+ var kHostIsLittleEndian = (function () {
+ var endianArrayBuffer = new ArrayBuffer(2);
+ var endianUint8Array = new Uint8Array(endianArrayBuffer);
+ var endianUint16Array = new Uint16Array(endianArrayBuffer);
+ endianUint16Array[0] = 1;
+ return endianUint8Array[0] == 1;
+ })();
+
+ function align(size) {
+ return size + (kAlignment - (size % kAlignment)) % kAlignment;
+ }
+
+ function getInt64(dataView, byteOffset, value) {
+ var lo, hi;
+ if (kHostIsLittleEndian) {
+ lo = dataView.getUint32(byteOffset, kHostIsLittleEndian);
+ hi = dataView.getInt32(byteOffset + 4, kHostIsLittleEndian);
+ } else {
+ hi = dataView.getInt32(byteOffset, kHostIsLittleEndian);
+ lo = dataView.getUint32(byteOffset + 4, kHostIsLittleEndian);
+ }
+ return lo + hi * kHighWordMultiplier;
+ }
+
+ function getUint64(dataView, byteOffset, value) {
+ var lo, hi;
+ if (kHostIsLittleEndian) {
+ lo = dataView.getUint32(byteOffset, kHostIsLittleEndian);
+ hi = dataView.getUint32(byteOffset + 4, kHostIsLittleEndian);
+ } else {
+ hi = dataView.getUint32(byteOffset, kHostIsLittleEndian);
+ lo = dataView.getUint32(byteOffset + 4, kHostIsLittleEndian);
+ }
+ return lo + hi * kHighWordMultiplier;
+ }
+
+ function setInt64(dataView, byteOffset, value) {
+ var hi = Math.floor(value / kHighWordMultiplier);
+ if (kHostIsLittleEndian) {
+ dataView.setInt32(byteOffset, value, kHostIsLittleEndian);
+ dataView.setInt32(byteOffset + 4, hi, kHostIsLittleEndian);
+ } else {
+ dataView.setInt32(byteOffset, hi, kHostIsLittleEndian);
+ dataView.setInt32(byteOffset + 4, value, kHostIsLittleEndian);
+ }
+ }
+
+ function setUint64(dataView, byteOffset, value) {
+ var hi = (value / kHighWordMultiplier) | 0;
+ if (kHostIsLittleEndian) {
+ dataView.setInt32(byteOffset, value, kHostIsLittleEndian);
+ dataView.setInt32(byteOffset + 4, hi, kHostIsLittleEndian);
+ } else {
+ dataView.setInt32(byteOffset, hi, kHostIsLittleEndian);
+ dataView.setInt32(byteOffset + 4, value, kHostIsLittleEndian);
+ }
+ }
+
+ function copyArrayBuffer(dstArrayBuffer, srcArrayBuffer) {
+ (new Uint8Array(dstArrayBuffer)).set(new Uint8Array(srcArrayBuffer));
+ }
+
+ // Buffer -------------------------------------------------------------------
+
+ function Buffer(sizeOrArrayBuffer) {
+ if (sizeOrArrayBuffer instanceof ArrayBuffer) {
+ this.arrayBuffer = sizeOrArrayBuffer;
+ } else {
+ this.arrayBuffer = new ArrayBuffer(sizeOrArrayBuffer);
+ };
+
+ this.dataView = new DataView(this.arrayBuffer);
+ this.next = 0;
+ }
+
+ Buffer.prototype.alloc = function(size) {
+ var pointer = this.next;
+ this.next += size;
+ if (this.next > this.arrayBuffer.byteLength) {
+ var newSize = (1.5 * (this.arrayBuffer.byteLength + size)) | 0;
+ this.grow(newSize);
+ }
+ return pointer;
+ };
+
+ Buffer.prototype.grow = function(size) {
+ var newArrayBuffer = new ArrayBuffer(size);
+ copyArrayBuffer(newArrayBuffer, this.arrayBuffer);
+ this.arrayBuffer = newArrayBuffer;
+ this.dataView = new DataView(this.arrayBuffer);
+ };
+
+ Buffer.prototype.trim = function() {
+ this.arrayBuffer = this.arrayBuffer.slice(0, this.next);
+ this.dataView = new DataView(this.arrayBuffer);
+ };
+
+ // Constants ----------------------------------------------------------------
+
+ var kArrayHeaderSize = 8;
+ var kStructHeaderSize = 8;
+ var kMessageHeaderSize = 16;
+ var kMessageWithRequestIDHeaderSize = 24;
+
+ // Decoder ------------------------------------------------------------------
+
+ function Decoder(buffer, handles, base) {
+ this.buffer = buffer;
+ this.handles = handles;
+ this.base = base;
+ this.next = base;
+ }
+
+ Decoder.prototype.skip = function(offset) {
+ this.next += offset;
+ };
+
+ Decoder.prototype.readInt8 = function() {
+ var result = this.buffer.dataView.getInt8(this.next, kHostIsLittleEndian);
+ this.next += 1;
+ return result;
+ };
+
+ Decoder.prototype.readUint8 = function() {
+ var result = this.buffer.dataView.getUint8(this.next, kHostIsLittleEndian);
+ this.next += 1;
+ return result;
+ };
+
+ Decoder.prototype.readInt16 = function() {
+ var result = this.buffer.dataView.getInt16(this.next, kHostIsLittleEndian);
+ this.next += 2;
+ return result;
+ };
+
+ Decoder.prototype.readUint16 = function() {
+ var result = this.buffer.dataView.getUint16(this.next, kHostIsLittleEndian);
+ this.next += 2;
+ return result;
+ };
+
+ Decoder.prototype.readInt32 = function() {
+ var result = this.buffer.dataView.getInt32(this.next, kHostIsLittleEndian);
+ this.next += 4;
+ return result;
+ };
+
+ Decoder.prototype.readUint32 = function() {
+ var result = this.buffer.dataView.getUint32(this.next, kHostIsLittleEndian);
+ this.next += 4;
+ return result;
+ };
+
+ Decoder.prototype.readInt64 = function() {
+ var result = getInt64(this.buffer.dataView, this.next, kHostIsLittleEndian);
+ this.next += 8;
+ return result;
+ };
+
+ Decoder.prototype.readUint64 = function() {
+ var result = getUint64(
+ this.buffer.dataView, this.next, kHostIsLittleEndian);
+ this.next += 8;
+ return result;
+ };
+
+ Decoder.prototype.readFloat = function() {
+ var result = this.buffer.dataView.getFloat32(
+ this.next, kHostIsLittleEndian);
+ this.next += 4;
+ return result;
+ };
+
+ Decoder.prototype.readDouble = function() {
+ var result = this.buffer.dataView.getFloat64(
+ this.next, kHostIsLittleEndian);
+ this.next += 8;
+ return result;
+ };
+
+ Decoder.prototype.decodePointer = function() {
+ // TODO(abarth): To correctly decode a pointer, we need to know the real
+ // base address of the array buffer.
+ var offsetPointer = this.next;
+ var offset = this.readUint64();
+ if (!offset)
+ return 0;
+ return offsetPointer + offset;
+ };
+
+ Decoder.prototype.decodeAndCreateDecoder = function(pointer) {
+ return new Decoder(this.buffer, this.handles, pointer);
+ };
+
+ Decoder.prototype.decodeHandle = function() {
+ return this.handles[this.readUint32()];
+ };
+
+ Decoder.prototype.decodeString = function() {
+ var numberOfBytes = this.readUint32();
+ var numberOfElements = this.readUint32();
+ var base = this.next;
+ this.next += numberOfElements;
+ return unicode.decodeUtf8String(
+ new Uint8Array(this.buffer.arrayBuffer, base, numberOfElements));
+ };
+
+ Decoder.prototype.decodeArray = function(cls) {
+ var numberOfBytes = this.readUint32();
+ var numberOfElements = this.readUint32();
+ var val = new Array(numberOfElements);
+ for (var i = 0; i < numberOfElements; ++i) {
+ val[i] = cls.decode(this);
+ }
+ return val;
+ };
+
+ Decoder.prototype.decodeStruct = function(cls) {
+ return cls.decode(this);
+ };
+
+ Decoder.prototype.decodeStructPointer = function(cls) {
+ var pointer = this.decodePointer();
+ if (!pointer) {
+ return null;
+ }
+ return cls.decode(this.decodeAndCreateDecoder(pointer));
+ };
+
+ Decoder.prototype.decodeArrayPointer = function(cls) {
+ var pointer = this.decodePointer();
+ if (!pointer) {
+ return null;
+ }
+ return this.decodeAndCreateDecoder(pointer).decodeArray(cls);
+ };
+
+ Decoder.prototype.decodeStringPointer = function() {
+ var pointer = this.decodePointer();
+ if (!pointer) {
+ return null;
+ }
+ return this.decodeAndCreateDecoder(pointer).decodeString();
+ };
+
+ // Encoder ------------------------------------------------------------------
+
+ function Encoder(buffer, handles, base) {
+ this.buffer = buffer;
+ this.handles = handles;
+ this.base = base;
+ this.next = base;
+ }
+
+ Encoder.prototype.skip = function(offset) {
+ this.next += offset;
+ };
+
+ Encoder.prototype.writeInt8 = function(val) {
+ // NOTE: Endianness doesn't come into play for single bytes.
+ this.buffer.dataView.setInt8(this.next, val);
+ this.next += 1;
+ };
+
+ Encoder.prototype.writeUint8 = function(val) {
+ if (val < 0) {
+ throw new Error(kErrorUnsigned);
+ }
+ // NOTE: Endianness doesn't come into play for single bytes.
+ this.buffer.dataView.setUint8(this.next, val);
+ this.next += 1;
+ };
+
+ Encoder.prototype.writeInt16 = function(val) {
+ this.buffer.dataView.setInt16(this.next, val, kHostIsLittleEndian);
+ this.next += 2;
+ };
+
+ Encoder.prototype.writeUint16 = function(val) {
+ if (val < 0) {
+ throw new Error(kErrorUnsigned);
+ }
+ this.buffer.dataView.setUint16(this.next, val, kHostIsLittleEndian);
+ this.next += 2;
+ };
+
+ Encoder.prototype.writeInt32 = function(val) {
+ this.buffer.dataView.setInt32(this.next, val, kHostIsLittleEndian);
+ this.next += 4;
+ };
+
+ Encoder.prototype.writeUint32 = function(val) {
+ if (val < 0) {
+ throw new Error(kErrorUnsigned);
+ }
+ this.buffer.dataView.setUint32(this.next, val, kHostIsLittleEndian);
+ this.next += 4;
+ };
+
+ Encoder.prototype.writeInt64 = function(val) {
+ setInt64(this.buffer.dataView, this.next, val);
+ this.next += 8;
+ };
+
+ Encoder.prototype.writeUint64 = function(val) {
+ if (val < 0) {
+ throw new Error(kErrorUnsigned);
+ }
+ setUint64(this.buffer.dataView, this.next, val);
+ this.next += 8;
+ };
+
+ Encoder.prototype.writeFloat = function(val) {
+ this.buffer.dataView.setFloat32(this.next, val, kHostIsLittleEndian);
+ this.next += 4;
+ };
+
+ Encoder.prototype.writeDouble = function(val) {
+ this.buffer.dataView.setFloat64(this.next, val, kHostIsLittleEndian);
+ this.next += 8;
+ };
+
+ Encoder.prototype.encodePointer = function(pointer) {
+ if (!pointer)
+ return this.writeUint64(0);
+ // TODO(abarth): To correctly encode a pointer, we need to know the real
+ // base address of the array buffer.
+ var offset = pointer - this.next;
+ this.writeUint64(offset);
+ };
+
+ Encoder.prototype.createAndEncodeEncoder = function(size) {
+ var pointer = this.buffer.alloc(align(size));
+ this.encodePointer(pointer);
+ return new Encoder(this.buffer, this.handles, pointer);
+ };
+
+ Encoder.prototype.encodeHandle = function(handle) {
+ this.handles.push(handle);
+ this.writeUint32(this.handles.length - 1);
+ };
+
+ Encoder.prototype.encodeString = function(val) {
+ var base = this.next + kArrayHeaderSize;
+ var numberOfElements = unicode.encodeUtf8String(
+ val, new Uint8Array(this.buffer.arrayBuffer, base));
+ var numberOfBytes = kArrayHeaderSize + numberOfElements;
+ this.writeUint32(numberOfBytes);
+ this.writeUint32(numberOfElements);
+ this.next += numberOfElements;
+ };
+
+ Encoder.prototype.encodeArray = function(cls, val) {
+ var numberOfElements = val.length;
+ var numberOfBytes = kArrayHeaderSize + cls.encodedSize * numberOfElements;
+ this.writeUint32(numberOfBytes);
+ this.writeUint32(numberOfElements);
+ for (var i = 0; i < numberOfElements; ++i) {
+ cls.encode(this, val[i]);
+ }
+ };
+
+ Encoder.prototype.encodeStruct = function(cls, val) {
+ return cls.encode(this, val);
+ };
+
+ Encoder.prototype.encodeStructPointer = function(cls, val) {
+ if (!val) {
+ this.encodePointer(val);
+ return;
+ }
+ var encoder = this.createAndEncodeEncoder(cls.encodedSize);
+ cls.encode(encoder, val);
+ };
+
+ Encoder.prototype.encodeArrayPointer = function(cls, val) {
+ if (!val) {
+ this.encodePointer(val);
+ return;
+ }
+ var encodedSize = kArrayHeaderSize + cls.encodedSize * val.length;
+ var encoder = this.createAndEncodeEncoder(encodedSize);
+ encoder.encodeArray(cls, val);
+ };
+
+ Encoder.prototype.encodeStringPointer = function(val) {
+ if (!val) {
+ this.encodePointer(val);
+ return;
+ }
+ var encodedSize = kArrayHeaderSize + unicode.utf8Length(val);
+ var encoder = this.createAndEncodeEncoder(encodedSize);
+ encoder.encodeString(val);
+ };
+
+ // Message ------------------------------------------------------------------
+
+ var kMessageExpectsResponse = 1 << 0;
+ var kMessageIsResponse = 1 << 1;
+
+ // Skip over num_bytes, num_fields, and message_name.
+ var kFlagsOffset = 4 + 4 + 4;
+
+ // Skip over num_bytes, num_fields, message_name, and flags.
+ var kRequestIDOffset = 4 + 4 + 4 + 4;
+
+ function Message(buffer, handles) {
+ this.buffer = buffer;
+ this.handles = handles;
+ }
+
+ Message.prototype.setRequestID = function(requestID) {
+ // TODO(darin): Verify that space was reserved for this field!
+ setUint64(this.buffer.dataView, kRequestIDOffset, requestID);
+ };
+
+ Message.prototype.getFlags = function() {
+ return this.buffer.dataView.getUint32(kFlagsOffset, kHostIsLittleEndian);
+ };
+
+ // MessageBuilder -----------------------------------------------------------
+
+ function MessageBuilder(messageName, payloadSize) {
+ // Currently, we don't compute the payload size correctly ahead of time.
+ // Instead, we resize the buffer at the end.
+ var numberOfBytes = kMessageHeaderSize + payloadSize;
+ this.buffer = new Buffer(numberOfBytes);
+ this.handles = [];
+ var encoder = this.createEncoder(kMessageHeaderSize);
+ encoder.writeUint32(kMessageHeaderSize);
+ encoder.writeUint32(2); // num_fields.
+ encoder.writeUint32(messageName);
+ encoder.writeUint32(0); // flags.
+ }
+
+ MessageBuilder.prototype.createEncoder = function(size) {
+ var pointer = this.buffer.alloc(size);
+ return new Encoder(this.buffer, this.handles, pointer);
+ };
+
+ MessageBuilder.prototype.encodeStruct = function(cls, val) {
+ cls.encode(this.createEncoder(cls.encodedSize), val);
+ };
+
+ MessageBuilder.prototype.finish = function() {
+ // TODO(abarth): Rather than resizing the buffer at the end, we could
+ // compute the size we need ahead of time, like we do in C++.
+ this.buffer.trim();
+ var message = new Message(this.buffer, this.handles);
+ this.buffer = null;
+ this.handles = null;
+ this.encoder = null;
+ return message;
+ };
+
+ // MessageWithRequestIDBuilder -----------------------------------------------
+
+ function MessageWithRequestIDBuilder(messageName, payloadSize, flags,
+ requestID) {
+ // Currently, we don't compute the payload size correctly ahead of time.
+ // Instead, we resize the buffer at the end.
+ var numberOfBytes = kMessageWithRequestIDHeaderSize + payloadSize;
+ this.buffer = new Buffer(numberOfBytes);
+ this.handles = [];
+ var encoder = this.createEncoder(kMessageWithRequestIDHeaderSize);
+ encoder.writeUint32(kMessageWithRequestIDHeaderSize);
+ encoder.writeUint32(3); // num_fields.
+ encoder.writeUint32(messageName);
+ encoder.writeUint32(flags);
+ encoder.writeUint64(requestID);
+ }
+
+ MessageWithRequestIDBuilder.prototype =
+ Object.create(MessageBuilder.prototype);
+
+ MessageWithRequestIDBuilder.prototype.constructor =
+ MessageWithRequestIDBuilder;
+
+ // MessageReader ------------------------------------------------------------
+
+ function MessageReader(message) {
+ this.decoder = new Decoder(message.buffer, message.handles, 0);
+ var messageHeaderSize = this.decoder.readUint32();
+ this.payloadSize =
+ message.buffer.arrayBuffer.byteLength - messageHeaderSize;
+ var numFields = this.decoder.readUint32();
+ this.messageName = this.decoder.readUint32();
+ this.flags = this.decoder.readUint32();
+ if (numFields >= 3)
+ this.requestID = this.decoder.readUint64();
+ this.decoder.skip(messageHeaderSize - this.decoder.next);
+ }
+
+ MessageReader.prototype.decodeStruct = function(cls) {
+ return cls.decode(this.decoder);
+ };
+
+ // Built-in types -----------------------------------------------------------
+
+ function Int8() {
+ }
+
+ Int8.encodedSize = 1;
+
+ Int8.decode = function(decoder) {
+ return decoder.readInt8();
+ };
+
+ Int8.encode = function(encoder, val) {
+ encoder.writeInt8(val);
+ };
+
+ Uint8.encode = function(encoder, val) {
+ encoder.writeUint8(val);
+ };
+
+ function Uint8() {
+ }
+
+ Uint8.encodedSize = 1;
+
+ Uint8.decode = function(decoder) {
+ return decoder.readUint8();
+ };
+
+ Uint8.encode = function(encoder, val) {
+ encoder.writeUint8(val);
+ };
+
+ function Int16() {
+ }
+
+ Int16.encodedSize = 2;
+
+ Int16.decode = function(decoder) {
+ return decoder.readInt16();
+ };
+
+ Int16.encode = function(encoder, val) {
+ encoder.writeInt16(val);
+ };
+
+ function Uint16() {
+ }
+
+ Uint16.encodedSize = 2;
+
+ Uint16.decode = function(decoder) {
+ return decoder.readUint16();
+ };
+
+ Uint16.encode = function(encoder, val) {
+ encoder.writeUint16(val);
+ };
+
+ function Int32() {
+ }
+
+ Int32.encodedSize = 4;
+
+ Int32.decode = function(decoder) {
+ return decoder.readInt32();
+ };
+
+ Int32.encode = function(encoder, val) {
+ encoder.writeInt32(val);
+ };
+
+ function Uint32() {
+ }
+
+ Uint32.encodedSize = 4;
+
+ Uint32.decode = function(decoder) {
+ return decoder.readUint32();
+ };
+
+ Uint32.encode = function(encoder, val) {
+ encoder.writeUint32(val);
+ };
+
+ function Int64() {
+ }
+
+ Int64.encodedSize = 8;
+
+ Int64.decode = function(decoder) {
+ return decoder.readInt64();
+ };
+
+ Int64.encode = function(encoder, val) {
+ encoder.writeInt64(val);
+ };
+
+ function Uint64() {
+ }
+
+ Uint64.encodedSize = 8;
+
+ Uint64.decode = function(decoder) {
+ return decoder.readUint64();
+ };
+
+ Uint64.encode = function(encoder, val) {
+ encoder.writeUint64(val);
+ };
+
+ function String() {
+ };
+
+ String.encodedSize = 8;
+
+ String.decode = function(decoder) {
+ return decoder.decodeStringPointer();
+ };
+
+ String.encode = function(encoder, val) {
+ encoder.encodeStringPointer(val);
+ };
+
+
+ function Float() {
+ }
+
+ Float.encodedSize = 4;
+
+ Float.decode = function(decoder) {
+ return decoder.readFloat();
+ };
+
+ Float.encode = function(encoder, val) {
+ encoder.writeFloat(val);
+ };
+
+ function Double() {
+ }
+
+ Double.encodedSize = 8;
+
+ Double.decode = function(decoder) {
+ return decoder.readDouble();
+ };
+
+ Double.encode = function(encoder, val) {
+ encoder.writeDouble(val);
+ };
+
+ function PointerTo(cls) {
+ this.cls = cls;
+ }
+
+ PointerTo.prototype.encodedSize = 8;
+
+ PointerTo.prototype.decode = function(decoder) {
+ var pointer = decoder.decodePointer();
+ if (!pointer) {
+ return null;
+ }
+ return this.cls.decode(decoder.decodeAndCreateDecoder(pointer));
+ };
+
+ PointerTo.prototype.encode = function(encoder, val) {
+ if (!val) {
+ encoder.encodePointer(val);
+ return;
+ }
+ var objectEncoder = encoder.createAndEncodeEncoder(this.cls.encodedSize);
+ this.cls.encode(objectEncoder, val);
+ };
+
+ function ArrayOf(cls) {
+ this.cls = cls;
+ }
+
+ ArrayOf.prototype.encodedSize = 8;
+
+ ArrayOf.prototype.decode = function(decoder) {
+ return decoder.decodeArrayPointer(this.cls);
+ };
+
+ ArrayOf.prototype.encode = function(encoder, val) {
+ encoder.encodeArrayPointer(this.cls, val);
+ };
+
+ function Handle() {
+ }
+
+ Handle.encodedSize = 4;
+
+ Handle.decode = function(decoder) {
+ return decoder.decodeHandle();
+ };
+
+ Handle.encode = function(encoder, val) {
+ encoder.encodeHandle(val);
+ };
+
+ var exports = {};
+ exports.align = align;
+ exports.Buffer = Buffer;
+ exports.Message = Message;
+ exports.MessageBuilder = MessageBuilder;
+ exports.MessageWithRequestIDBuilder = MessageWithRequestIDBuilder;
+ exports.MessageReader = MessageReader;
+ exports.kArrayHeaderSize = kArrayHeaderSize;
+ exports.kStructHeaderSize = kStructHeaderSize;
+ exports.kMessageHeaderSize = kMessageHeaderSize;
+ exports.kMessageExpectsResponse = kMessageExpectsResponse;
+ exports.kMessageIsResponse = kMessageIsResponse;
+ exports.Int8 = Int8;
+ exports.Uint8 = Uint8;
+ exports.Int16 = Int16;
+ exports.Uint16 = Uint16;
+ exports.Int32 = Int32;
+ exports.Uint32 = Uint32;
+ exports.Int64 = Int64;
+ exports.Uint64 = Uint64;
+ exports.Float = Float;
+ exports.Double = Double;
+ exports.String = String;
+ exports.PointerTo = PointerTo;
+ exports.ArrayOf = ArrayOf;
+ exports.Handle = Handle;
+ return exports;
+});
diff --git a/chromium/mojo/public/js/bindings/connection.js b/chromium/mojo/public/js/bindings/connection.js
new file mode 100644
index 00000000000..ebf60adb7f8
--- /dev/null
+++ b/chromium/mojo/public/js/bindings/connection.js
@@ -0,0 +1,30 @@
+// Copyright 2014 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.
+
+define("mojo/public/js/bindings/connection", [
+ "mojo/public/js/bindings/router",
+], function(router) {
+
+ function Connection(handle, localFactory, remoteFactory) {
+ this.router_ = new router.Router(handle);
+ this.remote = new remoteFactory(this.router_);
+ this.local = new localFactory(this.remote);
+ this.router_.setIncomingReceiver(this.local);
+ }
+
+ Connection.prototype.close = function() {
+ this.router_.close();
+ this.router_ = null;
+ this.local = null;
+ this.remote = null;
+ };
+
+ Connection.prototype.encounteredError = function() {
+ return this.router_.encounteredError();
+ };
+
+ var exports = {};
+ exports.Connection = Connection;
+ return exports;
+});
diff --git a/chromium/mojo/public/js/bindings/connector.js b/chromium/mojo/public/js/bindings/connector.js
new file mode 100644
index 00000000000..51022b0e45f
--- /dev/null
+++ b/chromium/mojo/public/js/bindings/connector.js
@@ -0,0 +1,109 @@
+// Copyright 2014 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.
+
+define("mojo/public/js/bindings/connector", [
+ "mojo/public/js/bindings/codec",
+ "mojo/public/js/bindings/core",
+ "mojo/public/js/bindings/support",
+], function(codec, core, support) {
+
+ function Connector(handle) {
+ this.handle_ = handle;
+ this.dropWrites_ = false;
+ this.error_ = false;
+ this.incomingReceiver_ = null;
+ this.readWaitCookie_ = null;
+ this.errorHandler_ = null;
+
+ this.waitToReadMore_();
+ }
+
+ Connector.prototype.close = function() {
+ if (this.readWaitCookie_) {
+ support.cancelWait(this.readWaitCookie_);
+ this.readWaitCookie_ = null;
+ }
+ if (this.handle_ != null) {
+ core.close(this.handle_);
+ this.handle_ = null;
+ }
+ };
+
+ Connector.prototype.accept = function(message) {
+ if (this.error_)
+ return false;
+
+ if (this.dropWrites_)
+ return true;
+
+ var result = core.writeMessage(this.handle_,
+ new Uint8Array(message.buffer.arrayBuffer),
+ message.handles,
+ core.WRITE_MESSAGE_FLAG_NONE);
+ switch (result) {
+ case core.RESULT_OK:
+ // The handles were successfully transferred, so we don't own them
+ // anymore.
+ message.handles = [];
+ break;
+ case core.RESULT_FAILED_PRECONDITION:
+ // There's no point in continuing to write to this pipe since the other
+ // end is gone. Avoid writing any future messages. Hide write failures
+ // from the caller since we'd like them to continue consuming any
+ // backlog of incoming messages before regarding the message pipe as
+ // closed.
+ this.dropWrites_ = true;
+ break;
+ default:
+ // This particular write was rejected, presumably because of bad input.
+ // The pipe is not necessarily in a bad state.
+ return false;
+ }
+ return true;
+ };
+
+ Connector.prototype.setIncomingReceiver = function(receiver) {
+ this.incomingReceiver_ = receiver;
+ };
+
+ Connector.prototype.setErrorHandler = function(handler) {
+ this.errorHandler_ = handler;
+ };
+
+ Connector.prototype.encounteredError = function() {
+ return this.error_;
+ };
+
+ Connector.prototype.waitToReadMore_ = function() {
+ this.readWaitCookie_ = support.asyncWait(this.handle_,
+ core.HANDLE_SIGNAL_READABLE,
+ this.readMore_.bind(this));
+ };
+
+ Connector.prototype.readMore_ = function(result) {
+ for (;;) {
+ var read = core.readMessage(this.handle_,
+ core.READ_MESSAGE_FLAG_NONE);
+ if (read.result == core.RESULT_SHOULD_WAIT) {
+ this.waitToReadMore_();
+ return;
+ }
+ if (read.result != core.RESULT_OK) {
+ this.error_ = true;
+ if (this.errorHandler_)
+ this.errorHandler_.onError(read.result);
+ return;
+ }
+ var buffer = new codec.Buffer(read.buffer);
+ var message = new codec.Message(buffer, read.handles);
+ if (this.incomingReceiver_) {
+ this.incomingReceiver_.accept(message);
+ }
+ }
+ };
+
+ var exports = {};
+ exports.Connector = Connector;
+ return exports;
+});
diff --git a/chromium/mojo/public/js/bindings/constants.cc b/chromium/mojo/public/js/bindings/constants.cc
new file mode 100644
index 00000000000..239b67d4139
--- /dev/null
+++ b/chromium/mojo/public/js/bindings/constants.cc
@@ -0,0 +1,15 @@
+// Copyright 2014 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 "mojo/public/js/bindings/constants.h"
+
+namespace mojo {
+
+const char kCodecModuleName[] = "mojo/public/js/bindings/codec";
+const char kConnectionModuleName[] = "mojo/public/js/bindings/connection";
+const char kConnectorModuleName[] = "mojo/public/js/bindings/connector";
+const char kUnicodeModuleName[] = "mojo/public/js/bindings/unicode";
+const char kRouterModuleName[] = "mojo/public/js/bindings/router";
+
+} // namespace mojo
diff --git a/chromium/mojo/public/js/bindings/constants.h b/chromium/mojo/public/js/bindings/constants.h
new file mode 100644
index 00000000000..50e5d7c0248
--- /dev/null
+++ b/chromium/mojo/public/js/bindings/constants.h
@@ -0,0 +1,19 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_JS_BINDINGS_CONSTANTS_H_
+#define MOJO_PUBLIC_JS_BINDINGS_CONSTANTS_H_
+
+namespace mojo {
+
+// JavaScript module names:
+extern const char kCodecModuleName[];
+extern const char kConnectionModuleName[];
+extern const char kConnectorModuleName[];
+extern const char kUnicodeModuleName[];
+extern const char kRouterModuleName[];
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_JS_BINDINGS_CONSTANTS_H_
diff --git a/chromium/mojo/public/js/bindings/core.js b/chromium/mojo/public/js/bindings/core.js
new file mode 100644
index 00000000000..0aff5310833
--- /dev/null
+++ b/chromium/mojo/public/js/bindings/core.js
@@ -0,0 +1,215 @@
+// Copyright 2014 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.
+
+// Module "mojo/public/js/bindings/core"
+//
+// Note: This file is for documentation purposes only. The code here is not
+// actually executed. The real module is implemented natively in Mojo.
+//
+// This module provides the JavaScript bindings for mojo/public/c/system/core.h.
+// Refer to that file for more detailed documentation for equivalent methods.
+
+while (1);
+
+/**
+ * MojoHandle: An opaque handles to a Mojo object (e.g. a message pipe).
+ */
+var kInvalidHandle;
+
+/**
+ * MojoResult {number}: Result codes for Mojo operations.
+ * See core.h for more information.
+ */
+var RESULT_OK;
+var RESULT_CANCELLED;
+var RESULT_UNKNOWN;
+var RESULT_INVALID_ARGUMENT;
+var RESULT_DEADLINE_EXCEEDED;
+var RESULT_NOT_FOUND;
+var RESULT_ALREADY_EXISTS;
+var RESULT_PERMISSION_DENIED;
+var RESULT_RESOURCE_EXHAUSTED;
+var RESULT_FAILED_PRECONDITION;
+var RESULT_ABORTED;
+var RESULT_OUT_OF_RANGE;
+var RESULT_UNIMPLEMENTED;
+var RESULT_INTERNAL;
+var RESULT_UNAVAILABLE;
+var RESULT_DATA_LOSS;
+var RESULT_BUSY;
+var RESULT_SHOULD_WAIT;
+
+/**
+ * MojoDeadline {number}: Used to specify deadlines (timeouts), in microseconds.
+ * See core.h for more information.
+ */
+var DEADLINE_INDEFINITE;
+
+/**
+ * MojoHandleSignals: Used to specify signals that can be waited on for a handle
+ *(and which can be triggered), e.g., the ability to read or write to
+ * the handle.
+ * See core.h for more information.
+ */
+var HANDLE_SIGNAL_NONE;
+var HANDLE_SIGNAL_READABLE;
+var HANDLE_SIGNAL_WRITABLE;
+
+/*
+ * MojoWriteMessageFlags: Used to specify different modes to |writeMessage()|.
+ * See core.h for more information.
+ */
+var WRITE_MESSAGE_FLAG_NONE;
+
+/**
+ * MojoReadMessageFlags: Used to specify different modes to |readMessage()|.
+ * See core.h for more information.
+ */
+var READ_MESSAGE_FLAG_NONE;
+var READ_MESSAGE_FLAG_MAY_DISCARD;
+
+/**
+ * MojoCreateDataPipeOptions: Used to specify creation parameters for a data
+ * pipe to |createDataPipe()|.
+ * See core.h for more information.
+ */
+dictionary MojoCreateDataPipeOptions {
+ MojoCreateDataPipeOptionsFlags flags; // See below.
+ int32 elementNumBytes; // The size of an element, in bytes.
+ int32 capacityNumBytes; // The capacity of the data pipe, in bytes.
+};
+
+// MojoCreateDataPipeOptionsFlags
+var CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
+var CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD;
+
+/*
+ * MojoWriteDataFlags: Used to specify different modes to |writeData()|.
+ * See core.h for more information.
+ */
+var WRITE_DATA_FLAG_NONE;
+var WRITE_DATA_FLAG_ALL_OR_NONE;
+
+/**
+ * MojoReadDataFlags: Used to specify different modes to |readData()|.
+ * See core.h for more information.
+ */
+var READ_DATA_FLAG_NONE;
+var READ_DATA_FLAG_ALL_OR_NONE;
+var READ_DATA_FLAG_DISCARD;
+var READ_DATA_FLAG_QUERY;
+
+/**
+ * Closes the given |handle|. See MojoClose for more info.
+ * @param {MojoHandle} Handle to close.
+ * @return {MojoResult} Result code.
+ */
+function close(handle) { [native code] }
+
+/**
+ * Waits on the given handle until a signal indicated by |signals| is
+ * satisfied or until |deadline| is passed. See MojoWait for more information.
+ *
+ * @param {MojoHandle} handle Handle to wait on.
+ * @param {MojoHandleSignals} signals Specifies the condition to wait for.
+ * @param {MojoDeadline} deadline Stops waiting if this is reached.
+ * @return {MojoResult} Result code.
+ */
+function wait(handle, signals, deadline) { [native code] }
+
+/**
+ * Waits on |handles[0]|, ..., |handles[handles.length-1]| for at least one of
+ * them to satisfy the state indicated by |flags[0]|, ...,
+ * |flags[handles.length-1]|, respectively, or until |deadline| has passed.
+ * See MojoWaitMany for more information.
+ *
+ * @param {Array.MojoHandle} handles Handles to wait on.
+ * @param {Array.MojoHandleSignals} signals Specifies the condition to wait for,
+ * for each corresponding handle. Must be the same length as |handles|.
+ * @param {MojoDeadline} deadline Stops waiting if this is reached.
+ * @return {MojoResult} Result code.
+ */
+function waitMany(handles, signals, deadline) { [native code] }
+
+/**
+ * Creates a message pipe. This function always succeeds.
+ * See MojoCreateMessagePipe for more information on message pipes.
+ *
+ * @return {MessagePipe} An object of the form {
+ * handle0,
+ * handle1,
+ * }
+ * where |handle0| and |handle1| are MojoHandles to each end of the channel.
+ */
+function createMessagePipe() { [native code] }
+
+/**
+ * Writes a message to the message pipe endpoint given by |handle|. See
+ * MojoWriteMessage for more information, including return codes.
+ *
+ * @param {MojoHandle} handle The endpoint to write to.
+ * @param {ArrayBufferView} buffer The message data. May be empty.
+ * @param {Array.MojoHandle} handlesArray Any handles to attach. Handles are
+ * transferred on success and will no longer be valid. May be empty.
+ * @param {MojoWriteMessageFlags} flags Flags.
+ * @return {MojoResult} Result code.
+ */
+function writeMessage(handle, buffer, handlesArray, flags) { [native code] }
+
+/**
+ * Reads a message from the message pipe endpoint given by |handle|. See
+ * MojoReadMessage for more information, including return codes.
+ *
+ * @param {MojoHandle} handle The endpoint to read from.
+ * @param {MojoReadMessageFlags} flags Flags.
+ * @return {object} An object of the form {
+ * result, // |RESULT_OK| on success, error code otherwise.
+ * buffer, // An ArrayBufferView of the message data (only on success).
+ * handles // An array of MojoHandles transferred, if any.
+ * }
+ */
+function readMessage(handle, flags) { [native code] }
+
+/**
+ * Creates a data pipe, which is a unidirectional communication channel for
+ * unframed data, with the given options. See MojoCreateDataPipe for more
+ * more information, including return codes.
+ *
+ * @param {MojoCreateDataPipeOptions} optionsDict Options to control the data
+ * pipe parameters. May be null.
+ * @return {object} An object of the form {
+ * result, // |RESULT_OK| on success, error code otherwise.
+ * producerHandle, // MojoHandle to use with writeData (only on success).
+ * consumerHandle, // MojoHandle to use with readData (only on success).
+ * }
+ */
+function createDataPipe(optionsDict) { [native code] }
+
+/**
+ * Writes the given data to the data pipe producer given by |handle|. See
+ * MojoWriteData for more information, including return codes.
+ *
+ * @param {MojoHandle} handle A producerHandle returned by createDataPipe.
+ * @param {ArrayBufferView} buffer The data to write.
+ * @param {MojoWriteDataFlags} flags Flags.
+ * @return {object} An object of the form {
+ * result, // |RESULT_OK| on success, error code otherwise.
+ * numBytes, // The number of bytes written.
+ * }
+ */
+function writeData(handle, buffer, flags) { [native code] }
+
+/**
+ * Reads data from the data pipe consumer given by |handle|. May also
+ * be used to discard data. See MojoReadData for more information, including
+ * return codes.
+ *
+ * @param {MojoHandle} handle A consumerHandle returned by createDataPipe.
+ * @param {MojoReadDataFlags} flags Flags.
+ * @return {object} An object of the form {
+ * result, // |RESULT_OK| on success, error code otherwise.
+ * buffer, // An ArrayBufferView of the data read (only on success).
+ * }
+ */
+function readData(handle, flags) { [native code] }
diff --git a/chromium/mojo/public/js/bindings/router.js b/chromium/mojo/public/js/bindings/router.js
new file mode 100644
index 00000000000..2718e8b0b78
--- /dev/null
+++ b/chromium/mojo/public/js/bindings/router.js
@@ -0,0 +1,93 @@
+// Copyright 2014 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.
+
+define("mojo/public/js/bindings/router", [
+ "mojo/public/js/bindings/codec",
+ "mojo/public/js/bindings/connector",
+], function(codec, connector) {
+
+ function Router(handle) {
+ this.connector_ = new connector.Connector(handle);
+ this.incomingReceiver_ = null;
+ this.nextRequestID_ = 0;
+ this.responders_ = {};
+
+ this.connector_.setIncomingReceiver({
+ accept: this.handleIncomingMessage_.bind(this),
+ });
+ this.connector_.setErrorHandler({
+ onError: this.handleConnectionError_.bind(this),
+ });
+ }
+
+ Router.prototype.close = function() {
+ this.responders_ = {}; // Drop any responders.
+ this.connector_.close();
+ };
+
+ Router.prototype.accept = function(message) {
+ this.connector_.accept(message);
+ };
+
+ Router.prototype.reject = function(message) {
+ // TODO(mpcomplete): no way to trasmit errors over a Connection.
+ };
+
+ Router.prototype.acceptWithResponder = function(message, responder) {
+ // Reserve 0 in case we want it to convey special meaning in the future.
+ var requestID = this.nextRequestID_++;
+ if (requestID == 0)
+ requestID = this.nextRequestID_++;
+
+ message.setRequestID(requestID);
+ var result = this.connector_.accept(message);
+
+ this.responders_[requestID] = responder;
+
+ // TODO(mpcomplete): accept should return a Promise too, maybe?
+ if (result)
+ return Promise.resolve();
+ return Promise.reject(Error("Connection error"));
+ };
+
+ Router.prototype.setIncomingReceiver = function(receiver) {
+ this.incomingReceiver_ = receiver;
+ };
+
+ Router.prototype.encounteredError = function() {
+ return this.connector_.encounteredError();
+ };
+
+ Router.prototype.handleIncomingMessage_ = function(message) {
+ var flags = message.getFlags();
+ if (flags & codec.kMessageExpectsResponse) {
+ if (this.incomingReceiver_) {
+ this.incomingReceiver_.acceptWithResponder(message, this);
+ } else {
+ // If we receive a request expecting a response when the client is not
+ // listening, then we have no choice but to tear down the pipe.
+ this.close();
+ }
+ } else if (flags & codec.kMessageIsResponse) {
+ var reader = new codec.MessageReader(message);
+ var requestID = reader.requestID;
+ var responder = this.responders_[requestID];
+ delete this.responders_[requestID];
+ responder.accept(message);
+ } else {
+ if (this.incomingReceiver_)
+ this.incomingReceiver_.accept(message);
+ }
+ };
+
+ Router.prototype.handleConnectionError_ = function(result) {
+ for (var each in this.responders_)
+ this.responders_[each].reject(result);
+ this.close();
+ };
+
+ var exports = {};
+ exports.Router = Router;
+ return exports;
+});
diff --git a/chromium/mojo/public/js/bindings/support.js b/chromium/mojo/public/js/bindings/support.js
new file mode 100644
index 00000000000..58df6bd4d02
--- /dev/null
+++ b/chromium/mojo/public/js/bindings/support.js
@@ -0,0 +1,30 @@
+// Copyright 2014 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.
+
+// Module "mojo/public/js/bindings/support"
+//
+// Note: This file is for documentation purposes only. The code here is not
+// actually executed. The real module is implemented natively in Mojo.
+
+while (1);
+
+/*
+ * Waits on the given handle until the state indicated by |signals| is
+ * satisfied.
+ *
+ * @param {MojoHandle} handle The handle to wait on.
+ * @param {MojoHandleSignals} signals Specifies the condition to wait for.
+ * @param {function (mojoResult)} callback Called with the result the wait is
+ * complete. See MojoWait for possible result codes.
+ *
+ * @return {MojoWaitId} A waitId that can be passed to cancelWait to cancel the
+ * wait.
+ */
+function asyncWait(handle, signals, callback) { [native code] }
+
+/*
+ * Cancels the asyncWait operation specified by the given |waitId|.
+ * @param {MojoWaitId} waitId The waitId returned by asyncWait.
+ */
+function cancelWait(waitId) { [native code] }
diff --git a/chromium/mojo/public/js/bindings/unicode.js b/chromium/mojo/public/js/bindings/unicode.js
new file mode 100644
index 00000000000..ba0f00f95ba
--- /dev/null
+++ b/chromium/mojo/public/js/bindings/unicode.js
@@ -0,0 +1,51 @@
+// Copyright 2014 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.
+
+/**
+ * Defines functions for translating between JavaScript strings and UTF8 strings
+ * stored in ArrayBuffers. There is much room for optimization in this code if
+ * it proves necessary.
+ */
+define("mojo/public/js/bindings/unicode", function() {
+ /**
+ * Decodes the UTF8 string from the given buffer.
+ * @param {ArrayBufferView} buffer The buffer containing UTF8 string data.
+ * @return {string} The corresponding JavaScript string.
+ */
+ function decodeUtf8String(buffer) {
+ return decodeURIComponent(escape(String.fromCharCode.apply(null, buffer)));
+ }
+
+ /**
+ * Encodes the given JavaScript string into UTF8.
+ * @param {string} str The string to encode.
+ * @param {ArrayBufferView} outputBuffer The buffer to contain the result.
+ * Should be pre-allocated to hold enough space. Use |utf8Length| to determine
+ * how much space is required.
+ * @return {number} The number of bytes written to |outputBuffer|.
+ */
+ function encodeUtf8String(str, outputBuffer) {
+ var utf8String = unescape(encodeURIComponent(str));
+ if (outputBuffer.length < utf8String.length)
+ throw new Error("Buffer too small for encodeUtf8String");
+ for (var i = 0; i < outputBuffer.length && i < utf8String.length; i++)
+ outputBuffer[i] = utf8String.charCodeAt(i);
+ return i;
+ }
+
+ /**
+ * Returns the number of bytes that a UTF8 encoding of the JavaScript string
+ * |str| would occupy.
+ */
+ function utf8Length(str) {
+ var utf8String = unescape(encodeURIComponent(str));
+ return utf8String.length;
+ }
+
+ var exports = {};
+ exports.decodeUtf8String = decodeUtf8String;
+ exports.encodeUtf8String = encodeUtf8String;
+ exports.utf8Length = utf8Length;
+ return exports;
+});
diff --git a/chromium/mojo/public/platform/native/system_thunks.cc b/chromium/mojo/public/platform/native/system_thunks.cc
new file mode 100644
index 00000000000..6fdc4f4530f
--- /dev/null
+++ b/chromium/mojo/public/platform/native/system_thunks.cc
@@ -0,0 +1,170 @@
+// Copyright 2013 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 "mojo/public/platform/native/system_thunks.h"
+
+#include <assert.h>
+
+extern "C" {
+
+static MojoSystemThunks g_thunks = {0};
+
+MojoTimeTicks MojoGetTimeTicksNow() {
+ assert(g_thunks.GetTimeTicksNow);
+ return g_thunks.GetTimeTicksNow();
+}
+
+MojoResult MojoClose(MojoHandle handle) {
+ assert(g_thunks.Close);
+ return g_thunks.Close(handle);
+}
+
+MojoResult MojoWait(MojoHandle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline) {
+ assert(g_thunks.Wait);
+ return g_thunks.Wait(handle, signals, deadline);
+}
+
+MojoResult MojoWaitMany(const MojoHandle* handles,
+ const MojoHandleSignals* signals,
+ uint32_t num_handles,
+ MojoDeadline deadline) {
+ assert(g_thunks.WaitMany);
+ return g_thunks.WaitMany(handles, signals, num_handles, deadline);
+}
+
+MojoResult MojoCreateMessagePipe(const MojoCreateMessagePipeOptions* options,
+ MojoHandle* message_pipe_handle0,
+ MojoHandle* message_pipe_handle1) {
+ assert(g_thunks.CreateMessagePipe);
+ return g_thunks.CreateMessagePipe(options, message_pipe_handle0,
+ message_pipe_handle1);
+}
+
+MojoResult MojoWriteMessage(MojoHandle message_pipe_handle,
+ const void* bytes,
+ uint32_t num_bytes,
+ const MojoHandle* handles,
+ uint32_t num_handles,
+ MojoWriteMessageFlags flags) {
+ assert(g_thunks.WriteMessage);
+ return g_thunks.WriteMessage(message_pipe_handle, bytes, num_bytes, handles,
+ num_handles, flags);
+}
+
+MojoResult MojoReadMessage(MojoHandle message_pipe_handle,
+ void* bytes,
+ uint32_t* num_bytes,
+ MojoHandle* handles,
+ uint32_t* num_handles,
+ MojoReadMessageFlags flags) {
+ assert(g_thunks.ReadMessage);
+ return g_thunks.ReadMessage(message_pipe_handle, bytes, num_bytes, handles,
+ num_handles, flags);
+}
+
+MojoResult MojoCreateDataPipe(const MojoCreateDataPipeOptions* options,
+ MojoHandle* data_pipe_producer_handle,
+ MojoHandle* data_pipe_consumer_handle) {
+ assert(g_thunks.CreateDataPipe);
+ return g_thunks.CreateDataPipe(options, data_pipe_producer_handle,
+ data_pipe_consumer_handle);
+}
+
+MojoResult MojoWriteData(MojoHandle data_pipe_producer_handle,
+ const void* elements,
+ uint32_t* num_elements,
+ MojoWriteDataFlags flags) {
+ assert(g_thunks.WriteData);
+ return g_thunks.WriteData(data_pipe_producer_handle, elements, num_elements,
+ flags);
+}
+
+MojoResult MojoBeginWriteData(MojoHandle data_pipe_producer_handle,
+ void** buffer,
+ uint32_t* buffer_num_elements,
+ MojoWriteDataFlags flags) {
+ assert(g_thunks.BeginWriteData);
+ return g_thunks.BeginWriteData(data_pipe_producer_handle, buffer,
+ buffer_num_elements, flags);
+}
+
+MojoResult MojoEndWriteData(MojoHandle data_pipe_producer_handle,
+ uint32_t num_elements_written) {
+ assert(g_thunks.EndWriteData);
+ return g_thunks.EndWriteData(data_pipe_producer_handle, num_elements_written);
+}
+
+MojoResult MojoReadData(MojoHandle data_pipe_consumer_handle,
+ void* elements,
+ uint32_t* num_elements,
+ MojoReadDataFlags flags) {
+ assert(g_thunks.ReadData);
+ return g_thunks.ReadData(data_pipe_consumer_handle, elements, num_elements,
+ flags);
+}
+
+MojoResult MojoBeginReadData(MojoHandle data_pipe_consumer_handle,
+ const void** buffer,
+ uint32_t* buffer_num_elements,
+ MojoReadDataFlags flags) {
+ assert(g_thunks.BeginReadData);
+ return g_thunks.BeginReadData(data_pipe_consumer_handle, buffer,
+ buffer_num_elements, flags);
+}
+
+MojoResult MojoEndReadData(MojoHandle data_pipe_consumer_handle,
+ uint32_t num_elements_read) {
+ assert(g_thunks.EndReadData);
+ return g_thunks.EndReadData(data_pipe_consumer_handle, num_elements_read);
+}
+
+MojoResult MojoCreateSharedBuffer(
+ const struct MojoCreateSharedBufferOptions* options,
+ uint64_t num_bytes,
+ MojoHandle* shared_buffer_handle) {
+ assert(g_thunks.CreateSharedBuffer);
+ return g_thunks.CreateSharedBuffer(options, num_bytes, shared_buffer_handle);
+}
+
+MojoResult MojoDuplicateBufferHandle(
+ MojoHandle buffer_handle,
+ const struct MojoDuplicateBufferHandleOptions* options,
+ MojoHandle* new_buffer_handle) {
+ assert(g_thunks.DuplicateBufferHandle);
+ return g_thunks.DuplicateBufferHandle(buffer_handle, options,
+ new_buffer_handle);
+}
+
+MojoResult MojoMapBuffer(MojoHandle buffer_handle,
+ uint64_t offset,
+ uint64_t num_bytes,
+ void** buffer,
+ MojoMapBufferFlags flags) {
+ assert(g_thunks.MapBuffer);
+ return g_thunks.MapBuffer(buffer_handle, offset, num_bytes, buffer, flags);
+}
+
+MojoResult MojoUnmapBuffer(void* buffer) {
+ assert(g_thunks.UnmapBuffer);
+ return g_thunks.UnmapBuffer(buffer);
+}
+
+// Call this function by looking
+// Always export this api.
+#if defined(WIN32)
+#define THUNK_EXPORT __declspec(dllexport)
+#else
+#define THUNK_EXPORT __attribute__((visibility("default")))
+#endif
+
+extern "C" THUNK_EXPORT size_t MojoSetSystemThunks(
+ const MojoSystemThunks* system_thunks) {
+ if (system_thunks->size >= sizeof(g_thunks))
+ g_thunks = *system_thunks;
+ return sizeof(g_thunks);
+}
+
+} // extern "C"
diff --git a/chromium/mojo/public/platform/native/system_thunks.h b/chromium/mojo/public/platform/native/system_thunks.h
new file mode 100644
index 00000000000..de82eba1bfc
--- /dev/null
+++ b/chromium/mojo/public/platform/native/system_thunks.h
@@ -0,0 +1,137 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_THUNKS_H_
+#define MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_THUNKS_H_
+
+#include <stddef.h>
+
+#include "mojo/public/c/system/core.h"
+
+// The embedder needs to bind the basic Mojo Core functions of a DSO to those of
+// the embedder when loading a DSO that is dependent on mojo_system.
+// The typical usage would look like:
+// base::ScopedNativeLibrary app_library(
+// base::LoadNativeLibrary(app_path_, &error));
+// typedef MojoResult (*MojoSetSystemThunksFn)(MojoSystemThunks*);
+// MojoSetSystemThunksFn mojo_set_system_thunks_fn =
+// reinterpret_cast<MojoSetSystemThunksFn>(app_library.GetFunctionPointer(
+// "MojoSetSystemThunks"));
+// MojoSystemThunks system_thunks = MojoMakeSystemThunks();
+// size_t expected_size = mojo_set_system_thunks_fn(&system_thunks);
+// if (expected_size > sizeof(MojoSystemThunks)) {
+// LOG(ERROR)
+// << "Invalid DSO. Expected MojoSystemThunks size: "
+// << expected_size;
+// break;
+// }
+
+// Structure used to bind the basic Mojo Core functions of a DSO to those of
+// the embedder.
+// This is the ABI between the embedder and the DSO. It can only have new
+// functions added to the end. No other changes are supported.
+#pragma pack(push, 8)
+struct MojoSystemThunks {
+ size_t size; // Should be set to sizeof(MojoSystemThunks).
+ MojoTimeTicks (*GetTimeTicksNow)();
+ MojoResult (*Close)(MojoHandle handle);
+ MojoResult (*Wait)(MojoHandle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline);
+ MojoResult (*WaitMany)(const MojoHandle* handles,
+ const MojoHandleSignals* signals,
+ uint32_t num_handles,
+ MojoDeadline deadline);
+ MojoResult (*CreateMessagePipe)(const MojoCreateMessagePipeOptions* options,
+ MojoHandle* message_pipe_handle0,
+ MojoHandle* message_pipe_handle1);
+ MojoResult (*WriteMessage)(MojoHandle message_pipe_handle,
+ const void* bytes,
+ uint32_t num_bytes,
+ const MojoHandle* handles,
+ uint32_t num_handles,
+ MojoWriteMessageFlags flags);
+ MojoResult (*ReadMessage)(MojoHandle message_pipe_handle,
+ void* bytes,
+ uint32_t* num_bytes,
+ MojoHandle* handles,
+ uint32_t* num_handles,
+ MojoReadMessageFlags flags);
+ MojoResult (*CreateDataPipe)(const MojoCreateDataPipeOptions* options,
+ MojoHandle* data_pipe_producer_handle,
+ MojoHandle* data_pipe_consumer_handle);
+ MojoResult (*WriteData)(MojoHandle data_pipe_producer_handle,
+ const void* elements,
+ uint32_t* num_elements,
+ MojoWriteDataFlags flags);
+ MojoResult (*BeginWriteData)(MojoHandle data_pipe_producer_handle,
+ void** buffer,
+ uint32_t* buffer_num_elements,
+ MojoWriteDataFlags flags);
+ MojoResult (*EndWriteData)(MojoHandle data_pipe_producer_handle,
+ uint32_t num_elements_written);
+ MojoResult (*ReadData)(MojoHandle data_pipe_consumer_handle,
+ void* elements,
+ uint32_t* num_elements,
+ MojoReadDataFlags flags);
+ MojoResult (*BeginReadData)(MojoHandle data_pipe_consumer_handle,
+ const void** buffer,
+ uint32_t* buffer_num_elements,
+ MojoReadDataFlags flags);
+ MojoResult (*EndReadData)(MojoHandle data_pipe_consumer_handle,
+ uint32_t num_elements_read);
+ MojoResult (*CreateSharedBuffer)(
+ const MojoCreateSharedBufferOptions* options,
+ uint64_t num_bytes,
+ MojoHandle* shared_buffer_handle);
+ MojoResult (*DuplicateBufferHandle)(
+ MojoHandle buffer_handle,
+ const MojoDuplicateBufferHandleOptions* options,
+ MojoHandle* new_buffer_handle);
+ MojoResult (*MapBuffer)(MojoHandle buffer_handle,
+ uint64_t offset,
+ uint64_t num_bytes,
+ void** buffer,
+ MojoMapBufferFlags flags);
+ MojoResult (*UnmapBuffer)(void* buffer);
+};
+#pragma pack(pop)
+
+// Intended to be called from the embedder. Returns a |MojoCore| initialized
+// to contain pointers to each of the embedder's MojoCore functions.
+inline MojoSystemThunks MojoMakeSystemThunks() {
+ MojoSystemThunks system_thunks = {
+ sizeof(MojoSystemThunks),
+ MojoGetTimeTicksNow,
+ MojoClose,
+ MojoWait,
+ MojoWaitMany,
+ MojoCreateMessagePipe,
+ MojoWriteMessage,
+ MojoReadMessage,
+ MojoCreateDataPipe,
+ MojoWriteData,
+ MojoBeginWriteData,
+ MojoEndWriteData,
+ MojoReadData,
+ MojoBeginReadData,
+ MojoEndReadData,
+ MojoCreateSharedBuffer,
+ MojoDuplicateBufferHandle,
+ MojoMapBuffer,
+ MojoUnmapBuffer
+ };
+ return system_thunks;
+}
+
+// Use this type for the function found by dynamically discovering it in
+// a DSO linked with mojo_system. For example:
+// MojoSetSystemThunksFn mojo_set_system_thunks_fn =
+// reinterpret_cast<MojoSetSystemThunksFn>(app_library.GetFunctionPointer(
+// "MojoSetSystemThunks"));
+// The expected size of |system_thunks} is returned.
+// The contents of |system_thunks| are copied.
+typedef size_t (*MojoSetSystemThunksFn)(const MojoSystemThunks* system_thunks);
+
+#endif // MOJO_PUBLIC_PLATFORM_NATIVE_SYSTEM_THUNKS_H_
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/enum_declaration.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/enum_declaration.tmpl
new file mode 100644
index 00000000000..bc2a097b098
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/enum_declaration.tmpl
@@ -0,0 +1,9 @@
+enum {{enum.name}} {
+{%- for field in enum.fields %}
+{%- if field.value %}
+ {{field.name}} = {{field.value|expression_to_text}},
+{%- else %}
+ {{field.name}},
+{%- endif %}
+{%- endfor %}
+};
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl
new file mode 100644
index 00000000000..30d20d8993e
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl
@@ -0,0 +1,49 @@
+{%- import "interface_macros.tmpl" as interface_macros %}
+class {{interface.name}}Proxy;
+class {{interface.name}}Stub;
+
+class {{interface.name}}RequestValidator;
+{%- if interface|has_callbacks %}
+class {{interface.name}}ResponseValidator;
+{%- endif %}
+{% if interface.client %}
+class {{interface.client}};
+{% endif %}
+
+class {{interface.name}} {
+ public:
+ static const char* Name_;
+
+ typedef {{interface.name}}Proxy Proxy_;
+ typedef {{interface.name}}Stub Stub_;
+
+ typedef {{interface.name}}RequestValidator RequestValidator_;
+{%- if interface|has_callbacks %}
+ typedef {{interface.name}}ResponseValidator ResponseValidator_;
+{%- else %}
+ typedef mojo::PassThroughFilter ResponseValidator_;
+{%- endif %}
+{% if interface.client %}
+ typedef {{interface.client}} Client;
+{% else %}
+ typedef mojo::NoInterface Client;
+{% endif %}
+
+{#--- Constants #}
+{%- for constant in interface.constants %}
+ static const {{constant.kind|cpp_pod_type}} {{constant.name}};
+{%- endfor %}
+
+{#--- Enums #}
+{%- for enum in interface.enums %}
+{% macro enum_def() %}{% include "enum_declaration.tmpl" %}{% endmacro %}
+ {{enum_def()|indent(2)}}
+{%- endfor %}
+
+{#--- Methods #}
+ virtual ~{{interface.name}}() {}
+
+{%- for method in interface.methods %}
+ virtual void {{method.name}}({{interface_macros.declare_request_params("", method)}}) = 0;
+{%- endfor %}
+};
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
new file mode 100644
index 00000000000..9b6a3249e55
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
@@ -0,0 +1,315 @@
+{%- import "interface_macros.tmpl" as interface_macros %}
+{%- set class_name = interface.name %}
+{%- set proxy_name = interface.name ~ "Proxy" %}
+{%- set namespace_as_string = "%s"|format(namespace|replace(".","::")) %}
+
+{%- macro alloc_params(parameters) %}
+{%- for param in parameters %}
+{%- if param.kind|is_object_kind %}
+{{param.kind|cpp_result_type}} p{{loop.index}};
+Deserialize_(params->{{param.name}}.ptr, &p{{loop.index}});
+{% endif -%}
+{%- endfor %}
+{%- endmacro %}
+
+{%- macro pass_params(parameters) %}
+{%- for param in parameters %}
+{%- if param.kind|is_string_kind -%}
+p{{loop.index}}
+{%- elif param.kind|is_object_kind -%}
+p{{loop.index}}.Pass()
+{%- elif param.kind|is_interface_kind -%}
+mojo::MakeProxy<{{param.kind|get_name_for_kind}}>(mojo::MakeScopedHandle(mojo::internal::FetchAndReset(&params->{{param.name}})))
+{%- elif param.kind|is_interface_request_kind -%}
+mojo::MakeRequest<{{param.kind.kind|get_name_for_kind}}>(mojo::MakeScopedHandle(mojo::internal::FetchAndReset(&params->{{param.name}})))
+{%- elif param.kind|is_handle_kind -%}
+mojo::MakeScopedHandle(mojo::internal::FetchAndReset(&params->{{param.name}}))
+{%- elif param.kind|is_enum_kind -%}
+static_cast<{{param.kind|cpp_wrapper_type}}>(params->{{param.name}})
+{%- else -%}
+params->{{param.name}}
+{%- endif -%}
+{%- if not loop.last %}, {% endif %}
+{%- endfor %}
+{%- endmacro %}
+
+{%- macro compute_payload_size(params_name, parameters) -%}
+ size_t payload_size =
+ mojo::internal::Align(sizeof({{params_name}}));
+{#--- Computes #}
+{%- for param in parameters %}
+{%- if param.kind|is_object_kind %}
+ payload_size += GetSerializedSize_(in_{{param.name}});
+{%- endif %}
+{%- endfor %}
+{%- endmacro %}
+
+{%- macro build_message(params_name, parameters) -%}
+ {{params_name}}* params =
+ {{params_name}}::New(builder.buffer());
+{#--- Sets #}
+{% for param in parameters %}
+{%- if param.kind|is_object_kind %}
+ Serialize_(mojo::internal::Forward(in_{{param.name}}), builder.buffer(), &params->{{param.name}}.ptr);
+{%- elif param.kind|is_interface_kind %}
+ if (!in_{{param.name}}.get()) {
+ params->{{param.name}} = mojo::MessagePipeHandle();
+ } else {
+ // Delegate handle.
+ params->{{param.name}} = in_{{param.name}}.PassMessagePipe().release();
+ }
+{%- elif param.kind|is_interface_request_kind %}
+ // Delegate handle.
+ params->{{param.name}} = in_{{param.name}}.PassMessagePipe().release();
+{%- elif param.kind|is_handle_kind %}
+ params->{{param.name}} = in_{{param.name}}.release();
+{%- else %}
+ params->{{param.name}} = in_{{param.name}};
+{%- endif %}
+{%- endfor %}
+ mojo::Message message;
+ params->EncodePointersAndHandles(message.mutable_handles());
+ builder.Finish(&message);
+{%- endmacro %}
+
+{#--- Begin #}
+const char* {{class_name}}::Name_ = "{{namespace_as_string}}::{{class_name}}";
+{#--- Constants #}
+{% for constant in interface.constants %}
+const {{constant.kind|cpp_pod_type}} {{interface.name}}::{{constant.name}} = {{constant.value|expression_to_text}};
+{%- endfor %}
+
+{#--- ForwardToCallback definition #}
+{%- for method in interface.methods -%}
+{%- if method.response_parameters != None %}
+class {{class_name}}_{{method.name}}_ForwardToCallback
+ : public mojo::MessageReceiver {
+ public:
+ {{class_name}}_{{method.name}}_ForwardToCallback(
+ const {{interface_macros.declare_callback(method)}}& callback)
+ : callback_(callback) {
+ }
+ virtual bool Accept(mojo::Message* message) MOJO_OVERRIDE;
+ private:
+ {{interface_macros.declare_callback(method)}} callback_;
+ MOJO_DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ForwardToCallback);
+};
+bool {{class_name}}_{{method.name}}_ForwardToCallback::Accept(
+ mojo::Message* message) {
+ internal::{{class_name}}_{{method.name}}_ResponseParams_Data* params =
+ reinterpret_cast<internal::{{class_name}}_{{method.name}}_ResponseParams_Data*>(
+ message->mutable_payload());
+
+ params->DecodePointersAndHandles(message->mutable_handles());
+ {{alloc_params(method.response_parameters)|indent(2)}}
+ callback_.Run({{pass_params(method.response_parameters)}});
+ return true;
+}
+{%- endif %}
+{%- endfor %}
+
+{{proxy_name}}::{{proxy_name}}(mojo::MessageReceiverWithResponder* receiver)
+ : receiver_(receiver) {
+}
+
+{#--- Proxy definitions #}
+
+{%- for method in interface.methods %}
+{%- set message_name =
+ "internal::k%s_%s_Name"|format(interface.name, method.name) %}
+{%- set params_name =
+ "internal::%s_%s_Params_Data"|format(interface.name, method.name) %}
+void {{proxy_name}}::{{method.name}}(
+ {{interface_macros.declare_request_params("in_", method)}}) {
+ {{compute_payload_size(params_name, method.parameters)}}
+
+{%- if method.response_parameters != None %}
+ mojo::internal::RequestMessageBuilder builder({{message_name}}, payload_size);
+{%- else %}
+ mojo::internal::MessageBuilder builder({{message_name}}, payload_size);
+{%- endif %}
+
+ {{build_message(params_name, method.parameters)}}
+
+{%- if method.response_parameters != None %}
+ mojo::MessageReceiver* responder =
+ new {{class_name}}_{{method.name}}_ForwardToCallback(callback);
+ if (!receiver_->AcceptWithResponder(&message, responder))
+ delete responder;
+{%- else %}
+ bool ok MOJO_ALLOW_UNUSED = receiver_->Accept(&message);
+ // This return value may be ignored as !ok implies the Connector has
+ // encountered an error, which will be visible through other means.
+{%- endif %}
+}
+{%- endfor %}
+
+{#--- ProxyToResponder definition #}
+{%- for method in interface.methods -%}
+{%- if method.response_parameters != None %}
+{%- set message_name =
+ "internal::k%s_%s_Name"|format(interface.name, method.name) %}
+{%- set params_name =
+ "internal::%s_%s_ResponseParams_Data"|format(interface.name, method.name) %}
+class {{class_name}}_{{method.name}}_ProxyToResponder
+ : public {{interface_macros.declare_callback(method)}}::Runnable {
+ public:
+ virtual ~{{class_name}}_{{method.name}}_ProxyToResponder() {
+ delete responder_;
+ }
+
+ {{class_name}}_{{method.name}}_ProxyToResponder(
+ uint64_t request_id,
+ mojo::MessageReceiver* responder)
+ : request_id_(request_id),
+ responder_(responder) {
+ }
+
+ virtual void Run({{interface_macros.declare_params("in_", method.response_parameters)}}) const;
+
+ private:
+ uint64_t request_id_;
+ mutable mojo::MessageReceiver* responder_;
+ MOJO_DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ProxyToResponder);
+};
+void {{class_name}}_{{method.name}}_ProxyToResponder::Run(
+ {{interface_macros.declare_params("in_", method.response_parameters)}}) const {
+ {{compute_payload_size(params_name, method.response_parameters)}}
+ mojo::internal::ResponseMessageBuilder builder(
+ {{message_name}}, payload_size, request_id_);
+ {{build_message(params_name, method.response_parameters)}}
+ bool ok MOJO_ALLOW_UNUSED = responder_->Accept(&message);
+ // TODO(darin): !ok returned here indicates a malformed message, and that may
+ // be good reason to close the connection. However, we don't have a way to do
+ // that from here. We should add a way.
+ delete responder_;
+ responder_ = NULL;
+}
+{%- endif -%}
+{%- endfor %}
+
+{{class_name}}Stub::{{class_name}}Stub()
+ : sink_(NULL) {
+}
+
+{#--- Stub definition #}
+
+bool {{class_name}}Stub::Accept(mojo::Message* message) {
+{%- if interface.methods %}
+ switch (message->header()->name) {
+{%- for method in interface.methods %}
+ case internal::k{{class_name}}_{{method.name}}_Name: {
+{%- if method.response_parameters == None %}
+ internal::{{class_name}}_{{method.name}}_Params_Data* params =
+ reinterpret_cast<internal::{{class_name}}_{{method.name}}_Params_Data*>(
+ message->mutable_payload());
+
+ params->DecodePointersAndHandles(message->mutable_handles());
+ {{alloc_params(method.parameters)|indent(6)}}
+ sink_->{{method.name}}({{pass_params(method.parameters)}});
+ return true;
+{%- else %}
+ break;
+{%- endif %}
+ }
+{%- endfor %}
+ }
+{%- endif %}
+ return false;
+}
+
+bool {{class_name}}Stub::AcceptWithResponder(
+ mojo::Message* message, mojo::MessageReceiver* responder) {
+{%- if interface.methods %}
+ switch (message->header()->name) {
+{%- for method in interface.methods %}
+ case internal::k{{class_name}}_{{method.name}}_Name: {
+{%- if method.response_parameters != None %}
+ internal::{{class_name}}_{{method.name}}_Params_Data* params =
+ reinterpret_cast<internal::{{class_name}}_{{method.name}}_Params_Data*>(
+ message->mutable_payload());
+
+ params->DecodePointersAndHandles(message->mutable_handles());
+ {{interface_macros.declare_callback(method)}}::Runnable* runnable =
+ new {{class_name}}_{{method.name}}_ProxyToResponder(
+ message->request_id(), responder);
+ {{interface_macros.declare_callback(method)}} callback(runnable);
+ {{alloc_params(method.parameters)|indent(6)}}
+ sink_->{{method.name}}(
+{%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}callback);
+ return true;
+{%- else %}
+ break;
+{%- endif %}
+ }
+{%- endfor %}
+ }
+{%- endif %}
+ return false;
+}
+
+{#--- Request validator definitions #}
+
+{{class_name}}RequestValidator::{{class_name}}RequestValidator(
+ mojo::MessageReceiver* sink) : MessageFilter(sink) {
+}
+
+bool {{class_name}}RequestValidator::Accept(mojo::Message* message) {
+{%- if interface.methods %}
+ switch (message->header()->name) {
+{%- for method in interface.methods %}
+ case internal::k{{class_name}}_{{method.name}}_Name: {
+{%- if method.response_parameters != None %}
+ if (!message->has_flag(mojo::internal::kMessageExpectsResponse))
+ break;
+{%- else %}
+ if (message->has_flag(mojo::internal::kMessageExpectsResponse) ||
+ message->has_flag(mojo::internal::kMessageIsResponse)) {
+ break;
+ }
+{%- endif %}
+ mojo::internal::BoundsChecker bounds_checker(
+ message->payload(), message->payload_num_bytes(),
+ message->handles()->size());
+ if (!internal::{{class_name}}_{{method.name}}_Params_Data::Validate(
+ message->payload(), &bounds_checker)) {
+ return false;
+ }
+ break;
+ }
+{%- endfor %}
+ }
+{%- endif %}
+
+ return sink_->Accept(message);
+}
+
+{#--- Response validator definitions #}
+{% if interface|has_callbacks %}
+{{class_name}}ResponseValidator::{{class_name}}ResponseValidator(
+ mojo::MessageReceiver* sink) : MessageFilter(sink) {
+}
+
+bool {{class_name}}ResponseValidator::Accept(mojo::Message* message) {
+{%- if interface.methods %}
+ switch (message->header()->name) {
+{%- for method in interface.methods if method.response_parameters != None %}
+ case internal::k{{class_name}}_{{method.name}}_Name: {
+ if (!message->has_flag(mojo::internal::kMessageIsResponse))
+ break;
+ mojo::internal::BoundsChecker bounds_checker(
+ message->payload(), message->payload_num_bytes(),
+ message->handles()->size());
+ if (!internal::{{class_name}}_{{method.name}}_ResponseParams_Data::Validate(
+ message->payload(), &bounds_checker)) {
+ return false;
+ }
+ break;
+ }
+{%- endfor %}
+ }
+{%- endif %}
+
+ return sink_->Accept(message);
+}
+{%- endif -%}
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl
new file mode 100644
index 00000000000..fbefce2d397
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl
@@ -0,0 +1,23 @@
+{%- macro declare_params(prefix, parameters) %}
+{%- for param in parameters -%}
+{{param.kind|cpp_const_wrapper_type}} {{prefix}}{{param.name}}
+{%- if not loop.last %}, {% endif %}
+{%- endfor %}
+{%- endmacro %}
+
+{%- macro declare_callback(method) -%}
+mojo::Callback<void(
+{%- for param in method.response_parameters -%}
+{{param.kind|cpp_result_type}}
+{%- if not loop.last %}, {% endif %}
+{%- endfor -%}
+)>
+{%- endmacro -%}
+
+{%- macro declare_request_params(prefix, method) -%}
+{{declare_params(prefix, method.parameters)}}
+{%- if method.response_parameters != None -%}
+{%- if method.parameters %}, {% endif %}
+const {{declare_callback(method)}}& callback
+{%- endif -%}
+{%- endmacro -%}
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl
new file mode 100644
index 00000000000..9451118a438
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl
@@ -0,0 +1,14 @@
+{%- import "interface_macros.tmpl" as interface_macros %}
+class {{interface.name}}Proxy : public {{interface.name}} {
+ public:
+ explicit {{interface.name}}Proxy(mojo::MessageReceiverWithResponder* receiver);
+
+{%- for method in interface.methods %}
+ virtual void {{method.name}}(
+ {{interface_macros.declare_request_params("", method)}}
+ ) MOJO_OVERRIDE;
+{%- endfor %}
+
+ private:
+ mojo::MessageReceiverWithResponder* receiver_;
+};
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl
new file mode 100644
index 00000000000..63c60ee2db8
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl
@@ -0,0 +1,6 @@
+class {{interface.name}}RequestValidator : public mojo::MessageFilter {
+ public:
+ explicit {{interface.name}}RequestValidator(mojo::MessageReceiver* sink = NULL);
+
+ virtual bool Accept(mojo::Message* message) MOJO_OVERRIDE;
+};
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl
new file mode 100644
index 00000000000..0719060c155
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl
@@ -0,0 +1,6 @@
+class {{interface.name}}ResponseValidator : public mojo::MessageFilter {
+ public:
+ explicit {{interface.name}}ResponseValidator(mojo::MessageReceiver* sink = NULL);
+
+ virtual bool Accept(mojo::Message* message) MOJO_OVERRIDE;
+};
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl
new file mode 100644
index 00000000000..25b28ec777a
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl
@@ -0,0 +1,14 @@
+class {{interface.name}}Stub : public mojo::MessageReceiverWithResponder {
+ public:
+ {{interface.name}}Stub();
+ void set_sink({{interface.name}}* sink) { sink_ = sink; }
+ {{interface.name}}* sink() { return sink_; }
+
+ virtual bool Accept(mojo::Message* message) MOJO_OVERRIDE;
+ virtual bool AcceptWithResponder(mojo::Message* message,
+ mojo::MessageReceiver* responder)
+ MOJO_OVERRIDE;
+
+ private:
+ {{interface.name}}* sink_;
+};
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl
new file mode 100644
index 00000000000..f0cf33b560b
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl
@@ -0,0 +1,49 @@
+// Copyright 2013 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.
+
+{%- set header_guard = "%s_INTERNAL_H_"|
+ format(module.path|upper|replace("/","_")|replace(".","_")) %}
+
+#ifndef {{header_guard}}
+#define {{header_guard}}
+
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/buffer.h"
+
+{%- for import in imports %}
+#include "{{import.module.path}}-internal.h"
+{%- endfor %}
+
+namespace mojo {
+namespace internal {
+class BoundsChecker;
+}
+}
+
+{%- for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%- endfor %}
+
+{#--- Wrapper forward declarations #}
+{% for struct in structs %}
+class {{struct.name}};
+{%- endfor %}
+
+namespace internal {
+
+#pragma pack(push, 1)
+
+{#--- Class declarations #}
+{% for struct in structs %}
+{% include "struct_declaration.tmpl" %}
+{%- endfor %}
+
+#pragma pack(pop)
+
+} // namespace internal
+{%- for namespace in namespaces_as_array|reverse %}
+} // namespace {{namespace}}
+{%- endfor %}
+
+#endif // {{header_guard}}
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
new file mode 100644
index 00000000000..8cf8e99713c
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
@@ -0,0 +1,86 @@
+// Copyright 2013 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.
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-private-field"
+#endif
+
+#include "{{module.path}}.h"
+
+#include "mojo/public/cpp/bindings/lib/array_serialization.h"
+#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
+#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/lib/string_serialization.h"
+#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+
+{%- for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%- endfor %}
+
+{#--- Constants #}
+{% for constant in module.constants %}
+const {{constant.kind|cpp_pod_type}} {{constant.name}} = {{constant.value|expression_to_text}};
+{%- endfor %}
+
+namespace internal {
+namespace {
+
+#pragma pack(push, 1)
+
+{#--- Interface parameter definitions #}
+{%- for interface in interfaces %}
+{%- for method in interface.methods %}
+{%- set method_name = "k%s_%s_Name"|format(interface.name, method.name) %}
+const uint32_t {{method_name}} = {{method.ordinal}};
+{% set struct = method|struct_from_method %}
+{%- include "params_definition.tmpl" %}
+{%- if method.response_parameters != None %}
+{%- set struct = method|response_struct_from_method %}
+{%- include "params_definition.tmpl" %}
+{%- endif %}
+{%- endfor %}
+{%- endfor %}
+
+#pragma pack(pop)
+
+} // namespace
+
+{#--- Struct definitions #}
+{% for struct in structs %}
+{%- include "struct_definition.tmpl" %}
+{%- endfor %}
+
+} // namespace internal
+
+{#--- Struct Constants #}
+{%- for struct in structs %}
+{% for constant in struct.constants %}
+const {{constant.kind|cpp_pod_type}} {{struct.name}}::{{constant.name}} = {{constant.value|expression_to_text}};
+{%- endfor %}
+{%- endfor %}
+
+{#--- Struct builder definitions #}
+{%- for struct in structs %}
+{%- include "wrapper_class_definition.tmpl" %}
+{%- endfor %}
+
+{#--- Interface definitions #}
+{%- for interface in interfaces %}
+{%- include "interface_definition.tmpl" %}
+{%- endfor %}
+
+{#--- Struct Serialization Helpers #}
+{%- for struct in structs %}
+{%- include "struct_serialization_definition.tmpl" %}
+{%- endfor %}
+
+{%- for namespace in namespaces_as_array|reverse %}
+} // namespace {{namespace}}
+{%- endfor %}
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
new file mode 100644
index 00000000000..4e21d4774bd
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
@@ -0,0 +1,108 @@
+// Copyright 2013 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.
+
+{%- set header_guard = "%s_H_"|
+ format(module.path|upper|replace("/","_")|replace(".","_")) %}
+
+#ifndef {{header_guard}}
+#define {{header_guard}}
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/callback.h"
+#include "mojo/public/cpp/bindings/interface_impl.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+#include "mojo/public/cpp/bindings/no_interface.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/bindings/struct_ptr.h"
+#include "{{module.path}}-internal.h"
+{%- for import in imports %}
+#include "{{import.module.path}}.h"
+{%- endfor %}
+
+{%- for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%- endfor %}
+
+{#--- Constants #}
+{% for constant in module.constants %}
+extern const {{constant.kind|cpp_pod_type}} {{constant.name}};
+{%- endfor %}
+
+{#--- Enums #}
+{% for enum in enums %}
+{% include "enum_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Interface Forward Declarations -#}
+{% for interface in interfaces %}
+class {{interface.name}};
+typedef mojo::InterfacePtr<{{interface.name}}> {{interface.name}}Ptr;
+{% endfor %}
+
+{#--- Struct Forward Declarations -#}
+{% for struct in structs %}
+class {{struct.name}};
+{% if struct|should_inline %}
+typedef mojo::InlinedStructPtr<{{struct.name}}> {{struct.name}}Ptr;
+{% else %}
+typedef mojo::StructPtr<{{struct.name}}> {{struct.name}}Ptr;
+{% endif %}
+{% endfor %}
+
+{#--- NOTE: Non-inlined structs may have pointers to inlined structs, so we #}
+{#--- need to fully define inlined structs ahead of the others. #}
+
+{#--- Inlined structs #}
+{% for struct in structs %}
+{% if struct|should_inline %}
+{% include "wrapper_class_declaration.tmpl" %}
+{% endif %}
+{%- endfor %}
+
+{#--- Non-inlined structs #}
+{% for struct in structs %}
+{% if not struct|should_inline %}
+{% include "wrapper_class_declaration.tmpl" %}
+{% endif %}
+{%- endfor %}
+
+{#--- Interfaces -#}
+{% for interface in interfaces %}
+{% include "interface_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Interface Proxies -#}
+{% for interface in interfaces %}
+{% include "interface_proxy_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Interface Stubs -#}
+{% for interface in interfaces %}
+{% include "interface_stub_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Interface Request Validators -#}
+{% for interface in interfaces %}
+{% include "interface_request_validator_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Interface Response Validators -#}
+{% for interface in interfaces if interface|has_callbacks %}
+{% include "interface_response_validator_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Struct Serialization Helpers -#}
+{% if structs %}
+{% for struct in structs %}
+{% include "struct_serialization_declaration.tmpl" %}
+{%- endfor %}
+{%- endif %}
+
+{%- for namespace in namespaces_as_array|reverse %}
+} // namespace {{namespace}}
+{%- endfor %}
+
+#endif // {{header_guard}}
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/params_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/params_definition.tmpl
new file mode 100644
index 00000000000..0b1104719bb
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/params_definition.tmpl
@@ -0,0 +1,33 @@
+{%- import "struct_macros.tmpl" as struct_macros %}
+{%- set class_name = struct.name ~ "_Data" %}
+class {{class_name}} {
+ public:
+ static {{class_name}}* New(mojo::internal::Buffer* buf) {
+ return new (buf->Allocate(sizeof({{class_name}})))
+ {{class_name}}();
+ }
+
+ static bool Validate(const void* data,
+ mojo::internal::BoundsChecker* bounds_checker) {
+ {{ struct_macros.validate(struct, class_name)|indent(4) }}
+ }
+
+ mojo::internal::StructHeader header_;
+{{struct_macros.fields(struct)}}
+
+ void EncodePointersAndHandles(std::vector<mojo::Handle>* handles) {
+ {{ struct_macros.encodes(struct)|indent(4) }}
+ }
+
+ void DecodePointersAndHandles(std::vector<mojo::Handle>* handles) {
+ {{ struct_macros.decodes(struct)|indent(4) }}
+ }
+
+ private:
+ {{class_name}}() {
+ header_.num_bytes = sizeof(*this);
+ header_.num_fields = {{struct.packed.packed_fields|length}};
+ }
+};
+MOJO_COMPILE_ASSERT(sizeof({{class_name}}) == {{struct.packed|struct_size}},
+ bad_sizeof_{{class_name}});
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl
new file mode 100644
index 00000000000..60a6a9e6d3e
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl
@@ -0,0 +1,22 @@
+{%- import "struct_macros.tmpl" as struct_macros %}
+{%- set class_name = struct.name ~ "_Data" -%}
+
+class {{class_name}} {
+ public:
+ static {{class_name}}* New(mojo::internal::Buffer* buf);
+
+ static bool Validate(const void* data,
+ mojo::internal::BoundsChecker* bounds_checker);
+
+ mojo::internal::StructHeader header_;
+{{struct_macros.fields(struct)}}
+
+ void EncodePointersAndHandles(std::vector<mojo::Handle>* handles);
+ void DecodePointersAndHandles(std::vector<mojo::Handle>* handles);
+
+ private:
+ {{class_name}}();
+ ~{{class_name}}(); // NOT IMPLEMENTED
+};
+MOJO_COMPILE_ASSERT(sizeof({{class_name}}) == {{struct.packed|struct_size}},
+ bad_sizeof_{{class_name}});
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl
new file mode 100644
index 00000000000..461f158602b
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl
@@ -0,0 +1,28 @@
+{%- import "struct_macros.tmpl" as struct_macros %}
+{%- set class_name = struct.name ~ "_Data" %}
+
+// static
+{{class_name}}* {{class_name}}::New(mojo::internal::Buffer* buf) {
+ return new (buf->Allocate(sizeof({{class_name}}))) {{class_name}}();
+}
+
+// static
+bool {{class_name}}::Validate(const void* data,
+ mojo::internal::BoundsChecker* bounds_checker) {
+ {{ struct_macros.validate(struct, class_name)|indent(2) }}
+}
+
+{{class_name}}::{{class_name}}() {
+ header_.num_bytes = sizeof(*this);
+ header_.num_fields = {{struct.packed.packed_fields|length}};
+}
+
+void {{class_name}}::EncodePointersAndHandles(
+ std::vector<mojo::Handle>* handles) {
+ {{ struct_macros.encodes(struct)|indent(2) }}
+}
+
+void {{class_name}}::DecodePointersAndHandles(
+ std::vector<mojo::Handle>* handles) {
+ {{ struct_macros.decodes(struct)|indent(2) }}
+}
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl
new file mode 100644
index 00000000000..bd05a06de72
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl
@@ -0,0 +1,91 @@
+{%- macro validate(struct, class_name) %}
+ if (!data)
+ return true;
+
+ if (!ValidateStructHeader(
+ data, sizeof({{class_name}}),
+ {{struct.packed.packed_fields|length}}, bounds_checker)) {
+ return false;
+ }
+
+ const {{class_name}}* MOJO_ALLOW_UNUSED object =
+ static_cast<const {{class_name}}*>(data);
+
+{%- for packed_field in struct.packed.packed_fields %}
+{%- set name = packed_field.field.name %}
+{%- if packed_field.field.kind|is_object_kind %}
+{%- set wrapper_type = packed_field.field.kind|cpp_wrapper_type %}
+ if (!mojo::internal::ValidateEncodedPointer(&object->{{name}}.offset)) {
+ ReportValidationError(mojo::internal::VALIDATION_ERROR_ILLEGAL_POINTER);
+ return false;
+ }
+ if (!{{wrapper_type}}::Data_::Validate(
+ mojo::internal::DecodePointerRaw(&object->{{name}}.offset),
+ bounds_checker)) {
+ return false;
+ }
+{%- elif packed_field.field.kind|is_handle_kind %}
+ if (!bounds_checker->ClaimHandle(object->{{name}})) {
+ ReportValidationError(mojo::internal::VALIDATION_ERROR_ILLEGAL_HANDLE);
+ return false;
+ }
+{%- endif %}
+{%- endfor %}
+
+ return true;
+{%- endmacro %}
+
+{%- macro field_line(field) %}
+{%- set type = field.kind|cpp_field_type %}
+{%- set name = field.name -%}
+{%- if field.kind.spec == 'b' -%}
+ uint8_t {{name}} : 1;
+{%- elif field.kind|is_enum_kind -%}
+ int32_t {{name}};
+{%- else -%}
+ {{type}} {{name}};
+{%- endif %}
+{%- endmacro %}
+
+{%- macro fields(struct) %}
+{%- for packed_field in struct.packed.packed_fields %}
+ {{field_line(packed_field.field)}}
+{%- if not loop.last %}
+{%- set next_pf = struct.packed.packed_fields[loop.index0 + 1] %}
+{%- set pad = next_pf.offset - (packed_field.offset + packed_field.size) %}
+{%- if pad > 0 %}
+ uint8_t pad{{loop.index0}}_[{{pad}}];
+{%- endif %}
+{%- endif %}
+{%- endfor -%}
+
+{%- set num_fields = struct.packed.packed_fields|length %}
+{%- if num_fields > 0 %}
+{%- set last_field = struct.packed.packed_fields[num_fields - 1] %}
+{%- set offset = last_field.offset + last_field.size %}
+{%- set pad = offset|get_pad(8) -%}
+{%- if pad > 0 %}
+ uint8_t padfinal_[{{pad}}];
+{%- endif %}
+{%- endif %}
+{%- endmacro %}
+
+{%- macro encodes(struct) -%}
+{%- for pf in struct.packed.packed_fields %}
+{%- if pf.field.kind|is_object_kind %}
+mojo::internal::Encode(&{{pf.field.name}}, handles);
+{%- elif pf.field.kind|is_handle_kind %}
+mojo::internal::EncodeHandle(&{{pf.field.name}}, handles);
+{%- endif %}
+{%- endfor %}
+{%- endmacro -%}
+
+{%- macro decodes(struct) -%}
+{%- for pf in struct.packed.packed_fields %}
+{%- if pf.field.kind|is_object_kind %}
+mojo::internal::Decode(&{{pf.field.name}}, handles);
+{%- elif pf.field.kind|is_handle_kind %}
+mojo::internal::DecodeHandle(&{{pf.field.name}}, handles);
+{%- endif %}
+{%- endfor %}
+{%- endmacro -%}
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl
new file mode 100644
index 00000000000..604be86c7e2
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl
@@ -0,0 +1,5 @@
+size_t GetSerializedSize_(const {{struct.name}}Ptr& input);
+void Serialize_({{struct.name}}Ptr input, mojo::internal::Buffer* buffer,
+ internal::{{struct.name}}_Data** output);
+void Deserialize_(internal::{{struct.name}}_Data* input,
+ {{struct.name}}Ptr* output);
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl
new file mode 100644
index 00000000000..aec2afa93e5
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl
@@ -0,0 +1,51 @@
+size_t GetSerializedSize_(const {{struct.name}}Ptr& input) {
+ if (!input)
+ return 0;
+ size_t size = sizeof(internal::{{struct.name}}_Data);
+{%- for pf in struct.packed.packed_fields if pf.field.kind|is_object_kind %}
+ size += GetSerializedSize_(input->{{pf.field.name}});
+{%- endfor %}
+ return size;
+}
+
+void Serialize_({{struct.name}}Ptr input, mojo::internal::Buffer* buf,
+ internal::{{struct.name}}_Data** output) {
+ if (input) {
+ internal::{{struct.name}}_Data* result =
+ internal::{{struct.name}}_Data::New(buf);
+{%- for pf in struct.packed.packed_fields %}
+{%- if pf.field.kind|is_object_kind %}
+ Serialize_(mojo::internal::Forward(input->{{pf.field.name}}), buf, &result->{{pf.field.name}}.ptr);
+{%- elif pf.field.kind|is_handle_kind %}
+ result->{{pf.field.name}} = input->{{pf.field.name}}.release();
+{%- else %}
+ result->{{pf.field.name}} = input->{{pf.field.name}};
+{%- endif %}
+{%- endfor %}
+ *output = result;
+ } else {
+ *output = NULL;
+ }
+}
+
+void Deserialize_(internal::{{struct.name}}_Data* input,
+ {{struct.name}}Ptr* output) {
+ if (input) {
+ {{struct.name}}Ptr result({{struct.name}}::New());
+{%- for pf in struct.packed.packed_fields %}
+{%- if pf.field.kind|is_object_kind %}
+ Deserialize_(input->{{pf.field.name}}.ptr, &result->{{pf.field.name}});
+{%- elif pf.field.kind|is_handle_kind %}
+ result->{{pf.field.name}}.reset(mojo::internal::FetchAndReset(&input->{{pf.field.name}}));
+{%- elif pf.field.kind|is_enum_kind %}
+ result->{{pf.field.name}} = static_cast<{{pf.field.kind|cpp_wrapper_type}}>(
+ input->{{pf.field.name}});
+{%- else %}
+ result->{{pf.field.name}} = input->{{pf.field.name}};
+{%- endif %}
+{%- endfor %}
+ *output = result.Pass();
+ } else {
+ output->reset();
+ }
+}
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl
new file mode 100644
index 00000000000..0e01976ce61
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl
@@ -0,0 +1,31 @@
+
+class {{struct.name}} {
+ public:
+ typedef internal::{{struct.name}}_Data Data_;
+
+{#--- Constants #}
+{%- for constant in struct.constants %}
+ static const {{constant.kind|cpp_pod_type}} {{constant.name}};
+{%- endfor %}
+{#--- Enums #}
+{%- for enum in struct.enums -%}
+{% macro enum_def() %}{% include "enum_declaration.tmpl" %}{% endmacro %}
+ {{enum_def()|indent(2)}}
+{%- endfor %}
+ static {{struct.name}}Ptr New();
+
+ template <typename U>
+ static {{struct.name}}Ptr From(const U& u) {
+ return mojo::TypeConverter<{{struct.name}}Ptr, U>::ConvertFrom(u);
+ }
+
+ {{struct.name}}();
+ ~{{struct.name}}();
+
+{#--- Getters #}
+{% for field in struct.fields %}
+{%- set type = field.kind|cpp_wrapper_type %}
+{%- set name = field.name %}
+ {{type}} {{name}};
+{%- endfor %}
+};
diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl
new file mode 100644
index 00000000000..29bdd8eeb95
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl
@@ -0,0 +1,15 @@
+// static
+{{struct.name}}Ptr {{struct.name}}::New() {
+ {{struct.name}}Ptr rv;
+ mojo::internal::StructHelper<{{struct.name}}>::Initialize(&rv);
+ return rv.Pass();
+}
+
+{{struct.name}}::{{struct.name}}()
+{%-for field in struct.fields %}
+ {% if loop.first %}:{% else %} {% endif %} {{field.name}}({{field|default_value}}){% if not loop.last %},{% endif %}
+{%- endfor %} {
+}
+
+{{struct.name}}::~{{struct.name}}() {
+}
diff --git a/chromium/mojo/public/tools/bindings/generators/java_templates/constant_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/java_templates/constant_definition.tmpl
new file mode 100644
index 00000000000..f69f657d314
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/java_templates/constant_definition.tmpl
@@ -0,0 +1,5 @@
+{% from "java_macros.tmpl" import build_default %}
+
+{% macro constant_def(constant) %}
+public static final {{constant.kind|java_type}} {{constant|name}} = {{build_default(module, constant.kind, constant.value)|indent(4)}};
+{% endmacro %}
diff --git a/chromium/mojo/public/tools/bindings/generators/java_templates/constants.java.tmpl b/chromium/mojo/public/tools/bindings/generators/java_templates/constants.java.tmpl
new file mode 100644
index 00000000000..0a4e29956b6
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/java_templates/constants.java.tmpl
@@ -0,0 +1,12 @@
+{% from "constant_definition.tmpl" import constant_def %}
+{% include "header.java.tmpl" %}
+
+public final class {{main_entity}} {
+{% for constant in constants %}
+
+ {{constant_def(constant)|indent(4)}}
+{% endfor %}
+
+ private {{main_entity}}() {}
+
+}
diff --git a/chromium/mojo/public/tools/bindings/generators/java_templates/enum.java.tmpl b/chromium/mojo/public/tools/bindings/generators/java_templates/enum.java.tmpl
new file mode 100644
index 00000000000..7096a18747f
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/java_templates/enum.java.tmpl
@@ -0,0 +1,4 @@
+{% from "enum_definition.tmpl" import enum_def %}
+{% include "header.java.tmpl" %}
+
+{{enum_def(enum, true)}}
diff --git a/chromium/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl
new file mode 100644
index 00000000000..d72bd7ff4a7
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl
@@ -0,0 +1,21 @@
+{%- macro enum_value(enum, field, index) -%}
+{%- if field.value -%}
+(int) ({{field.value|expression_to_text}})
+{%- elif index == 0 -%}
+0
+{%- else -%}
+{{enum.fields[index - 1].name}} + 1
+{%- endif -%}
+{%- endmacro -%}
+
+{%- macro enum_def(enum, top_level) -%}
+public {{ 'static ' if not top_level }}final class {{enum|name}} {
+
+{% for field in enum.fields %}
+ public static final int {{field.name}} = {{enum_value(enum, field, loop.index0)}};
+{% endfor %}
+
+ private {{enum|name}}() {}
+
+}
+{%- endmacro -%}
diff --git a/chromium/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl b/chromium/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl
new file mode 100644
index 00000000000..ec6a88b1474
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl
@@ -0,0 +1,11 @@
+// Copyright 2014 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.
+
+// This file is autogenerated by:
+// mojo/public/tools/bindings/mojom_bindings_generator.py
+// For:
+// {{module.path}}
+//
+
+package {{package}};
diff --git a/chromium/mojo/public/tools/bindings/generators/java_templates/java_macros.tmpl b/chromium/mojo/public/tools/bindings/generators/java_templates/java_macros.tmpl
new file mode 100644
index 00000000000..d7339f5a420
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/java_templates/java_macros.tmpl
@@ -0,0 +1,3 @@
+{% macro build_default(module, kind, value) %}
+({{kind|java_type}}) {{value|expression_to_text}}
+{% endmacro %}
diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl
new file mode 100644
index 00000000000..795116d8dd9
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl
@@ -0,0 +1,14 @@
+{%- macro enum_def(enum_name, enum, module) -%}
+ {{enum_name}} = {};
+
+{%- set prev_enum = 0 %}
+{%- for field in enum.fields %}
+{%- if field.value %}
+ {{enum_name}}.{{field.name}} = {{field.value|expression_to_text}};
+{%- elif loop.first %}
+ {{enum_name}}.{{field.name}} = 0;
+{%- else %}
+ {{enum_name}}.{{field.name}} = {{enum_name}}.{{enum.fields[loop.index0 - 1].name}} + 1;
+{%- endif %}
+{%- endfor %}
+{%- endmacro %}
diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
new file mode 100644
index 00000000000..d46f8eeac9c
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
@@ -0,0 +1,122 @@
+{%- set namespace_as_string = namespace|replace(".","::") %}
+{%- for method in interface.methods %}
+ var k{{interface.name}}_{{method.name}}_Name = {{method.ordinal}};
+{%- endfor %}
+
+ function {{interface.name}}Proxy(receiver) {
+ this.receiver_ = receiver;
+ }
+
+ {{interface.name}}Proxy.NAME_ = '{{namespace_as_string}}::{{interface.name}}';
+
+{%- for method in interface.methods %}
+ {{interface.name}}Proxy.prototype.{{method.name|stylize_method}} = function(
+{%- for parameter in method.parameters -%}
+{{parameter.name}}{% if not loop.last %}, {% endif %}
+{%- endfor -%}
+) {
+ var params = new {{interface.name}}_{{method.name}}_Params();
+{%- for parameter in method.parameters %}
+ params.{{parameter.name}} = {{parameter.name}};
+{%- endfor %}
+
+{%- if method.response_parameters == None %}
+ var builder = new codec.MessageBuilder(
+ k{{interface.name}}_{{method.name}}_Name,
+ codec.align({{interface.name}}_{{method.name}}_Params.encodedSize));
+ builder.encodeStruct({{interface.name}}_{{method.name}}_Params, params);
+ var message = builder.finish();
+ this.receiver_.accept(message);
+{%- else %}
+ return new Promise(function(resolve, reject) {
+ var builder = new codec.MessageWithRequestIDBuilder(
+ k{{interface.name}}_{{method.name}}_Name,
+ codec.align({{interface.name}}_{{method.name}}_Params.encodedSize),
+ codec.kMessageExpectsResponse, 0);
+ builder.encodeStruct({{interface.name}}_{{method.name}}_Params, params);
+ var message = builder.finish();
+ this.receiver_.acceptWithResponder(message, {
+ accept: function(message) {
+ var reader = new codec.MessageReader(message);
+ var responseParams =
+ reader.decodeStruct({{interface.name}}_{{method.name}}_ResponseParams);
+ resolve(responseParams);
+ },
+ reject: function(result) {
+ reject(Error("Connection error: " + result));
+ },
+ }).catch(reject);
+ }.bind(this));
+{%- endif %}
+ };
+{%- endfor %}
+
+ function {{interface.name}}Stub() {
+ }
+
+ {{interface.name}}Stub.NAME_ = '{{namespace_as_string}}::{{interface.name}}';
+
+ {{interface.name}}Stub.prototype.accept = function(message) {
+ var reader = new codec.MessageReader(message);
+ switch (reader.messageName) {
+{%- for method in interface.methods %}
+{%- if method.response_parameters == None %}
+ case k{{interface.name}}_{{method.name}}_Name:
+ var params = reader.decodeStruct({{interface.name}}_{{method.name}}_Params);
+ this.{{method.name|stylize_method}}(
+{%- for parameter in method.parameters -%}
+params.{{parameter.name}}{% if not loop.last %}, {% endif %}
+{%- endfor %});
+ return true;
+{%- endif %}
+{%- endfor %}
+ default:
+ return false;
+ }
+ };
+
+ {{interface.name}}Stub.prototype.acceptWithResponder =
+ function(message, responder) {
+ var reader = new codec.MessageReader(message);
+ switch (reader.messageName) {
+{%- for method in interface.methods %}
+{%- if method.response_parameters != None %}
+ case k{{interface.name}}_{{method.name}}_Name:
+ var params = reader.decodeStruct({{interface.name}}_{{method.name}}_Params);
+ return this.{{method.name|stylize_method}}(
+{%- for parameter in method.parameters -%}
+params.{{parameter.name}}{% if not loop.last %}, {% endif -%}
+{%- endfor %}).then(function(response) {
+ var responseParams =
+ new {{interface.name}}_{{method.name}}_ResponseParams();
+{%- for parameter in method.response_parameters %}
+ responseParams.{{parameter.name}} = response.{{parameter.name}};
+{%- endfor %}
+ var builder = new codec.MessageWithRequestIDBuilder(
+ k{{interface.name}}_{{method.name}}_Name,
+ codec.align({{interface.name}}_{{method.name}}_ResponseParams.encodedSize),
+ codec.kMessageIsResponse, reader.requestID);
+ builder.encodeStruct({{interface.name}}_{{method.name}}_ResponseParams,
+ responseParams);
+ var message = builder.finish();
+ responder.accept(message);
+ });
+{%- endif %}
+{%- endfor %}
+ default:
+ return Promise.reject(Error("Unhandled message: " + reader.messageName));
+ }
+ };
+
+{#--- Enums #}
+{% from "enum_definition.tmpl" import enum_def -%}
+{% for enum in interface.enums %}
+ {{enum_def("%sProxy.%s"|format(interface.name, enum.name), enum, module)}}
+ {{interface.name}}Stub.{{enum.name}} = {{interface.name}}Proxy.{{enum.name}};
+{%- endfor %}
+
+{#--- Constants. #}
+{% for constant in interface.constants %}
+ {{interface.name}}Proxy.{{constant.name}} = {{constant.value|expression_to_text}};
+ {{interface.name}}Stub.{{constant.name}} = {{interface.name}}Proxy.{{constant.name}};
+{% endfor %}
diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/module.js.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/module.js.tmpl
new file mode 100644
index 00000000000..db570cd79df
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/js_templates/module.js.tmpl
@@ -0,0 +1,52 @@
+// Copyright 2013 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.
+
+define("{{module.path}}", [
+ "mojo/public/js/bindings/codec",
+{%- for import in imports %}
+ "{{import.module.path}}",
+{%- endfor %}
+ ], function(codec
+{%- for import in imports -%}
+ , {{import.unique_name}}
+{%- endfor -%}
+) {
+
+{#--- Constants #}
+{% for constant in module.constants %}
+ var {{constant.name}} = {{constant.value|expression_to_text}};
+{%- endfor %}
+
+{#--- Enums #}
+{%- from "enum_definition.tmpl" import enum_def %}
+{%- for enum in enums %}
+ var {{ enum_def(enum.name, enum, module) }}
+{%- endfor %}
+
+{#--- Struct definitions #}
+{% for struct in structs %}
+{%- include "struct_definition.tmpl" %}
+{%- endfor %}
+
+{#--- Interface definitions #}
+{%- for interface in interfaces %}
+{%- include "interface_definition.tmpl" %}
+{%- endfor %}
+
+ var exports = {};
+{% for constant in module.constants %}
+ exports.{{constant.name}} = {{constant.name}};
+{%- endfor %}
+{%- for enum in enums %}
+ exports.{{enum.name}} = {{enum.name}};
+{%- endfor %}
+{%- for struct in structs if struct.exported %}
+ exports.{{struct.name}} = {{struct.name}};
+{%- endfor %}
+{%- for interface in interfaces %}
+ exports.{{interface.name}}Proxy = {{interface.name}}Proxy;
+ exports.{{interface.name}}Stub = {{interface.name}}Stub;
+{%- endfor %}
+ return exports;
+});
diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
new file mode 100644
index 00000000000..ae1ccb079af
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
@@ -0,0 +1,72 @@
+{#--- Begin #}
+ function {{struct.name}}() {
+ this.initDefaults_();
+ }
+
+{#--- Enums #}
+{%- from "enum_definition.tmpl" import enum_def %}
+{% for enum in struct.enums %}
+ {{enum_def("%s.%s"|format(struct.name, enum.name), enum, module)}}
+{% endfor %}
+
+{#--- Constants #}
+{% for constant in struct.constants %}
+ {{struct.name}}.{{constant.name}} = {{constant.value|expression_to_text}};
+{% endfor %}
+
+{#--- Set up defaults #}
+ {{struct.name}}.prototype.initDefaults_ = function() {
+{%- for packed_field in struct.packed.packed_fields %}
+ this.{{packed_field.field.name}} = {{packed_field.field|default_value}};
+{%- endfor %}
+ };
+
+{#--- Encoding and decoding #}
+
+ {{struct.name}}.encodedSize = codec.kStructHeaderSize + {{struct.packed|payload_size}};
+
+ {{struct.name}}.decode = function(decoder) {
+ var packed;
+ var val = new {{struct.name}}();
+ var numberOfBytes = decoder.readUint32();
+ var numberOfFields = decoder.readUint32();
+{%- for byte in struct.bytes %}
+{%- if byte.packed_fields|length > 1 %}
+ packed = decoder.readUint8();
+{%- for packed_field in byte.packed_fields %}
+ val.{{packed_field.field.name}} = (packed >> {{packed_field.bit}}) & 1 ? true : false;
+{%- endfor %}
+{%- else %}
+{%- for packed_field in byte.packed_fields %}
+ val.{{packed_field.field.name}} = decoder.{{packed_field.field.kind|decode_snippet}};
+{%- endfor %}
+{%- endif %}
+{%- if byte.is_padding %}
+ decoder.skip(1);
+{%- endif %}
+{%- endfor %}
+ return val;
+ };
+
+ {{struct.name}}.encode = function(encoder, val) {
+ var packed;
+ encoder.writeUint32({{struct.name}}.encodedSize);
+ encoder.writeUint32({{struct.packed.packed_fields|length}});
+
+{%- for byte in struct.bytes %}
+{%- if byte.packed_fields|length > 1 %}
+ packed = 0;
+{%- for packed_field in byte.packed_fields %}
+ packed |= (val.{{packed_field.field.name}} & 1) << {{packed_field.bit}}
+{%- endfor %}
+ encoder.writeUint8(packed);
+{%- else %}
+{%- for packed_field in byte.packed_fields %}
+ encoder.{{packed_field.field.kind|encode_snippet}}val.{{packed_field.field.name}});
+{%- endfor %}
+{%- endif %}
+{%- if byte.is_padding %}
+ encoder.skip(1);
+{%- endif %}
+{%- endfor %}
+ };
diff --git a/chromium/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/chromium/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
new file mode 100644
index 00000000000..812dd47c609
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
@@ -0,0 +1,286 @@
+# Copyright 2013 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.
+
+"""Generates C++ source files from a mojom.Module."""
+
+import mojom.generate.generator as generator
+import mojom.generate.module as mojom
+import mojom.generate.pack as pack
+from mojom.generate.template_expander import UseJinja
+
+
+_kind_to_cpp_type = {
+ mojom.BOOL: "bool",
+ mojom.INT8: "int8_t",
+ mojom.UINT8: "uint8_t",
+ mojom.INT16: "int16_t",
+ mojom.UINT16: "uint16_t",
+ mojom.INT32: "int32_t",
+ mojom.UINT32: "uint32_t",
+ mojom.FLOAT: "float",
+ mojom.HANDLE: "mojo::Handle",
+ mojom.DCPIPE: "mojo::DataPipeConsumerHandle",
+ mojom.DPPIPE: "mojo::DataPipeProducerHandle",
+ mojom.MSGPIPE: "mojo::MessagePipeHandle",
+ mojom.SHAREDBUFFER: "mojo::SharedBufferHandle",
+ mojom.INT64: "int64_t",
+ mojom.UINT64: "uint64_t",
+ mojom.DOUBLE: "double",
+}
+
+def DefaultValue(field):
+ if field.default:
+ if isinstance(field.kind, mojom.Struct):
+ assert field.default == "default"
+ return "%s::New()" % GetNameForKind(field.kind)
+ return ExpressionToText(field.default)
+ return ""
+
+def NamespaceToArray(namespace):
+ return namespace.split('.') if namespace else []
+
+def GetNameForKind(kind, internal = False):
+ parts = []
+ if kind.imported_from:
+ parts.extend(NamespaceToArray(kind.imported_from["namespace"]))
+ if internal:
+ parts.append("internal")
+ if kind.parent_kind:
+ parts.append(kind.parent_kind.name)
+ parts.append(kind.name)
+ return "::".join(parts)
+
+def GetCppType(kind):
+ if isinstance(kind, mojom.Struct):
+ return "%s_Data*" % GetNameForKind(kind, internal=True)
+ if isinstance(kind, mojom.Array):
+ return "mojo::internal::Array_Data<%s>*" % GetCppType(kind.kind)
+ if isinstance(kind, mojom.Interface) or \
+ isinstance(kind, mojom.InterfaceRequest):
+ return "mojo::MessagePipeHandle"
+ if isinstance(kind, mojom.Enum):
+ return "int32_t"
+ if kind.spec == 's':
+ return "mojo::internal::String_Data*"
+ return _kind_to_cpp_type[kind]
+
+def GetCppPodType(kind):
+ if kind.spec == 's':
+ return "char*"
+ return _kind_to_cpp_type[kind]
+
+def GetCppArrayArgWrapperType(kind):
+ if isinstance(kind, mojom.Enum):
+ return GetNameForKind(kind)
+ if isinstance(kind, mojom.Struct):
+ return "%sPtr" % GetNameForKind(kind)
+ if isinstance(kind, mojom.Array):
+ return "mojo::Array<%s> " % GetCppArrayArgWrapperType(kind.kind)
+ if isinstance(kind, mojom.Interface):
+ raise Exception("Arrays of interfaces not yet supported!")
+ if isinstance(kind, mojom.InterfaceRequest):
+ raise Exception("Arrays of interface requests not yet supported!")
+ if kind.spec == 's':
+ return "mojo::String"
+ if kind.spec == 'h':
+ return "mojo::ScopedHandle"
+ if kind.spec == 'h:d:c':
+ return "mojo::ScopedDataPipeConsumerHandle"
+ if kind.spec == 'h:d:p':
+ return "mojo::ScopedDataPipeProducerHandle"
+ if kind.spec == 'h:m':
+ return "mojo::ScopedMessagePipeHandle"
+ if kind.spec == 'h:s':
+ return "mojo::ScopedSharedBufferHandle"
+ return _kind_to_cpp_type[kind]
+
+def GetCppResultWrapperType(kind):
+ if isinstance(kind, mojom.Enum):
+ return GetNameForKind(kind)
+ if isinstance(kind, mojom.Struct):
+ return "%sPtr" % GetNameForKind(kind)
+ if isinstance(kind, mojom.Array):
+ return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind)
+ if isinstance(kind, mojom.Interface):
+ return "%sPtr" % GetNameForKind(kind)
+ if isinstance(kind, mojom.InterfaceRequest):
+ return "mojo::InterfaceRequest<%s>" % GetNameForKind(kind.kind)
+ if kind.spec == 's':
+ return "mojo::String"
+ if kind.spec == 'h':
+ return "mojo::ScopedHandle"
+ if kind.spec == 'h:d:c':
+ return "mojo::ScopedDataPipeConsumerHandle"
+ if kind.spec == 'h:d:p':
+ return "mojo::ScopedDataPipeProducerHandle"
+ if kind.spec == 'h:m':
+ return "mojo::ScopedMessagePipeHandle"
+ if kind.spec == 'h:s':
+ return "mojo::ScopedSharedBufferHandle"
+ return _kind_to_cpp_type[kind]
+
+def GetCppWrapperType(kind):
+ if isinstance(kind, mojom.Enum):
+ return GetNameForKind(kind)
+ if isinstance(kind, mojom.Struct):
+ return "%sPtr" % GetNameForKind(kind)
+ if isinstance(kind, mojom.Array):
+ return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind)
+ if isinstance(kind, mojom.Interface):
+ return "mojo::ScopedMessagePipeHandle"
+ if isinstance(kind, mojom.InterfaceRequest):
+ raise Exception("InterfaceRequest fields not supported!")
+ if kind.spec == 's':
+ return "mojo::String"
+ if kind.spec == 'h':
+ return "mojo::ScopedHandle"
+ if kind.spec == 'h:d:c':
+ return "mojo::ScopedDataPipeConsumerHandle"
+ if kind.spec == 'h:d:p':
+ return "mojo::ScopedDataPipeProducerHandle"
+ if kind.spec == 'h:m':
+ return "mojo::ScopedMessagePipeHandle"
+ if kind.spec == 'h:s':
+ return "mojo::ScopedSharedBufferHandle"
+ return _kind_to_cpp_type[kind]
+
+def GetCppConstWrapperType(kind):
+ if isinstance(kind, mojom.Struct):
+ return "%sPtr" % GetNameForKind(kind)
+ if isinstance(kind, mojom.Array):
+ return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind)
+ if isinstance(kind, mojom.Interface):
+ return "%sPtr" % GetNameForKind(kind)
+ if isinstance(kind, mojom.InterfaceRequest):
+ return "mojo::InterfaceRequest<%s>" % GetNameForKind(kind.kind)
+ if isinstance(kind, mojom.Enum):
+ return GetNameForKind(kind)
+ if kind.spec == 's':
+ return "const mojo::String&"
+ if kind.spec == 'h':
+ return "mojo::ScopedHandle"
+ if kind.spec == 'h:d:c':
+ return "mojo::ScopedDataPipeConsumerHandle"
+ if kind.spec == 'h:d:p':
+ return "mojo::ScopedDataPipeProducerHandle"
+ if kind.spec == 'h:m':
+ return "mojo::ScopedMessagePipeHandle"
+ if kind.spec == 'h:s':
+ return "mojo::ScopedSharedBufferHandle"
+ if not kind in _kind_to_cpp_type:
+ print "missing:", kind.spec
+ return _kind_to_cpp_type[kind]
+
+def GetCppFieldType(kind):
+ if isinstance(kind, mojom.Struct):
+ return ("mojo::internal::StructPointer<%s_Data>" %
+ GetNameForKind(kind, internal=True))
+ if isinstance(kind, mojom.Array):
+ return "mojo::internal::ArrayPointer<%s>" % GetCppType(kind.kind)
+ if isinstance(kind, mojom.Interface) or \
+ isinstance(kind, mojom.InterfaceRequest):
+ return "mojo::MessagePipeHandle"
+ if isinstance(kind, mojom.Enum):
+ return GetNameForKind(kind)
+ if kind.spec == 's':
+ return "mojo::internal::StringPointer"
+ return _kind_to_cpp_type[kind]
+
+def IsStructWithHandles(struct):
+ for pf in struct.packed.packed_fields:
+ if generator.IsHandleKind(pf.field.kind):
+ return True
+ return False
+
+def TranslateConstants(token):
+ if isinstance(token, (mojom.NamedValue, mojom.EnumValue)):
+ # Both variable and enum constants are constructed like:
+ # Namespace::Struct::CONSTANT_NAME
+ name = []
+ if token.imported_from:
+ name.extend(NamespaceToArray(token.namespace))
+ if token.parent_kind:
+ name.append(token.parent_kind.name)
+ name.append(token.name)
+ return "::".join(name)
+ return token
+
+def ExpressionToText(value):
+ return TranslateConstants(value)
+
+def HasCallbacks(interface):
+ for method in interface.methods:
+ if method.response_parameters != None:
+ return True
+ return False
+
+def ShouldInlineStruct(struct):
+ # TODO(darin): Base this on the size of the wrapper class.
+ if len(struct.fields) > 4:
+ return False
+ for field in struct.fields:
+ if generator.IsHandleKind(field.kind) or generator.IsObjectKind(field.kind):
+ return False
+ return True
+
+_HEADER_SIZE = 8
+
+class Generator(generator.Generator):
+
+ cpp_filters = {
+ "cpp_const_wrapper_type": GetCppConstWrapperType,
+ "cpp_field_type": GetCppFieldType,
+ "cpp_pod_type": GetCppPodType,
+ "cpp_result_type": GetCppResultWrapperType,
+ "cpp_type": GetCppType,
+ "cpp_wrapper_type": GetCppWrapperType,
+ "default_value": DefaultValue,
+ "expression_to_text": ExpressionToText,
+ "get_name_for_kind": GetNameForKind,
+ "get_pad": pack.GetPad,
+ "has_callbacks": HasCallbacks,
+ "should_inline": ShouldInlineStruct,
+ "is_enum_kind": generator.IsEnumKind,
+ "is_move_only_kind": generator.IsMoveOnlyKind,
+ "is_handle_kind": generator.IsHandleKind,
+ "is_interface_kind": generator.IsInterfaceKind,
+ "is_interface_request_kind": generator.IsInterfaceRequestKind,
+ "is_object_kind": generator.IsObjectKind,
+ "is_string_kind": generator.IsStringKind,
+ "is_struct_with_handles": IsStructWithHandles,
+ "struct_size": lambda ps: ps.GetTotalSize() + _HEADER_SIZE,
+ "struct_from_method": generator.GetStructFromMethod,
+ "response_struct_from_method": generator.GetResponseStructFromMethod,
+ "stylize_method": generator.StudlyCapsToCamel,
+ }
+
+ def GetJinjaExports(self):
+ return {
+ "module": self.module,
+ "namespace": self.module.namespace,
+ "namespaces_as_array": NamespaceToArray(self.module.namespace),
+ "imports": self.module.imports,
+ "kinds": self.module.kinds,
+ "enums": self.module.enums,
+ "structs": self.GetStructs(),
+ "interfaces": self.module.interfaces,
+ }
+
+ @UseJinja("cpp_templates/module.h.tmpl", filters=cpp_filters)
+ def GenerateModuleHeader(self):
+ return self.GetJinjaExports()
+
+ @UseJinja("cpp_templates/module-internal.h.tmpl", filters=cpp_filters)
+ def GenerateModuleInternalHeader(self):
+ return self.GetJinjaExports()
+
+ @UseJinja("cpp_templates/module.cc.tmpl", filters=cpp_filters)
+ def GenerateModuleSource(self):
+ return self.GetJinjaExports()
+
+ def GenerateFiles(self, args):
+ self.Write(self.GenerateModuleHeader(), "%s.h" % self.module.name)
+ self.Write(self.GenerateModuleInternalHeader(),
+ "%s-internal.h" % self.module.name)
+ self.Write(self.GenerateModuleSource(), "%s.cc" % self.module.name)
diff --git a/chromium/mojo/public/tools/bindings/generators/mojom_java_generator.py b/chromium/mojo/public/tools/bindings/generators/mojom_java_generator.py
new file mode 100644
index 00000000000..ca43b845630
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/mojom_java_generator.py
@@ -0,0 +1,187 @@
+# Copyright 2014 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.
+
+"""Generates java source files from a mojom.Module."""
+
+import argparse
+import os
+import re
+
+import mojom.generate.generator as generator
+import mojom.generate.module as mojom
+from mojom.generate.template_expander import UseJinja
+
+
+GENERATOR_PREFIX = 'java'
+
+_spec_to_java_type = {
+ 'b': 'boolean',
+ 'd': 'double',
+ 'f': 'float',
+ 'h:d:c': 'org.chromium.mojo.system.DataPipe.ConsumerHandle',
+ 'h:d:p': 'org.chromium.mojo.system.DataPipe.ProducerHandle',
+ 'h:m': 'org.chromium.mojo.system.MessagePipeHandle',
+ 'h': 'org.chromium.mojo.system.UntypedHandle',
+ 'h:s': 'org.chromium.mojo.system.SharedBufferHandle',
+ 'i16': 'short',
+ 'i32': 'int',
+ 'i64': 'long',
+ 'i8': 'byte',
+ 's': 'String',
+ 'u16': 'short',
+ 'u32': 'int',
+ 'u64': 'long',
+ 'u8': 'byte',
+}
+
+
+def NameToComponent(name):
+ # insert '_' between anything and a Title name (e.g, HTTPEntry2FooBar ->
+ # HTTP_Entry2_FooBar)
+ name = re.sub('([^_])([A-Z][^A-Z_]+)', r'\1_\2', name)
+ # insert '_' between non upper and start of upper blocks (e.g.,
+ # HTTP_Entry2_FooBar -> HTTP_Entry2_Foo_Bar)
+ name = re.sub('([^A-Z_])([A-Z])', r'\1_\2', name)
+ return [x.lower() for x in name.split('_')]
+
+def CapitalizeFirst(string):
+ return string[0].upper() + string[1:]
+
+def UpperCamelCase(name):
+ return ''.join([CapitalizeFirst(x) for x in NameToComponent(name)])
+
+def CamelCase(name):
+ uccc = UpperCamelCase(name)
+ return uccc[0].lower() + uccc[1:]
+
+def ConstantStyle(name):
+ components = NameToComponent(name)
+ if components[0] == 'k':
+ components = components[1:]
+ return '_'.join([x.upper() for x in components])
+
+def GetNameForElement(element):
+ if (isinstance(element, mojom.Enum) or
+ isinstance(element, mojom.Interface) or
+ isinstance(element, mojom.Struct)):
+ return UpperCamelCase(element.name)
+ if (isinstance(element, mojom.Method) or
+ isinstance(element, mojom.Parameter) or
+ isinstance(element, mojom.Field)):
+ return CamelCase(element.name)
+ if isinstance(element, mojom.EnumValue):
+ return (UpperCamelCase(element.enum_name) + '.' +
+ ConstantStyle(element.name))
+ if (isinstance(element, mojom.NamedValue) or
+ isinstance(element, mojom.Constant)):
+ return ConstantStyle(element.name)
+ raise Exception("Unexpected element: " % element)
+
+def ParseStringAttribute(attribute):
+ assert isinstance(attribute, basestring)
+ return attribute
+
+def GetPackage(module):
+ if 'JavaPackage' in module.attributes:
+ return ParseStringAttribute(module.attributes['JavaPackage'])
+ # Default package.
+ return "org.chromium.mojom." + module.namespace
+
+def GetNameForKind(kind):
+ def _GetNameHierachy(kind):
+ hierachy = []
+ if kind.parent_kind:
+ hierachy = _GetNameHierachy(kind.parent_kind)
+ hierachy.append(kind.name)
+ return hierachy
+
+ elements = [GetPackage(kind.module)]
+ elements += _GetNameHierachy(kind)
+ return '.'.join(elements)
+
+def GetJavaType(kind):
+ if isinstance(kind, (mojom.Struct, mojom.Interface)):
+ return GetNameForKind(kind)
+ if isinstance(kind, mojom.Array):
+ return "%s[]" % GetJavaType(kind.kind)
+ if isinstance(kind, mojom.Enum):
+ return "int"
+ return _spec_to_java_type[kind.spec]
+
+def ExpressionToText(token):
+ def _TranslateNamedValue(named_value):
+ entity_name = GetNameForElement(named_value)
+ if named_value.parent_kind:
+ return GetJavaType(named_value.parent_kind) + '.' + entity_name
+ # Handle the case where named_value is a module level constant:
+ if not isinstance(named_value, mojom.EnumValue):
+ entity_name = (GetConstantsMainEntityName(named_value.module) + '.' +
+ entity_name)
+ return GetPackage(named_value.module) + '.' + entity_name
+
+ if isinstance(token, mojom.NamedValue):
+ return _TranslateNamedValue(token)
+ # Add Long suffix to all number literals.
+ if re.match('^[0-9]+$', token):
+ return token + 'L'
+ return token
+
+def GetConstantsMainEntityName(module):
+ if 'JavaConstantsClassName' in module.attributes:
+ return ParseStringAttribute(module.attributes['JavaConstantsClassName'])
+ # This constructs the name of the embedding classes for module level constants
+ # by extracting the mojom's filename and prepending it to Constants.
+ return (UpperCamelCase(module.path.split('/')[-1].rsplit('.', 1)[0]) +
+ 'Constants')
+
+class Generator(generator.Generator):
+
+ java_filters = {
+ "expression_to_text": ExpressionToText,
+ "java_type": GetJavaType,
+ "name": GetNameForElement,
+ }
+
+ def GetJinjaExports(self):
+ return {
+ "module": self.module,
+ "package": GetPackage(self.module),
+ }
+
+ @UseJinja("java_templates/enum.java.tmpl", filters=java_filters,
+ lstrip_blocks=True, trim_blocks=True)
+ def GenerateEnumSource(self, enum):
+ exports = self.GetJinjaExports()
+ exports.update({"enum": enum})
+ return exports
+
+ @UseJinja("java_templates/constants.java.tmpl", filters=java_filters,
+ lstrip_blocks=True, trim_blocks=True)
+ def GenerateConstantsSource(self, module):
+ exports = self.GetJinjaExports()
+ exports.update({"main_entity": GetConstantsMainEntityName(module),
+ "constants": module.constants})
+ return exports
+
+ def GenerateFiles(self, unparsed_args):
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--java_output_directory", dest="java_output_directory")
+ args = parser.parse_args(unparsed_args)
+ if self.output_dir and args.java_output_directory:
+ self.output_dir = os.path.join(args.java_output_directory,
+ GetPackage(self.module).replace('.', '/'))
+ if not os.path.exists(self.output_dir):
+ try:
+ os.makedirs(self.output_dir)
+ except:
+ # Ignore errors on directory creation.
+ pass
+
+ for enum in self.module.enums:
+ self.Write(self.GenerateEnumSource(enum),
+ "%s.java" % GetNameForElement(enum))
+
+ if self.module.constants:
+ self.Write(self.GenerateConstantsSource(self.module),
+ "%s.java" % GetConstantsMainEntityName(self.module))
diff --git a/chromium/mojo/public/tools/bindings/generators/mojom_js_generator.py b/chromium/mojo/public/tools/bindings/generators/mojom_js_generator.py
new file mode 100644
index 00000000000..1fa351037c6
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/mojom_js_generator.py
@@ -0,0 +1,186 @@
+# Copyright 2013 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.
+
+"""Generates JavaScript source files from a mojom.Module."""
+
+import mojom.generate.generator as generator
+import mojom.generate.module as mojom
+import mojom.generate.pack as pack
+from mojom.generate.template_expander import UseJinja
+
+_kind_to_javascript_default_value = {
+ mojom.BOOL: "false",
+ mojom.INT8: "0",
+ mojom.UINT8: "0",
+ mojom.INT16: "0",
+ mojom.UINT16: "0",
+ mojom.INT32: "0",
+ mojom.UINT32: "0",
+ mojom.FLOAT: "0",
+ mojom.HANDLE: "null",
+ mojom.DCPIPE: "null",
+ mojom.DPPIPE: "null",
+ mojom.MSGPIPE: "null",
+ mojom.SHAREDBUFFER: "null",
+ mojom.INT64: "0",
+ mojom.UINT64: "0",
+ mojom.DOUBLE: "0",
+ mojom.STRING: '""',
+}
+
+
+def JavaScriptDefaultValue(field):
+ if field.default:
+ if isinstance(field.kind, mojom.Struct):
+ assert field.default == "default"
+ return "new %s()" % JavascriptType(field.kind)
+ return ExpressionToText(field.default)
+ if field.kind in mojom.PRIMITIVES:
+ return _kind_to_javascript_default_value[field.kind]
+ if isinstance(field.kind, mojom.Struct):
+ return "null"
+ if isinstance(field.kind, mojom.Array):
+ return "[]"
+ if isinstance(field.kind, mojom.Interface) or \
+ isinstance(field.kind, mojom.InterfaceRequest):
+ return _kind_to_javascript_default_value[mojom.MSGPIPE]
+ if isinstance(field.kind, mojom.Enum):
+ return "0"
+
+
+def JavaScriptPayloadSize(packed):
+ packed_fields = packed.packed_fields
+ if not packed_fields:
+ return 0
+ last_field = packed_fields[-1]
+ offset = last_field.offset + last_field.size
+ pad = pack.GetPad(offset, 8)
+ return offset + pad
+
+
+_kind_to_codec_type = {
+ mojom.BOOL: "codec.Uint8",
+ mojom.INT8: "codec.Int8",
+ mojom.UINT8: "codec.Uint8",
+ mojom.INT16: "codec.Int16",
+ mojom.UINT16: "codec.Uint16",
+ mojom.INT32: "codec.Int32",
+ mojom.UINT32: "codec.Uint32",
+ mojom.FLOAT: "codec.Float",
+ mojom.HANDLE: "codec.Handle",
+ mojom.DCPIPE: "codec.Handle",
+ mojom.DPPIPE: "codec.Handle",
+ mojom.MSGPIPE: "codec.Handle",
+ mojom.SHAREDBUFFER: "codec.Handle",
+ mojom.INT64: "codec.Int64",
+ mojom.UINT64: "codec.Uint64",
+ mojom.DOUBLE: "codec.Double",
+ mojom.STRING: "codec.String",
+}
+
+
+def CodecType(kind):
+ if kind in mojom.PRIMITIVES:
+ return _kind_to_codec_type[kind]
+ if isinstance(kind, mojom.Struct):
+ return "new codec.PointerTo(%s)" % CodecType(kind.name)
+ if isinstance(kind, mojom.Array):
+ return "new codec.ArrayOf(%s)" % CodecType(kind.kind)
+ if isinstance(kind, mojom.Interface) or \
+ isinstance(kind, mojom.InterfaceRequest):
+ return CodecType(mojom.MSGPIPE)
+ if isinstance(kind, mojom.Enum):
+ return _kind_to_codec_type[mojom.INT32]
+ return kind
+
+
+def JavaScriptDecodeSnippet(kind):
+ if kind in mojom.PRIMITIVES:
+ return "decodeStruct(%s)" % CodecType(kind)
+ if isinstance(kind, mojom.Struct):
+ return "decodeStructPointer(%s)" % CodecType(kind.name)
+ if isinstance(kind, mojom.Array):
+ return "decodeArrayPointer(%s)" % CodecType(kind.kind)
+ if isinstance(kind, mojom.Interface) or \
+ isinstance(kind, mojom.InterfaceRequest):
+ return JavaScriptDecodeSnippet(mojom.MSGPIPE)
+ if isinstance(kind, mojom.Enum):
+ return JavaScriptDecodeSnippet(mojom.INT32)
+
+
+def JavaScriptEncodeSnippet(kind):
+ if kind in mojom.PRIMITIVES:
+ return "encodeStruct(%s, " % CodecType(kind)
+ if isinstance(kind, mojom.Struct):
+ return "encodeStructPointer(%s, " % CodecType(kind.name)
+ if isinstance(kind, mojom.Array):
+ return "encodeArrayPointer(%s, " % CodecType(kind.kind)
+ if isinstance(kind, mojom.Interface) or \
+ isinstance(kind, mojom.InterfaceRequest):
+ return JavaScriptEncodeSnippet(mojom.MSGPIPE)
+ if isinstance(kind, mojom.Enum):
+ return JavaScriptEncodeSnippet(mojom.INT32)
+
+
+def TranslateConstants(token):
+ if isinstance(token, (mojom.EnumValue, mojom.NamedValue)):
+ # Both variable and enum constants are constructed like:
+ # NamespaceUid.Struct[.Enum].CONSTANT_NAME
+ name = []
+ if token.imported_from:
+ name.append(token.imported_from["unique_name"])
+ if token.parent_kind:
+ name.append(token.parent_kind.name)
+ if isinstance(token, mojom.EnumValue):
+ name.append(token.enum_name)
+ name.append(token.name)
+ return ".".join(name)
+ return token
+
+
+def ExpressionToText(value):
+ return TranslateConstants(value)
+
+
+def JavascriptType(kind):
+ if kind.imported_from:
+ return kind.imported_from["unique_name"] + "." + kind.name
+ return kind.name
+
+
+class Generator(generator.Generator):
+
+ js_filters = {
+ "default_value": JavaScriptDefaultValue,
+ "payload_size": JavaScriptPayloadSize,
+ "decode_snippet": JavaScriptDecodeSnippet,
+ "encode_snippet": JavaScriptEncodeSnippet,
+ "expression_to_text": ExpressionToText,
+ "js_type": JavascriptType,
+ "stylize_method": generator.StudlyCapsToCamel,
+ }
+
+ @UseJinja("js_templates/module.js.tmpl", filters=js_filters)
+ def GenerateJsModule(self):
+ return {
+ "namespace": self.module.namespace,
+ "imports": self.GetImports(),
+ "kinds": self.module.kinds,
+ "enums": self.module.enums,
+ "module": self.module,
+ "structs": self.GetStructs() + self.GetStructsFromMethods(),
+ "interfaces": self.module.interfaces,
+ }
+
+ def GenerateFiles(self, args):
+ self.Write(self.GenerateJsModule(), "%s.js" % self.module.name)
+
+ def GetImports(self):
+ # Since each import is assigned a variable in JS, they need to have unique
+ # names.
+ counter = 1
+ for each in self.module.imports:
+ each["unique_name"] = "import" + str(counter)
+ counter += 1
+ return self.module.imports
diff --git a/chromium/mojo/public/tools/bindings/generators/run_cpp_generator.py b/chromium/mojo/public/tools/bindings/generators/run_cpp_generator.py
new file mode 100755
index 00000000000..4c6f5974069
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/generators/run_cpp_generator.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+# Copyright 2013 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.
+
+import ast
+import os
+import sys
+
+script_dir = os.path.dirname(os.path.realpath(__file__))
+sys.path.insert(0, os.path.join(script_dir, os.pardir, "pylib"))
+
+from mojom.generate.data
+import mojom_cpp_generator
+
+def ReadDict(file):
+ with open(file, 'r') as f:
+ s = f.read()
+ dict = ast.literal_eval(s)
+ return dict
+
+dict = ReadDict(sys.argv[1])
+module = mojom.generate.data.ModuleFromData(dict)
+dir = None
+if len(sys.argv) > 2:
+ dir = sys.argv[2]
+cpp = mojom_cpp_generator.Generator(module, ".", dir)
+cpp.GenerateFiles([])
diff --git a/chromium/mojo/public/tools/bindings/mojom.gni b/chromium/mojo/public/tools/bindings/mojom.gni
new file mode 100644
index 00000000000..3376abfb6d0
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/mojom.gni
@@ -0,0 +1,90 @@
+# Copyright 2014 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.
+
+# Generate C++ and JavaScript source files from mojom files.
+template("mojom") {
+ assert(defined(invoker.sources),
+ "\"sources\" must be defined for the $target_name template.")
+
+ generator_root = "//mojo/public/tools/bindings"
+ generator_script = "$generator_root/mojom_bindings_generator.py"
+ generator_sources = [
+ generator_script,
+ "$generator_root/generators/cpp_templates/enum_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/interface_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/interface_definition.tmpl",
+ "$generator_root/generators/cpp_templates/interface_macros.tmpl",
+ "$generator_root/generators/cpp_templates/interface_proxy_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/interface_request_validator_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/interface_response_validator_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/interface_stub_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/module.cc.tmpl",
+ "$generator_root/generators/cpp_templates/module.h.tmpl",
+ "$generator_root/generators/cpp_templates/module-internal.h.tmpl",
+ "$generator_root/generators/cpp_templates/params_definition.tmpl",
+ "$generator_root/generators/cpp_templates/struct_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/struct_definition.tmpl",
+ "$generator_root/generators/cpp_templates/struct_serialization_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/struct_serialization_definition.tmpl",
+ "$generator_root/generators/cpp_templates/struct_macros.tmpl",
+ "$generator_root/generators/cpp_templates/wrapper_class_declaration.tmpl",
+ "$generator_root/generators/cpp_templates/wrapper_class_definition.tmpl",
+ "$generator_root/generators/js_templates/enum_definition.tmpl",
+ "$generator_root/generators/js_templates/interface_definition.tmpl",
+ "$generator_root/generators/js_templates/module.js.tmpl",
+ "$generator_root/generators/js_templates/struct_definition.tmpl",
+ "$generator_root/generators/mojom_cpp_generator.py",
+ "$generator_root/generators/mojom_js_generator.py",
+ "$generator_root/pylib/mojom/__init__.py",
+ "$generator_root/pylib/mojom/error.py",
+ "$generator_root/pylib/mojom/generate/__init__.py",
+ "$generator_root/pylib/mojom/generate/data.py",
+ "$generator_root/pylib/mojom/generate/generator.py",
+ "$generator_root/pylib/mojom/generate/module.py",
+ "$generator_root/pylib/mojom/generate/pack.py",
+ "$generator_root/pylib/mojom/generate/template_expander.py",
+ "$generator_root/pylib/mojom/parse/__init__.py",
+ "$generator_root/pylib/mojom/parse/ast.py",
+ "$generator_root/pylib/mojom/parse/lexer.py",
+ "$generator_root/pylib/mojom/parse/parser.py",
+ "$generator_root/pylib/mojom/parse/translate.py",
+ ]
+ generator_cpp_outputs = [
+ "$target_gen_dir/{{source_name_part}}.mojom.cc",
+ "$target_gen_dir/{{source_name_part}}.mojom.h",
+ "$target_gen_dir/{{source_name_part}}.mojom-internal.h",
+ ]
+ generator_js_outputs = [
+ "$target_gen_dir/{{source_name_part}}.mojom.js",
+ ]
+
+ target_visibility = ":$target_name"
+
+ generator_target_name = target_name + "_generator"
+ action_foreach(generator_target_name) {
+ visibility = target_visibility
+ script = generator_script
+ source_prereqs = generator_sources
+ sources = invoker.sources
+ outputs = generator_cpp_outputs + generator_js_outputs
+ args = [
+ "{{source}}",
+ "--use_chromium_bundled_pylibs",
+ "-d", rebase_path("//", root_build_dir),
+ "-o", rebase_path(target_gen_dir, root_build_dir),
+ ]
+ }
+
+ source_set(target_name) {
+ if (defined(invoker.visibility)) {
+ visibility = invoker.visibility
+ }
+ sources = process_file_template(invoker.sources, generator_cpp_outputs)
+ data = process_file_template(invoker.sources, generator_js_outputs)
+ deps = [
+ ":$generator_target_name",
+ "//mojo/public/cpp/bindings",
+ ]
+ }
+}
diff --git a/chromium/mojo/public/tools/bindings/mojom_bindings_generator.gypi b/chromium/mojo/public/tools/bindings/mojom_bindings_generator.gypi
new file mode 100644
index 00000000000..1341685a2ee
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/mojom_bindings_generator.gypi
@@ -0,0 +1,99 @@
+# Copyright 2013 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.
+
+{
+ 'rules': [
+ {
+ 'rule_name': 'Generate C++, JS and Java source files from mojom files',
+ 'extension': 'mojom',
+ 'variables': {
+ 'mojom_base_output_dir':
+ '<!(python <(DEPTH)/build/inverse_depth.py <(DEPTH))',
+ 'mojom_bindings_generator':
+ '<(DEPTH)/mojo/public/tools/bindings/mojom_bindings_generator.py',
+ 'java_out_dir': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/src',
+ },
+ 'inputs': [
+ '<(mojom_bindings_generator)',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/enum_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/params_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/constant_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/constants.java.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/enum_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/enum.java.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/header.java.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/java_templates/java_macros.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/js_templates/module.js.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/mojom_cpp_generator.py',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/mojom_java_generator.py',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/mojom_js_generator.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/__init__.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/error.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/data.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/generator.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/module.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/pack.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/ast.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/parser.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/translate.py',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom.cc',
+ '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom.h',
+ '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom.js',
+ '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom-internal.h',
+ ],
+ 'action': [
+ 'python', '<@(mojom_bindings_generator)',
+ './<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom',
+ '--use_chromium_bundled_pylibs',
+ '-d', '<(DEPTH)',
+ '-o', '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)',
+ '--java_output_directory=<(java_out_dir)',
+ ],
+ 'message': 'Generating Mojo bindings from <(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom',
+ 'process_outputs_as_sources': 1,
+ }
+ ],
+ 'include_dirs': [
+ '<(DEPTH)',
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(DEPTH)',
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ 'variables': {
+ 'generated_src_dirs': [
+ '<(PRODUCT_DIR)/java_mojo/<(_target_name)/src',
+ ],
+ },
+ },
+ 'hard_dependency': 1,
+}
diff --git a/chromium/mojo/public/tools/bindings/mojom_bindings_generator.py b/chromium/mojo/public/tools/bindings/mojom_bindings_generator.py
new file mode 100755
index 00000000000..93325767b2a
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/mojom_bindings_generator.py
@@ -0,0 +1,179 @@
+#!/usr/bin/env python
+# Copyright 2013 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.
+
+"""The frontend for the Mojo bindings system."""
+
+
+import argparse
+import imp
+import os
+import pprint
+import sys
+
+# Disable lint check for finding modules:
+# pylint: disable=F0401
+
+def _GetDirAbove(dirname):
+ """Returns the directory "above" this file containing |dirname| (which must
+ also be "above" this file)."""
+ path = os.path.abspath(__file__)
+ while True:
+ path, tail = os.path.split(path)
+ assert tail
+ if tail == dirname:
+ return path
+
+# Manually check for the command-line flag. (This isn't quite right, since it
+# ignores, e.g., "--", but it's close enough.)
+if "--use_chromium_bundled_pylibs" in sys.argv[1:]:
+ sys.path.insert(0, os.path.join(_GetDirAbove("mojo"), "third_party"))
+
+sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ "pylib"))
+
+from mojom.error import Error
+from mojom.generate.data import OrderedModuleFromData
+from mojom.parse.parser import Parse
+from mojom.parse.translate import Translate
+
+
+def LoadGenerators(generators_string):
+ if not generators_string:
+ return [] # No generators.
+
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ generators = []
+ for generator_name in [s.strip() for s in generators_string.split(",")]:
+ # "Built-in" generators:
+ if generator_name.lower() == "c++":
+ generator_name = os.path.join(script_dir, "generators",
+ "mojom_cpp_generator.py")
+ elif generator_name.lower() == "javascript":
+ generator_name = os.path.join(script_dir, "generators",
+ "mojom_js_generator.py")
+ elif generator_name.lower() == "java":
+ generator_name = os.path.join(script_dir, "generators",
+ "mojom_java_generator.py")
+ # Specified generator python module:
+ elif generator_name.endswith(".py"):
+ pass
+ else:
+ print "Unknown generator name %s" % generator_name
+ sys.exit(1)
+ generator_module = imp.load_source(os.path.basename(generator_name)[:-3],
+ generator_name)
+ generators.append(generator_module)
+ return generators
+
+
+def MakeImportStackMessage(imported_filename_stack):
+ """Make a (human-readable) message listing a chain of imports. (Returned
+ string begins with a newline (if nonempty) and does not end with one.)"""
+ return ''.join(
+ reversed(["\n %s was imported by %s" % (a, b) for (a, b) in \
+ zip(imported_filename_stack[1:], imported_filename_stack)]))
+
+
+# Disable check for dangerous default arguments (they're "private" keyword
+# arguments; note that we want |_processed_files| to memoize across invocations
+# of |ProcessFile()|):
+# pylint: disable=W0102
+def ProcessFile(args, remaining_args, generator_modules, filename,
+ _processed_files={}, _imported_filename_stack=None):
+ # Memoized results.
+ if filename in _processed_files:
+ return _processed_files[filename]
+
+ if _imported_filename_stack is None:
+ _imported_filename_stack = []
+
+ # Ensure we only visit each file once.
+ if filename in _imported_filename_stack:
+ print "%s: Error: Circular dependency" % filename + \
+ MakeImportStackMessage(_imported_filename_stack + [filename])
+ sys.exit(1)
+
+ try:
+ with open(filename) as f:
+ source = f.read()
+ except IOError as e:
+ print "%s: Error: %s" % (e.filename, e.strerror) + \
+ MakeImportStackMessage(_imported_filename_stack + [filename])
+ sys.exit(1)
+
+ try:
+ tree = Parse(source, filename)
+ except Error as e:
+ print str(e) + MakeImportStackMessage(_imported_filename_stack + [filename])
+ sys.exit(1)
+
+ dirname, name = os.path.split(filename)
+ mojom = Translate(tree, name)
+ if args.debug_print_intermediate:
+ pprint.PrettyPrinter().pprint(mojom)
+
+ # Process all our imports first and collect the module object for each.
+ # We use these to generate proper type info.
+ for import_data in mojom['imports']:
+ import_filename = os.path.join(dirname, import_data['filename'])
+ import_data['module'] = ProcessFile(
+ args, remaining_args, generator_modules, import_filename,
+ _processed_files=_processed_files,
+ _imported_filename_stack=_imported_filename_stack + [filename])
+
+ module = OrderedModuleFromData(mojom)
+
+ # Set the path as relative to the source root.
+ module.path = os.path.relpath(os.path.abspath(filename),
+ os.path.abspath(args.depth))
+
+ # Normalize to unix-style path here to keep the generators simpler.
+ module.path = module.path.replace('\\', '/')
+
+ for generator_module in generator_modules:
+ generator = generator_module.Generator(module, args.output_dir)
+ filtered_args = []
+ if hasattr(generator_module, 'GENERATOR_PREFIX'):
+ prefix = '--' + generator_module.GENERATOR_PREFIX + '_'
+ filtered_args = [arg for arg in remaining_args if arg.startswith(prefix)]
+ generator.GenerateFiles(filtered_args)
+
+ # Save result.
+ _processed_files[filename] = module
+ return module
+# pylint: enable=W0102
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Generate bindings from mojom files.")
+ parser.add_argument("filename", nargs="+",
+ help="mojom input file")
+ parser.add_argument("-d", "--depth", dest="depth", default=".",
+ help="depth from source root")
+ parser.add_argument("-o", "--output_dir", dest="output_dir", default=".",
+ help="output directory for generated files")
+ parser.add_argument("-g", "--generators", dest="generators_string",
+ metavar="GENERATORS", default="c++,javascript,java",
+ help="comma-separated list of generators")
+ parser.add_argument("--debug_print_intermediate", action="store_true",
+ help="print the intermediate representation")
+ parser.add_argument("--use_chromium_bundled_pylibs", action="store_true",
+ help="use Python modules bundled in the Chromium source")
+ (args, remaining_args) = parser.parse_known_args()
+
+ generator_modules = LoadGenerators(args.generators_string)
+
+ if not os.path.exists(args.output_dir):
+ os.makedirs(args.output_dir)
+
+ for filename in args.filename:
+ ProcessFile(args, remaining_args, generator_modules, filename)
+
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/chromium/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py b/chromium/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py
new file mode 100644
index 00000000000..de388561cb5
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/mojom_bindings_generator_unittest.py
@@ -0,0 +1,23 @@
+# Copyright 2014 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.
+
+import unittest
+
+from mojom_bindings_generator import MakeImportStackMessage
+
+
+class MojoBindingsGeneratorTest(unittest.TestCase):
+ """Tests mojo_bindings_generator."""
+
+ def testMakeImportStackMessage(self):
+ """Tests MakeImportStackMessage()."""
+ self.assertEquals(MakeImportStackMessage(["x"]), "")
+ self.assertEquals(MakeImportStackMessage(["x", "y"]),
+ "\n y was imported by x")
+ self.assertEquals(MakeImportStackMessage(["x", "y", "z"]),
+ "\n z was imported by y\n y was imported by x")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/__init__.py b/chromium/mojo/public/tools/bindings/pylib/mojom/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/__init__.py
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/error.py b/chromium/mojo/public/tools/bindings/pylib/mojom/error.py
new file mode 100644
index 00000000000..99522b9507f
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/error.py
@@ -0,0 +1,27 @@
+# Copyright 2014 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.
+
+class Error(Exception):
+ """Base class for Mojo IDL bindings parser/generator errors."""
+
+ def __init__(self, filename, message, lineno=None, addenda=None, **kwargs):
+ """|filename| is the (primary) file which caused the error, |message| is the
+ error message, |lineno| is the 1-based line number (or |None| if not
+ applicable/available), and |addenda| is a list of additional lines to append
+ to the final error message."""
+ Exception.__init__(self, **kwargs)
+ self.filename = filename
+ self.message = message
+ self.lineno = lineno
+ self.addenda = addenda
+
+ def __str__(self):
+ if self.lineno:
+ s = "%s:%d: Error: %s" % (self.filename, self.lineno, self.message)
+ else:
+ s = "%s: Error: %s" % (self.filename, self.message)
+ return "\n".join([s] + self.addenda) if self.addenda else s
+
+ def __repr__(self):
+ return str(self)
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/data.py b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/data.py
new file mode 100644
index 00000000000..608603b8584
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/data.py
@@ -0,0 +1,348 @@
+# Copyright 2013 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.
+
+# TODO(vtl): "data" is a pretty vague name. Rename it?
+
+import copy
+
+import module as mojom
+
+# This module provides a mechanism to turn mojom Modules to dictionaries and
+# back again. This can be used to persist a mojom Module created progromatically
+# or to read a dictionary from code or a file.
+# Example:
+# test_dict = {
+# 'name': 'test',
+# 'namespace': 'testspace',
+# 'structs': [{
+# 'name': 'teststruct',
+# 'fields': [
+# {'name': 'testfield1', 'kind': 'i32'},
+# {'name': 'testfield2', 'kind': 'a:i32', 'ordinal': 42}]}],
+# 'interfaces': [{
+# 'name': 'Server',
+# 'methods': [{
+# 'name': 'Foo',
+# 'parameters': [{
+# 'name': 'foo', 'kind': 'i32'},
+# {'name': 'bar', 'kind': 'a:x:teststruct'}],
+# 'ordinal': 42}]}]
+# }
+# test_module = data.ModuleFromData(test_dict)
+
+# Used to create a subclass of str that supports sorting by index, to make
+# pretty printing maintain the order.
+def istr(index, string):
+ class IndexedString(str):
+ def __lt__(self, other):
+ return self.__index__ < other.__index__
+
+ istr = IndexedString(string)
+ istr.__index__ = index
+ return istr
+
+def LookupKind(kinds, spec, scope):
+ """Tries to find which Kind a spec refers to, given the scope in which its
+ referenced. Starts checking from the narrowest scope to most general. For
+ example, given a struct field like
+ Foo.Bar x;
+ Foo.Bar could refer to the type 'Bar' in the 'Foo' namespace, or an inner
+ type 'Bar' in the struct 'Foo' in the current namespace.
+
+ |scope| is a tuple that looks like (namespace, struct/interface), referring
+ to the location where the type is referenced."""
+ if spec.startswith('x:'):
+ name = spec[2:]
+ for i in xrange(len(scope), -1, -1):
+ test_spec = 'x:'
+ if i > 0:
+ test_spec += '.'.join(scope[:i]) + '.'
+ test_spec += name
+ kind = kinds.get(test_spec)
+ if kind:
+ return kind
+
+ return kinds.get(spec)
+
+def LookupValue(values, name, scope):
+ """Like LookupKind, but for constant values."""
+ for i in xrange(len(scope), -1, -1):
+ if i > 0:
+ test_spec = '.'.join(scope[:i]) + '.'
+ test_spec += name
+ value = values.get(test_spec)
+ if value:
+ return value
+
+ return values.get(name)
+
+def FixupExpression(module, value, scope):
+ """Translates an IDENTIFIER into a structured Value object."""
+ if isinstance(value, tuple) and value[0] == 'IDENTIFIER':
+ result = LookupValue(module.values, value[1], scope)
+ if result:
+ return result
+ return value
+
+def KindToData(kind):
+ return kind.spec
+
+def KindFromData(kinds, data, scope):
+ kind = LookupKind(kinds, data, scope)
+ if kind:
+ return kind
+ if data.startswith('a:'):
+ kind = mojom.Array()
+ kind.kind = KindFromData(kinds, data[2:], scope)
+ elif data.startswith('r:'):
+ kind = mojom.InterfaceRequest()
+ kind.kind = KindFromData(kinds, data[2:], scope)
+ else:
+ kind = mojom.Kind()
+ kind.spec = data
+ kinds[data] = kind
+ return kind
+
+def KindFromImport(original_kind, imported_from):
+ """Used with 'import module' - clones the kind imported from the given
+ module's namespace. Only used with Structs, Interfaces and Enums."""
+ kind = copy.deepcopy(original_kind)
+ kind.imported_from = imported_from
+ return kind
+
+def ImportFromData(module, data):
+ import_module = data['module']
+
+ import_item = {}
+ import_item['module_name'] = import_module.name
+ import_item['namespace'] = import_module.namespace
+ import_item['module'] = import_module
+
+ # Copy the struct kinds from our imports into the current module.
+ for kind in import_module.kinds.itervalues():
+ if (isinstance(kind, (mojom.Struct, mojom.Enum, mojom.Interface)) and
+ kind.imported_from is None):
+ kind = KindFromImport(kind, import_item)
+ module.kinds[kind.spec] = kind
+ # Ditto for values.
+ for value in import_module.values.itervalues():
+ if value.imported_from is None:
+ value = copy.deepcopy(value)
+ value.imported_from = import_item
+ module.values[value.GetSpec()] = value
+
+ return import_item
+
+def StructToData(struct):
+ return {
+ istr(0, 'name'): struct.name,
+ istr(1, 'fields'): map(FieldToData, struct.fields)
+ }
+
+def StructFromData(module, data):
+ struct = mojom.Struct(module=module)
+ struct.name = data['name']
+ struct.attributes = data['attributes']
+ struct.spec = 'x:' + module.namespace + '.' + struct.name
+ module.kinds[struct.spec] = struct
+ struct.enums = map(lambda enum:
+ EnumFromData(module, enum, struct), data['enums'])
+ struct.constants = map(lambda constant:
+ ConstantFromData(module, constant, struct), data['constants'])
+ # Stash fields data here temporarily.
+ struct.fields_data = data['fields']
+ return struct
+
+def FieldToData(field):
+ data = {
+ istr(0, 'name'): field.name,
+ istr(1, 'kind'): KindToData(field.kind)
+ }
+ if field.ordinal != None:
+ data[istr(2, 'ordinal')] = field.ordinal
+ if field.default != None:
+ data[istr(3, 'default')] = field.default
+ return data
+
+def FieldFromData(module, data, struct):
+ field = mojom.Field()
+ field.name = data['name']
+ field.kind = KindFromData(
+ module.kinds, data['kind'], (module.namespace, struct.name))
+ field.ordinal = data.get('ordinal')
+ field.default = FixupExpression(
+ module, data.get('default'), (module.namespace, struct.name))
+ return field
+
+def ParameterToData(parameter):
+ data = {
+ istr(0, 'name'): parameter.name,
+ istr(1, 'kind'): parameter.kind.spec
+ }
+ if parameter.ordinal != None:
+ data[istr(2, 'ordinal')] = parameter.ordinal
+ if parameter.default != None:
+ data[istr(3, 'default')] = parameter.default
+ return data
+
+def ParameterFromData(module, data, interface):
+ parameter = mojom.Parameter()
+ parameter.name = data['name']
+ parameter.kind = KindFromData(
+ module.kinds, data['kind'], (module.namespace, interface.name))
+ parameter.ordinal = data.get('ordinal')
+ parameter.default = data.get('default')
+ return parameter
+
+def MethodToData(method):
+ data = {
+ istr(0, 'name'): method.name,
+ istr(1, 'parameters'): map(ParameterToData, method.parameters)
+ }
+ if method.ordinal != None:
+ data[istr(2, 'ordinal')] = method.ordinal
+ if method.response_parameters != None:
+ data[istr(3, 'response_parameters')] = map(
+ ParameterToData, method.response_parameters)
+ return data
+
+def MethodFromData(module, data, interface):
+ method = mojom.Method(interface, data['name'], ordinal=data.get('ordinal'))
+ method.default = data.get('default')
+ method.parameters = map(lambda parameter:
+ ParameterFromData(module, parameter, interface), data['parameters'])
+ if data.has_key('response_parameters'):
+ method.response_parameters = map(
+ lambda parameter: ParameterFromData(module, parameter, interface),
+ data['response_parameters'])
+ return method
+
+def InterfaceToData(interface):
+ return {
+ istr(0, 'name'): interface.name,
+ istr(1, 'client'): interface.client,
+ istr(2, 'methods'): map(MethodToData, interface.methods)
+ }
+
+def InterfaceFromData(module, data):
+ interface = mojom.Interface(module=module)
+ interface.name = data['name']
+ interface.spec = 'x:' + module.namespace + '.' + interface.name
+ interface.client = data['client'] if data.has_key('client') else None
+ module.kinds[interface.spec] = interface
+ interface.enums = map(lambda enum:
+ EnumFromData(module, enum, interface), data['enums'])
+ interface.constants = map(lambda constant:
+ ConstantFromData(module, constant, interface), data['constants'])
+ # Stash methods data here temporarily.
+ interface.methods_data = data['methods']
+ return interface
+
+def EnumFieldFromData(module, enum, data, parent_kind):
+ field = mojom.EnumField()
+ field.name = data['name']
+ # TODO(mpcomplete): FixupExpression should be done in the second pass,
+ # so constants and enums can refer to each other.
+ # TODO(mpcomplete): But then, what if constants are initialized to an enum? Or
+ # vice versa?
+ if parent_kind:
+ field.value = FixupExpression(
+ module, data['value'], (module.namespace, parent_kind.name))
+ else:
+ field.value = FixupExpression(
+ module, data['value'], (module.namespace, ))
+ value = mojom.EnumValue(module, enum, field)
+ module.values[value.GetSpec()] = value
+ return field
+
+def EnumFromData(module, data, parent_kind):
+ enum = mojom.Enum(module=module)
+ enum.name = data['name']
+ name = enum.name
+ if parent_kind:
+ name = parent_kind.name + '.' + name
+ enum.spec = 'x:%s.%s' % (module.namespace, name)
+ enum.parent_kind = parent_kind
+
+ enum.fields = map(
+ lambda field: EnumFieldFromData(module, enum, field, parent_kind),
+ data['fields'])
+ module.kinds[enum.spec] = enum
+ return enum
+
+def ConstantFromData(module, data, parent_kind):
+ constant = mojom.Constant()
+ constant.name = data['name']
+ if parent_kind:
+ scope = (module.namespace, parent_kind.name)
+ else:
+ scope = (module.namespace, )
+ # TODO(mpcomplete): maybe we should only support POD kinds.
+ constant.kind = KindFromData(module.kinds, data['kind'], scope)
+ constant.value = FixupExpression(module, data.get('value'), scope)
+
+ value = mojom.NamedValue(module, parent_kind, constant.name)
+ module.values[value.GetSpec()] = value
+ return constant
+
+def ModuleToData(module):
+ return {
+ istr(0, 'name'): module.name,
+ istr(1, 'namespace'): module.namespace,
+ istr(2, 'structs'): map(StructToData, module.structs),
+ istr(3, 'interfaces'): map(InterfaceToData, module.interfaces)
+ }
+
+def ModuleFromData(data):
+ module = mojom.Module()
+ module.kinds = {}
+ for kind in mojom.PRIMITIVES:
+ module.kinds[kind.spec] = kind
+
+ module.values = {}
+
+ module.name = data['name']
+ module.namespace = data['namespace']
+ module.attributes = data['attributes']
+ # Imports must come first, because they add to module.kinds which is used
+ # by by the others.
+ module.imports = map(
+ lambda import_data: ImportFromData(module, import_data),
+ data['imports'])
+
+ # First pass collects kinds.
+ module.enums = map(
+ lambda enum: EnumFromData(module, enum, None), data['enums'])
+ module.structs = map(
+ lambda struct: StructFromData(module, struct), data['structs'])
+ module.interfaces = map(
+ lambda interface: InterfaceFromData(module, interface),
+ data['interfaces'])
+ module.constants = map(
+ lambda constant: ConstantFromData(module, constant, None),
+ data['constants'])
+
+ # Second pass expands fields and methods. This allows fields and parameters
+ # to refer to kinds defined anywhere in the mojom.
+ for struct in module.structs:
+ struct.fields = map(lambda field:
+ FieldFromData(module, field, struct), struct.fields_data)
+ del struct.fields_data
+ for interface in module.interfaces:
+ interface.methods = map(lambda method:
+ MethodFromData(module, method, interface), interface.methods_data)
+ del interface.methods_data
+
+ return module
+
+def OrderedModuleFromData(data):
+ module = ModuleFromData(data)
+ next_interface_ordinal = 0
+ for interface in module.interfaces:
+ next_ordinal = 0
+ for method in interface.methods:
+ if method.ordinal is None:
+ method.ordinal = next_ordinal
+ next_ordinal = method.ordinal + 1
+ return module
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py
new file mode 100644
index 00000000000..096554c61a9
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py
@@ -0,0 +1,86 @@
+# Copyright 2013 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.
+
+import sys
+
+import data
+import test_support
+
+EXPECT_EQ = test_support.EXPECT_EQ
+EXPECT_TRUE = test_support.EXPECT_TRUE
+RunTest = test_support.RunTest
+
+
+def DeepEquals(d1, d2):
+ if d1 == d2:
+ return True
+ if d2.__class__ != d2.__class__:
+ return False
+ if isinstance(d1, dict):
+ if set(d1.keys()) != set(d2.keys()):
+ return False
+ for key in d1.keys():
+ if not DeepEquals(d1[key], d2[key]):
+ return False
+ return True
+ if isinstance(d1, (list, tuple)):
+ if len(d1) != len(d2):
+ return False
+ for i in range(len(d1)):
+ if not DeepEquals(d1[i], d2[i]):
+ return False
+ return True
+ return False
+
+
+test_dict = {
+ 'name': 'test',
+ 'namespace': 'testspace',
+ 'structs': [{
+ 'name': 'teststruct',
+ 'fields': [
+ {'name': 'testfield1', 'kind': 'i32'},
+ {'name': 'testfield2', 'kind': 'a:i32', 'ordinal': 42}]}],
+ 'interfaces': [{
+ 'name': 'Server',
+ 'client': None,
+ 'methods': [{
+ 'name': 'Foo',
+ 'parameters': [
+ {'name': 'foo', 'kind': 'i32'},
+ {'name': 'bar', 'kind': 'a:x:teststruct'}],
+ 'ordinal': 42}]}]
+}
+
+
+def TestRead():
+ module = data.ModuleFromData(test_dict)
+ return test_support.TestTestModule(module)
+
+
+def TestWrite():
+ module = test_support.BuildTestModule()
+ d = data.ModuleToData(module)
+ return EXPECT_TRUE(DeepEquals(test_dict, d))
+
+
+def TestWriteRead():
+ module1 = test_support.BuildTestModule()
+
+ dict1 = data.ModuleToData(module1)
+ module2 = data.ModuleFromData(dict1)
+ return EXPECT_TRUE(test_support.ModulesAreEqual(module1, module2))
+
+
+def Main(args):
+ errors = 0
+ errors += RunTest(TestWriteRead)
+ errors += RunTest(TestRead)
+ errors += RunTest(TestWrite)
+
+ return errors
+
+
+if __name__ == '__main__':
+ sys.exit(Main(sys.argv[1:]))
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/generator.py b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
new file mode 100644
index 00000000000..707028568c3
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
@@ -0,0 +1,90 @@
+# Copyright 2013 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.
+
+"""Code shared by the various language-specific code generators."""
+
+from functools import partial
+import os.path
+
+import module as mojom
+import pack
+
+def GetStructFromMethod(method):
+ """Converts a method's parameters into the fields of a struct."""
+ params_class = "%s_%s_Params" % (method.interface.name, method.name)
+ struct = mojom.Struct(params_class, module=method.interface.module)
+ for param in method.parameters:
+ struct.AddField(param.name, param.kind, param.ordinal)
+ struct.packed = pack.PackedStruct(struct)
+ return struct
+
+def GetResponseStructFromMethod(method):
+ """Converts a method's response_parameters into the fields of a struct."""
+ params_class = "%s_%s_ResponseParams" % (method.interface.name, method.name)
+ struct = mojom.Struct(params_class, module=method.interface.module)
+ for param in method.response_parameters:
+ struct.AddField(param.name, param.kind, param.ordinal)
+ struct.packed = pack.PackedStruct(struct)
+ return struct
+
+def GetDataHeader(exported, struct):
+ struct.packed = pack.PackedStruct(struct)
+ struct.bytes = pack.GetByteLayout(struct.packed)
+ struct.exported = exported
+ return struct
+
+def IsStringKind(kind):
+ return kind.spec == 's'
+
+def IsEnumKind(kind):
+ return isinstance(kind, mojom.Enum)
+
+def IsObjectKind(kind):
+ return isinstance(kind, (mojom.Struct, mojom.Array)) or IsStringKind(kind)
+
+def IsHandleKind(kind):
+ return kind.spec.startswith('h') or \
+ isinstance(kind, mojom.Interface) or \
+ isinstance(kind, mojom.InterfaceRequest)
+
+def IsInterfaceKind(kind):
+ return isinstance(kind, mojom.Interface)
+
+def IsInterfaceRequestKind(kind):
+ return isinstance(kind, mojom.InterfaceRequest)
+
+def IsMoveOnlyKind(kind):
+ return IsObjectKind(kind) or IsHandleKind(kind)
+
+def StudlyCapsToCamel(studly):
+ return studly[0].lower() + studly[1:]
+
+class Generator(object):
+ # Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all
+ # files to stdout.
+ def __init__(self, module, output_dir=None):
+ self.module = module
+ self.output_dir = output_dir
+
+ def GetStructsFromMethods(self):
+ result = []
+ for interface in self.module.interfaces:
+ for method in interface.methods:
+ result.append(GetStructFromMethod(method))
+ if method.response_parameters != None:
+ result.append(GetResponseStructFromMethod(method))
+ return map(partial(GetDataHeader, False), result)
+
+ def GetStructs(self):
+ return map(partial(GetDataHeader, True), self.module.structs)
+
+ def Write(self, contents, filename):
+ if self.output_dir is None:
+ print contents
+ return
+ with open(os.path.join(self.output_dir, filename), "w+") as f:
+ f.write(contents)
+
+ def GenerateFiles(self, args):
+ raise NotImplementedError("Subclasses must override/implement this method")
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/module.py b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/module.py
new file mode 100644
index 00000000000..345f432030f
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/module.py
@@ -0,0 +1,217 @@
+# Copyright 2013 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.
+
+# This module's classes provide an interface to mojo modules. Modules are
+# collections of interfaces and structs to be used by mojo ipc clients and
+# servers.
+#
+# A simple interface would be created this way:
+# module = mojom.generate.module.Module('Foo')
+# interface = module.AddInterface('Bar')
+# method = interface.AddMethod('Tat', 0)
+# method.AddParameter('baz', 0, mojom.INT32)
+#
+
+class Kind(object):
+ def __init__(self, spec=None):
+ self.spec = spec
+ self.parent_kind = None
+
+# Initialize the set of primitive types. These can be accessed by clients.
+BOOL = Kind('b')
+INT8 = Kind('i8')
+INT16 = Kind('i16')
+INT32 = Kind('i32')
+INT64 = Kind('i64')
+UINT8 = Kind('u8')
+UINT16 = Kind('u16')
+UINT32 = Kind('u32')
+UINT64 = Kind('u64')
+FLOAT = Kind('f')
+DOUBLE = Kind('d')
+STRING = Kind('s')
+HANDLE = Kind('h')
+DCPIPE = Kind('h:d:c')
+DPPIPE = Kind('h:d:p')
+MSGPIPE = Kind('h:m')
+SHAREDBUFFER = Kind('h:s')
+
+
+# Collection of all Primitive types
+PRIMITIVES = (
+ BOOL,
+ INT8,
+ INT16,
+ INT32,
+ INT64,
+ UINT8,
+ UINT16,
+ UINT32,
+ UINT64,
+ FLOAT,
+ DOUBLE,
+ STRING,
+ HANDLE,
+ DCPIPE,
+ DPPIPE,
+ MSGPIPE,
+ SHAREDBUFFER
+)
+
+
+class NamedValue(object):
+ def __init__(self, module, parent_kind, name):
+ self.module = module
+ self.namespace = module.namespace
+ self.parent_kind = parent_kind
+ self.name = name
+ self.imported_from = None
+
+ def GetSpec(self):
+ return (self.namespace + '.' +
+ (self.parent_kind and (self.parent_kind.name + '.') or "") +
+ self.name)
+
+
+class EnumValue(NamedValue):
+ def __init__(self, module, enum, field):
+ NamedValue.__init__(self, module, enum.parent_kind, field.name)
+ self.enum_name = enum.name
+
+
+class Constant(object):
+ def __init__(self, name=None, kind=None, value=None):
+ self.name = name
+ self.kind = kind
+ self.value = value
+
+
+class Field(object):
+ def __init__(self, name=None, kind=None, ordinal=None, default=None):
+ self.name = name
+ self.kind = kind
+ self.ordinal = ordinal
+ self.default = default
+
+
+class Struct(Kind):
+ def __init__(self, name=None, module=None):
+ self.name = name
+ self.module = module
+ self.imported_from = None
+ if name != None:
+ spec = 'x:' + name
+ else:
+ spec = None
+ Kind.__init__(self, spec)
+ self.fields = []
+
+ def AddField(self, name, kind, ordinal=None, default=None):
+ field = Field(name, kind, ordinal, default)
+ self.fields.append(field)
+ return field
+
+
+class Array(Kind):
+ def __init__(self, kind=None):
+ self.kind = kind
+ if kind != None:
+ Kind.__init__(self, 'a:' + kind.spec)
+ else:
+ Kind.__init__(self)
+
+
+class InterfaceRequest(Kind):
+ def __init__(self, kind=None):
+ self.kind = kind
+ if kind != None:
+ Kind.__init__(self, 'r:' + kind.spec)
+ else:
+ Kind.__init__(self)
+
+
+class Parameter(object):
+ def __init__(self, name=None, kind=None, ordinal=None, default=None):
+ self.name = name
+ self.ordinal = ordinal
+ self.kind = kind
+ self.default = default
+
+
+class Method(object):
+ def __init__(self, interface, name, ordinal=None):
+ self.interface = interface
+ self.name = name
+ self.ordinal = ordinal
+ self.parameters = []
+ self.response_parameters = None
+
+ def AddParameter(self, name, kind, ordinal=None, default=None):
+ parameter = Parameter(name, kind, ordinal, default)
+ self.parameters.append(parameter)
+ return parameter
+
+ def AddResponseParameter(self, name, kind, ordinal=None, default=None):
+ if self.response_parameters == None:
+ self.response_parameters = []
+ parameter = Parameter(name, kind, ordinal, default)
+ self.response_parameters.append(parameter)
+ return parameter
+
+
+class Interface(Kind):
+ def __init__(self, name=None, client=None, module=None):
+ self.module = module
+ self.name = name
+ self.imported_from = None
+ if name != None:
+ spec = 'x:' + name
+ else:
+ spec = None
+ Kind.__init__(self, spec)
+ self.client = client
+ self.methods = []
+
+ def AddMethod(self, name, ordinal=None):
+ method = Method(self, name, ordinal=ordinal)
+ self.methods.append(method)
+ return method
+
+
+class EnumField(object):
+ def __init__(self, name=None, value=None):
+ self.name = name
+ self.value = value
+
+
+class Enum(Kind):
+ def __init__(self, name=None, module=None):
+ self.module = module
+ self.name = name
+ self.imported_from = None
+ if name != None:
+ spec = 'x:' + name
+ else:
+ spec = None
+ Kind.__init__(self, spec)
+ self.fields = []
+
+
+class Module(object):
+ def __init__(self, name=None, namespace=None):
+ self.name = name
+ self.path = name
+ self.namespace = namespace
+ self.structs = []
+ self.interfaces = []
+
+ def AddInterface(self, name):
+ interface=Interface(name, module=self);
+ self.interfaces.append(interface)
+ return interface
+
+ def AddStruct(self, name):
+ struct=Struct(name, module=self)
+ self.structs.append(struct)
+ return struct
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/module_tests.py b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/module_tests.py
new file mode 100644
index 00000000000..a887686e1b5
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/module_tests.py
@@ -0,0 +1,34 @@
+# Copyright 2013 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.
+
+import sys
+
+import test_support
+
+EXPECT_EQ = test_support.EXPECT_EQ
+EXPECT_TRUE = test_support.EXPECT_TRUE
+RunTest = test_support.RunTest
+ModulesAreEqual = test_support.ModulesAreEqual
+BuildTestModule = test_support.BuildTestModule
+TestTestModule = test_support.TestTestModule
+
+
+def BuildAndTestModule():
+ return TestTestModule(BuildTestModule())
+
+
+def TestModulesEqual():
+ return EXPECT_TRUE(ModulesAreEqual(BuildTestModule(), BuildTestModule()))
+
+
+def Main(args):
+ errors = 0
+ errors += RunTest(BuildAndTestModule)
+ errors += RunTest(TestModulesEqual)
+
+ return errors
+
+
+if __name__ == '__main__':
+ sys.exit(Main(sys.argv[1:]))
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/pack.py b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/pack.py
new file mode 100644
index 00000000000..21d2c184180
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/pack.py
@@ -0,0 +1,150 @@
+# Copyright 2013 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.
+
+import module as mojom
+
+# This module provides a mechanism for determining the packed order and offsets
+# of a mojom.Struct.
+#
+# ps = pack.PackedStruct(struct)
+# ps.packed_fields will access a list of PackedField objects, each of which
+# will have an offset, a size and a bit (for mojom.BOOLs).
+
+class PackedField(object):
+ kind_to_size = {
+ mojom.BOOL: 1,
+ mojom.INT8: 1,
+ mojom.UINT8: 1,
+ mojom.INT16: 2,
+ mojom.UINT16: 2,
+ mojom.INT32: 4,
+ mojom.UINT32: 4,
+ mojom.FLOAT: 4,
+ mojom.HANDLE: 4,
+ mojom.MSGPIPE: 4,
+ mojom.SHAREDBUFFER: 4,
+ mojom.DCPIPE: 4,
+ mojom.DPPIPE: 4,
+ mojom.INT64: 8,
+ mojom.UINT64: 8,
+ mojom.DOUBLE: 8,
+ mojom.STRING: 8
+ }
+
+ @classmethod
+ def GetSizeForKind(cls, kind):
+ if isinstance(kind, mojom.Array) or isinstance(kind, mojom.Struct):
+ return 8
+ if isinstance(kind, mojom.Interface) or \
+ isinstance(kind, mojom.InterfaceRequest):
+ kind = mojom.MSGPIPE
+ if isinstance(kind, mojom.Enum):
+ # TODO(mpcomplete): what about big enums?
+ return cls.kind_to_size[mojom.INT32]
+ if not kind in cls.kind_to_size:
+ raise Exception("Invalid kind: %s" % kind.spec)
+ return cls.kind_to_size[kind]
+
+ def __init__(self, field, ordinal):
+ self.field = field
+ self.ordinal = ordinal
+ self.size = self.GetSizeForKind(field.kind)
+ self.offset = None
+ self.bit = None
+
+
+# Returns the pad necessary to reserve space for alignment of |size|.
+def GetPad(offset, size):
+ return (size - (offset % size)) % size
+
+
+# Returns a 2-tuple of the field offset and bit (for BOOLs)
+def GetFieldOffset(field, last_field):
+ if field.field.kind == mojom.BOOL and \
+ last_field.field.kind == mojom.BOOL and \
+ last_field.bit < 7:
+ return (last_field.offset, last_field.bit + 1)
+
+ offset = last_field.offset + last_field.size
+ pad = GetPad(offset, field.size)
+ return (offset + pad, 0)
+
+
+class PackedStruct(object):
+ def __init__(self, struct):
+ self.struct = struct
+ self.packed_fields = []
+
+ # No fields.
+ if (len(struct.fields) == 0):
+ return
+
+ # Start by sorting by ordinal.
+ src_fields = []
+ ordinal = 0
+ for field in struct.fields:
+ if field.ordinal is not None:
+ ordinal = field.ordinal
+ src_fields.append(PackedField(field, ordinal))
+ ordinal += 1
+ src_fields.sort(key=lambda field: field.ordinal)
+
+ src_field = src_fields[0]
+ src_field.offset = 0
+ src_field.bit = 0
+ # dst_fields will contain each of the fields, in increasing offset order.
+ dst_fields = self.packed_fields
+ dst_fields.append(src_field)
+
+ # Then find first slot that each field will fit.
+ for src_field in src_fields[1:]:
+ last_field = dst_fields[0]
+ for i in xrange(1, len(dst_fields)):
+ next_field = dst_fields[i]
+ offset, bit = GetFieldOffset(src_field, last_field)
+ if offset + src_field.size <= next_field.offset:
+ # Found hole.
+ src_field.offset = offset
+ src_field.bit = bit
+ dst_fields.insert(i, src_field)
+ break
+ last_field = next_field
+ if src_field.offset is None:
+ # Add to end
+ src_field.offset, src_field.bit = GetFieldOffset(src_field, last_field)
+ dst_fields.append(src_field)
+
+ def GetTotalSize(self):
+ if not self.packed_fields:
+ return 0
+ last_field = self.packed_fields[-1]
+ offset = last_field.offset + last_field.size
+ pad = GetPad(offset, 8)
+ return offset + pad
+
+
+class ByteInfo(object):
+ def __init__(self):
+ self.is_padding = False
+ self.packed_fields = []
+
+
+def GetByteLayout(packed_struct):
+ bytes = [ByteInfo() for i in xrange(packed_struct.GetTotalSize())]
+
+ limit_of_previous_field = 0
+ for packed_field in packed_struct.packed_fields:
+ for i in xrange(limit_of_previous_field, packed_field.offset):
+ bytes[i].is_padding = True
+ bytes[packed_field.offset].packed_fields.append(packed_field)
+ limit_of_previous_field = packed_field.offset + packed_field.size
+
+ for i in xrange(limit_of_previous_field, len(bytes)):
+ bytes[i].is_padding = True
+
+ for byte in bytes:
+ # A given byte cannot both be padding and have a fields packed into it.
+ assert not (byte.is_padding and byte.packed_fields)
+
+ return bytes
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py
new file mode 100644
index 00000000000..4e61004e5f4
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/pack_tests.py
@@ -0,0 +1,176 @@
+# Copyright 2013 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.
+
+import sys
+
+import module as mojom
+import pack
+import test_support
+
+
+EXPECT_EQ = test_support.EXPECT_EQ
+EXPECT_TRUE = test_support.EXPECT_TRUE
+RunTest = test_support.RunTest
+
+
+def TestOrdinalOrder():
+ errors = 0
+ struct = mojom.Struct('test')
+ struct.AddField('testfield1', mojom.INT32, 2)
+ struct.AddField('testfield2', mojom.INT32, 1)
+ ps = pack.PackedStruct(struct)
+
+ errors += EXPECT_EQ(2, len(ps.packed_fields))
+ errors += EXPECT_EQ('testfield2', ps.packed_fields[0].field.name)
+ errors += EXPECT_EQ('testfield1', ps.packed_fields[1].field.name)
+
+ return errors
+
+def TestZeroFields():
+ errors = 0
+ struct = mojom.Struct('test')
+ ps = pack.PackedStruct(struct)
+ errors += EXPECT_EQ(0, len(ps.packed_fields))
+ return errors
+
+
+def TestOneField():
+ errors = 0
+ struct = mojom.Struct('test')
+ struct.AddField('testfield1', mojom.INT8)
+ ps = pack.PackedStruct(struct)
+ errors += EXPECT_EQ(1, len(ps.packed_fields))
+ return errors
+
+# Pass three tuples.
+# |kinds| is a sequence of mojom.Kinds that specify the fields that are to
+# be created.
+# |fields| is the expected order of the resulting fields, with the integer
+# "1" first.
+# |offsets| is the expected order of offsets, with the integer "0" first.
+def TestSequence(kinds, fields, offsets):
+ errors = 0
+ struct = mojom.Struct('test')
+ index = 1
+ for kind in kinds:
+ struct.AddField("%d" % index, kind)
+ index += 1
+ ps = pack.PackedStruct(struct)
+ num_fields = len(ps.packed_fields)
+ errors += EXPECT_EQ(len(kinds), num_fields)
+ for i in xrange(num_fields):
+ EXPECT_EQ("%d" % fields[i], ps.packed_fields[i].field.name)
+ EXPECT_EQ(offsets[i], ps.packed_fields[i].offset)
+
+ return errors
+
+
+def TestPaddingPackedInOrder():
+ return TestSequence(
+ (mojom.INT8, mojom.UINT8, mojom.INT32),
+ (1, 2, 3),
+ (0, 1, 4))
+
+
+def TestPaddingPackedOutOfOrder():
+ return TestSequence(
+ (mojom.INT8, mojom.INT32, mojom.UINT8),
+ (1, 3, 2),
+ (0, 1, 4))
+
+
+def TestPaddingPackedOverflow():
+ kinds = (mojom.INT8, mojom.INT32, mojom.INT16, mojom.INT8, mojom.INT8)
+ # 2 bytes should be packed together first, followed by short, then by int.
+ fields = (1, 4, 3, 2, 5)
+ offsets = (0, 1, 2, 4, 8)
+ return TestSequence(kinds, fields, offsets)
+
+
+def TestAllTypes():
+ struct = mojom.Struct('test')
+ array = mojom.Array()
+ return TestSequence(
+ (mojom.BOOL, mojom.INT8, mojom.STRING, mojom.UINT8,
+ mojom.INT16, mojom.DOUBLE, mojom.UINT16,
+ mojom.INT32, mojom.UINT32, mojom.INT64,
+ mojom.FLOAT, mojom.STRING, mojom.HANDLE,
+ mojom.UINT64, mojom.Struct('test'), mojom.Array()),
+ (1, 2, 4, 5, 7, 3, 6, 8, 9, 10, 11, 13, 12, 14, 15, 16, 17),
+ (0, 1, 2, 4, 6, 8, 16, 24, 28, 32, 40, 44, 48, 56, 64, 72, 80))
+
+
+def TestPaddingPackedOutOfOrderByOrdinal():
+ errors = 0
+ struct = mojom.Struct('test')
+ struct.AddField('testfield1', mojom.INT8)
+ struct.AddField('testfield3', mojom.UINT8, 3)
+ struct.AddField('testfield2', mojom.INT32, 2)
+ ps = pack.PackedStruct(struct)
+ errors += EXPECT_EQ(3, len(ps.packed_fields))
+
+ # Second byte should be packed in behind first, altering order.
+ errors += EXPECT_EQ('testfield1', ps.packed_fields[0].field.name)
+ errors += EXPECT_EQ('testfield3', ps.packed_fields[1].field.name)
+ errors += EXPECT_EQ('testfield2', ps.packed_fields[2].field.name)
+
+ # Second byte should be packed with first.
+ errors += EXPECT_EQ(0, ps.packed_fields[0].offset)
+ errors += EXPECT_EQ(1, ps.packed_fields[1].offset)
+ errors += EXPECT_EQ(4, ps.packed_fields[2].offset)
+
+ return errors
+
+
+def TestBools():
+ errors = 0
+ struct = mojom.Struct('test')
+ struct.AddField('bit0', mojom.BOOL)
+ struct.AddField('bit1', mojom.BOOL)
+ struct.AddField('int', mojom.INT32)
+ struct.AddField('bit2', mojom.BOOL)
+ struct.AddField('bit3', mojom.BOOL)
+ struct.AddField('bit4', mojom.BOOL)
+ struct.AddField('bit5', mojom.BOOL)
+ struct.AddField('bit6', mojom.BOOL)
+ struct.AddField('bit7', mojom.BOOL)
+ struct.AddField('bit8', mojom.BOOL)
+ ps = pack.PackedStruct(struct)
+ errors += EXPECT_EQ(10, len(ps.packed_fields))
+
+ # First 8 bits packed together.
+ for i in xrange(8):
+ pf = ps.packed_fields[i]
+ errors += EXPECT_EQ(0, pf.offset)
+ errors += EXPECT_EQ("bit%d" % i, pf.field.name)
+ errors += EXPECT_EQ(i, pf.bit)
+
+ # Ninth bit goes into second byte.
+ errors += EXPECT_EQ("bit8", ps.packed_fields[8].field.name)
+ errors += EXPECT_EQ(1, ps.packed_fields[8].offset)
+ errors += EXPECT_EQ(0, ps.packed_fields[8].bit)
+
+ # int comes last.
+ errors += EXPECT_EQ("int", ps.packed_fields[9].field.name)
+ errors += EXPECT_EQ(4, ps.packed_fields[9].offset)
+
+ return errors
+
+
+def Main(args):
+ errors = 0
+ errors += RunTest(TestZeroFields)
+ errors += RunTest(TestOneField)
+ errors += RunTest(TestPaddingPackedInOrder)
+ errors += RunTest(TestPaddingPackedOutOfOrder)
+ errors += RunTest(TestPaddingPackedOverflow)
+ errors += RunTest(TestAllTypes)
+ errors += RunTest(TestPaddingPackedOutOfOrderByOrdinal)
+ errors += RunTest(TestBools)
+
+ return errors
+
+
+if __name__ == '__main__':
+ sys.exit(Main(sys.argv[1:]))
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py
new file mode 100755
index 00000000000..41f11a2b71e
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/run_tests.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+# Copyright 2013 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.
+
+""" Test runner for Mojom """
+
+import subprocess
+import sys
+
+def TestMojom(testname, args):
+ print '\nRunning unit tests for %s.' % testname
+ try:
+ args = [sys.executable, testname] + args
+ subprocess.check_call(args, stdout=sys.stdout)
+ print 'Succeeded'
+ return 0
+ except subprocess.CalledProcessError as err:
+ print 'Failed with %s.' % str(err)
+ return 1
+
+
+def main(args):
+ errors = 0
+ errors += TestMojom('data_tests.py', ['--test'])
+ errors += TestMojom('module_tests.py', ['--test'])
+ errors += TestMojom('pack_tests.py', ['--test'])
+
+ if errors:
+ print '\nFailed tests.'
+ return min(errors, 127) # Make sure the return value doesn't "wrap".
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
new file mode 100644
index 00000000000..86ea0eac0b4
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
@@ -0,0 +1,54 @@
+# Copyright 2013 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.
+
+# Based on:
+# http://src.chromium.org/viewvc/blink/trunk/Source/build/scripts/template_expander.py
+
+import imp
+import inspect
+import os.path
+import sys
+
+# Disable lint check for finding modules:
+# pylint: disable=F0401
+
+def _GetDirAbove(dirname):
+ """Returns the directory "above" this file containing |dirname| (which must
+ also be "above" this file)."""
+ path = os.path.abspath(__file__)
+ while True:
+ path, tail = os.path.split(path)
+ assert tail
+ if tail == dirname:
+ return path
+
+try:
+ imp.find_module("jinja2")
+except ImportError:
+ sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party"))
+import jinja2
+
+
+def ApplyTemplate(base_dir, path_to_template, params, filters=None, **kwargs):
+ template_directory, template_name = os.path.split(path_to_template)
+ path_to_templates = os.path.join(base_dir, template_directory)
+ loader = jinja2.FileSystemLoader([path_to_templates])
+ jinja_env = jinja2.Environment(loader=loader, keep_trailing_newline=True,
+ **kwargs)
+ if filters:
+ jinja_env.filters.update(filters)
+ template = jinja_env.get_template(template_name)
+ return template.render(params)
+
+
+def UseJinja(path_to_template, **kwargs):
+ # Get the directory of our caller's file.
+ base_dir = os.path.dirname(inspect.getfile(sys._getframe(1)))
+ def RealDecorator(generator):
+ def GeneratorInternal(*args, **kwargs2):
+ parameters = generator(*args, **kwargs2)
+ return ApplyTemplate(base_dir, path_to_template, parameters, **kwargs)
+ GeneratorInternal.func_name = generator.func_name
+ return GeneratorInternal
+ return RealDecorator
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py
new file mode 100644
index 00000000000..eb394619d2b
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/test_support.py
@@ -0,0 +1,193 @@
+# Copyright 2013 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.
+
+import sys
+import traceback
+
+import module as mojom
+
+# Support for writing mojom test cases.
+# RunTest(fn) will execute fn, catching any exceptions. fn should return
+# the number of errors that are encountered.
+#
+# EXPECT_EQ(a, b) and EXPECT_TRUE(b) will print error information if the
+# expectations are not true and return a non zero value. This allows test cases
+# to be written like this
+#
+# def Foo():
+# errors = 0
+# errors += EXPECT_EQ('test', test())
+# ...
+# return errors
+#
+# RunTest(foo)
+
+def FieldsAreEqual(field1, field2):
+ if field1 == field2:
+ return True
+ return field1.name == field2.name and \
+ KindsAreEqual(field1.kind, field2.kind) and \
+ field1.ordinal == field2.ordinal and \
+ field1.default == field2.default
+
+
+def KindsAreEqual(kind1, kind2):
+ if kind1 == kind2:
+ return True
+ if kind1.__class__ != kind2.__class__ or kind1.spec != kind2.spec:
+ return False
+ if kind1.__class__ == mojom.Kind:
+ return kind1.spec == kind2.spec
+ if kind1.__class__ == mojom.Struct:
+ if kind1.name != kind2.name or \
+ kind1.spec != kind2.spec or \
+ len(kind1.fields) != len(kind2.fields):
+ return False
+ for i in range(len(kind1.fields)):
+ if not FieldsAreEqual(kind1.fields[i], kind2.fields[i]):
+ return False
+ return True
+ if kind1.__class__ == mojom.Array:
+ return KindsAreEqual(kind1.kind, kind2.kind)
+ print 'Unknown Kind class: ', kind1.__class__.__name__
+ return False
+
+
+def ParametersAreEqual(parameter1, parameter2):
+ if parameter1 == parameter2:
+ return True
+ return parameter1.name == parameter2.name and \
+ parameter1.ordinal == parameter2.ordinal and \
+ parameter1.default == parameter2.default and \
+ KindsAreEqual(parameter1.kind, parameter2.kind)
+
+
+def MethodsAreEqual(method1, method2):
+ if method1 == method2:
+ return True
+ if method1.name != method2.name or \
+ method1.ordinal != method2.ordinal or \
+ len(method1.parameters) != len(method2.parameters):
+ return False
+ for i in range(len(method1.parameters)):
+ if not ParametersAreEqual(method1.parameters[i], method2.parameters[i]):
+ return False
+ return True
+
+
+def InterfacesAreEqual(interface1, interface2):
+ if interface1 == interface2:
+ return True
+ if interface1.name != interface2.name or \
+ len(interface1.methods) != len(interface2.methods):
+ return False
+ for i in range(len(interface1.methods)):
+ if not MethodsAreEqual(interface1.methods[i], interface2.methods[i]):
+ return False
+ return True
+
+
+def ModulesAreEqual(module1, module2):
+ if module1 == module2:
+ return True
+ if module1.name != module2.name or \
+ module1.namespace != module2.namespace or \
+ len(module1.structs) != len(module2.structs) or \
+ len(module1.interfaces) != len(module2.interfaces):
+ return False
+ for i in range(len(module1.structs)):
+ if not KindsAreEqual(module1.structs[i], module2.structs[i]):
+ return False
+ for i in range(len(module1.interfaces)):
+ if not InterfacesAreEqual(module1.interfaces[i], module2.interfaces[i]):
+ return False
+ return True
+
+
+# Builds and returns a Module suitable for testing/
+def BuildTestModule():
+ module = mojom.Module('test', 'testspace')
+ struct = module.AddStruct('teststruct')
+ struct.AddField('testfield1', mojom.INT32)
+ struct.AddField('testfield2', mojom.Array(mojom.INT32), 42)
+
+ interface = module.AddInterface('Server')
+ method = interface.AddMethod('Foo', 42)
+ method.AddParameter('foo', mojom.INT32)
+ method.AddParameter('bar', mojom.Array(struct))
+
+ return module
+
+
+# Tests if |module| is as built by BuildTestModule(). Returns the number of
+# errors
+def TestTestModule(module):
+ errors = 0
+
+ errors += EXPECT_EQ('test', module.name)
+ errors += EXPECT_EQ('testspace', module.namespace)
+ errors += EXPECT_EQ(1, len(module.structs))
+ errors += EXPECT_EQ('teststruct', module.structs[0].name)
+ errors += EXPECT_EQ(2, len(module.structs[0].fields))
+ errors += EXPECT_EQ('testfield1', module.structs[0].fields[0].name)
+ errors += EXPECT_EQ(mojom.INT32, module.structs[0].fields[0].kind)
+ errors += EXPECT_EQ('testfield2', module.structs[0].fields[1].name)
+ errors += EXPECT_EQ(mojom.Array, module.structs[0].fields[1].kind.__class__)
+ errors += EXPECT_EQ(mojom.INT32, module.structs[0].fields[1].kind.kind)
+
+ errors += EXPECT_EQ(1, len(module.interfaces))
+ errors += EXPECT_EQ('Server', module.interfaces[0].name)
+ errors += EXPECT_EQ(1, len(module.interfaces[0].methods))
+ errors += EXPECT_EQ('Foo', module.interfaces[0].methods[0].name)
+ errors += EXPECT_EQ(2, len(module.interfaces[0].methods[0].parameters))
+ errors += EXPECT_EQ('foo', module.interfaces[0].methods[0].parameters[0].name)
+ errors += EXPECT_EQ(mojom.INT32,
+ module.interfaces[0].methods[0].parameters[0].kind)
+ errors += EXPECT_EQ('bar', module.interfaces[0].methods[0].parameters[1].name)
+ errors += EXPECT_EQ(
+ mojom.Array,
+ module.interfaces[0].methods[0].parameters[1].kind.__class__)
+ errors += EXPECT_EQ(
+ module.structs[0],
+ module.interfaces[0].methods[0].parameters[1].kind.kind)
+ return errors
+
+
+def PrintFailure(string):
+ stack = traceback.extract_stack()
+ frame = stack[len(stack)-3]
+ sys.stderr.write("ERROR at %s:%d, %s\n" % (frame[0], frame[1], string))
+ print "Traceback:"
+ for line in traceback.format_list(stack[:len(stack)-2]):
+ sys.stderr.write(line)
+
+
+def EXPECT_EQ(a, b):
+ if a != b:
+ PrintFailure("%s != %s" % (a, b))
+ return 1
+ return 0
+
+
+def EXPECT_TRUE(a):
+ if not a:
+ PrintFailure('Expecting True')
+ return 1
+ return 0
+
+
+def RunTest(fn):
+ sys.stdout.write('Running %s...' % fn.__name__)
+ try:
+ errors = fn()
+ except:
+ traceback.print_exc(sys.stderr)
+ errors = 1
+ if errors == 0:
+ sys.stdout.write('OK\n')
+ elif errors == 1:
+ sys.stdout.write('1 ERROR\n')
+ else:
+ sys.stdout.write('%d ERRORS\n' % errors)
+ return errors
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py b/chromium/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/parse/ast.py b/chromium/mojo/public/tools/bindings/pylib/mojom/parse/ast.py
new file mode 100644
index 00000000000..bd398c18ecb
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/parse/ast.py
@@ -0,0 +1,25 @@
+# Copyright 2014 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.
+
+"""Node objects for the AST for a Mojo IDL file."""
+
+# Note: For convenience of testing, you probably want to define __eq__() methods
+# for all node types; it's okay to be slightly lax (e.g., not compare filename
+# and lineno).
+
+
+class BaseNode(object):
+ def __init__(self, filename=None, lineno=None):
+ self.filename = filename
+ self.lineno = lineno
+
+
+class Ordinal(BaseNode):
+ """Represents an ordinal value labeling, e.g., a struct field."""
+ def __init__(self, value, **kwargs):
+ BaseNode.__init__(self, **kwargs)
+ self.value = value
+
+ def __eq__(self, other):
+ return self.value == other.value
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py b/chromium/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py
new file mode 100644
index 00000000000..34419acd53c
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py
@@ -0,0 +1,277 @@
+# Copyright 2014 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.
+
+import imp
+import os.path
+import sys
+
+# Disable lint check for finding modules:
+# pylint: disable=F0401
+
+def _GetDirAbove(dirname):
+ """Returns the directory "above" this file containing |dirname| (which must
+ also be "above" this file)."""
+ path = os.path.abspath(__file__)
+ while True:
+ path, tail = os.path.split(path)
+ assert tail
+ if tail == dirname:
+ return path
+
+try:
+ imp.find_module("ply")
+except ImportError:
+ sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party"))
+from ply.lex import TOKEN
+
+from ..error import Error
+
+
+# Disable lint check for exceptions deriving from Exception:
+# pylint: disable=W0710
+class LexError(Error):
+ """Class for errors from the lexer."""
+
+ def __init__(self, filename, message, lineno):
+ Error.__init__(self, filename, message, lineno=lineno)
+
+
+# We have methods which look like they could be functions:
+# pylint: disable=R0201
+class Lexer(object):
+
+ def __init__(self, filename):
+ self.filename = filename
+
+ ######################-- PRIVATE --######################
+
+ ##
+ ## Internal auxiliary methods
+ ##
+ def _error(self, msg, token):
+ raise LexError(self.filename, msg, token.lineno)
+
+ ##
+ ## Reserved keywords
+ ##
+ keywords = (
+ 'HANDLE',
+
+ 'IMPORT',
+ 'MODULE',
+ 'STRUCT',
+ 'INTERFACE',
+ 'ENUM',
+ 'CONST',
+ 'TRUE',
+ 'FALSE',
+ 'DEFAULT',
+ )
+
+ keyword_map = {}
+ for keyword in keywords:
+ keyword_map[keyword.lower()] = keyword
+
+ ##
+ ## All the tokens recognized by the lexer
+ ##
+ tokens = keywords + (
+ # Identifiers
+ 'NAME',
+
+ # Constants
+ 'ORDINAL',
+ 'INT_CONST_DEC', 'INT_CONST_HEX',
+ 'FLOAT_CONST',
+ 'CHAR_CONST',
+
+ # String literals
+ 'STRING_LITERAL',
+
+ # Operators
+ 'MINUS',
+ 'PLUS',
+ 'AMP',
+
+ # Assignment
+ 'EQUALS',
+
+ # Request / response
+ 'RESPONSE',
+
+ # Delimiters
+ 'LPAREN', 'RPAREN', # ( )
+ 'LBRACKET', 'RBRACKET', # [ ]
+ 'LBRACE', 'RBRACE', # { }
+ 'LANGLE', 'RANGLE', # < >
+ 'SEMI', # ;
+ 'COMMA', 'DOT' # , .
+ )
+
+ ##
+ ## Regexes for use in tokens
+ ##
+
+ # valid C identifiers (K&R2: A.2.3)
+ identifier = r'[a-zA-Z_][0-9a-zA-Z_]*'
+
+ hex_prefix = '0[xX]'
+ hex_digits = '[0-9a-fA-F]+'
+
+ # integer constants (K&R2: A.2.5.1)
+ decimal_constant = '0|([1-9][0-9]*)'
+ hex_constant = hex_prefix+hex_digits
+ # Don't allow octal constants (even invalid octal).
+ octal_constant_disallowed = '0[0-9]+'
+
+ # character constants (K&R2: A.2.5.2)
+ # Note: a-zA-Z and '.-~^_!=&;,' are allowed as escape chars to support #line
+ # directives with Windows paths as filenames (..\..\dir\file)
+ # For the same reason, decimal_escape allows all digit sequences. We want to
+ # parse all correct code, even if it means to sometimes parse incorrect
+ # code.
+ #
+ simple_escape = r"""([a-zA-Z._~!=&\^\-\\?'"])"""
+ decimal_escape = r"""(\d+)"""
+ hex_escape = r"""(x[0-9a-fA-F]+)"""
+ bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-7])"""
+
+ escape_sequence = \
+ r"""(\\("""+simple_escape+'|'+decimal_escape+'|'+hex_escape+'))'
+ cconst_char = r"""([^'\\\n]|"""+escape_sequence+')'
+ char_const = "'"+cconst_char+"'"
+ unmatched_quote = "('"+cconst_char+"*\\n)|('"+cconst_char+"*$)"
+ bad_char_const = \
+ r"""('"""+cconst_char+"""[^'\n]+')|('')|('"""+ \
+ bad_escape+r"""[^'\n]*')"""
+
+ # string literals (K&R2: A.2.6)
+ string_char = r"""([^"\\\n]|"""+escape_sequence+')'
+ string_literal = '"'+string_char+'*"'
+ bad_string_literal = '"'+string_char+'*'+bad_escape+string_char+'*"'
+
+ # floating constants (K&R2: A.2.5.3)
+ exponent_part = r"""([eE][-+]?[0-9]+)"""
+ fractional_constant = r"""([0-9]*\.[0-9]+)|([0-9]+\.)"""
+ floating_constant = \
+ '(((('+fractional_constant+')'+ \
+ exponent_part+'?)|([0-9]+'+exponent_part+')))'
+
+ # Ordinals
+ ordinal = r'@[0-9]+'
+ missing_ordinal_value = r'@'
+ # Don't allow ordinal values in octal (even invalid octal, like 09) or
+ # hexadecimal.
+ octal_or_hex_ordinal_disallowed = r'@((0[0-9]+)|('+hex_prefix+hex_digits+'))'
+
+ ##
+ ## Rules for the normal state
+ ##
+ t_ignore = ' \t\r'
+
+ # Newlines
+ def t_NEWLINE(self, t):
+ r'\n+'
+ t.lexer.lineno += len(t.value)
+
+ # Operators
+ t_MINUS = r'-'
+ t_PLUS = r'\+'
+ t_AMP = r'&'
+
+ # =
+ t_EQUALS = r'='
+
+ # =>
+ t_RESPONSE = r'=>'
+
+ # Delimiters
+ t_LPAREN = r'\('
+ t_RPAREN = r'\)'
+ t_LBRACKET = r'\['
+ t_RBRACKET = r'\]'
+ t_LBRACE = r'\{'
+ t_RBRACE = r'\}'
+ t_LANGLE = r'<'
+ t_RANGLE = r'>'
+ t_COMMA = r','
+ t_DOT = r'\.'
+ t_SEMI = r';'
+
+ t_STRING_LITERAL = string_literal
+
+ # The following floating and integer constants are defined as
+ # functions to impose a strict order (otherwise, decimal
+ # is placed before the others because its regex is longer,
+ # and this is bad)
+ #
+ @TOKEN(floating_constant)
+ def t_FLOAT_CONST(self, t):
+ return t
+
+ @TOKEN(hex_constant)
+ def t_INT_CONST_HEX(self, t):
+ return t
+
+ @TOKEN(octal_constant_disallowed)
+ def t_OCTAL_CONSTANT_DISALLOWED(self, t):
+ msg = "Octal values not allowed"
+ self._error(msg, t)
+
+ @TOKEN(decimal_constant)
+ def t_INT_CONST_DEC(self, t):
+ return t
+
+ # Must come before bad_char_const, to prevent it from
+ # catching valid char constants as invalid
+ #
+ @TOKEN(char_const)
+ def t_CHAR_CONST(self, t):
+ return t
+
+ @TOKEN(unmatched_quote)
+ def t_UNMATCHED_QUOTE(self, t):
+ msg = "Unmatched '"
+ self._error(msg, t)
+
+ @TOKEN(bad_char_const)
+ def t_BAD_CHAR_CONST(self, t):
+ msg = "Invalid char constant %s" % t.value
+ self._error(msg, t)
+
+ # unmatched string literals are caught by the preprocessor
+
+ @TOKEN(bad_string_literal)
+ def t_BAD_STRING_LITERAL(self, t):
+ msg = "String contains invalid escape code"
+ self._error(msg, t)
+
+ # Handle ordinal-related tokens in the right order:
+ @TOKEN(octal_or_hex_ordinal_disallowed)
+ def t_OCTAL_OR_HEX_ORDINAL_DISALLOWED(self, t):
+ msg = "Octal and hexadecimal ordinal values not allowed"
+ self._error(msg, t)
+
+ @TOKEN(ordinal)
+ def t_ORDINAL(self, t):
+ return t
+
+ @TOKEN(missing_ordinal_value)
+ def t_BAD_ORDINAL(self, t):
+ msg = "Missing ordinal value"
+ self._error(msg, t)
+
+ @TOKEN(identifier)
+ def t_NAME(self, t):
+ t.type = self.keyword_map.get(t.value, "NAME")
+ return t
+
+ # Ignore C and C++ style comments
+ def t_COMMENT(self, t):
+ r'(/\*(.|\n)*?\*/)|(//.*(\n[ \t]*//.*)*)'
+ t.lexer.lineno += t.value.count("\n")
+
+ def t_error(self, t):
+ msg = "Illegal character %s" % repr(t.value[0])
+ self._error(msg, t)
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/parse/parser.py b/chromium/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
new file mode 100644
index 00000000000..da441e3806c
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
@@ -0,0 +1,323 @@
+# Copyright 2014 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.
+
+"""Generates a syntax tree from a Mojo IDL file."""
+
+import imp
+import os.path
+import sys
+
+# Disable lint check for finding modules:
+# pylint: disable=F0401
+
+def _GetDirAbove(dirname):
+ """Returns the directory "above" this file containing |dirname| (which must
+ also be "above" this file)."""
+ path = os.path.abspath(__file__)
+ while True:
+ path, tail = os.path.split(path)
+ assert tail
+ if tail == dirname:
+ return path
+
+try:
+ imp.find_module("ply")
+except ImportError:
+ sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party"))
+from ply import lex
+from ply import yacc
+
+from ..error import Error
+import ast
+from lexer import Lexer
+
+
+_MAX_ORDINAL_VALUE = 0xffffffff
+
+
+def _ListFromConcat(*items):
+ """Generate list by concatenating inputs (note: only concatenates lists, not
+ tuples or other iterables)."""
+ itemsout = []
+ for item in items:
+ if item is None:
+ continue
+ if type(item) is not type([]):
+ itemsout.append(item)
+ else:
+ itemsout.extend(item)
+ return itemsout
+
+
+# Disable lint check for exceptions deriving from Exception:
+# pylint: disable=W0710
+class ParseError(Error):
+ """Class for errors from the parser."""
+
+ def __init__(self, filename, message, lineno=None, snippet=None):
+ Error.__init__(self, filename, message, lineno=lineno,
+ addenda=([snippet] if snippet else None))
+
+
+# We have methods which look like they could be functions:
+# pylint: disable=R0201
+class Parser(object):
+
+ def __init__(self, lexer, source, filename):
+ self.tokens = lexer.tokens
+ self.source = source
+ self.filename = filename
+
+ def p_root(self, p):
+ """root : import root
+ | module
+ | definitions"""
+ if len(p) > 2:
+ p[0] = _ListFromConcat(p[1], p[2])
+ else:
+ # Generator expects a module. If one wasn't specified insert one with an
+ # empty name.
+ if p[1][0] != 'MODULE':
+ p[0] = [('MODULE', '', None, p[1])]
+ else:
+ p[0] = [p[1]]
+
+ def p_import(self, p):
+ """import : IMPORT STRING_LITERAL"""
+ # 'eval' the literal to strip the quotes.
+ p[0] = ('IMPORT', eval(p[2]))
+
+ def p_module(self, p):
+ """module : attribute_section MODULE identifier LBRACE definitions RBRACE"""
+ p[0] = ('MODULE', p[3], p[1], p[5])
+
+ def p_definitions(self, p):
+ """definitions : definition definitions
+ | """
+ if len(p) > 1:
+ p[0] = _ListFromConcat(p[1], p[2])
+
+ def p_definition(self, p):
+ """definition : struct
+ | interface
+ | enum
+ | const"""
+ p[0] = p[1]
+
+ def p_attribute_section(self, p):
+ """attribute_section : LBRACKET attributes RBRACKET
+ | """
+ if len(p) > 3:
+ p[0] = p[2]
+
+ def p_attributes(self, p):
+ """attributes : attribute
+ | attribute COMMA attributes
+ | """
+ if len(p) == 2:
+ p[0] = _ListFromConcat(p[1])
+ elif len(p) > 3:
+ p[0] = _ListFromConcat(p[1], p[3])
+
+ def p_attribute(self, p):
+ """attribute : NAME EQUALS evaled_literal
+ | NAME EQUALS NAME"""
+ p[0] = ('ATTRIBUTE', p[1], p[3])
+
+ def p_evaled_literal(self, p):
+ """evaled_literal : literal"""
+ # 'eval' the literal to strip the quotes.
+ p[0] = eval(p[1])
+
+ def p_struct(self, p):
+ """struct : attribute_section STRUCT NAME LBRACE struct_body RBRACE SEMI"""
+ p[0] = ('STRUCT', p[3], p[1], p[5])
+
+ def p_struct_body(self, p):
+ """struct_body : field struct_body
+ | enum struct_body
+ | const struct_body
+ | """
+ if len(p) > 1:
+ p[0] = _ListFromConcat(p[1], p[2])
+
+ def p_field(self, p):
+ """field : typename NAME ordinal default SEMI"""
+ p[0] = ('FIELD', p[1], p[2], p[3], p[4])
+
+ def p_default(self, p):
+ """default : EQUALS constant
+ | """
+ if len(p) > 2:
+ p[0] = p[2]
+
+ def p_interface(self, p):
+ """interface : attribute_section INTERFACE NAME LBRACE interface_body \
+ RBRACE SEMI"""
+ p[0] = ('INTERFACE', p[3], p[1], p[5])
+
+ def p_interface_body(self, p):
+ """interface_body : method interface_body
+ | enum interface_body
+ | const interface_body
+ | """
+ if len(p) > 1:
+ p[0] = _ListFromConcat(p[1], p[2])
+
+ def p_response(self, p):
+ """response : RESPONSE LPAREN parameters RPAREN
+ | """
+ if len(p) > 3:
+ p[0] = p[3]
+
+ def p_method(self, p):
+ """method : NAME ordinal LPAREN parameters RPAREN response SEMI"""
+ p[0] = ('METHOD', p[1], p[4], p[2], p[6])
+
+ def p_parameters(self, p):
+ """parameters : parameter
+ | parameter COMMA parameters
+ | """
+ if len(p) == 1:
+ p[0] = []
+ elif len(p) == 2:
+ p[0] = _ListFromConcat(p[1])
+ elif len(p) > 3:
+ p[0] = _ListFromConcat(p[1], p[3])
+
+ def p_parameter(self, p):
+ """parameter : typename NAME ordinal"""
+ p[0] = ('PARAM', p[1], p[2], p[3])
+
+ def p_typename(self, p):
+ """typename : basictypename
+ | array
+ | interfacerequest"""
+ p[0] = p[1]
+
+ def p_basictypename(self, p):
+ """basictypename : identifier
+ | handletype"""
+ p[0] = p[1]
+
+ def p_handletype(self, p):
+ """handletype : HANDLE
+ | HANDLE LANGLE NAME RANGLE"""
+ if len(p) == 2:
+ p[0] = p[1]
+ else:
+ if p[3] not in ('data_pipe_consumer',
+ 'data_pipe_producer',
+ 'message_pipe',
+ 'shared_buffer'):
+ # Note: We don't enable tracking of line numbers for everything, so we
+ # can't use |p.lineno(3)|.
+ raise ParseError(self.filename, "Invalid handle type %r:" % p[3],
+ lineno=p.lineno(1),
+ snippet=self._GetSnippet(p.lineno(1)))
+ p[0] = "handle<" + p[3] + ">"
+
+ def p_array(self, p):
+ """array : typename LBRACKET RBRACKET"""
+ p[0] = p[1] + "[]"
+
+ def p_interfacerequest(self, p):
+ """interfacerequest : identifier AMP"""
+ p[0] = p[1] + "&"
+
+ def p_ordinal(self, p):
+ """ordinal : ORDINAL
+ | """
+ if len(p) > 1:
+ value = int(p[1][1:])
+ if value > _MAX_ORDINAL_VALUE:
+ raise ParseError(self.filename, "Ordinal value %d too large:" % value,
+ lineno=p.lineno(1),
+ snippet=self._GetSnippet(p.lineno(1)))
+ p[0] = ast.Ordinal(value, filename=self.filename, lineno=p.lineno(1))
+ else:
+ p[0] = ast.Ordinal(None)
+
+ def p_enum(self, p):
+ """enum : ENUM NAME LBRACE enum_fields RBRACE SEMI"""
+ p[0] = ('ENUM', p[2], p[4])
+
+ def p_enum_fields(self, p):
+ """enum_fields : enum_field
+ | enum_field COMMA enum_fields
+ | """
+ if len(p) == 2:
+ p[0] = _ListFromConcat(p[1])
+ elif len(p) > 3:
+ p[0] = _ListFromConcat(p[1], p[3])
+
+ def p_enum_field(self, p):
+ """enum_field : NAME
+ | NAME EQUALS constant"""
+ if len(p) == 2:
+ p[0] = ('ENUM_FIELD', p[1], None)
+ else:
+ p[0] = ('ENUM_FIELD', p[1], p[3])
+
+ def p_const(self, p):
+ """const : CONST typename NAME EQUALS constant SEMI"""
+ p[0] = ('CONST', p[2], p[3], p[5])
+
+ def p_constant(self, p):
+ """constant : literal
+ | identifier_wrapped"""
+ p[0] = p[1]
+
+ def p_identifier_wrapped(self, p):
+ """identifier_wrapped : identifier"""
+ p[0] = ('IDENTIFIER', p[1])
+
+ def p_identifier(self, p):
+ """identifier : NAME
+ | NAME DOT identifier"""
+ p[0] = ''.join(p[1:])
+
+ def p_literal(self, p):
+ """literal : number
+ | CHAR_CONST
+ | TRUE
+ | FALSE
+ | DEFAULT
+ | STRING_LITERAL"""
+ p[0] = p[1]
+
+ def p_number(self, p):
+ """number : digits
+ | PLUS digits
+ | MINUS digits"""
+ p[0] = ''.join(p[1:])
+
+ def p_digits(self, p):
+ """digits : INT_CONST_DEC
+ | INT_CONST_HEX
+ | FLOAT_CONST"""
+ p[0] = p[1]
+
+ def p_error(self, e):
+ if e is None:
+ # Unexpected EOF.
+ # TODO(vtl): Can we figure out what's missing?
+ raise ParseError(self.filename, "Unexpected end of file")
+
+ raise ParseError(self.filename, "Unexpected %r:" % e.value, lineno=e.lineno,
+ snippet=self._GetSnippet(e.lineno))
+
+ def _GetSnippet(self, lineno):
+ return self.source.split('\n')[lineno - 1]
+
+
+def Parse(source, filename):
+ lexer = Lexer(filename)
+ parser = Parser(lexer, source, filename)
+
+ lex.lex(object=lexer)
+ yacc.yacc(module=parser, debug=0, write_tables=0)
+
+ tree = yacc.parse(source)
+ return tree
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/parse/translate.py b/chromium/mojo/public/tools/bindings/pylib/mojom/parse/translate.py
new file mode 100644
index 00000000000..8407a088c6b
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/parse/translate.py
@@ -0,0 +1,144 @@
+# Copyright 2014 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.
+
+"""Translates parse tree to Mojom IR."""
+
+
+import ast
+
+
+def _MapTree(func, tree, name):
+ if not tree:
+ return []
+ return [func(subtree) for subtree in tree if subtree[0] == name]
+
+def _MapKind(kind):
+ map_to_kind = { 'bool': 'b',
+ 'int8': 'i8',
+ 'int16': 'i16',
+ 'int32': 'i32',
+ 'int64': 'i64',
+ 'uint8': 'u8',
+ 'uint16': 'u16',
+ 'uint32': 'u32',
+ 'uint64': 'u64',
+ 'float': 'f',
+ 'double': 'd',
+ 'string': 's',
+ 'handle': 'h',
+ 'handle<data_pipe_consumer>': 'h:d:c',
+ 'handle<data_pipe_producer>': 'h:d:p',
+ 'handle<message_pipe>': 'h:m',
+ 'handle<shared_buffer>': 'h:s'}
+ if kind.endswith('[]'):
+ return 'a:' + _MapKind(kind[0:len(kind)-2])
+ if kind.endswith('&'):
+ return 'r:' + _MapKind(kind[0:len(kind)-1])
+ if kind in map_to_kind:
+ return map_to_kind[kind]
+ return 'x:' + kind
+
+def _MapAttributes(attributes):
+ if not attributes:
+ return {}
+ return dict([(attribute[1], attribute[2])
+ for attribute in attributes if attribute[0] == 'ATTRIBUTE'])
+
+def _GetAttribute(attributes, name):
+ out = None
+ if attributes:
+ for attribute in attributes:
+ if attribute[0] == 'ATTRIBUTE' and attribute[1] == name:
+ out = attribute[2]
+ return out
+
+def _MapField(tree):
+ assert type(tree[3]) is ast.Ordinal
+ return {'name': tree[2],
+ 'kind': _MapKind(tree[1]),
+ 'ordinal': tree[3].value,
+ 'default': tree[4]}
+
+def _MapParameter(tree):
+ assert type(tree[3]) is ast.Ordinal
+ return {'name': tree[2],
+ 'kind': _MapKind(tree[1]),
+ 'ordinal': tree[3].value}
+
+def _MapMethod(tree):
+ assert type(tree[3]) is ast.Ordinal
+ method = {'name': tree[1],
+ 'parameters': _MapTree(_MapParameter, tree[2], 'PARAM'),
+ 'ordinal': tree[3].value}
+ if tree[4] != None:
+ method['response_parameters'] = _MapTree(_MapParameter, tree[4], 'PARAM')
+ return method
+
+def _MapEnumField(tree):
+ return {'name': tree[1],
+ 'value': tree[2]}
+
+def _MapStruct(tree):
+ struct = {}
+ struct['name'] = tree[1]
+ struct['attributes'] = _MapAttributes(tree[2])
+ struct['fields'] = _MapTree(_MapField, tree[3], 'FIELD')
+ struct['enums'] = _MapTree(_MapEnum, tree[3], 'ENUM')
+ struct['constants'] = _MapTree(_MapConstant, tree[3], 'CONST')
+ return struct
+
+def _MapInterface(tree):
+ interface = {}
+ interface['name'] = tree[1]
+ interface['client'] = _GetAttribute(tree[2], 'Client')
+ interface['methods'] = _MapTree(_MapMethod, tree[3], 'METHOD')
+ interface['enums'] = _MapTree(_MapEnum, tree[3], 'ENUM')
+ interface['constants'] = _MapTree(_MapConstant, tree[3], 'CONST')
+ return interface
+
+def _MapEnum(tree):
+ enum = {}
+ enum['name'] = tree[1]
+ enum['fields'] = _MapTree(_MapEnumField, tree[2], 'ENUM_FIELD')
+ return enum
+
+def _MapConstant(tree):
+ constant = {}
+ constant['name'] = tree[2]
+ constant['kind'] = _MapKind(tree[1])
+ constant['value'] = tree[3]
+ return constant
+
+def _MapModule(tree, name):
+ mojom = {}
+ mojom['name'] = name
+ mojom['namespace'] = tree[1]
+ mojom['attributes'] = _MapAttributes(tree[2])
+ mojom['structs'] = _MapTree(_MapStruct, tree[3], 'STRUCT')
+ mojom['interfaces'] = _MapTree(_MapInterface, tree[3], 'INTERFACE')
+ mojom['enums'] = _MapTree(_MapEnum, tree[3], 'ENUM')
+ mojom['constants'] = _MapTree(_MapConstant, tree[3], 'CONST')
+ return mojom
+
+def _MapImport(tree):
+ import_item = {}
+ import_item['filename'] = tree[1]
+ return import_item
+
+
+class _MojomBuilder(object):
+ def __init__(self):
+ self.mojom = {}
+
+ def Build(self, tree, name):
+ modules = [_MapModule(item, name) for item in tree if item[0] == 'MODULE']
+ if len(modules) != 1:
+ raise Exception('A mojom file must contain exactly 1 module.')
+ self.mojom = modules[0]
+ self.mojom['imports'] = _MapTree(_MapImport, tree, 'IMPORT')
+ return self.mojom
+
+
+def Translate(tree, name):
+ return _MojomBuilder().Build(tree, name)
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom_tests/__init__.py b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/__init__.py
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/__init__.py b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/__init__.py
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py
new file mode 100644
index 00000000000..28c936d20b8
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/lexer_unittest.py
@@ -0,0 +1,187 @@
+# Copyright 2014 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.
+
+import imp
+import os.path
+import sys
+import unittest
+
+# Disable lint check for finding modules:
+# pylint: disable=F0401
+
+def _GetDirAbove(dirname):
+ """Returns the directory "above" this file containing |dirname| (which must
+ also be "above" this file)."""
+ path = os.path.abspath(__file__)
+ while True:
+ path, tail = os.path.split(path)
+ assert tail
+ if tail == dirname:
+ return path
+
+try:
+ imp.find_module("ply")
+except ImportError:
+ sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party"))
+from ply import lex
+
+try:
+ imp.find_module("mojom")
+except ImportError:
+ sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
+import mojom.parse.lexer
+
+
+# This (monkey-patching LexToken to make comparison value-based) is evil, but
+# we'll do it anyway. (I'm pretty sure ply's lexer never cares about comparing
+# for object identity.)
+def _LexTokenEq(self, other):
+ return self.type == other.type and self.value == other.value and \
+ self.lineno == other.lineno and self.lexpos == other.lexpos
+setattr(lex.LexToken, '__eq__', _LexTokenEq)
+
+
+def _MakeLexToken(token_type, value, lineno=1, lexpos=0):
+ """Makes a LexToken with the given parameters. (Note that lineno is 1-based,
+ but lexpos is 0-based.)"""
+ rv = lex.LexToken()
+ rv.type, rv.value, rv.lineno, rv.lexpos = token_type, value, lineno, lexpos
+ return rv
+
+
+def _MakeLexTokenForKeyword(keyword, **kwargs):
+ """Makes a LexToken for the given keyword."""
+ return _MakeLexToken(keyword.upper(), keyword.lower(), **kwargs)
+
+
+class LexerTest(unittest.TestCase):
+ """Tests |mojom.parse.lexer.Lexer|."""
+
+ def __init__(self, *args, **kwargs):
+ unittest.TestCase.__init__(self, *args, **kwargs)
+ # Clone all lexer instances from this one, since making a lexer is slow.
+ self._zygote_lexer = lex.lex(mojom.parse.lexer.Lexer("my_file.mojom"))
+
+ def testValidKeywords(self):
+ """Tests valid keywords."""
+ self.assertEquals(self._SingleTokenForInput("handle"),
+ _MakeLexTokenForKeyword("handle"))
+ self.assertEquals(self._SingleTokenForInput("import"),
+ _MakeLexTokenForKeyword("import"))
+ self.assertEquals(self._SingleTokenForInput("module"),
+ _MakeLexTokenForKeyword("module"))
+ self.assertEquals(self._SingleTokenForInput("struct"),
+ _MakeLexTokenForKeyword("struct"))
+ self.assertEquals(self._SingleTokenForInput("interface"),
+ _MakeLexTokenForKeyword("interface"))
+ self.assertEquals(self._SingleTokenForInput("enum"),
+ _MakeLexTokenForKeyword("enum"))
+ self.assertEquals(self._SingleTokenForInput("const"),
+ _MakeLexTokenForKeyword("const"))
+ self.assertEquals(self._SingleTokenForInput("true"),
+ _MakeLexTokenForKeyword("true"))
+ self.assertEquals(self._SingleTokenForInput("false"),
+ _MakeLexTokenForKeyword("false"))
+ self.assertEquals(self._SingleTokenForInput("default"),
+ _MakeLexTokenForKeyword("default"))
+
+ def testValidIdentifiers(self):
+ """Tests identifiers."""
+ self.assertEquals(self._SingleTokenForInput("abcd"),
+ _MakeLexToken("NAME", "abcd"))
+ self.assertEquals(self._SingleTokenForInput("AbC_d012_"),
+ _MakeLexToken("NAME", "AbC_d012_"))
+ self.assertEquals(self._SingleTokenForInput("_0123"),
+ _MakeLexToken("NAME", "_0123"))
+
+ def testInvalidIdentifiers(self):
+ with self.assertRaisesRegexp(
+ mojom.parse.lexer.LexError,
+ r"^my_file\.mojom:1: Error: Illegal character '\$'$"):
+ self._TokensForInput("$abc")
+ with self.assertRaisesRegexp(
+ mojom.parse.lexer.LexError,
+ r"^my_file\.mojom:1: Error: Illegal character '\$'$"):
+ self._TokensForInput("a$bc")
+
+ def testDecimalIntegerConstants(self):
+ self.assertEquals(self._SingleTokenForInput("0"),
+ _MakeLexToken("INT_CONST_DEC", "0"))
+ self.assertEquals(self._SingleTokenForInput("1"),
+ _MakeLexToken("INT_CONST_DEC", "1"))
+ self.assertEquals(self._SingleTokenForInput("123"),
+ _MakeLexToken("INT_CONST_DEC", "123"))
+ self.assertEquals(self._SingleTokenForInput("10"),
+ _MakeLexToken("INT_CONST_DEC", "10"))
+
+ def testValidTokens(self):
+ """Tests valid tokens (which aren't tested elsewhere)."""
+ # Keywords tested in |testValidKeywords|.
+ # NAME tested in |testValidIdentifiers|.
+ self.assertEquals(self._SingleTokenForInput("@123"),
+ _MakeLexToken("ORDINAL", "@123"))
+ self.assertEquals(self._SingleTokenForInput("456"),
+ _MakeLexToken("INT_CONST_DEC", "456"))
+ self.assertEquals(self._SingleTokenForInput("0x01aB2eF3"),
+ _MakeLexToken("INT_CONST_HEX", "0x01aB2eF3"))
+ self.assertEquals(self._SingleTokenForInput("123.456"),
+ _MakeLexToken("FLOAT_CONST", "123.456"))
+ self.assertEquals(self._SingleTokenForInput("'x'"),
+ _MakeLexToken("CHAR_CONST", "'x'"))
+ self.assertEquals(self._SingleTokenForInput("\"hello\""),
+ _MakeLexToken("STRING_LITERAL", "\"hello\""))
+ self.assertEquals(self._SingleTokenForInput("+"),
+ _MakeLexToken("PLUS", "+"))
+ self.assertEquals(self._SingleTokenForInput("-"),
+ _MakeLexToken("MINUS", "-"))
+ self.assertEquals(self._SingleTokenForInput("&"),
+ _MakeLexToken("AMP", "&"))
+ self.assertEquals(self._SingleTokenForInput("="),
+ _MakeLexToken("EQUALS", "="))
+ self.assertEquals(self._SingleTokenForInput("=>"),
+ _MakeLexToken("RESPONSE", "=>"))
+ self.assertEquals(self._SingleTokenForInput("("),
+ _MakeLexToken("LPAREN", "("))
+ self.assertEquals(self._SingleTokenForInput(")"),
+ _MakeLexToken("RPAREN", ")"))
+ self.assertEquals(self._SingleTokenForInput("["),
+ _MakeLexToken("LBRACKET", "["))
+ self.assertEquals(self._SingleTokenForInput("]"),
+ _MakeLexToken("RBRACKET", "]"))
+ self.assertEquals(self._SingleTokenForInput("{"),
+ _MakeLexToken("LBRACE", "{"))
+ self.assertEquals(self._SingleTokenForInput("}"),
+ _MakeLexToken("RBRACE", "}"))
+ self.assertEquals(self._SingleTokenForInput("<"),
+ _MakeLexToken("LANGLE", "<"))
+ self.assertEquals(self._SingleTokenForInput(">"),
+ _MakeLexToken("RANGLE", ">"))
+ self.assertEquals(self._SingleTokenForInput(";"),
+ _MakeLexToken("SEMI", ";"))
+ self.assertEquals(self._SingleTokenForInput(","),
+ _MakeLexToken("COMMA", ","))
+ self.assertEquals(self._SingleTokenForInput("."),
+ _MakeLexToken("DOT", "."))
+
+ def _TokensForInput(self, input_string):
+ """Gets a list of tokens for the given input string."""
+ lexer = self._zygote_lexer.clone()
+ lexer.input(input_string)
+ rv = []
+ while True:
+ tok = lexer.token()
+ if not tok:
+ return rv
+ rv.append(tok)
+
+ def _SingleTokenForInput(self, input_string):
+ """Gets the single token for the given input string. (Raises an exception if
+ the input string does not result in exactly one token.)"""
+ toks = self._TokensForInput(input_string)
+ assert len(toks) == 1
+ return toks[0]
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py
new file mode 100644
index 00000000000..024b433410b
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py
@@ -0,0 +1,493 @@
+# Copyright 2014 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.
+
+import imp
+import os.path
+import sys
+import unittest
+
+# Disable lint check for finding modules:
+# pylint: disable=F0401
+
+def _GetDirAbove(dirname):
+ """Returns the directory "above" this file containing |dirname| (which must
+ also be "above" this file)."""
+ path = os.path.abspath(__file__)
+ while True:
+ path, tail = os.path.split(path)
+ assert tail
+ if tail == dirname:
+ return path
+
+try:
+ imp.find_module("mojom")
+except ImportError:
+ sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
+import mojom.parse.ast as ast
+import mojom.parse.lexer as lexer
+import mojom.parse.parser as parser
+
+
+class ParserTest(unittest.TestCase):
+ """Tests |parser.Parse()|."""
+
+ def testTrivialValidSource(self):
+ """Tests a trivial, but valid, .mojom source."""
+ source = """\
+// This is a comment.
+
+module my_module {
+}
+"""
+ self.assertEquals(parser.Parse(source, "my_file.mojom"),
+ [("MODULE", "my_module", None, None)])
+
+ def testSourceWithCrLfs(self):
+ """Tests a .mojom source with CR-LFs instead of LFs."""
+ source = "// This is a comment.\r\n\r\nmodule my_module {\r\n}\r\n"
+ self.assertEquals(parser.Parse(source, "my_file.mojom"),
+ [("MODULE", "my_module", None, None)])
+
+ def testUnexpectedEOF(self):
+ """Tests a "truncated" .mojom source."""
+ source = """\
+// This is a comment.
+
+module my_module {
+"""
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom: Error: Unexpected end of file$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testCommentLineNumbers(self):
+ """Tests that line numbers are correctly tracked when comments are
+ present."""
+ source1 = """\
+// Isolated C++-style comments.
+
+// Foo.
+asdf1
+"""
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:4: Error: Unexpected 'asdf1':\nasdf1$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+// Consecutive C++-style comments.
+// Foo.
+ // Bar.
+
+struct Yada { // Baz.
+// Quux.
+ int32 x;
+};
+
+asdf2
+"""
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:10: Error: Unexpected 'asdf2':\nasdf2$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+/* Single-line C-style comments. */
+/* Foobar. */
+
+/* Baz. */
+asdf3
+"""
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:5: Error: Unexpected 'asdf3':\nasdf3$"):
+ parser.Parse(source3, "my_file.mojom")
+
+ source4 = """\
+/* Multi-line C-style comments.
+*/
+/*
+Foo.
+Bar.
+*/
+
+/* Baz
+ Quux. */
+asdf4
+"""
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:10: Error: Unexpected 'asdf4':\nasdf4$"):
+ parser.Parse(source4, "my_file.mojom")
+
+
+ def testSimpleStruct(self):
+ """Tests a simple .mojom source that just defines a struct."""
+ source = """\
+module my_module {
+
+struct MyStruct {
+ int32 a;
+ double b;
+};
+
+} // module my_module
+"""
+ expected = \
+[('MODULE',
+ 'my_module',
+ None,
+ [('STRUCT',
+ 'MyStruct',
+ None,
+ [('FIELD', 'int32', 'a', ast.Ordinal(None), None),
+ ('FIELD', 'double', 'b', ast.Ordinal(None), None)])])]
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testSimpleStructWithoutModule(self):
+ """Tests a simple struct without an enclosing module."""
+ source = """\
+struct MyStruct {
+ int32 a;
+ double b;
+};
+"""
+ expected = \
+[('MODULE',
+ '',
+ None,
+ [('STRUCT',
+ 'MyStruct',
+ None,
+ [('FIELD', 'int32', 'a', ast.Ordinal(None), None),
+ ('FIELD', 'double', 'b', ast.Ordinal(None), None)])])]
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testMissingModuleName(self):
+ """Tests an (invalid) .mojom with a missing module name."""
+ source1 = """\
+// Missing module name.
+module {
+struct MyStruct {
+ int32 a;
+};
+}
+"""
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected '{':\nmodule {$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ # Another similar case, but make sure that line-number tracking/reporting
+ # is correct.
+ source2 = """\
+module
+// This line intentionally left unblank.
+
+{
+}
+"""
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:4: Error: Unexpected '{':\n{$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ def testEnumInitializers(self):
+ """Tests an enum with simple initialized values."""
+ source = """\
+module my_module {
+
+enum MyEnum {
+ MY_ENUM_NEG1 = -1,
+ MY_ENUM_ZERO = 0,
+ MY_ENUM_1 = +1,
+ MY_ENUM_2,
+};
+
+} // my_module
+"""
+ expected = \
+[('MODULE',
+ 'my_module',
+ None,
+ [('ENUM',
+ 'MyEnum',
+ [('ENUM_FIELD', 'MY_ENUM_NEG1', '-1'),
+ ('ENUM_FIELD', 'MY_ENUM_ZERO', '0'),
+ ('ENUM_FIELD', 'MY_ENUM_1', '+1'),
+ ('ENUM_FIELD', 'MY_ENUM_2', None)])])]
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testConst(self):
+ """Tests some constants and struct memebers initialized with them."""
+ source = """\
+module my_module {
+
+struct MyStruct {
+ const int8 kNumber = -1;
+ int8 number@0 = kNumber;
+};
+
+} // my_module
+"""
+ expected = \
+[('MODULE',
+ 'my_module',
+ None,
+ [('STRUCT',
+ 'MyStruct', None,
+ [('CONST', 'int8', 'kNumber', '-1'),
+ ('FIELD', 'int8', 'number',
+ ast.Ordinal(0), ('IDENTIFIER', 'kNumber'))])])]
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testNoConditionals(self):
+ """Tests that ?: is not allowed."""
+ source = """\
+module my_module {
+
+enum MyEnum {
+ MY_ENUM_1 = 1 ? 2 : 3
+};
+
+} // my_module
+"""
+ with self.assertRaisesRegexp(
+ lexer.LexError,
+ r"^my_file\.mojom:4: Error: Illegal character '\?'$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testSimpleOrdinals(self):
+ """Tests that (valid) ordinal values are scanned correctly."""
+ source = """\
+module my_module {
+
+// This isn't actually valid .mojom, but the problem (missing ordinals) should
+// be handled at a different level.
+struct MyStruct {
+ int32 a0@0;
+ int32 a1@1;
+ int32 a2@2;
+ int32 a9@9;
+ int32 a10 @10;
+ int32 a11 @11;
+ int32 a29 @29;
+ int32 a1234567890 @1234567890;
+};
+
+} // module my_module
+"""
+ expected = \
+[('MODULE',
+ 'my_module',
+ None,
+ [('STRUCT',
+ 'MyStruct',
+ None,
+ [('FIELD', 'int32', 'a0', ast.Ordinal(0), None),
+ ('FIELD', 'int32', 'a1', ast.Ordinal(1), None),
+ ('FIELD', 'int32', 'a2', ast.Ordinal(2), None),
+ ('FIELD', 'int32', 'a9', ast.Ordinal(9), None),
+ ('FIELD', 'int32', 'a10', ast.Ordinal(10), None),
+ ('FIELD', 'int32', 'a11', ast.Ordinal(11), None),
+ ('FIELD', 'int32', 'a29', ast.Ordinal(29), None),
+ ('FIELD', 'int32', 'a1234567890', ast.Ordinal(1234567890), None)])])]
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testInvalidOrdinals(self):
+ """Tests that (lexically) invalid ordinals are correctly detected."""
+ source1 = """\
+module my_module {
+
+struct MyStruct {
+ int32 a_missing@;
+};
+
+} // module my_module
+"""
+ with self.assertRaisesRegexp(
+ lexer.LexError,
+ r"^my_file\.mojom:4: Error: Missing ordinal value$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+module my_module {
+
+struct MyStruct {
+ int32 a_octal@01;
+};
+
+} // module my_module
+"""
+ with self.assertRaisesRegexp(
+ lexer.LexError,
+ r"^my_file\.mojom:4: Error: "
+ r"Octal and hexadecimal ordinal values not allowed$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+module my_module { struct MyStruct { int32 a_invalid_octal@08; }; }
+"""
+ with self.assertRaisesRegexp(
+ lexer.LexError,
+ r"^my_file\.mojom:1: Error: "
+ r"Octal and hexadecimal ordinal values not allowed$"):
+ parser.Parse(source3, "my_file.mojom")
+
+ source4 = """\
+module my_module { struct MyStruct { int32 a_hex@0x1aB9; }; }
+"""
+ with self.assertRaisesRegexp(
+ lexer.LexError,
+ r"^my_file\.mojom:1: Error: "
+ r"Octal and hexadecimal ordinal values not allowed$"):
+ parser.Parse(source4, "my_file.mojom")
+
+ source5 = """\
+module my_module { struct MyStruct { int32 a_hex@0X0; }; }
+"""
+ with self.assertRaisesRegexp(
+ lexer.LexError,
+ r"^my_file\.mojom:1: Error: "
+ r"Octal and hexadecimal ordinal values not allowed$"):
+ parser.Parse(source5, "my_file.mojom")
+
+ source6 = """\
+struct MyStruct {
+ int32 a_too_big@999999999999;
+};
+"""
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: "
+ r"Ordinal value 999999999999 too large:\n"
+ r" int32 a_too_big@999999999999;$"):
+ parser.Parse(source6, "my_file.mojom")
+
+ def testNestedNamespace(self):
+ """Tests that "nested" namespaces work."""
+ source = """\
+module my.mod {
+
+struct MyStruct {
+ int32 a;
+};
+
+} // module my.mod
+"""
+ expected = \
+[('MODULE',
+ 'my.mod',
+ None,
+ [('STRUCT',
+ 'MyStruct',
+ None,
+ [('FIELD', 'int32', 'a', ast.Ordinal(None), None)])])]
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testValidHandleTypes(self):
+ """Tests (valid) handle types."""
+ source = """\
+struct MyStruct {
+ handle a;
+ handle<data_pipe_consumer> b;
+ handle <data_pipe_producer> c;
+ handle < message_pipe > d;
+ handle
+ < shared_buffer
+ > e;
+};
+"""
+ expected = \
+[('MODULE',
+ '',
+ None,
+ [('STRUCT',
+ 'MyStruct',
+ None,
+ [('FIELD', 'handle', 'a', ast.Ordinal(None), None),
+ ('FIELD', 'handle<data_pipe_consumer>', 'b', ast.Ordinal(None), None),
+ ('FIELD', 'handle<data_pipe_producer>', 'c', ast.Ordinal(None), None),
+ ('FIELD', 'handle<message_pipe>', 'd', ast.Ordinal(None), None),
+ ('FIELD', 'handle<shared_buffer>', 'e', ast.Ordinal(None), None)])])]
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testInvalidHandleType(self):
+ """Tests an invalid (unknown) handle type."""
+ source = """\
+struct MyStruct {
+ handle<wtf_is_this> foo;
+};
+"""
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: "
+ r"Invalid handle type 'wtf_is_this':\n"
+ r" handle<wtf_is_this> foo;$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testValidDefaultValues(self):
+ """Tests default values that are valid (to the parser)."""
+ source = """\
+struct MyStruct {
+ int16 a0 = 0;
+ uint16 a1 = 0x0;
+ uint16 a2 = 0x00;
+ uint16 a3 = 0x01;
+ uint16 a4 = 0xcd;
+ int32 a5 = 12345;
+ int64 a6 = -12345;
+ int64 a7 = +12345;
+ uint32 a8 = 0x12cd3;
+ uint32 a9 = -0x12cD3;
+ uint32 a10 = +0x12CD3;
+ bool a11 = true;
+ bool a12 = false;
+ float a13 = 1.2345;
+ float a14 = -1.2345;
+ float a15 = +1.2345;
+ float a16 = 123.;
+ float a17 = .123;
+ double a18 = 1.23E10;
+ double a19 = 1.E-10;
+ double a20 = .5E+10;
+ double a21 = -1.23E10;
+ double a22 = +.123E10;
+};
+"""
+ expected = \
+[('MODULE',
+ '',
+ None,
+ [('STRUCT',
+ 'MyStruct',
+ None,
+ [('FIELD', 'int16', 'a0', ast.Ordinal(None), '0'),
+ ('FIELD', 'uint16', 'a1', ast.Ordinal(None), '0x0'),
+ ('FIELD', 'uint16', 'a2', ast.Ordinal(None), '0x00'),
+ ('FIELD', 'uint16', 'a3', ast.Ordinal(None), '0x01'),
+ ('FIELD', 'uint16', 'a4', ast.Ordinal(None), '0xcd'),
+ ('FIELD', 'int32', 'a5' , ast.Ordinal(None), '12345'),
+ ('FIELD', 'int64', 'a6', ast.Ordinal(None), '-12345'),
+ ('FIELD', 'int64', 'a7', ast.Ordinal(None), '+12345'),
+ ('FIELD', 'uint32', 'a8', ast.Ordinal(None), '0x12cd3'),
+ ('FIELD', 'uint32', 'a9', ast.Ordinal(None), '-0x12cD3'),
+ ('FIELD', 'uint32', 'a10', ast.Ordinal(None), '+0x12CD3'),
+ ('FIELD', 'bool', 'a11', ast.Ordinal(None), 'true'),
+ ('FIELD', 'bool', 'a12', ast.Ordinal(None), 'false'),
+ ('FIELD', 'float', 'a13', ast.Ordinal(None), '1.2345'),
+ ('FIELD', 'float', 'a14', ast.Ordinal(None), '-1.2345'),
+ ('FIELD', 'float', 'a15', ast.Ordinal(None), '+1.2345'),
+ ('FIELD', 'float', 'a16', ast.Ordinal(None), '123.'),
+ ('FIELD', 'float', 'a17', ast.Ordinal(None), '.123'),
+ ('FIELD', 'double', 'a18', ast.Ordinal(None), '1.23E10'),
+ ('FIELD', 'double', 'a19', ast.Ordinal(None), '1.E-10'),
+ ('FIELD', 'double', 'a20', ast.Ordinal(None), '.5E+10'),
+ ('FIELD', 'double', 'a21', ast.Ordinal(None), '-1.23E10'),
+ ('FIELD', 'double', 'a22', ast.Ordinal(None), '+.123E10')])])]
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py
new file mode 100755
index 00000000000..edca7e11ff1
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_parser.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+# Copyright 2014 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.
+
+"""Simple testing utility to just run the mojom parser."""
+
+
+import os.path
+import sys
+
+sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ os.path.pardir, os.path.pardir))
+
+# Disable lint check for finding modules:
+# pylint: disable=F0401
+
+from mojom.parse.parser import Parse, ParseError
+
+
+def main(argv):
+ if len(argv) < 2:
+ print "usage: %s filename" % argv[0]
+ return 0
+
+ for filename in argv[1:]:
+ with open(filename) as f:
+ print "%s:" % filename
+ try:
+ print Parse(f.read(), filename)
+ except ParseError, e:
+ print e
+ return 1
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py
new file mode 100755
index 00000000000..833af6292a7
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/parse/run_translate.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+# Copyright 2014 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.
+
+"""Simple testing utility to just run the mojom translate stage."""
+
+
+import os.path
+import sys
+
+sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ os.path.pardir, os.path.pardir))
+
+# Disable lint check for finding modules:
+# pylint: disable=F0401
+
+from mojom.parse.parser import Parse
+from mojom.parse.translate import Translate
+
+
+def main(argv):
+ if len(argv) < 2:
+ print "usage: %s filename" % sys.argv[0]
+ return 1
+
+ for filename in argv[1:]:
+ with open(filename) as f:
+ print "%s:" % filename
+ print Translate(Parse(f.read(), filename),
+ os.path.splitext(os.path.basename(filename))[0])
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom_tests/support/__init__.py b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/support/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/support/__init__.py
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py
new file mode 100644
index 00000000000..00524a2413a
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/support/find_files.py
@@ -0,0 +1,32 @@
+# Copyright 2014 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.
+
+from fnmatch import filter
+from os import walk
+from os.path import join
+import sys
+
+
+def FindFiles(top, pattern, **kwargs):
+ """Finds files under |top| matching the glob pattern |pattern|, returning a
+ list of paths."""
+ matches = []
+ for dirpath, _, filenames in walk(top, **kwargs):
+ for filename in filter(filenames, pattern):
+ matches.append(join(dirpath, filename))
+ return matches
+
+
+def main(argv):
+ if len(argv) != 3:
+ print "usage: %s path pattern" % argv[0]
+ return 1
+
+ for filename in FindFiles(argv[1], argv[2]):
+ print filename
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py
new file mode 100644
index 00000000000..20ef4619699
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom_tests/support/run_bindings_generator.py
@@ -0,0 +1,47 @@
+# Copyright 2014 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.
+
+import os.path
+from subprocess import check_call
+import sys
+
+
+def RunBindingsGenerator(out_dir, root_dir, mojom_file, extra_flags=None):
+ out_dir = os.path.abspath(out_dir)
+ root_dir = os.path.abspath(root_dir)
+ mojom_file = os.path.abspath(mojom_file)
+
+ # The mojom file should be under the root directory somewhere.
+ assert mojom_file.startswith(root_dir)
+ mojom_reldir = os.path.dirname(os.path.relpath(mojom_file, root_dir))
+
+ # TODO(vtl): Abstract out the "main" functions, so that we can just import
+ # the bindings generator (which would be more portable and easier to use in
+ # tests).
+ this_dir = os.path.dirname(os.path.abspath(__file__))
+ # We're in src/mojo/public/tools/bindings/pylib/mojom_tests/support;
+ # mojom_bindings_generator.py is in .../bindings.
+ bindings_generator = os.path.join(this_dir, os.pardir, os.pardir, os.pardir,
+ "mojom_bindings_generator.py")
+
+ args = ["python", bindings_generator,
+ "-o", os.path.join(out_dir, mojom_reldir)]
+ if extra_flags:
+ args.extend(extra_flags)
+ args.append(mojom_file)
+
+ check_call(args)
+
+
+def main(argv):
+ if len(argv) < 4:
+ print "usage: %s out_dir root_dir mojom_file [extra_flags]" % argv[0]
+ return 1
+
+ RunBindingsGenerator(argv[1], argv[2], argv[3], extra_flags=argv[4:])
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/chromium/mojo/service_manager/BUILD.gn b/chromium/mojo/service_manager/BUILD.gn
new file mode 100644
index 00000000000..584b4f50f97
--- /dev/null
+++ b/chromium/mojo/service_manager/BUILD.gn
@@ -0,0 +1,33 @@
+# Copyright 2014 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.
+
+# GYP version: mojo.gyp:mojo_service_manager
+component("service_manager") {
+ output_name = "mojo_service_manager"
+ sources = [
+ "background_service_loader.cc",
+ "background_service_loader.h",
+ "service_loader.h",
+ "service_manager.cc",
+ "service_manager.h",
+ "service_manager_export.h",
+ ]
+
+ defines = [ "MOJO_SERVICE_MANAGER_IMPLEMENTATION" ]
+
+ deps = [
+ "//base",
+ "//base/third_party/dynamic_annotations",
+ "//net",
+ "//url",
+ "//mojo/common",
+ "//mojo/environment:chromium",
+ "//mojo/public/interfaces/service_provider:service_provider",
+ "//mojo/system",
+ ]
+
+ forward_dependent_configs_from = [
+ "//mojo/public/interfaces/service_provider:service_provider",
+ ]
+}
diff --git a/chromium/mojo/service_manager/background_service_loader.cc b/chromium/mojo/service_manager/background_service_loader.cc
new file mode 100644
index 00000000000..96a2c430a89
--- /dev/null
+++ b/chromium/mojo/service_manager/background_service_loader.cc
@@ -0,0 +1,104 @@
+// Copyright 2014 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 "mojo/service_manager/background_service_loader.h"
+
+#include "base/bind.h"
+#include "mojo/service_manager/service_manager.h"
+
+namespace mojo {
+
+class BackgroundServiceLoader::BackgroundLoader {
+ public:
+ explicit BackgroundLoader(ServiceLoader* loader) : loader_(loader) {}
+ ~BackgroundLoader() {}
+
+ void LoadService(ServiceManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle service_provider_handle) {
+ loader_->LoadService(manager, url, service_provider_handle.Pass());
+ }
+
+ void OnServiceError(ServiceManager* manager, const GURL& url) {
+ loader_->OnServiceError(manager, url);
+ }
+
+ private:
+ base::MessageLoop::Type message_loop_type_;
+ ServiceLoader* loader_; // Owned by BackgroundServiceLoader
+
+ DISALLOW_COPY_AND_ASSIGN(BackgroundLoader);
+};
+
+BackgroundServiceLoader::BackgroundServiceLoader(
+ scoped_ptr<ServiceLoader> real_loader,
+ const char* thread_name,
+ base::MessageLoop::Type message_loop_type)
+ : loader_(real_loader.Pass()),
+ thread_(thread_name),
+ message_loop_type_(message_loop_type),
+ background_loader_(NULL) {
+}
+
+BackgroundServiceLoader::~BackgroundServiceLoader() {
+ if (thread_.IsRunning()) {
+ thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&BackgroundServiceLoader::ShutdownOnBackgroundThread,
+ base::Unretained(this)));
+ }
+ thread_.Stop();
+}
+
+void BackgroundServiceLoader::LoadService(
+ ServiceManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle service_handle) {
+ const int kDefaultStackSize = 0;
+ if (!thread_.IsRunning())
+ thread_.StartWithOptions(
+ base::Thread::Options(message_loop_type_, kDefaultStackSize));
+ thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&BackgroundServiceLoader::LoadServiceOnBackgroundThread,
+ base::Unretained(this), manager, url,
+ base::Owned(
+ new ScopedMessagePipeHandle(service_handle.Pass()))));
+}
+
+void BackgroundServiceLoader::OnServiceError(ServiceManager* manager,
+ const GURL& url) {
+ if (!thread_.IsRunning())
+ thread_.Start();
+ thread_.message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&BackgroundServiceLoader::OnServiceErrorOnBackgroundThread,
+ base::Unretained(this), manager, url));
+}
+
+void BackgroundServiceLoader::LoadServiceOnBackgroundThread(
+ ServiceManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle* service_provider_handle) {
+ if (!background_loader_)
+ background_loader_ = new BackgroundLoader(loader_.get());
+ background_loader_->LoadService(
+ manager, url, service_provider_handle->Pass());
+}
+
+void BackgroundServiceLoader::OnServiceErrorOnBackgroundThread(
+ ServiceManager* manager,
+ const GURL& url) {
+ if (!background_loader_)
+ background_loader_ = new BackgroundLoader(loader_.get());
+ background_loader_->OnServiceError(manager, url);
+}
+
+void BackgroundServiceLoader::ShutdownOnBackgroundThread() {
+ delete background_loader_;
+ // Destroy |loader_| on the thread it's actually used on.
+ loader_.reset();
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/service_manager/background_service_loader.h b/chromium/mojo/service_manager/background_service_loader.h
new file mode 100644
index 00000000000..59dbc0f86db
--- /dev/null
+++ b/chromium/mojo/service_manager/background_service_loader.h
@@ -0,0 +1,61 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICE_MANAGER_BACKGROUND_SERVICE_LOADER_H_
+#define MOJO_SERVICE_MANAGER_BACKGROUND_SERVICE_LOADER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread.h"
+#include "mojo/service_manager/service_loader.h"
+
+namespace mojo {
+
+class ServiceManager;
+
+// ServiceLoader implementation that creates a background thread and issues load
+// requests there.
+class MOJO_SERVICE_MANAGER_EXPORT BackgroundServiceLoader
+ : public ServiceLoader {
+ public:
+ BackgroundServiceLoader(scoped_ptr<ServiceLoader> real_loader,
+ const char* thread_name,
+ base::MessageLoop::Type message_loop_type);
+ virtual ~BackgroundServiceLoader();
+
+ // ServiceLoader overrides:
+ virtual void LoadService(ServiceManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle service_handle) OVERRIDE;
+ virtual void OnServiceError(ServiceManager* manager,
+ const GURL& url) OVERRIDE;
+
+ private:
+ class BackgroundLoader;
+
+ // These functions are exected on the background thread. They call through
+ // to |background_loader_| to do the actual loading.
+ // TODO: having this code take a |manager| is fragile (as ServiceManager isn't
+ // thread safe).
+ void LoadServiceOnBackgroundThread(
+ ServiceManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle* service_provider_handle);
+ void OnServiceErrorOnBackgroundThread(ServiceManager* manager,
+ const GURL& url);
+ void ShutdownOnBackgroundThread();
+
+ scoped_ptr<ServiceLoader> loader_;
+ base::Thread thread_;
+ base::MessageLoop::Type message_loop_type_;
+
+ // Lives on |thread_|. Trivial interface that calls through to |loader_|.
+ BackgroundLoader* background_loader_;
+
+ DISALLOW_COPY_AND_ASSIGN(BackgroundServiceLoader);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICE_MANAGER_BACKGROUND_SERVICE_LOADER_H_
diff --git a/chromium/mojo/service_manager/service_loader.h b/chromium/mojo/service_manager/service_loader.h
new file mode 100644
index 00000000000..32c15cccd8d
--- /dev/null
+++ b/chromium/mojo/service_manager/service_loader.h
@@ -0,0 +1,32 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICE_MANAGER_SERVICE_LOADER_H_
+#define MOJO_SERVICE_MANAGER_SERVICE_LOADER_H_
+
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/service_manager/service_manager_export.h"
+#include "url/gurl.h"
+
+namespace mojo {
+
+class ServiceManager;
+
+// Interface to allowing default loading behavior to be overridden for a
+// specific url.
+class MOJO_SERVICE_MANAGER_EXPORT ServiceLoader {
+ public:
+ virtual ~ServiceLoader() {}
+ virtual void LoadService(ServiceManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle service_handle) = 0;
+ virtual void OnServiceError(ServiceManager* manager, const GURL& url) = 0;
+
+ protected:
+ ServiceLoader() {}
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICE_MANAGER_SERVICE_LOADER_H_
diff --git a/chromium/mojo/service_manager/service_manager.cc b/chromium/mojo/service_manager/service_manager.cc
new file mode 100644
index 00000000000..060ca4ff10f
--- /dev/null
+++ b/chromium/mojo/service_manager/service_manager.cc
@@ -0,0 +1,206 @@
+// Copyright 2013 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 <stdio.h>
+
+#include "mojo/service_manager/service_manager.h"
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "mojo/service_manager/service_loader.h"
+
+namespace mojo {
+
+namespace {
+// Used by TestAPI.
+bool has_created_instance = false;
+}
+
+class ServiceManager::ServiceFactory : public InterfaceImpl<ServiceProvider> {
+ public:
+ ServiceFactory(ServiceManager* manager, const GURL& url)
+ : manager_(manager),
+ url_(url) {
+ }
+
+ virtual ~ServiceFactory() {
+ }
+
+ void ConnectToClient(const std::string& service_name,
+ ScopedMessagePipeHandle handle,
+ const GURL& requestor_url) {
+ if (handle.is_valid()) {
+ client()->ConnectToService(
+ url_.spec(), service_name, handle.Pass(), requestor_url.spec());
+ }
+ }
+
+ // ServiceProvider implementation:
+ virtual void ConnectToService(const String& service_url,
+ const String& service_name,
+ ScopedMessagePipeHandle client_pipe,
+ const String& requestor_url) OVERRIDE {
+ // Ignore provided requestor_url and use url from connection.
+ manager_->ConnectToService(
+ GURL(service_url), service_name, client_pipe.Pass(), url_);
+ }
+
+ const GURL& url() const { return url_; }
+
+ private:
+ virtual void OnConnectionError() OVERRIDE {
+ manager_->OnServiceFactoryError(this);
+ }
+
+ ServiceManager* const manager_;
+ const GURL url_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceFactory);
+};
+
+class ServiceManager::TestAPI::TestServiceProviderConnection
+ : public InterfaceImpl<ServiceProvider> {
+ public:
+ explicit TestServiceProviderConnection(ServiceManager* manager)
+ : manager_(manager) {}
+ virtual ~TestServiceProviderConnection() {}
+
+ virtual void OnConnectionError() OVERRIDE {
+ // TODO(darin): How should we handle this error?
+ }
+
+ // ServiceProvider:
+ virtual void ConnectToService(const String& service_url,
+ const String& service_name,
+ ScopedMessagePipeHandle client_pipe,
+ const String& requestor_url) OVERRIDE {
+ manager_->ConnectToService(GURL(service_url),
+ service_name,
+ client_pipe.Pass(),
+ GURL(requestor_url));
+ }
+
+ private:
+ ServiceManager* manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestServiceProviderConnection);
+};
+
+// static
+ServiceManager::TestAPI::TestAPI(ServiceManager* manager) : manager_(manager) {
+}
+
+ServiceManager::TestAPI::~TestAPI() {
+}
+
+bool ServiceManager::TestAPI::HasCreatedInstance() {
+ return has_created_instance;
+}
+
+ScopedMessagePipeHandle ServiceManager::TestAPI::GetServiceProviderHandle() {
+ MessagePipe pipe;
+ service_provider_.reset(
+ BindToPipe(new TestServiceProviderConnection(manager_),
+ pipe.handle0.Pass()));
+ return pipe.handle1.Pass();
+}
+
+bool ServiceManager::TestAPI::HasFactoryForURL(const GURL& url) const {
+ return manager_->url_to_service_factory_.find(url) !=
+ manager_->url_to_service_factory_.end();
+}
+
+ServiceManager::ServiceManager()
+ : interceptor_(NULL) {
+}
+
+ServiceManager::~ServiceManager() {
+ STLDeleteValues(&url_to_service_factory_);
+ STLDeleteValues(&url_to_loader_);
+ STLDeleteValues(&scheme_to_loader_);
+}
+
+// static
+ServiceManager* ServiceManager::GetInstance() {
+ static base::LazyInstance<ServiceManager> instance =
+ LAZY_INSTANCE_INITIALIZER;
+ has_created_instance = true;
+ return &instance.Get();
+}
+
+void ServiceManager::ConnectToService(const GURL& url,
+ const std::string& name,
+ ScopedMessagePipeHandle client_handle,
+ const GURL& requestor_url) {
+ URLToServiceFactoryMap::const_iterator service_it =
+ url_to_service_factory_.find(url);
+ ServiceFactory* service_factory;
+ if (service_it != url_to_service_factory_.end()) {
+ service_factory = service_it->second;
+ } else {
+ MessagePipe pipe;
+ GetLoaderForURL(url)->LoadService(this, url, pipe.handle0.Pass());
+
+ service_factory =
+ BindToPipe(new ServiceFactory(this, url), pipe.handle1.Pass());
+
+ url_to_service_factory_[url] = service_factory;
+ }
+ if (interceptor_) {
+ service_factory->ConnectToClient(
+ name,
+ interceptor_->OnConnectToClient(url, client_handle.Pass()),
+ requestor_url);
+ } else {
+ service_factory->ConnectToClient(name, client_handle.Pass(), requestor_url);
+ }
+}
+
+void ServiceManager::SetLoaderForURL(scoped_ptr<ServiceLoader> loader,
+ const GURL& url) {
+ URLToLoaderMap::iterator it = url_to_loader_.find(url);
+ if (it != url_to_loader_.end())
+ delete it->second;
+ url_to_loader_[url] = loader.release();
+}
+
+void ServiceManager::SetLoaderForScheme(scoped_ptr<ServiceLoader> loader,
+ const std::string& scheme) {
+ SchemeToLoaderMap::iterator it = scheme_to_loader_.find(scheme);
+ if (it != scheme_to_loader_.end())
+ delete it->second;
+ scheme_to_loader_[scheme] = loader.release();
+}
+
+void ServiceManager::SetInterceptor(Interceptor* interceptor) {
+ interceptor_ = interceptor;
+}
+
+ServiceLoader* ServiceManager::GetLoaderForURL(const GURL& url) {
+ URLToLoaderMap::const_iterator url_it = url_to_loader_.find(url);
+ if (url_it != url_to_loader_.end())
+ return url_it->second;
+ SchemeToLoaderMap::const_iterator scheme_it =
+ scheme_to_loader_.find(url.scheme());
+ if (scheme_it != scheme_to_loader_.end())
+ return scheme_it->second;
+ DCHECK(default_loader_);
+ return default_loader_.get();
+}
+
+void ServiceManager::OnServiceFactoryError(ServiceFactory* service_factory) {
+ // Called from ~ServiceFactory, so we do not need to call Destroy here.
+ const GURL url = service_factory->url();
+ URLToServiceFactoryMap::iterator it = url_to_service_factory_.find(url);
+ DCHECK(it != url_to_service_factory_.end());
+ delete it->second;
+ url_to_service_factory_.erase(it);
+ ServiceLoader* loader = GetLoaderForURL(url);
+ if (loader)
+ loader->OnServiceError(this, url);
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/service_manager/service_manager.h b/chromium/mojo/service_manager/service_manager.h
new file mode 100644
index 00000000000..170e3b7e311
--- /dev/null
+++ b/chromium/mojo/service_manager/service_manager.h
@@ -0,0 +1,117 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SERVICE_MANAGER_SERVICE_MANAGER_H_
+#define MOJO_SERVICE_MANAGER_SERVICE_MANAGER_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+#include "mojo/service_manager/service_loader.h"
+#include "mojo/service_manager/service_manager_export.h"
+#include "url/gurl.h"
+
+namespace mojo {
+
+class MOJO_SERVICE_MANAGER_EXPORT ServiceManager {
+ public:
+ // API for testing.
+ class MOJO_SERVICE_MANAGER_EXPORT TestAPI {
+ public:
+ explicit TestAPI(ServiceManager* manager);
+ ~TestAPI();
+
+ // Returns a handle to a unique ServiceProvider instance.
+ ScopedMessagePipeHandle GetServiceProviderHandle();
+
+ // Returns true if the shared instance has been created.
+ static bool HasCreatedInstance();
+ // Returns true if there is a ServiceFactory for this URL.
+ bool HasFactoryForURL(const GURL& url) const;
+
+ private:
+ class TestServiceProviderConnection;
+
+ ServiceManager* manager_;
+ scoped_ptr<TestServiceProviderConnection> service_provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestAPI);
+ };
+
+ // Interface class for debugging only.
+ class Interceptor {
+ public:
+ virtual ~Interceptor() {}
+ // Called when ServiceManager::Connect is called.
+ virtual ScopedMessagePipeHandle OnConnectToClient(
+ const GURL& url, ScopedMessagePipeHandle handle) = 0;
+ };
+
+ ServiceManager();
+ ~ServiceManager();
+
+ // Returns a shared instance, creating it if necessary.
+ static ServiceManager* GetInstance();
+
+ // Loads a service if necessary and establishes a new client connection.
+ void ConnectToService(const GURL& service_url,
+ const std::string& service_name,
+ ScopedMessagePipeHandle client_handle,
+ const GURL& requestor_url);
+
+ template <typename Interface>
+ void ConnectTo(const GURL& service_url,
+ InterfacePtr<Interface>* ptr,
+ const GURL& requestor_url) {
+ MessagePipe pipe;
+ ptr->Bind(pipe.handle0.Pass());
+ ConnectToService(service_url,
+ Interface::Name_,
+ pipe.handle1.Pass(),
+ requestor_url);
+ }
+
+ // Sets the default Loader to be used if not overridden by SetLoaderForURL()
+ // or SetLoaderForScheme().
+ void set_default_loader(scoped_ptr<ServiceLoader> loader) {
+ default_loader_ = loader.Pass();
+ }
+ // Sets a Loader to be used for a specific url.
+ void SetLoaderForURL(scoped_ptr<ServiceLoader> loader, const GURL& url);
+ // Sets a Loader to be used for a specific url scheme.
+ void SetLoaderForScheme(scoped_ptr<ServiceLoader> loader,
+ const std::string& scheme);
+ // Allows to interpose a debugger to service connections.
+ void SetInterceptor(Interceptor* interceptor);
+
+ private:
+ class ServiceFactory;
+ typedef std::map<std::string, ServiceLoader*> SchemeToLoaderMap;
+ typedef std::map<GURL, ServiceLoader*> URLToLoaderMap;
+ typedef std::map<GURL, ServiceFactory*> URLToServiceFactoryMap;
+
+ // Returns the Loader to use for a url (using default if not overridden.)
+ // The preference is to use a loader that's been specified for an url first,
+ // then one that's been specified for a scheme, then the default.
+ ServiceLoader* GetLoaderForURL(const GURL& url);
+
+ // Removes a ServiceFactory when it encounters an error.
+ void OnServiceFactoryError(ServiceFactory* service_factory);
+
+ // Loader management.
+ URLToLoaderMap url_to_loader_;
+ SchemeToLoaderMap scheme_to_loader_;
+ scoped_ptr<ServiceLoader> default_loader_;
+ Interceptor* interceptor_;
+
+ URLToServiceFactoryMap url_to_service_factory_;
+ DISALLOW_COPY_AND_ASSIGN(ServiceManager);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICE_MANAGER_SERVICE_MANAGER_H_
diff --git a/chromium/mojo/service_manager/service_manager_export.h b/chromium/mojo/service_manager/service_manager_export.h
new file mode 100644
index 00000000000..251aad95d52
--- /dev/null
+++ b/chromium/mojo/service_manager/service_manager_export.h
@@ -0,0 +1,32 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICE_MANAGER_SERVICE_MANAGER_EXPORT_H_
+#define MOJO_SERVICE_MANAGER_SERVICE_MANAGER_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_SERVICE_MANAGER_IMPLEMENTATION)
+#define MOJO_SERVICE_MANAGER_EXPORT __declspec(dllexport)
+#else
+#define MOJO_SERVICE_MANAGER_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_SERVICE_MANAGER_IMPLEMENTATION)
+#define MOJO_SERVICE_MANAGER_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_SERVICE_MANAGER_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#else // !defined(COMPONENT_BUILD)
+#define MOJO_SERVICE_MANAGER_EXPORT
+#endif
+
+#endif // MOJO_SERVICE_MANAGER_SERVICE_MANAGER_EXPORT_H_
diff --git a/chromium/mojo/service_manager/service_manager_unittest.cc b/chromium/mojo/service_manager/service_manager_unittest.cc
new file mode 100644
index 00000000000..a2122017c27
--- /dev/null
+++ b/chromium/mojo/service_manager/service_manager_unittest.cc
@@ -0,0 +1,406 @@
+// Copyright 2014 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 "base/at_exit.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+#include "mojo/service_manager/service_loader.h"
+#include "mojo/service_manager/service_manager.h"
+#include "mojo/service_manager/test.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace {
+
+const char kTestURLString[] = "test:testService";
+const char kTestAURLString[] = "test:TestA";
+const char kTestBURLString[] = "test:TestB";
+
+struct TestContext {
+ TestContext() : num_impls(0), num_loader_deletes(0) {}
+ std::string last_test_string;
+ int num_impls;
+ int num_loader_deletes;
+};
+
+class QuitMessageLoopErrorHandler : public ErrorHandler {
+ public:
+ QuitMessageLoopErrorHandler() {}
+ virtual ~QuitMessageLoopErrorHandler() {}
+
+ // |ErrorHandler| implementation:
+ virtual void OnConnectionError() OVERRIDE {
+ base::MessageLoop::current()->QuitWhenIdle();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuitMessageLoopErrorHandler);
+};
+
+class TestServiceImpl : public InterfaceImpl<TestService> {
+ public:
+ explicit TestServiceImpl(TestContext* context) : context_(context) {
+ ++context_->num_impls;
+ }
+
+ virtual ~TestServiceImpl() {
+ --context_->num_impls;
+ }
+
+ virtual void OnConnectionError() OVERRIDE {
+ base::MessageLoop::current()->Quit();
+ }
+
+ // TestService implementation:
+ virtual void Test(const String& test_string) OVERRIDE {
+ context_->last_test_string = test_string;
+ client()->AckTest();
+ }
+
+ private:
+ TestContext* context_;
+};
+
+class TestClientImpl : public TestClient {
+ public:
+ explicit TestClientImpl(TestServicePtr service)
+ : service_(service.Pass()),
+ quit_after_ack_(false) {
+ service_.set_client(this);
+ }
+
+ virtual ~TestClientImpl() {}
+
+ virtual void AckTest() OVERRIDE {
+ if (quit_after_ack_)
+ base::MessageLoop::current()->Quit();
+ }
+
+ void Test(std::string test_string) {
+ quit_after_ack_ = true;
+ service_->Test(test_string);
+ }
+
+ private:
+ TestServicePtr service_;
+ bool quit_after_ack_;
+ DISALLOW_COPY_AND_ASSIGN(TestClientImpl);
+};
+
+class TestServiceLoader : public ServiceLoader {
+ public:
+ TestServiceLoader()
+ : context_(NULL),
+ num_loads_(0) {
+ }
+
+ virtual ~TestServiceLoader() {
+ if (context_)
+ ++context_->num_loader_deletes;
+ test_app_.reset(NULL);
+ }
+
+ void set_context(TestContext* context) { context_ = context; }
+ int num_loads() const { return num_loads_; }
+
+ private:
+ virtual void LoadService(
+ ServiceManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle service_provider_handle) OVERRIDE {
+ ++num_loads_;
+ test_app_.reset(new Application(service_provider_handle.Pass()));
+ test_app_->AddService<TestServiceImpl>(context_);
+ }
+
+ virtual void OnServiceError(ServiceManager* manager,
+ const GURL& url) OVERRIDE {
+ }
+
+ scoped_ptr<Application> test_app_;
+ TestContext* context_;
+ int num_loads_;
+ DISALLOW_COPY_AND_ASSIGN(TestServiceLoader);
+};
+
+// Used to test that the requestor url will be correctly passed.
+class TestAImpl : public InterfaceImpl<TestA> {
+ public:
+ explicit TestAImpl(Application* app) : app_(app) {}
+
+ virtual void LoadB() OVERRIDE {
+ TestBPtr b;
+ app_->ConnectTo(kTestBURLString, &b);
+ b->Test();
+ }
+
+ private:
+ Application* app_;
+};
+
+class TestBImpl : public InterfaceImpl<TestB> {
+ public:
+ virtual void Test() OVERRIDE {
+ base::MessageLoop::current()->Quit();
+ }
+};
+
+class TestApp : public Application, public ServiceLoader {
+ public:
+ explicit TestApp(std::string requestor_url)
+ : requestor_url_(requestor_url),
+ num_connects_(0) {
+ }
+
+ int num_connects() const { return num_connects_; }
+
+ private:
+ virtual void LoadService(
+ ServiceManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle service_provider_handle) OVERRIDE {
+ BindServiceProvider(service_provider_handle.Pass());
+ }
+
+ virtual bool AllowIncomingConnection(const mojo::String& service_name,
+ const mojo::String& requestor_url)
+ MOJO_OVERRIDE {
+ if (requestor_url_.empty() || requestor_url_ == requestor_url) {
+ ++num_connects_;
+ return true;
+ } else {
+ base::MessageLoop::current()->Quit();
+ return false;
+ }
+ }
+
+ virtual void OnServiceError(ServiceManager* manager,
+ const GURL& url) OVERRIDE {}
+ std::string requestor_url_;
+ int num_connects_;
+};
+
+class TestServiceInterceptor : public ServiceManager::Interceptor {
+ public:
+ TestServiceInterceptor() : call_count_(0) {}
+
+ virtual ScopedMessagePipeHandle OnConnectToClient(
+ const GURL& url, ScopedMessagePipeHandle handle) OVERRIDE {
+ ++call_count_;
+ url_ = url;
+ return handle.Pass();
+ }
+
+ std::string url_spec() const {
+ if (!url_.is_valid())
+ return "invalid url";
+ return url_.spec();
+ }
+
+ int call_count() const {
+ return call_count_;
+ }
+
+ private:
+ int call_count_;
+ GURL url_;
+ DISALLOW_COPY_AND_ASSIGN(TestServiceInterceptor);
+};
+
+} // namespace
+
+class ServiceManagerTest : public testing::Test {
+ public:
+ ServiceManagerTest() {}
+
+ virtual ~ServiceManagerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ GURL test_url(kTestURLString);
+ service_manager_.reset(new ServiceManager);
+
+ MessagePipe pipe;
+ TestServicePtr service_proxy = MakeProxy<TestService>(pipe.handle0.Pass());
+ test_client_.reset(new TestClientImpl(service_proxy.Pass()));
+
+ TestServiceLoader* default_loader = new TestServiceLoader;
+ default_loader->set_context(&context_);
+ service_manager_->set_default_loader(
+ scoped_ptr<ServiceLoader>(default_loader));
+
+ service_manager_->ConnectToService(
+ test_url, TestService::Name_, pipe.handle1.Pass(), GURL());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ test_client_.reset(NULL);
+ service_manager_.reset(NULL);
+ }
+
+ bool HasFactoryForTestURL() {
+ ServiceManager::TestAPI manager_test_api(service_manager_.get());
+ return manager_test_api.HasFactoryForURL(GURL(kTestURLString));
+ }
+
+ protected:
+ base::ShadowingAtExitManager at_exit_;
+ base::MessageLoop loop_;
+ TestContext context_;
+ scoped_ptr<TestClientImpl> test_client_;
+ scoped_ptr<ServiceManager> service_manager_;
+ DISALLOW_COPY_AND_ASSIGN(ServiceManagerTest);
+};
+
+TEST_F(ServiceManagerTest, Basic) {
+ test_client_->Test("test");
+ loop_.Run();
+ EXPECT_EQ(std::string("test"), context_.last_test_string);
+}
+
+TEST_F(ServiceManagerTest, ClientError) {
+ test_client_->Test("test");
+ EXPECT_TRUE(HasFactoryForTestURL());
+ loop_.Run();
+ EXPECT_EQ(1, context_.num_impls);
+ test_client_.reset(NULL);
+ loop_.Run();
+ EXPECT_EQ(0, context_.num_impls);
+ EXPECT_TRUE(HasFactoryForTestURL());
+}
+
+TEST_F(ServiceManagerTest, Deletes) {
+ {
+ ServiceManager sm;
+ TestServiceLoader* default_loader = new TestServiceLoader;
+ default_loader->set_context(&context_);
+ TestServiceLoader* url_loader1 = new TestServiceLoader;
+ TestServiceLoader* url_loader2 = new TestServiceLoader;
+ url_loader1->set_context(&context_);
+ url_loader2->set_context(&context_);
+ TestServiceLoader* scheme_loader1 = new TestServiceLoader;
+ TestServiceLoader* scheme_loader2 = new TestServiceLoader;
+ scheme_loader1->set_context(&context_);
+ scheme_loader2->set_context(&context_);
+ sm.set_default_loader(scoped_ptr<ServiceLoader>(default_loader));
+ sm.SetLoaderForURL(scoped_ptr<ServiceLoader>(url_loader1),
+ GURL("test:test1"));
+ sm.SetLoaderForURL(scoped_ptr<ServiceLoader>(url_loader2),
+ GURL("test:test1"));
+ sm.SetLoaderForScheme(scoped_ptr<ServiceLoader>(scheme_loader1), "test");
+ sm.SetLoaderForScheme(scoped_ptr<ServiceLoader>(scheme_loader2), "test");
+ }
+ EXPECT_EQ(5, context_.num_loader_deletes);
+}
+
+// Confirm that both urls and schemes can have their loaders explicitly set.
+TEST_F(ServiceManagerTest, SetLoaders) {
+ ServiceManager sm;
+ TestServiceLoader* default_loader = new TestServiceLoader;
+ TestServiceLoader* url_loader = new TestServiceLoader;
+ TestServiceLoader* scheme_loader = new TestServiceLoader;
+ sm.set_default_loader(scoped_ptr<ServiceLoader>(default_loader));
+ sm.SetLoaderForURL(scoped_ptr<ServiceLoader>(url_loader), GURL("test:test1"));
+ sm.SetLoaderForScheme(scoped_ptr<ServiceLoader>(scheme_loader), "test");
+
+ // test::test1 should go to url_loader.
+ TestServicePtr test_service;
+ sm.ConnectTo(GURL("test:test1"), &test_service, GURL());
+ EXPECT_EQ(1, url_loader->num_loads());
+ EXPECT_EQ(0, scheme_loader->num_loads());
+ EXPECT_EQ(0, default_loader->num_loads());
+
+ // test::test2 should go to scheme loader.
+ sm.ConnectTo(GURL("test:test2"), &test_service, GURL());
+ EXPECT_EQ(1, url_loader->num_loads());
+ EXPECT_EQ(1, scheme_loader->num_loads());
+ EXPECT_EQ(0, default_loader->num_loads());
+
+ // http::test1 should go to default loader.
+ sm.ConnectTo(GURL("http:test1"), &test_service, GURL());
+ EXPECT_EQ(1, url_loader->num_loads());
+ EXPECT_EQ(1, scheme_loader->num_loads());
+ EXPECT_EQ(1, default_loader->num_loads());
+}
+
+// Confirm that the url of a service is correctly passed to another service that
+// it loads.
+TEST_F(ServiceManagerTest, ALoadB) {
+ ServiceManager sm;
+
+ // Any url can load a.
+ TestApp* a_app = new TestApp(std::string());
+ a_app->AddService<TestAImpl>(a_app);
+ sm.SetLoaderForURL(scoped_ptr<ServiceLoader>(a_app), GURL(kTestAURLString));
+
+ // Only a can load b.
+ TestApp* b_app = new TestApp(kTestAURLString);
+ b_app->AddService<TestBImpl>();
+ sm.SetLoaderForURL(scoped_ptr<ServiceLoader>(b_app), GURL(kTestBURLString));
+
+ TestAPtr a;
+ sm.ConnectTo(GURL(kTestAURLString), &a, GURL());
+ a->LoadB();
+ loop_.Run();
+ EXPECT_EQ(1, b_app->num_connects());
+}
+
+// Confirm that the url of a service is correctly passed to another service that
+// it loads, and that it can be rejected.
+TEST_F(ServiceManagerTest, ANoLoadB) {
+ ServiceManager sm;
+
+ // Any url can load a.
+ TestApp* a_app = new TestApp(std::string());
+ a_app->AddService<TestAImpl>(a_app);
+ sm.SetLoaderForURL(scoped_ptr<ServiceLoader>(a_app), GURL(kTestAURLString));
+
+ // Only c can load b, so this will fail.
+ TestApp* b_app = new TestApp("test:TestC");
+ b_app->AddService<TestBImpl>();
+ sm.SetLoaderForURL(scoped_ptr<ServiceLoader>(b_app), GURL(kTestBURLString));
+
+ TestAPtr a;
+ sm.ConnectTo(GURL(kTestAURLString), &a, GURL());
+ a->LoadB();
+ loop_.Run();
+ EXPECT_EQ(0, b_app->num_connects());
+}
+
+TEST_F(ServiceManagerTest, NoServiceNoLoad) {
+ ServiceManager sm;
+
+ TestApp* b_app = new TestApp(std::string());
+ b_app->AddService<TestBImpl>();
+ sm.SetLoaderForURL(scoped_ptr<ServiceLoader>(b_app), GURL(kTestBURLString));
+
+ // There is no TestA service implementation registered with ServiceManager,
+ // so this cannot succeed (but also shouldn't crash).
+ TestAPtr a;
+ sm.ConnectTo(GURL(kTestBURLString), &a, GURL());
+ QuitMessageLoopErrorHandler quitter;
+ a.set_error_handler(&quitter);
+ a->LoadB();
+
+ loop_.Run();
+ EXPECT_TRUE(a.encountered_error());
+ EXPECT_EQ(0, b_app->num_connects());
+}
+
+TEST_F(ServiceManagerTest, Interceptor) {
+ ServiceManager sm;
+ TestServiceInterceptor interceptor;
+ TestServiceLoader* default_loader = new TestServiceLoader;
+ sm.set_default_loader(scoped_ptr<ServiceLoader>(default_loader));
+ sm.SetInterceptor(&interceptor);
+
+ std::string url("test:test3");
+ TestServicePtr test_service;
+ sm.ConnectTo(GURL(url), &test_service, GURL());
+ EXPECT_EQ(1, interceptor.call_count());
+ EXPECT_EQ(url, interceptor.url_spec());
+ EXPECT_EQ(1, default_loader->num_loads());
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/service_manager/test.mojom b/chromium/mojo/service_manager/test.mojom
new file mode 100644
index 00000000000..47921cda9af
--- /dev/null
+++ b/chromium/mojo/service_manager/test.mojom
@@ -0,0 +1,24 @@
+// Copyright 2013 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.
+
+module mojo {
+
+[Client=TestClient]
+interface TestService {
+ Test(string test_string);
+};
+
+interface TestClient {
+ AckTest();
+};
+
+interface TestA {
+ LoadB();
+};
+
+interface TestB {
+ Test();
+};
+
+}
diff --git a/chromium/mojo/services/DEPS b/chromium/mojo/services/DEPS
new file mode 100644
index 00000000000..2bbe62df877
--- /dev/null
+++ b/chromium/mojo/services/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+ "-mojo",
+ "+mojo/common",
+ "+mojo/public",
+ "+jni",
+
+ # TODO(abarth) Instead of having the services depend on the shell, we
+ # probably should create a layer below the services.
+ "+mojo/shell",
+]
diff --git a/chromium/mojo/services/dbus_echo/DEPS b/chromium/mojo/services/dbus_echo/DEPS
new file mode 100644
index 00000000000..35a7b6c826f
--- /dev/null
+++ b/chromium/mojo/services/dbus_echo/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+base",
+ "+mojo/dbus",
+ "+mojo/embedder",
+]
diff --git a/chromium/mojo/services/dbus_echo/dbus_echo_service.cc b/chromium/mojo/services/dbus_echo/dbus_echo_service.cc
new file mode 100644
index 00000000000..ae8029f435b
--- /dev/null
+++ b/chromium/mojo/services/dbus_echo/dbus_echo_service.cc
@@ -0,0 +1,58 @@
+// Copyright 2014 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 "base/at_exit.h"
+#include "base/callback.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/dbus/dbus_external_service.h"
+#include "mojo/embedder/channel_init.h"
+#include "mojo/embedder/embedder.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/services/dbus_echo/echo.mojom.h"
+
+namespace {
+class EchoServiceImpl : public mojo::InterfaceImpl<mojo::EchoService> {
+ public:
+ EchoServiceImpl() {}
+ virtual ~EchoServiceImpl() {}
+
+ protected:
+ virtual void Echo(
+ const mojo::String& in_to_echo,
+ const mojo::Callback<void(mojo::String)>& callback) OVERRIDE {
+ DVLOG(1) << "Asked to echo " << in_to_echo;
+ callback.Run(in_to_echo);
+ }
+};
+
+const char kServiceName[] = "org.chromium.EchoService";
+} // anonymous namespace
+
+int main(int argc, char** argv) {
+ base::AtExitManager exit_manager;
+ base::CommandLine::Init(argc, argv);
+
+ logging::LoggingSettings settings;
+ settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+ logging::InitLogging(settings);
+ logging::SetLogItems(false, // Process ID
+ false, // Thread ID
+ false, // Timestamp
+ false); // Tick count
+
+ mojo::embedder::Init();
+
+ base::MessageLoopForIO message_loop;
+ base::RunLoop run_loop;
+
+ mojo::DBusExternalService<EchoServiceImpl> echo_service(kServiceName);
+ echo_service.Start();
+
+ run_loop.Run();
+ return 0;
+}
diff --git a/chromium/mojo/services/dbus_echo/echo.mojom b/chromium/mojo/services/dbus_echo/echo.mojom
new file mode 100644
index 00000000000..937737c223c
--- /dev/null
+++ b/chromium/mojo/services/dbus_echo/echo.mojom
@@ -0,0 +1,11 @@
+// Copyright 2014 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.
+
+module mojo {
+
+interface EchoService {
+ Echo(string to_echo) => (string echoed);
+};
+
+}
diff --git a/chromium/mojo/services/gles2/DEPS b/chromium/mojo/services/gles2/DEPS
new file mode 100644
index 00000000000..acd992d92a2
--- /dev/null
+++ b/chromium/mojo/services/gles2/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+gpu/command_buffer",
+ "+ui/gfx",
+ "+ui/gl",
+]
diff --git a/chromium/mojo/services/gles2/command_buffer.mojom b/chromium/mojo/services/gles2/command_buffer.mojom
new file mode 100644
index 00000000000..1f91362eb4d
--- /dev/null
+++ b/chromium/mojo/services/gles2/command_buffer.mojom
@@ -0,0 +1,49 @@
+// Copyright 2014 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.
+
+module mojo {
+
+struct CommandBufferState {
+ int32 num_entries;
+ int32 get_offset;
+ int32 put_offset;
+ int32 token;
+ int32 error; // TODO(piman): enum
+ int32 context_lost_reason; // TODO(piman): enum
+ uint32 generation;
+};
+
+interface CommandBufferSyncClient {
+ DidInitialize(bool success);
+ DidMakeProgress(CommandBufferState state);
+};
+
+[Client=CommandBufferClient]
+interface CommandBuffer {
+ Initialize(CommandBufferSyncClient sync_client,
+ handle<shared_buffer> shared_state);
+ SetGetBuffer(int32 buffer);
+ Flush(int32 put_offset);
+ MakeProgress(int32 last_get_offset);
+ RegisterTransferBuffer(
+ int32 id, handle<shared_buffer> transfer_buffer, uint32 size);
+ DestroyTransferBuffer(int32 id);
+ Echo() => ();
+
+ // TODO(piman): move to somewhere else (native_viewport?).
+ RequestAnimationFrames();
+ CancelAnimationFrames();
+
+ // TODO(piman): sync points
+};
+
+interface CommandBufferClient {
+ DidDestroy();
+ LostContext(int32 lost_reason); // TODO(piman): enum
+
+ // TODO(piman): move to somewhere else (native_viewport?).
+ DrawAnimationFrame();
+};
+
+}
diff --git a/chromium/mojo/services/gles2/command_buffer_impl.cc b/chromium/mojo/services/gles2/command_buffer_impl.cc
new file mode 100644
index 00000000000..090b78c3707
--- /dev/null
+++ b/chromium/mojo/services/gles2/command_buffer_impl.cc
@@ -0,0 +1,198 @@
+// Copyright 2013 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 "mojo/services/gles2/command_buffer_impl.h"
+
+#include "base/bind.h"
+#include "base/memory/shared_memory.h"
+
+#include "gpu/command_buffer/common/constants.h"
+#include "gpu/command_buffer/service/command_buffer_service.h"
+#include "gpu/command_buffer/service/context_group.h"
+#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
+#include "gpu/command_buffer/service/gpu_control_service.h"
+#include "gpu/command_buffer/service/gpu_scheduler.h"
+#include "gpu/command_buffer/service/image_manager.h"
+#include "gpu/command_buffer/service/mailbox_manager.h"
+#include "gpu/command_buffer/service/memory_tracking.h"
+#include "mojo/services/gles2/command_buffer_type_conversions.h"
+#include "mojo/services/gles2/mojo_buffer_backing.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/gl_surface.h"
+
+namespace mojo {
+namespace services {
+
+namespace {
+
+class MemoryTrackerStub : public gpu::gles2::MemoryTracker {
+ public:
+ MemoryTrackerStub() {}
+
+ virtual void TrackMemoryAllocatedChange(size_t old_size,
+ size_t new_size,
+ gpu::gles2::MemoryTracker::Pool pool)
+ OVERRIDE {}
+
+ virtual bool EnsureGPUMemoryAvailable(size_t size_needed) OVERRIDE {
+ return true;
+ };
+
+ private:
+ virtual ~MemoryTrackerStub() {}
+
+ DISALLOW_COPY_AND_ASSIGN(MemoryTrackerStub);
+};
+
+} // anonymous namespace
+
+CommandBufferImpl::CommandBufferImpl(gfx::AcceleratedWidget widget,
+ const gfx::Size& size)
+ : widget_(widget), size_(size) {}
+
+CommandBufferImpl::~CommandBufferImpl() {
+ client()->DidDestroy();
+ if (decoder_.get()) {
+ bool have_context = decoder_->MakeCurrent();
+ decoder_->Destroy(have_context);
+ }
+}
+
+void CommandBufferImpl::OnConnectionError() {
+ // TODO(darin): How should we handle this error?
+}
+
+void CommandBufferImpl::Initialize(
+ CommandBufferSyncClientPtr sync_client,
+ mojo::ScopedSharedBufferHandle shared_state) {
+ sync_client_ = sync_client.Pass();
+ sync_client_->DidInitialize(DoInitialize(shared_state.Pass()));
+}
+
+bool CommandBufferImpl::DoInitialize(
+ mojo::ScopedSharedBufferHandle shared_state) {
+ // TODO(piman): offscreen surface.
+ scoped_refptr<gfx::GLSurface> surface =
+ gfx::GLSurface::CreateViewGLSurface(widget_);
+ if (!surface.get())
+ return false;
+
+ // TODO(piman): context sharing, virtual contexts, gpu preference.
+ scoped_refptr<gfx::GLContext> context = gfx::GLContext::CreateGLContext(
+ NULL, surface.get(), gfx::PreferIntegratedGpu);
+ if (!context.get())
+ return false;
+
+ if (!context->MakeCurrent(surface.get()))
+ return false;
+
+ // TODO(piman): ShaderTranslatorCache is currently per-ContextGroup but
+ // only needs to be per-thread.
+ scoped_refptr<gpu::gles2::ContextGroup> context_group =
+ new gpu::gles2::ContextGroup(NULL,
+ NULL,
+ new MemoryTrackerStub,
+ new gpu::gles2::ShaderTranslatorCache,
+ NULL,
+ true);
+
+ command_buffer_.reset(
+ new gpu::CommandBufferService(context_group->transfer_buffer_manager()));
+ bool result = command_buffer_->Initialize();
+ DCHECK(result);
+
+ decoder_.reset(::gpu::gles2::GLES2Decoder::Create(context_group.get()));
+ scheduler_.reset(new gpu::GpuScheduler(
+ command_buffer_.get(), decoder_.get(), decoder_.get()));
+ decoder_->set_engine(scheduler_.get());
+
+ gpu::gles2::DisallowedFeatures disallowed_features;
+
+ // TODO(piman): attributes.
+ std::vector<int32> attrib_vector;
+ if (!decoder_->Initialize(surface,
+ context,
+ false /* offscreen */,
+ size_,
+ disallowed_features,
+ attrib_vector))
+ return false;
+
+ gpu_control_.reset(
+ new gpu::GpuControlService(context_group->image_manager(), NULL));
+
+ command_buffer_->SetPutOffsetChangeCallback(base::Bind(
+ &gpu::GpuScheduler::PutChanged, base::Unretained(scheduler_.get())));
+ command_buffer_->SetGetBufferChangeCallback(base::Bind(
+ &gpu::GpuScheduler::SetGetBuffer, base::Unretained(scheduler_.get())));
+ command_buffer_->SetParseErrorCallback(
+ base::Bind(&CommandBufferImpl::OnParseError, base::Unretained(this)));
+
+ // TODO(piman): other callbacks
+
+ const size_t kSize = sizeof(gpu::CommandBufferSharedState);
+ scoped_ptr<gpu::BufferBacking> backing(
+ gles2::MojoBufferBacking::Create(shared_state.Pass(), kSize));
+ if (!backing.get())
+ return false;
+
+ command_buffer_->SetSharedStateBuffer(backing.Pass());
+ return true;
+}
+
+void CommandBufferImpl::SetGetBuffer(int32_t buffer) {
+ command_buffer_->SetGetBuffer(buffer);
+}
+
+void CommandBufferImpl::Flush(int32_t put_offset) {
+ command_buffer_->Flush(put_offset);
+}
+
+void CommandBufferImpl::MakeProgress(int32_t last_get_offset) {
+ // TODO(piman): handle out-of-order.
+ sync_client_->DidMakeProgress(
+ CommandBufferState::From(command_buffer_->GetLastState()));
+}
+
+void CommandBufferImpl::RegisterTransferBuffer(
+ int32_t id,
+ mojo::ScopedSharedBufferHandle transfer_buffer,
+ uint32_t size) {
+ // Take ownership of the memory and map it into this process.
+ // This validates the size.
+ scoped_ptr<gpu::BufferBacking> backing(
+ gles2::MojoBufferBacking::Create(transfer_buffer.Pass(), size));
+ if (!backing.get()) {
+ DVLOG(0) << "Failed to map shared memory.";
+ return;
+ }
+ command_buffer_->RegisterTransferBuffer(id, backing.Pass());
+}
+
+void CommandBufferImpl::DestroyTransferBuffer(int32_t id) {
+ command_buffer_->DestroyTransferBuffer(id);
+}
+
+void CommandBufferImpl::Echo(const Callback<void()>& callback) {
+ callback.Run();
+}
+
+void CommandBufferImpl::RequestAnimationFrames() {
+ timer_.Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(16),
+ this,
+ &CommandBufferImpl::DrawAnimationFrame);
+}
+
+void CommandBufferImpl::CancelAnimationFrames() { timer_.Stop(); }
+
+void CommandBufferImpl::OnParseError() {
+ gpu::CommandBuffer::State state = command_buffer_->GetLastState();
+ client()->LostContext(state.context_lost_reason);
+}
+
+void CommandBufferImpl::DrawAnimationFrame() { client()->DrawAnimationFrame(); }
+
+} // namespace services
+} // namespace mojo
diff --git a/chromium/mojo/services/gles2/command_buffer_impl.h b/chromium/mojo/services/gles2/command_buffer_impl.h
new file mode 100644
index 00000000000..5d1c7bdd205
--- /dev/null
+++ b/chromium/mojo/services/gles2/command_buffer_impl.h
@@ -0,0 +1,72 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SERVICES_GLES2_COMMAND_BUFFER_IMPL_H_
+#define MOJO_SERVICES_GLES2_COMMAND_BUFFER_IMPL_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/timer/timer.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/services/gles2/command_buffer.mojom.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/size.h"
+
+namespace gpu {
+class CommandBufferService;
+class GpuScheduler;
+class GpuControlService;
+namespace gles2 {
+class GLES2Decoder;
+}
+}
+
+namespace mojo {
+namespace services {
+
+class CommandBufferImpl : public InterfaceImpl<CommandBuffer> {
+ public:
+ CommandBufferImpl(gfx::AcceleratedWidget widget,
+ const gfx::Size& size);
+ virtual ~CommandBufferImpl();
+
+ virtual void OnConnectionError() OVERRIDE;
+ virtual void Initialize(CommandBufferSyncClientPtr sync_client,
+ mojo::ScopedSharedBufferHandle shared_state) OVERRIDE;
+ virtual void SetGetBuffer(int32_t buffer) OVERRIDE;
+ virtual void Flush(int32_t put_offset) OVERRIDE;
+ virtual void MakeProgress(int32_t last_get_offset) OVERRIDE;
+ virtual void RegisterTransferBuffer(
+ int32_t id,
+ mojo::ScopedSharedBufferHandle transfer_buffer,
+ uint32_t size) OVERRIDE;
+ virtual void DestroyTransferBuffer(int32_t id) OVERRIDE;
+ virtual void Echo(const Callback<void()>& callback) OVERRIDE;
+
+ virtual void RequestAnimationFrames() OVERRIDE;
+ virtual void CancelAnimationFrames() OVERRIDE;
+
+ private:
+ bool DoInitialize(mojo::ScopedSharedBufferHandle shared_state);
+
+ void OnParseError();
+
+ void DrawAnimationFrame();
+
+ CommandBufferSyncClientPtr sync_client_;
+
+ gfx::AcceleratedWidget widget_;
+ gfx::Size size_;
+ scoped_ptr<gpu::CommandBufferService> command_buffer_;
+ scoped_ptr<gpu::gles2::GLES2Decoder> decoder_;
+ scoped_ptr<gpu::GpuScheduler> scheduler_;
+ scoped_ptr<gpu::GpuControlService> gpu_control_;
+ base::RepeatingTimer<CommandBufferImpl> timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(CommandBufferImpl);
+};
+
+} // namespace services
+} // namespace mojo
+
+#endif // MOJO_SERVICES_GLES2_COMMAND_BUFFER_IMPL_H_
diff --git a/chromium/mojo/services/gles2/command_buffer_type_conversions.cc b/chromium/mojo/services/gles2/command_buffer_type_conversions.cc
new file mode 100644
index 00000000000..3952bef3179
--- /dev/null
+++ b/chromium/mojo/services/gles2/command_buffer_type_conversions.cc
@@ -0,0 +1,40 @@
+// Copyright 2014 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 "mojo/services/gles2/command_buffer_type_conversions.h"
+
+#include "mojo/services/gles2/command_buffer.mojom.h"
+
+namespace mojo {
+
+CommandBufferStatePtr
+TypeConverter<CommandBufferStatePtr, gpu::CommandBuffer::State>::ConvertFrom(
+ const gpu::CommandBuffer::State& input) {
+ CommandBufferStatePtr result(CommandBufferState::New());
+ result->num_entries = input.num_entries;
+ result->get_offset = input.get_offset;
+ result->put_offset = input.put_offset;
+ result->token = input.token;
+ result->error = input.error;
+ result->context_lost_reason = input.context_lost_reason;
+ result->generation = input.generation;
+ return result.Pass();
+}
+
+gpu::CommandBuffer::State
+TypeConverter<CommandBufferStatePtr, gpu::CommandBuffer::State>::ConvertTo(
+ const CommandBufferStatePtr& input) {
+ gpu::CommandBuffer::State state;
+ state.num_entries = input->num_entries;
+ state.get_offset = input->get_offset;
+ state.put_offset = input->put_offset;
+ state.token = input->token;
+ state.error = static_cast<gpu::error::Error>(input->error);
+ state.context_lost_reason =
+ static_cast<gpu::error::ContextLostReason>(input->context_lost_reason);
+ state.generation = input->generation;
+ return state;
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/services/gles2/command_buffer_type_conversions.h b/chromium/mojo/services/gles2/command_buffer_type_conversions.h
new file mode 100644
index 00000000000..b2334d008ac
--- /dev/null
+++ b/chromium/mojo/services/gles2/command_buffer_type_conversions.h
@@ -0,0 +1,27 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_GLES2_COMMAND_BUFFER_TYPE_CONVERSIONS_H_
+#define MOJO_SERVICES_GLES2_COMMAND_BUFFER_TYPE_CONVERSIONS_H_
+
+#include "gpu/command_buffer/common/command_buffer.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+#include "mojo/services/gles2/command_buffer.mojom.h"
+
+namespace mojo {
+
+class CommandBufferState;
+
+template <>
+class TypeConverter<CommandBufferStatePtr, gpu::CommandBuffer::State> {
+ public:
+ static CommandBufferStatePtr ConvertFrom(
+ const gpu::CommandBuffer::State& input);
+ static gpu::CommandBuffer::State ConvertTo(
+ const CommandBufferStatePtr& input);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_GLES2_COMMAND_BUFFER_TYPE_CONVERSIONS_H_
diff --git a/chromium/mojo/services/gles2/mojo_buffer_backing.cc b/chromium/mojo/services/gles2/mojo_buffer_backing.cc
new file mode 100644
index 00000000000..3c908787ff5
--- /dev/null
+++ b/chromium/mojo/services/gles2/mojo_buffer_backing.cc
@@ -0,0 +1,36 @@
+// Copyright 2014 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 "mojo/services/gles2/mojo_buffer_backing.h"
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace gles2 {
+
+MojoBufferBacking::MojoBufferBacking(mojo::ScopedSharedBufferHandle handle,
+ void* memory,
+ size_t size)
+ : handle_(handle.Pass()), memory_(memory), size_(size) {}
+
+MojoBufferBacking::~MojoBufferBacking() { mojo::UnmapBuffer(memory_); }
+
+// static
+scoped_ptr<gpu::BufferBacking> MojoBufferBacking::Create(
+ mojo::ScopedSharedBufferHandle handle,
+ size_t size) {
+ void* memory = NULL;
+ MojoResult result = mojo::MapBuffer(
+ handle.get(), 0, size, &memory, MOJO_MAP_BUFFER_FLAG_NONE);
+ if (result != MOJO_RESULT_OK)
+ return scoped_ptr<BufferBacking>();
+ DCHECK(memory);
+ return scoped_ptr<BufferBacking>(
+ new MojoBufferBacking(handle.Pass(), memory, size));
+}
+void* MojoBufferBacking::GetMemory() const { return memory_; }
+size_t MojoBufferBacking::GetSize() const { return size_; }
+
+} // namespace gles2
+} // namespace mojo
diff --git a/chromium/mojo/services/gles2/mojo_buffer_backing.h b/chromium/mojo/services/gles2/mojo_buffer_backing.h
new file mode 100644
index 00000000000..4048b6cbff2
--- /dev/null
+++ b/chromium/mojo/services/gles2/mojo_buffer_backing.h
@@ -0,0 +1,40 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_GLES2_MOJO_BUFFER_BACKING_H_
+#define MOJO_SERVICES_GLES2_MOJO_BUFFER_BACKING_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "gpu/command_buffer/common/buffer.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace gles2 {
+
+class MojoBufferBacking : public gpu::BufferBacking {
+ public:
+ MojoBufferBacking(mojo::ScopedSharedBufferHandle handle,
+ void* memory,
+ size_t size);
+ virtual ~MojoBufferBacking();
+
+ static scoped_ptr<gpu::BufferBacking> Create(
+ mojo::ScopedSharedBufferHandle handle,
+ size_t size);
+
+ virtual void* GetMemory() const OVERRIDE;
+ virtual size_t GetSize() const OVERRIDE;
+
+ private:
+ mojo::ScopedSharedBufferHandle handle_;
+ void* memory_;
+ size_t size_;
+
+ DISALLOW_COPY_AND_ASSIGN(MojoBufferBacking);
+};
+
+} // namespace gles2
+} // namespace mojo
+
+#endif // MOJO_SERVICES_GLES2_MOJO_BUFFER_BACKING_H_
diff --git a/chromium/mojo/services/launcher/DEPS b/chromium/mojo/services/launcher/DEPS
new file mode 100644
index 00000000000..28f1ec1d788
--- /dev/null
+++ b/chromium/mojo/services/launcher/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+mojo/services/public",
+]
diff --git a/chromium/mojo/services/launcher/launcher.cc b/chromium/mojo/services/launcher/launcher.cc
new file mode 100644
index 00000000000..eb4de409c8b
--- /dev/null
+++ b/chromium/mojo/services/launcher/launcher.cc
@@ -0,0 +1,176 @@
+// Copyright 2014 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 "base/compiler_specific.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_tokenizer.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/interfaces/launcher/launcher.mojom.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace launcher {
+
+class LauncherApp;
+
+class LauncherConnection : public InterfaceImpl<Launcher> {
+ public:
+ explicit LauncherConnection(LauncherApp* app) : app_(app) {}
+ virtual ~LauncherConnection() {}
+
+ private:
+ // Overridden from Launcher:
+ virtual void Launch(const String& url) OVERRIDE;
+
+ LauncherApp* app_;
+
+ DISALLOW_COPY_AND_ASSIGN(LauncherConnection);
+};
+
+class LaunchInstance : public URLLoaderClient {
+ public:
+ LaunchInstance(LauncherApp* app,
+ LauncherClient* client,
+ const String& url);
+ virtual ~LaunchInstance() {}
+
+ private:
+ // Overridden from URLLoaderClient:
+ virtual void OnReceivedRedirect(URLResponsePtr response,
+ const String& new_url,
+ const String& new_method) OVERRIDE {
+ }
+ virtual void OnReceivedResponse(URLResponsePtr response) OVERRIDE;
+ virtual void OnReceivedError(NetworkErrorPtr error) OVERRIDE {
+ ScheduleDestroy();
+ }
+ virtual void OnReceivedEndOfResponseBody() OVERRIDE {
+ ScheduleDestroy();
+ }
+
+ std::string GetContentType(const Array<String>& headers) {
+ for (size_t i = 0; i < headers.size(); ++i) {
+ base::StringTokenizer t(headers[i], ": ;=");
+ while (t.GetNext()) {
+ if (!t.token_is_delim() && t.token() == "Content-Type") {
+ while (t.GetNext()) {
+ if (!t.token_is_delim())
+ return t.token();
+ }
+ }
+ }
+ }
+ return "";
+ }
+
+ void ScheduleDestroy() {
+ if (destroy_scheduled_)
+ return;
+ destroy_scheduled_ = true;
+ base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+ }
+
+ LauncherApp* app_;
+ bool destroy_scheduled_;
+ LauncherClient* client_;
+ URLLoaderPtr url_loader_;
+ ScopedDataPipeConsumerHandle response_body_stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(LaunchInstance);
+};
+
+class LauncherApp : public Application {
+ public:
+ LauncherApp() {
+ handler_map_["text/html"] = "mojo:mojo_html_viewer";
+ handler_map_["image/png"] = "mojo:mojo_image_viewer";
+ }
+ virtual ~LauncherApp() {}
+
+ URLLoaderPtr CreateURLLoader() {
+ URLLoaderPtr loader;
+ network_service_->CreateURLLoader(Get(&loader));
+ return loader.Pass();
+ }
+
+ std::string GetHandlerForContentType(const std::string& content_type) {
+ HandlerMap::const_iterator it = handler_map_.find(content_type);
+ return it != handler_map_.end() ? it->second : "";
+ }
+
+ private:
+ typedef std::map<std::string, std::string> HandlerMap;
+
+ // Overridden from Application:
+ virtual void Initialize() OVERRIDE {
+ AddService<LauncherConnection>(this);
+ ConnectTo("mojo:mojo_network_service", &network_service_);
+ }
+
+ HandlerMap handler_map_;
+
+ NetworkServicePtr network_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(LauncherApp);
+};
+
+void LauncherConnection::Launch(const String& url_string) {
+ GURL url(url_string.To<std::string>());
+
+ // For Mojo URLs, the handler can always be found at the origin.
+ // TODO(aa): Return error for invalid URL?
+ if (url.is_valid() && url.SchemeIs("mojo")) {
+ client()->OnLaunch(url_string,
+ url.GetOrigin().spec(),
+ navigation::ResponseDetailsPtr());
+ return;
+ }
+
+ new LaunchInstance(app_, client(), url_string);
+}
+
+LaunchInstance::LaunchInstance(LauncherApp* app,
+ LauncherClient* client,
+ const String& url)
+ : app_(app),
+ destroy_scheduled_(false),
+ client_(client) {
+ url_loader_ = app_->CreateURLLoader();
+ url_loader_.set_client(this);
+
+ URLRequestPtr request(URLRequest::New());
+ request->url = url;
+ request->method = "GET";
+ request->auto_follow_redirects = true;
+
+ DataPipe data_pipe;
+ response_body_stream_ = data_pipe.consumer_handle.Pass();
+
+ url_loader_->Start(request.Pass(), data_pipe.producer_handle.Pass());
+}
+
+void LaunchInstance::OnReceivedResponse(URLResponsePtr response) {
+ std::string content_type = GetContentType(response->headers);
+ std::string handler_url = app_->GetHandlerForContentType(content_type);
+ if (!handler_url.empty()) {
+ navigation::ResponseDetailsPtr nav_response(
+ navigation::ResponseDetails::New());
+ nav_response->response = response.Pass();
+ nav_response->response_body_stream = response_body_stream_.Pass();
+ client_->OnLaunch(nav_response->response->url, handler_url,
+ nav_response.Pass());
+ }
+}
+
+} // namespace launcher
+
+// static
+Application* Application::Create() {
+ return new launcher::LauncherApp;
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/services/native_viewport/DEPS b/chromium/mojo/services/native_viewport/DEPS
new file mode 100644
index 00000000000..be8903cd0fd
--- /dev/null
+++ b/chromium/mojo/services/native_viewport/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+ "+mojo/services/public/cpp/geometry",
+ "+mojo/services/public/cpp/input_events",
+ "+mojo/services/public/interfaces/native_viewport",
+ "+mojo/services/gles2",
+ "+ui/events",
+ "+ui/gfx",
+]
diff --git a/chromium/mojo/services/native_viewport/native_viewport.h b/chromium/mojo/services/native_viewport/native_viewport.h
new file mode 100644
index 00000000000..7ec54329b15
--- /dev/null
+++ b/chromium/mojo/services/native_viewport/native_viewport.h
@@ -0,0 +1,62 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_H_
+#define MOJO_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/size.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace ui {
+class Event;
+}
+
+namespace mojo {
+namespace shell {
+class Context;
+}
+
+namespace services {
+
+class NativeViewportDelegate {
+ public:
+ virtual ~NativeViewportDelegate() {}
+
+ virtual void OnBoundsChanged(const gfx::Rect& size) = 0;
+ virtual void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) = 0;
+ virtual bool OnEvent(ui::Event* ui_event) = 0;
+ virtual void OnDestroyed() = 0;
+};
+
+// Encapsulation of platform-specific Viewport.
+// TODO(abarth): Rename this class so that it doesn't conflict with the name of
+// the service.
+class NativeViewport {
+ public:
+ virtual ~NativeViewport() {}
+
+ virtual void Init(const gfx::Rect& bounds) = 0;
+ virtual void Show() = 0;
+ virtual void Hide() = 0;
+ virtual void Close() = 0;
+ virtual gfx::Size GetSize() = 0;
+ virtual void SetBounds(const gfx::Rect& bounds) = 0;
+
+ virtual void SetCapture() = 0;
+ virtual void ReleaseCapture() = 0;
+
+ // |context| is NULL when loaded into separate process.
+ static scoped_ptr<NativeViewport> Create(shell::Context* context,
+ NativeViewportDelegate* delegate);
+};
+
+} // namespace services
+} // namespace mojo
+
+#endif // MOJO_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_H_
diff --git a/chromium/mojo/services/native_viewport/native_viewport_android.cc b/chromium/mojo/services/native_viewport/native_viewport_android.cc
new file mode 100644
index 00000000000..1763f170ad3
--- /dev/null
+++ b/chromium/mojo/services/native_viewport/native_viewport_android.cc
@@ -0,0 +1,161 @@
+// Copyright 2013 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 "mojo/services/native_viewport/native_viewport_android.h"
+
+#include <android/input.h>
+#include <android/native_window_jni.h>
+
+#include "base/android/jni_android.h"
+#include "jni/NativeViewportAndroid_jni.h"
+#include "mojo/shell/context.h"
+#include "ui/events/event.h"
+#include "ui/gfx/point.h"
+
+namespace mojo {
+namespace services {
+
+ui::EventType MotionEventActionToEventType(jint action) {
+ switch (action) {
+ case AMOTION_EVENT_ACTION_DOWN:
+ return ui::ET_TOUCH_PRESSED;
+ case AMOTION_EVENT_ACTION_MOVE:
+ return ui::ET_TOUCH_MOVED;
+ case AMOTION_EVENT_ACTION_UP:
+ return ui::ET_TOUCH_RELEASED;
+ default:
+ NOTREACHED();
+ }
+ return ui::ET_UNKNOWN;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeViewportAndroid, public:
+
+// static
+bool NativeViewportAndroid::Register(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+NativeViewportAndroid::NativeViewportAndroid(shell::Context* context,
+ NativeViewportDelegate* delegate)
+ : delegate_(delegate),
+ context_(context),
+ window_(NULL),
+ id_generator_(0),
+ weak_factory_(this) {
+}
+
+NativeViewportAndroid::~NativeViewportAndroid() {
+ if (window_)
+ ReleaseWindow();
+}
+
+void NativeViewportAndroid::Destroy(JNIEnv* env, jobject obj) {
+ delegate_->OnDestroyed();
+}
+
+void NativeViewportAndroid::SurfaceCreated(JNIEnv* env,
+ jobject obj,
+ jobject jsurface) {
+ base::android::ScopedJavaLocalRef<jobject> protector(env, jsurface);
+ // Note: This ensures that any local references used by
+ // ANativeWindow_fromSurface are released immediately. This is needed as a
+ // workaround for https://code.google.com/p/android/issues/detail?id=68174
+ {
+ base::android::ScopedJavaLocalFrame scoped_local_reference_frame(env);
+ window_ = ANativeWindow_fromSurface(env, jsurface);
+ }
+ delegate_->OnAcceleratedWidgetAvailable(window_);
+}
+
+void NativeViewportAndroid::SurfaceDestroyed(JNIEnv* env, jobject obj) {
+ DCHECK(window_);
+ ReleaseWindow();
+}
+
+void NativeViewportAndroid::SurfaceSetSize(JNIEnv* env, jobject obj,
+ jint width, jint height) {
+ bounds_ = gfx::Rect(width, height);
+ delegate_->OnBoundsChanged(bounds_);
+}
+
+bool NativeViewportAndroid::TouchEvent(JNIEnv* env, jobject obj,
+ jint pointer_id,
+ jint action,
+ jfloat x, jfloat y,
+ jlong time_ms) {
+ gfx::Point location(static_cast<int>(x), static_cast<int>(y));
+ ui::TouchEvent event(MotionEventActionToEventType(action), location,
+ id_generator_.GetGeneratedID(pointer_id),
+ base::TimeDelta::FromMilliseconds(time_ms));
+ // TODO(beng): handle multiple touch-points.
+ delegate_->OnEvent(&event);
+ if (action == ui::ET_TOUCH_RELEASED)
+ id_generator_.ReleaseNumber(pointer_id);
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeViewportAndroid, NativeViewport implementation:
+
+void NativeViewportAndroid::Init(const gfx::Rect& bounds) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_NativeViewportAndroid_createForActivity(env, context_->activity(),
+ reinterpret_cast<jlong>(this));
+}
+
+void NativeViewportAndroid::Show() {
+ // Nothing to do. View is created visible.
+}
+
+void NativeViewportAndroid::Hide() {
+ // Nothing to do. View is always visible.
+}
+
+void NativeViewportAndroid::Close() {
+ // TODO(beng): close activity containing MojoView?
+
+ // TODO(beng): perform this in response to view destruction.
+ delegate_->OnDestroyed();
+}
+
+gfx::Size NativeViewportAndroid::GetSize() {
+ return bounds_.size();
+}
+
+void NativeViewportAndroid::SetBounds(const gfx::Rect& bounds) {
+ NOTIMPLEMENTED();
+}
+
+void NativeViewportAndroid::SetCapture() {
+ NOTIMPLEMENTED();
+}
+
+void NativeViewportAndroid::ReleaseCapture() {
+ NOTIMPLEMENTED();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeViewportAndroid, private:
+
+void NativeViewportAndroid::ReleaseWindow() {
+ ANativeWindow_release(window_);
+ window_ = NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeViewport, public:
+
+// static
+scoped_ptr<NativeViewport> NativeViewport::Create(
+ shell::Context* context,
+ NativeViewportDelegate* delegate) {
+ return scoped_ptr<NativeViewport>(
+ new NativeViewportAndroid(context, delegate)).Pass();
+}
+
+} // namespace services
+} // namespace mojo
diff --git a/chromium/mojo/services/native_viewport/native_viewport_android.h b/chromium/mojo/services/native_viewport/native_viewport_android.h
new file mode 100644
index 00000000000..c24e38e7858
--- /dev/null
+++ b/chromium/mojo/services/native_viewport/native_viewport_android.h
@@ -0,0 +1,71 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_ANDROID_H_
+#define MOJO_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_ANDROID_H_
+
+#include "base/android/jni_weak_ref.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/services/native_viewport/native_viewport.h"
+#include "mojo/services/native_viewport/native_viewport_export.h"
+#include "ui/events/event_constants.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/sequential_id_generator.h"
+#include "ui/gfx/size.h"
+
+namespace gpu {
+class GLInProcessContext;
+}
+
+struct ANativeWindow;
+
+namespace mojo {
+namespace services {
+
+class MOJO_NATIVE_VIEWPORT_EXPORT NativeViewportAndroid
+ : public NativeViewport {
+ public:
+ static MOJO_NATIVE_VIEWPORT_EXPORT bool Register(JNIEnv* env);
+
+ explicit NativeViewportAndroid(shell::Context* context,
+ NativeViewportDelegate* delegate);
+ virtual ~NativeViewportAndroid();
+
+ void Destroy(JNIEnv* env, jobject obj);
+ void SurfaceCreated(JNIEnv* env, jobject obj, jobject jsurface);
+ void SurfaceDestroyed(JNIEnv* env, jobject obj);
+ void SurfaceSetSize(JNIEnv* env, jobject obj, jint width, jint height);
+ bool TouchEvent(JNIEnv* env, jobject obj, jint pointer_id, jint action,
+ jfloat x, jfloat y, jlong time_ms);
+
+ private:
+ // Overridden from NativeViewport:
+ virtual void Init(const gfx::Rect& bounds) OVERRIDE;
+ virtual void Show() OVERRIDE;
+ virtual void Hide() OVERRIDE;
+ virtual void Close() OVERRIDE;
+ virtual gfx::Size GetSize() OVERRIDE;
+ virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE;
+ virtual void SetCapture() OVERRIDE;
+ virtual void ReleaseCapture() OVERRIDE;
+
+ void ReleaseWindow();
+
+ NativeViewportDelegate* delegate_;
+ shell::Context* context_;
+ ANativeWindow* window_;
+ gfx::Rect bounds_;
+ ui::SequentialIDGenerator id_generator_;
+
+ base::WeakPtrFactory<NativeViewportAndroid> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeViewportAndroid);
+};
+
+
+} // namespace services
+} // namespace mojo
+
+#endif // MOJO_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_ANDROID_H_
diff --git a/chromium/mojo/services/native_viewport/native_viewport_export.h b/chromium/mojo/services/native_viewport/native_viewport_export.h
new file mode 100644
index 00000000000..4870c566f9d
--- /dev/null
+++ b/chromium/mojo/services/native_viewport/native_viewport_export.h
@@ -0,0 +1,32 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SERVICES_NATIVE_VIEWPORT_EXPORT_H_
+#define MOJO_SERVICES_NATIVE_VIEWPORT_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_NATIVE_VIEWPORT_IMPLEMENTATION)
+#define MOJO_NATIVE_VIEWPORT_EXPORT __declspec(dllexport)
+#else
+#define MOJO_NATIVE_VIEWPORT_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_NATIVE_VIEWPORT_IMPLEMENTATION)
+#define MOJO_NATIVE_VIEWPORT_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_NATIVE_VIEWPORT_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#else // !defined(COMPONENT_BUILD)
+#define MOJO_NATIVE_VIEWPORT_EXPORT
+#endif
+
+#endif // MOJO_SERVICES_NATIVE_VIEWPORT_EXPORT_H_
diff --git a/chromium/mojo/services/native_viewport/native_viewport_mac.mm b/chromium/mojo/services/native_viewport/native_viewport_mac.mm
new file mode 100644
index 00000000000..9d7301f6767
--- /dev/null
+++ b/chromium/mojo/services/native_viewport/native_viewport_mac.mm
@@ -0,0 +1,88 @@
+// Copyright 2013 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 "mojo/services/native_viewport/native_viewport.h"
+
+#import <AppKit/NSApplication.h>
+#import <AppKit/NSView.h>
+#import <AppKit/NSWindow.h>
+
+#include "base/bind.h"
+#include "ui/gfx/rect.h"
+
+namespace mojo {
+namespace services {
+
+class NativeViewportMac : public NativeViewport {
+ public:
+ NativeViewportMac(NativeViewportDelegate* delegate)
+ : delegate_(delegate),
+ window_(nil) {
+ }
+
+ virtual ~NativeViewportMac() {
+ [window_ orderOut:nil];
+ [window_ close];
+ }
+
+ private:
+ // Overridden from NativeViewport:
+ virtual void Init(const gfx::Rect& bounds) OVERRIDE {
+ [NSApplication sharedApplication];
+
+ rect_ = bounds;
+ window_ = [[NSWindow alloc]
+ initWithContentRect:NSRectFromCGRect(rect_.ToCGRect())
+ styleMask:NSTitledWindowMask
+ backing:NSBackingStoreBuffered
+ defer:NO];
+ delegate_->OnAcceleratedWidgetAvailable([window_ contentView]);
+ delegate_->OnBoundsChanged(rect_);
+ }
+
+ virtual void Show() OVERRIDE {
+ [window_ orderFront:nil];
+ }
+
+ virtual void Hide() OVERRIDE {
+ [window_ orderOut:nil];
+ }
+
+ virtual void Close() OVERRIDE {
+ // TODO(beng): perform this in response to NSWindow destruction.
+ delegate_->OnDestroyed();
+ }
+
+ virtual gfx::Size GetSize() OVERRIDE {
+ return rect_.size();
+ }
+
+ virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE {
+ NOTIMPLEMENTED();
+ }
+
+ virtual void SetCapture() OVERRIDE {
+ NOTIMPLEMENTED();
+ }
+
+ virtual void ReleaseCapture() OVERRIDE {
+ NOTIMPLEMENTED();
+ }
+
+ NativeViewportDelegate* delegate_;
+ NSWindow* window_;
+ gfx::Rect rect_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeViewportMac);
+};
+
+// static
+scoped_ptr<NativeViewport> NativeViewport::Create(
+ shell::Context* context,
+ NativeViewportDelegate* delegate) {
+ return scoped_ptr<NativeViewport>(new NativeViewportMac(delegate)).Pass();
+}
+
+} // namespace services
+} // namespace mojo
diff --git a/chromium/mojo/services/native_viewport/native_viewport_service.cc b/chromium/mojo/services/native_viewport/native_viewport_service.cc
new file mode 100644
index 00000000000..886c4dcb4b9
--- /dev/null
+++ b/chromium/mojo/services/native_viewport/native_viewport_service.cc
@@ -0,0 +1,165 @@
+// Copyright 2013 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 "mojo/services/native_viewport/native_viewport_service.h"
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/time/time.h"
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+#include "mojo/services/gles2/command_buffer_impl.h"
+#include "mojo/services/native_viewport/native_viewport.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/input_events/input_events_type_converters.h"
+#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h"
+#include "ui/events/event.h"
+
+namespace mojo {
+namespace services {
+namespace {
+
+bool IsRateLimitedEventType(ui::Event* event) {
+ return event->type() == ui::ET_MOUSE_MOVED ||
+ event->type() == ui::ET_MOUSE_DRAGGED ||
+ event->type() == ui::ET_TOUCH_MOVED;
+}
+
+}
+
+class NativeViewportImpl
+ : public InterfaceImpl<mojo::NativeViewport>,
+ public NativeViewportDelegate {
+ public:
+ NativeViewportImpl(shell::Context* context)
+ : context_(context),
+ widget_(gfx::kNullAcceleratedWidget),
+ waiting_for_event_ack_(false),
+ weak_factory_(this) {}
+ virtual ~NativeViewportImpl() {
+ // Destroy the NativeViewport early on as it may call us back during
+ // destruction and we want to be in a known state.
+ native_viewport_.reset();
+ }
+
+ virtual void Create(RectPtr bounds) OVERRIDE {
+ native_viewport_ =
+ services::NativeViewport::Create(context_, this);
+ native_viewport_->Init(bounds.To<gfx::Rect>());
+ client()->OnCreated();
+ OnBoundsChanged(bounds.To<gfx::Rect>());
+ }
+
+ virtual void Show() OVERRIDE {
+ native_viewport_->Show();
+ }
+
+ virtual void Hide() OVERRIDE {
+ native_viewport_->Hide();
+ }
+
+ virtual void Close() OVERRIDE {
+ command_buffer_.reset();
+ DCHECK(native_viewport_);
+ native_viewport_->Close();
+ }
+
+ virtual void SetBounds(RectPtr bounds) OVERRIDE {
+ native_viewport_->SetBounds(bounds.To<gfx::Rect>());
+ }
+
+ virtual void CreateGLES2Context(
+ InterfaceRequest<CommandBuffer> command_buffer_request) OVERRIDE {
+ if (command_buffer_.get() || command_buffer_request_.is_pending()) {
+ LOG(ERROR) << "Can't create multiple contexts on a NativeViewport";
+ return;
+ }
+ command_buffer_request_ = command_buffer_request.Pass();
+ CreateCommandBufferIfNeeded();
+ }
+
+ void AckEvent() {
+ waiting_for_event_ack_ = false;
+ }
+
+ void CreateCommandBufferIfNeeded() {
+ if (!command_buffer_request_.is_pending())
+ return;
+ DCHECK(!command_buffer_.get());
+ if (widget_ == gfx::kNullAcceleratedWidget)
+ return;
+ gfx::Size size = native_viewport_->GetSize();
+ if (size.IsEmpty())
+ return;
+ command_buffer_.reset(
+ new CommandBufferImpl(widget_, native_viewport_->GetSize()));
+ BindToRequest(command_buffer_.get(), &command_buffer_request_);
+ }
+
+ virtual bool OnEvent(ui::Event* ui_event) OVERRIDE {
+ // Must not return early before updating capture.
+ switch (ui_event->type()) {
+ case ui::ET_MOUSE_PRESSED:
+ case ui::ET_TOUCH_PRESSED:
+ native_viewport_->SetCapture();
+ break;
+ case ui::ET_MOUSE_RELEASED:
+ case ui::ET_TOUCH_RELEASED:
+ native_viewport_->ReleaseCapture();
+ break;
+ default:
+ break;
+ }
+
+ if (waiting_for_event_ack_ && IsRateLimitedEventType(ui_event))
+ return false;
+
+ client()->OnEvent(
+ TypeConverter<EventPtr, ui::Event>::ConvertFrom(*ui_event),
+ base::Bind(&NativeViewportImpl::AckEvent,
+ weak_factory_.GetWeakPtr()));
+ waiting_for_event_ack_ = true;
+ return false;
+ }
+
+ virtual void OnAcceleratedWidgetAvailable(
+ gfx::AcceleratedWidget widget) OVERRIDE {
+ widget_ = widget;
+ CreateCommandBufferIfNeeded();
+ }
+
+ virtual void OnBoundsChanged(const gfx::Rect& bounds) OVERRIDE {
+ CreateCommandBufferIfNeeded();
+ client()->OnBoundsChanged(Rect::From(bounds));
+ }
+
+ virtual void OnDestroyed() OVERRIDE {
+ command_buffer_.reset();
+ client()->OnDestroyed();
+ base::MessageLoop::current()->Quit();
+ }
+
+ private:
+ shell::Context* context_;
+ gfx::AcceleratedWidget widget_;
+ scoped_ptr<services::NativeViewport> native_viewport_;
+ InterfaceRequest<CommandBuffer> command_buffer_request_;
+ scoped_ptr<CommandBufferImpl> command_buffer_;
+ bool waiting_for_event_ack_;
+ base::WeakPtrFactory<NativeViewportImpl> weak_factory_;
+};
+
+} // namespace services
+} // namespace mojo
+
+
+MOJO_NATIVE_VIEWPORT_EXPORT mojo::Application*
+ CreateNativeViewportService(
+ mojo::shell::Context* context,
+ mojo::ScopedMessagePipeHandle service_provider_handle) {
+ mojo::Application* app = new mojo::Application(
+ service_provider_handle.Pass());
+ app->AddService<mojo::services::NativeViewportImpl>(context);
+ return app;
+}
diff --git a/chromium/mojo/services/native_viewport/native_viewport_service.h b/chromium/mojo/services/native_viewport/native_viewport_service.h
new file mode 100644
index 00000000000..99b455c9bae
--- /dev/null
+++ b/chromium/mojo/services/native_viewport/native_viewport_service.h
@@ -0,0 +1,18 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SERVICES_NATIVE_VIEWPORT_SERVICE_H_
+#define MOJO_SERVICES_NATIVE_VIEWPORT_SERVICE_H_
+
+#include "base/memory/scoped_vector.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/services/native_viewport/native_viewport_export.h"
+#include "mojo/shell/context.h"
+
+MOJO_NATIVE_VIEWPORT_EXPORT mojo::Application*
+ CreateNativeViewportService(
+ mojo::shell::Context* context,
+ mojo::ScopedMessagePipeHandle service_provider_handle);
+
+#endif // MOJO_SERVICES_NATIVE_VIEWPORT_SERVICE_H_
diff --git a/chromium/mojo/services/native_viewport/native_viewport_stub.cc b/chromium/mojo/services/native_viewport/native_viewport_stub.cc
new file mode 100644
index 00000000000..9dcb33a8806
--- /dev/null
+++ b/chromium/mojo/services/native_viewport/native_viewport_stub.cc
@@ -0,0 +1,50 @@
+// Copyright 2013 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 "mojo/services/native_viewport/native_viewport.h"
+
+// Stub to build on platforms we don't fully support yet.
+
+namespace mojo {
+namespace services {
+
+class NativeViewportStub : public NativeViewport {
+ public:
+ NativeViewportStub(NativeViewportDelegate* delegate)
+ : delegate_(delegate) {
+ }
+ virtual ~NativeViewportStub() {
+ }
+
+ private:
+ // Overridden from NativeViewport:
+ virtual void Init() OVERRIDE {
+ }
+ virtual void Show() OVERRIDE {
+ }
+ virtual void Hide() OVERRIDE {
+ }
+ virtual void Close() OVERRIDE {
+ delegate_->OnDestroyed();
+ }
+ virtual gfx::Size GetSize() OVERRIDE {
+ return gfx::Size();
+ }
+ virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE {
+ }
+
+ NativeViewportDelegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeViewportStub);
+};
+
+// static
+scoped_ptr<NativeViewport> NativeViewport::Create(
+ shell::Context* context,
+ NativeViewportDelegate* delegate) {
+ return scoped_ptr<NativeViewport>(new NativeViewportStub(delegate)).Pass();
+}
+
+} // namespace services
+} // namespace mojo
diff --git a/chromium/mojo/services/native_viewport/native_viewport_win.cc b/chromium/mojo/services/native_viewport/native_viewport_win.cc
new file mode 100644
index 00000000000..52ff6cb6f0a
--- /dev/null
+++ b/chromium/mojo/services/native_viewport/native_viewport_win.cc
@@ -0,0 +1,167 @@
+// Copyright 2013 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 "mojo/services/native_viewport/native_viewport.h"
+
+#include "ui/events/event.h"
+#include "ui/gfx/win/msg_util.h"
+#include "ui/gfx/win/window_impl.h"
+
+namespace mojo {
+namespace services {
+namespace {
+
+gfx::Rect GetWindowBoundsForClientBounds(DWORD style, DWORD ex_style,
+ const gfx::Rect& bounds) {
+ RECT wr;
+ wr.left = bounds.x();
+ wr.top = bounds.y();
+ wr.right = bounds.x() + bounds.width();
+ wr.bottom = bounds.y() + bounds.height();
+ AdjustWindowRectEx(&wr, style, FALSE, ex_style);
+
+ // Make sure to keep the window onscreen, as AdjustWindowRectEx() may have
+ // moved part of it offscreen.
+ gfx::Rect window_bounds(wr.left, wr.top,
+ wr.right - wr.left, wr.bottom - wr.top);
+ window_bounds.set_x(std::max(0, window_bounds.x()));
+ window_bounds.set_y(std::max(0, window_bounds.y()));
+ return window_bounds;
+}
+
+}
+
+class NativeViewportWin : public gfx::WindowImpl,
+ public NativeViewport {
+ public:
+ explicit NativeViewportWin(NativeViewportDelegate* delegate)
+ : delegate_(delegate) {
+ }
+ virtual ~NativeViewportWin() {
+ if (IsWindow(hwnd()))
+ DestroyWindow(hwnd());
+ }
+
+ private:
+ // Overridden from NativeViewport:
+ virtual void Init(const gfx::Rect& bounds) OVERRIDE {
+ gfx::Rect window_bounds = GetWindowBoundsForClientBounds(
+ WS_OVERLAPPEDWINDOW, window_ex_style(), bounds);
+ gfx::WindowImpl::Init(NULL, window_bounds);
+ SetWindowText(hwnd(), L"native_viewport::NativeViewportWin!");
+ }
+
+ virtual void Show() OVERRIDE {
+ ShowWindow(hwnd(), SW_SHOWNORMAL);
+ }
+
+ virtual void Hide() OVERRIDE {
+ ShowWindow(hwnd(), SW_HIDE);
+ }
+
+ virtual void Close() OVERRIDE {
+ DestroyWindow(hwnd());
+ }
+
+ virtual gfx::Size GetSize() OVERRIDE {
+ RECT cr;
+ GetClientRect(hwnd(), &cr);
+ return gfx::Size(cr.right - cr.left, cr.bottom - cr.top);
+ }
+
+ virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE {
+ gfx::Rect window_bounds = GetWindowBoundsForClientBounds(
+ GetWindowLong(hwnd(), GWL_STYLE),
+ GetWindowLong(hwnd(), GWL_EXSTYLE),
+ bounds);
+ SetWindowPos(hwnd(), NULL, window_bounds.x(), window_bounds.y(),
+ window_bounds.width(), window_bounds.height(),
+ SWP_NOREPOSITION);
+ }
+
+ virtual void SetCapture() OVERRIDE {
+ DCHECK(::GetCapture() != hwnd());
+ ::SetCapture(hwnd());
+ }
+
+ virtual void ReleaseCapture() OVERRIDE {
+ if (::GetCapture() == hwnd())
+ ::ReleaseCapture();
+ }
+
+ CR_BEGIN_MSG_MAP_EX(NativeViewportWin)
+ CR_MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseRange)
+
+ CR_MESSAGE_HANDLER_EX(WM_KEYDOWN, OnKeyEvent)
+ CR_MESSAGE_HANDLER_EX(WM_KEYUP, OnKeyEvent)
+ CR_MESSAGE_HANDLER_EX(WM_SYSKEYDOWN, OnKeyEvent)
+ CR_MESSAGE_HANDLER_EX(WM_SYSKEYUP, OnKeyEvent)
+ CR_MESSAGE_HANDLER_EX(WM_CHAR, OnKeyEvent)
+ CR_MESSAGE_HANDLER_EX(WM_SYSCHAR, OnKeyEvent)
+ CR_MESSAGE_HANDLER_EX(WM_IME_CHAR, OnKeyEvent)
+
+ CR_MSG_WM_CREATE(OnCreate)
+ CR_MSG_WM_DESTROY(OnDestroy)
+ CR_MSG_WM_PAINT(OnPaint)
+ CR_MSG_WM_WINDOWPOSCHANGED(OnWindowPosChanged)
+ CR_END_MSG_MAP()
+
+ LRESULT OnMouseRange(UINT message, WPARAM w_param, LPARAM l_param) {
+ MSG msg = { hwnd(), message, w_param, l_param, 0,
+ { CR_GET_X_LPARAM(l_param), CR_GET_Y_LPARAM(l_param) } };
+ ui::MouseEvent event(msg);
+ SetMsgHandled(delegate_->OnEvent(&event));
+ return 0;
+ }
+ LRESULT OnKeyEvent(UINT message, WPARAM w_param, LPARAM l_param) {
+ MSG msg = { hwnd(), message, w_param, l_param };
+ ui::KeyEvent event(msg, message == WM_CHAR);
+ SetMsgHandled(delegate_->OnEvent(&event));
+ return 0;
+ }
+ LRESULT OnCreate(CREATESTRUCT* create_struct) {
+ delegate_->OnAcceleratedWidgetAvailable(hwnd());
+ return 0;
+ }
+ void OnDestroy() {
+ delegate_->OnDestroyed();
+ }
+ void OnPaint(HDC) {
+ RECT cr;
+ GetClientRect(hwnd(), &cr);
+
+ PAINTSTRUCT ps;
+ HDC dc = BeginPaint(hwnd(), &ps);
+ HBRUSH red_brush = CreateSolidBrush(RGB(255, 0, 0));
+ HGDIOBJ old_object = SelectObject(dc, red_brush);
+ Rectangle(dc, cr.left, cr.top, cr.right, cr.bottom);
+ SelectObject(dc, old_object);
+ DeleteObject(red_brush);
+ EndPaint(hwnd(), &ps);
+ }
+ void OnWindowPosChanged(WINDOWPOS* window_pos) {
+ if (!(window_pos->flags & SWP_NOSIZE) ||
+ !(window_pos->flags & SWP_NOMOVE)) {
+ RECT cr;
+ GetClientRect(hwnd(), &cr);
+ delegate_->OnBoundsChanged(
+ gfx::Rect(window_pos->x, window_pos->y,
+ cr.right - cr.left, cr.bottom - cr.top));
+ }
+ }
+
+ NativeViewportDelegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeViewportWin);
+};
+
+// static
+scoped_ptr<NativeViewport> NativeViewport::Create(
+ shell::Context* context,
+ NativeViewportDelegate* delegate) {
+ return scoped_ptr<NativeViewport>(new NativeViewportWin(delegate)).Pass();
+}
+
+} // namespace services
+} // namespace mojo
diff --git a/chromium/mojo/services/native_viewport/native_viewport_x11.cc b/chromium/mojo/services/native_viewport/native_viewport_x11.cc
new file mode 100644
index 00000000000..f1b4863529c
--- /dev/null
+++ b/chromium/mojo/services/native_viewport/native_viewport_x11.cc
@@ -0,0 +1,163 @@
+// Copyright 2013 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 "mojo/services/native_viewport/native_viewport.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include "base/message_loop/message_loop.h"
+#include "ui/events/event.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/events/platform/x11/x11_event_source.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/x/x11_types.h"
+
+namespace mojo {
+namespace services {
+
+class NativeViewportX11 : public NativeViewport,
+ public ui::PlatformEventDispatcher {
+ public:
+ NativeViewportX11(NativeViewportDelegate* delegate)
+ : delegate_(delegate) {
+ }
+
+ virtual ~NativeViewportX11() {
+ event_source_->RemovePlatformEventDispatcher(this);
+
+ XDestroyWindow(gfx::GetXDisplay(), window_);
+ }
+
+ private:
+ // Overridden from NativeViewport:
+ virtual void Init(const gfx::Rect& bounds) OVERRIDE {
+ XDisplay* display = gfx::GetXDisplay();
+
+ XSetWindowAttributes swa;
+ memset(&swa, 0, sizeof(swa));
+ swa.override_redirect = False;
+
+ bounds_ = bounds;
+ window_ = XCreateWindow(
+ display,
+ DefaultRootWindow(display),
+ bounds_.x(), bounds_.y(), bounds_.width(), bounds_.height(),
+ 0, // border width
+ CopyFromParent, // depth
+ InputOutput,
+ CopyFromParent, // visual
+ CWBackPixmap | CWOverrideRedirect,
+ &swa);
+
+ atom_wm_protocols_ = XInternAtom(display, "WM_PROTOCOLS", 1);
+ atom_wm_delete_window_ = XInternAtom(display, "WM_DELETE_WINDOW", 1);
+ XSetWMProtocols(display, window_, &atom_wm_delete_window_, 1);
+
+ event_source_ = ui::PlatformEventSource::CreateDefault();
+ event_source_->AddPlatformEventDispatcher(this);
+
+ long event_mask = ButtonPressMask | ButtonReleaseMask | FocusChangeMask |
+ KeyPressMask | KeyReleaseMask | EnterWindowMask | LeaveWindowMask |
+ ExposureMask | VisibilityChangeMask | StructureNotifyMask |
+ PropertyChangeMask | PointerMotionMask;
+ XSelectInput(display, window_, event_mask);
+
+ // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with
+ // the desktop environment.
+ XSetWMProperties(display, window_, NULL, NULL, NULL, 0, NULL, NULL, NULL);
+
+ // TODO(aa): Setup xinput2 events.
+ // See desktop_aura/desktop_window_tree_host_x11.cc.
+
+ delegate_->OnAcceleratedWidgetAvailable(window_);
+ }
+
+ virtual void Show() OVERRIDE {
+ XDisplay* display = gfx::GetXDisplay();
+ XMapWindow(display, window_);
+ static_cast<ui::X11EventSource*>(
+ event_source_.get())->BlockUntilWindowMapped(window_);
+ XFlush(display);
+ }
+
+ virtual void Hide() OVERRIDE {
+ XWithdrawWindow(gfx::GetXDisplay(), window_, 0);
+ }
+
+ virtual void Close() OVERRIDE {
+ // TODO(beng): perform this in response to XWindow destruction.
+ delegate_->OnDestroyed();
+ }
+
+ virtual gfx::Size GetSize() OVERRIDE {
+ return bounds_.size();
+ }
+
+ virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE {
+ NOTIMPLEMENTED();
+ }
+
+ virtual void SetCapture() OVERRIDE {
+ NOTIMPLEMENTED();
+ }
+
+ virtual void ReleaseCapture() OVERRIDE {
+ NOTIMPLEMENTED();
+ }
+
+ // ui::PlatformEventDispatcher:
+ virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE {
+ // TODO(aa): This is going to have to be thought through more carefully.
+ // Which events are appropriate to pass to clients?
+ switch (event->type) {
+ case KeyPress:
+ case KeyRelease:
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ return true;
+ case ClientMessage:
+ return event->xclient.message_type == atom_wm_protocols_;
+ default:
+ return false;
+ }
+ }
+
+ virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE {
+ if (event->type == ClientMessage) {
+ Atom protocol = static_cast<Atom>(event->xclient.data.l[0]);
+ if (protocol == atom_wm_delete_window_)
+ delegate_->OnDestroyed();
+ } else if (event->type == KeyPress || event->type == KeyRelease) {
+ ui::KeyEvent key_event(event, false);
+ delegate_->OnEvent(&key_event);
+ } else if (event->type == ButtonPress || event->type == ButtonRelease ||
+ event->type == MotionNotify) {
+ ui::MouseEvent mouse_event(event);
+ delegate_->OnEvent(&mouse_event);
+ }
+ return ui::POST_DISPATCH_NONE;
+ }
+
+ scoped_ptr<ui::PlatformEventSource> event_source_;
+ NativeViewportDelegate* delegate_;
+ gfx::Rect bounds_;
+ XID window_;
+ Atom atom_wm_protocols_;
+ Atom atom_wm_delete_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeViewportX11);
+};
+
+// static
+scoped_ptr<NativeViewport> NativeViewport::Create(
+ shell::Context* context,
+ NativeViewportDelegate* delegate) {
+ return scoped_ptr<NativeViewport>(new NativeViewportX11(delegate)).Pass();
+}
+
+} // namespace services
+} // namespace mojo
diff --git a/chromium/mojo/services/network/DEPS b/chromium/mojo/services/network/DEPS
new file mode 100644
index 00000000000..dc0a052ff7a
--- /dev/null
+++ b/chromium/mojo/services/network/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+mojo/services",
+ "+net",
+]
diff --git a/chromium/mojo/services/network/main.cc b/chromium/mojo/services/network/main.cc
new file mode 100644
index 00000000000..1b69638a038
--- /dev/null
+++ b/chromium/mojo/services/network/main.cc
@@ -0,0 +1,30 @@
+// Copyright 2014 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 "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/services/network/network_context.h"
+#include "mojo/services/network/network_service_impl.h"
+
+extern "C" APPLICATION_EXPORT MojoResult CDECL MojoMain(
+ MojoHandle service_provider_handle) {
+ base::CommandLine::Init(0, NULL);
+ base::AtExitManager at_exit;
+
+ // The IO message loop allows us to use net::URLRequest on this thread.
+ base::MessageLoopForIO loop;
+
+ mojo::NetworkContext context;
+
+ mojo::Application app;
+ app.BindServiceProvider(
+ mojo::MakeScopedHandle(mojo::MessagePipeHandle(service_provider_handle)));
+
+ app.AddService<mojo::NetworkServiceImpl>(&context);
+
+ loop.Run();
+ return MOJO_RESULT_OK;
+}
diff --git a/chromium/mojo/services/network/network_context.cc b/chromium/mojo/services/network/network_context.cc
new file mode 100644
index 00000000000..099432ae9f2
--- /dev/null
+++ b/chromium/mojo/services/network/network_context.cc
@@ -0,0 +1,123 @@
+// Copyright 2014 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 "mojo/services/network/network_context.h"
+
+#include "base/base_paths.h"
+#include "base/path_service.h"
+#include "net/cert/cert_verifier.h"
+#include "net/cookies/cookie_monster.h"
+#include "net/http/http_cache.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_server_properties_impl.h"
+#include "net/http/transport_security_persister.h"
+#include "net/http/transport_security_state.h"
+#include "net/proxy/proxy_service.h"
+#include "net/ssl/default_server_bound_cert_store.h"
+#include "net/ssl/server_bound_cert_service.h"
+#include "net/ssl/ssl_config_service_defaults.h"
+#include "net/url_request/file_protocol_handler.h"
+#include "net/url_request/static_http_user_agent_settings.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_storage.h"
+#include "net/url_request/url_request_job_factory_impl.h"
+
+namespace mojo {
+
+NetworkContext::NetworkContext()
+ : file_thread_("network_file_thread"),
+ cache_thread_("network_cache_thread") {
+ file_thread_.Start();
+
+ base::Thread::Options options;
+ options.message_loop_type = base::MessageLoop::TYPE_IO;
+ cache_thread_.StartWithOptions(options);
+
+ // TODO(darin): Need to figure out a better base path, obviously.
+ base::FilePath base_path;
+ PathService::Get(base::DIR_TEMP, &base_path);
+ base_path = base_path.Append(FILE_PATH_LITERAL("network_service"));
+
+ url_request_context_.reset(new net::URLRequestContext());
+ url_request_context_->set_net_log(net_log_.get());
+
+ storage_.reset(
+ new net::URLRequestContextStorage(url_request_context_.get()));
+
+ storage_->set_cookie_store(new net::CookieMonster(NULL, NULL));
+
+ // TODO(darin): This is surely the wrong UA string.
+ storage_->set_http_user_agent_settings(
+ new net::StaticHttpUserAgentSettings("en-us,en", "Mojo/0.1"));
+
+ storage_->set_proxy_service(net::ProxyService::CreateDirect());
+ storage_->set_ssl_config_service(new net::SSLConfigServiceDefaults);
+ storage_->set_cert_verifier(net::CertVerifier::CreateDefault());
+
+ net::TransportSecurityState* transport_security_state =
+ new net::TransportSecurityState();
+ storage_->set_transport_security_state(transport_security_state);
+
+ transport_security_persister_.reset(
+ new net::TransportSecurityPersister(
+ transport_security_state,
+ base_path,
+ file_thread_.message_loop_proxy(),
+ false));
+
+ storage_->set_server_bound_cert_service(
+ new net::ServerBoundCertService(
+ new net::DefaultServerBoundCertStore(NULL),
+ file_thread_.message_loop_proxy()));
+ storage_->set_http_server_properties(
+ scoped_ptr<net::HttpServerProperties>(
+ new net::HttpServerPropertiesImpl()));
+ storage_->set_host_resolver(net::HostResolver::CreateDefaultResolver(
+ url_request_context_->net_log()));
+
+ net::HttpNetworkSession::Params network_session_params;
+ network_session_params.cert_verifier =
+ url_request_context_->cert_verifier();
+ network_session_params.transport_security_state =
+ url_request_context_->transport_security_state();
+ network_session_params.server_bound_cert_service =
+ url_request_context_->server_bound_cert_service();
+ network_session_params.net_log =
+ url_request_context_->net_log();
+ network_session_params.proxy_service =
+ url_request_context_->proxy_service();
+ network_session_params.ssl_config_service =
+ url_request_context_->ssl_config_service();
+ network_session_params.http_server_properties =
+ url_request_context_->http_server_properties();
+ network_session_params.host_resolver =
+ url_request_context_->host_resolver();
+
+ base::FilePath cache_path = base_path.Append(FILE_PATH_LITERAL("Cache"));
+
+ net::HttpCache::DefaultBackend* main_backend =
+ new net::HttpCache::DefaultBackend(
+ net::DISK_CACHE,
+ net::CACHE_BACKEND_DEFAULT,
+ cache_path,
+ 0,
+ cache_thread_.message_loop_proxy());
+
+ net::HttpCache* main_cache = new net::HttpCache(
+ network_session_params, main_backend);
+ storage_->set_http_transaction_factory(main_cache);
+
+ scoped_ptr<net::URLRequestJobFactoryImpl> job_factory(
+ new net::URLRequestJobFactoryImpl());
+ job_factory->SetProtocolHandler(
+ "file",
+ new net::FileProtocolHandler(file_thread_.message_loop_proxy()));
+ storage_->set_job_factory(job_factory.release());
+}
+
+NetworkContext::~NetworkContext() {
+ // TODO(darin): Be careful about destruction order of member variables?
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/services/network/network_context.h b/chromium/mojo/services/network/network_context.h
new file mode 100644
index 00000000000..b156c081213
--- /dev/null
+++ b/chromium/mojo/services/network/network_context.h
@@ -0,0 +1,42 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_NETWORK_NETWORK_CONTEXT_H_
+#define MOJO_SERVICES_NETWORK_NETWORK_CONTEXT_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/thread.h"
+
+namespace net {
+class NetLog;
+class URLRequestContext;
+class URLRequestContextStorage;
+class TransportSecurityPersister;
+}
+
+namespace mojo {
+
+class NetworkContext {
+ public:
+ NetworkContext();
+ ~NetworkContext();
+
+ net::URLRequestContext* url_request_context() {
+ return url_request_context_.get();
+ }
+
+ private:
+ base::Thread file_thread_;
+ base::Thread cache_thread_;
+ scoped_ptr<net::NetLog> net_log_;
+ scoped_ptr<net::URLRequestContextStorage> storage_;
+ scoped_ptr<net::URLRequestContext> url_request_context_;
+ scoped_ptr<net::TransportSecurityPersister> transport_security_persister_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetworkContext);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_NETWORK_NETWORK_CONTEXT_H_
diff --git a/chromium/mojo/services/network/network_service_impl.cc b/chromium/mojo/services/network/network_service_impl.cc
new file mode 100644
index 00000000000..a216738ad46
--- /dev/null
+++ b/chromium/mojo/services/network/network_service_impl.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 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 "mojo/services/network/network_service_impl.h"
+
+#include "mojo/services/network/url_loader_impl.h"
+
+namespace mojo {
+
+NetworkServiceImpl::NetworkServiceImpl(NetworkContext* context)
+ : context_(context) {
+}
+
+NetworkServiceImpl::~NetworkServiceImpl() {
+}
+
+void NetworkServiceImpl::CreateURLLoader(
+ InterfaceRequest<URLLoader> loader) {
+ BindToRequest(new URLLoaderImpl(context_), &loader);
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/services/network/network_service_impl.h b/chromium/mojo/services/network/network_service_impl.h
new file mode 100644
index 00000000000..32d1713cb12
--- /dev/null
+++ b/chromium/mojo/services/network/network_service_impl.h
@@ -0,0 +1,29 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_NETWORK_NETWORK_SERVICE_IMPL_H_
+#define MOJO_SERVICES_NETWORK_NETWORK_SERVICE_IMPL_H_
+
+#include "base/compiler_specific.h"
+#include "mojo/public/cpp/bindings/interface_impl.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+
+namespace mojo {
+class NetworkContext;
+
+class NetworkServiceImpl : public InterfaceImpl<NetworkService> {
+ public:
+ explicit NetworkServiceImpl(NetworkContext* context);
+ virtual ~NetworkServiceImpl();
+
+ // NetworkService methods:
+ virtual void CreateURLLoader(InterfaceRequest<URLLoader> loader) OVERRIDE;
+
+ private:
+ NetworkContext* context_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_NETWORK_NETWORK_SERVICE_IMPL_H_
diff --git a/chromium/mojo/services/network/url_loader_impl.cc b/chromium/mojo/services/network/url_loader_impl.cc
new file mode 100644
index 00000000000..9b0cce9d525
--- /dev/null
+++ b/chromium/mojo/services/network/url_loader_impl.cc
@@ -0,0 +1,253 @@
+// Copyright 2014 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 "mojo/services/network/url_loader_impl.h"
+
+#include "mojo/common/common_type_converters.h"
+#include "mojo/services/network/network_context.h"
+#include "net/base/io_buffer.h"
+#include "net/base/load_flags.h"
+#include "net/http/http_response_headers.h"
+
+namespace mojo {
+namespace {
+
+const uint32_t kMaxReadSize = 64 * 1024;
+
+// Generates an URLResponsePtr from the response state of a net::URLRequest.
+URLResponsePtr MakeURLResponse(const net::URLRequest* url_request) {
+ URLResponsePtr response(URLResponse::New());
+ response->url = url_request->url().spec();
+
+ const net::HttpResponseHeaders* headers = url_request->response_headers();
+ if (headers) {
+ response->status_code = headers->response_code();
+ response->status_line = headers->GetStatusLine();
+
+ std::vector<String> header_lines;
+ void* iter = NULL;
+ std::string name, value;
+ while (headers->EnumerateHeaderLines(&iter, &name, &value))
+ header_lines.push_back(name + ": " + value);
+ if (!header_lines.empty())
+ response->headers.Swap(&header_lines);
+ }
+
+ return response.Pass();
+}
+
+} // namespace
+
+// Keeps track of a pending two-phase write on a DataPipeProducerHandle.
+class URLLoaderImpl::PendingWriteToDataPipe :
+ public base::RefCountedThreadSafe<PendingWriteToDataPipe> {
+ public:
+ explicit PendingWriteToDataPipe(ScopedDataPipeProducerHandle handle)
+ : handle_(handle.Pass()),
+ buffer_(NULL) {
+ }
+
+ bool BeginWrite(uint32_t* num_bytes) {
+ MojoResult result = BeginWriteDataRaw(handle_.get(), &buffer_, num_bytes,
+ MOJO_WRITE_DATA_FLAG_NONE);
+ if (*num_bytes > kMaxReadSize)
+ *num_bytes = kMaxReadSize;
+
+ return result == MOJO_RESULT_OK;
+ }
+
+ ScopedDataPipeProducerHandle Complete(uint32_t num_bytes) {
+ EndWriteDataRaw(handle_.get(), num_bytes);
+ buffer_ = NULL;
+ return handle_.Pass();
+ }
+
+ char* buffer() { return static_cast<char*>(buffer_); }
+
+ private:
+ friend class base::RefCountedThreadSafe<PendingWriteToDataPipe>;
+
+ ~PendingWriteToDataPipe() {
+ if (handle_.is_valid())
+ EndWriteDataRaw(handle_.get(), 0);
+ }
+
+ ScopedDataPipeProducerHandle handle_;
+ void* buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(PendingWriteToDataPipe);
+};
+
+// Takes ownership of a pending two-phase write on a DataPipeProducerHandle,
+// and makes its buffer available as a net::IOBuffer.
+class URLLoaderImpl::DependentIOBuffer : public net::WrappedIOBuffer {
+ public:
+ DependentIOBuffer(PendingWriteToDataPipe* pending_write)
+ : net::WrappedIOBuffer(pending_write->buffer()),
+ pending_write_(pending_write) {
+ }
+ private:
+ virtual ~DependentIOBuffer() {}
+ scoped_refptr<PendingWriteToDataPipe> pending_write_;
+};
+
+URLLoaderImpl::URLLoaderImpl(NetworkContext* context)
+ : context_(context),
+ auto_follow_redirects_(true),
+ weak_ptr_factory_(this) {
+}
+
+URLLoaderImpl::~URLLoaderImpl() {
+}
+
+void URLLoaderImpl::OnConnectionError() {
+ delete this;
+}
+
+void URLLoaderImpl::Start(URLRequestPtr request,
+ ScopedDataPipeProducerHandle response_body_stream) {
+ // Do not allow starting another request.
+ if (url_request_) {
+ SendError(net::ERR_UNEXPECTED);
+ url_request_.reset();
+ response_body_stream_.reset();
+ return;
+ }
+
+ if (!request) {
+ SendError(net::ERR_INVALID_ARGUMENT);
+ return;
+ }
+
+ response_body_stream_ = response_body_stream.Pass();
+
+ GURL url(request->url);
+ url_request_.reset(
+ new net::URLRequest(url,
+ net::DEFAULT_PRIORITY,
+ this,
+ context_->url_request_context()));
+ url_request_->set_method(request->method);
+ if (request->headers) {
+ net::HttpRequestHeaders headers;
+ for (size_t i = 0; i < request->headers.size(); ++i)
+ headers.AddHeaderFromString(request->headers[i].To<base::StringPiece>());
+ url_request_->SetExtraRequestHeaders(headers);
+ }
+ if (request->bypass_cache)
+ url_request_->SetLoadFlags(net::LOAD_BYPASS_CACHE);
+ // TODO(darin): Handle request body.
+
+ auto_follow_redirects_ = request->auto_follow_redirects;
+
+ url_request_->Start();
+}
+
+void URLLoaderImpl::FollowRedirect() {
+ if (auto_follow_redirects_) {
+ DLOG(ERROR) << "Spurious call to FollowRedirect";
+ } else {
+ if (url_request_)
+ url_request_->FollowDeferredRedirect();
+ }
+}
+
+void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* url_request,
+ const GURL& new_url,
+ bool* defer_redirect) {
+ DCHECK(url_request == url_request_.get());
+ DCHECK(url_request->status().is_success());
+
+ URLResponsePtr response = MakeURLResponse(url_request);
+ std::string redirect_method =
+ net::URLRequest::ComputeMethodForRedirect(url_request->method(),
+ response->status_code);
+ client()->OnReceivedRedirect(
+ response.Pass(), new_url.spec(), redirect_method);
+
+ *defer_redirect = !auto_follow_redirects_;
+}
+
+void URLLoaderImpl::OnResponseStarted(net::URLRequest* url_request) {
+ DCHECK(url_request == url_request_.get());
+
+ if (!url_request->status().is_success()) {
+ SendError(url_request->status().error());
+ return;
+ }
+
+ client()->OnReceivedResponse(MakeURLResponse(url_request));
+
+ // Start reading...
+ ReadMore();
+}
+
+void URLLoaderImpl::OnReadCompleted(net::URLRequest* url_request,
+ int bytes_read) {
+ if (url_request->status().is_success()) {
+ DidRead(static_cast<uint32_t>(bytes_read), false);
+ } else {
+ pending_write_ = NULL; // This closes the data pipe.
+ // TODO(darin): Perhaps we should communicate this error to our client.
+ }
+}
+
+void URLLoaderImpl::SendError(int error_code) {
+ NetworkErrorPtr error(NetworkError::New());
+ error->code = error_code;
+ error->description = net::ErrorToString(error_code);
+ client()->OnReceivedError(error.Pass());
+}
+
+void URLLoaderImpl::ReadMore() {
+ DCHECK(!pending_write_);
+
+ pending_write_ = new PendingWriteToDataPipe(response_body_stream_.Pass());
+
+ uint32_t num_bytes;
+ if (!pending_write_->BeginWrite(&num_bytes))
+ CHECK(false); // Oops! TODO(darin): crbug/386877: The pipe might be full!
+ if (num_bytes > static_cast<uint32_t>(std::numeric_limits<int>::max()))
+ CHECK(false); // Oops!
+
+ scoped_refptr<net::IOBuffer> buf = new DependentIOBuffer(pending_write_);
+
+ int bytes_read;
+ url_request_->Read(buf, static_cast<int>(num_bytes), &bytes_read);
+
+ // Drop our reference to the buffer.
+ buf = NULL;
+
+ if (url_request_->status().is_io_pending()) {
+ // Wait for OnReadCompleted.
+ } else if (url_request_->status().is_success() && bytes_read > 0) {
+ DidRead(static_cast<uint32_t>(bytes_read), true);
+ } else {
+ pending_write_->Complete(0);
+ pending_write_ = NULL; // This closes the data pipe.
+ if (bytes_read == 0) {
+ client()->OnReceivedEndOfResponseBody();
+ } else {
+ DCHECK(!url_request_->status().is_success());
+ SendError(url_request_->status().error());
+ }
+ }
+}
+
+void URLLoaderImpl::DidRead(uint32_t num_bytes, bool completed_synchronously) {
+ DCHECK(url_request_->status().is_success());
+
+ response_body_stream_ = pending_write_->Complete(num_bytes);
+ pending_write_ = NULL;
+
+ if (completed_synchronously) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&URLLoaderImpl::ReadMore, weak_ptr_factory_.GetWeakPtr()));
+ } else {
+ ReadMore();
+ }
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/services/network/url_loader_impl.h b/chromium/mojo/services/network/url_loader_impl.h
new file mode 100644
index 00000000000..a76a9669738
--- /dev/null
+++ b/chromium/mojo/services/network/url_loader_impl.h
@@ -0,0 +1,61 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_NETWORK_URL_LOADER_IMPL_H_
+#define MOJO_SERVICES_NETWORK_URL_LOADER_IMPL_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/bindings/interface_impl.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+#include "net/base/net_errors.h"
+#include "net/url_request/url_request.h"
+
+namespace mojo {
+class NetworkContext;
+
+class URLLoaderImpl : public InterfaceImpl<URLLoader>,
+ public net::URLRequest::Delegate {
+ public:
+ explicit URLLoaderImpl(NetworkContext* context);
+ virtual ~URLLoaderImpl();
+
+ private:
+ class PendingWriteToDataPipe;
+ class DependentIOBuffer;
+
+ // InterfaceImpl<> methods:
+ virtual void OnConnectionError() OVERRIDE;
+
+ // URLLoader methods:
+ virtual void Start(
+ URLRequestPtr request,
+ ScopedDataPipeProducerHandle response_body_stream) OVERRIDE;
+ virtual void FollowRedirect() OVERRIDE;
+
+ // net::URLRequest::Delegate methods:
+ virtual void OnReceivedRedirect(net::URLRequest* url_request,
+ const GURL& new_url,
+ bool* defer_redirect) OVERRIDE;
+ virtual void OnResponseStarted(net::URLRequest* url_request) OVERRIDE;
+ virtual void OnReadCompleted(net::URLRequest* url_request, int bytes_read)
+ OVERRIDE;
+
+ void SendError(int error);
+ void ReadMore();
+ void DidRead(uint32_t num_bytes, bool completed_synchronously);
+
+ NetworkContext* context_;
+ scoped_ptr<net::URLRequest> url_request_;
+ ScopedDataPipeProducerHandle response_body_stream_;
+ scoped_refptr<PendingWriteToDataPipe> pending_write_;
+ bool auto_follow_redirects_;
+
+ base::WeakPtrFactory<URLLoaderImpl> weak_ptr_factory_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_NETWORK_URL_LOADER_IMPL_H_
diff --git a/chromium/mojo/services/public/cpp/geometry/DEPS b/chromium/mojo/services/public/cpp/geometry/DEPS
new file mode 100644
index 00000000000..194be1c46b7
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/geometry/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+ui/gfx/geometry",
+]
diff --git a/chromium/mojo/services/public/cpp/geometry/geometry_type_converters.h b/chromium/mojo/services/public/cpp/geometry/geometry_type_converters.h
new file mode 100644
index 00000000000..3c09f6707be
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/geometry/geometry_type_converters.h
@@ -0,0 +1,37 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_GEOMETRY_GEOMETRY_TYPE_CONVERTERS_H_
+#define MOJO_SERVICES_PUBLIC_CPP_GEOMETRY_GEOMETRY_TYPE_CONVERTERS_H_
+
+#include "mojo/services/public/cpp/geometry/mojo_geometry_export.h"
+#include "mojo/services/public/interfaces/geometry/geometry.mojom.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace mojo {
+
+template<>
+class MOJO_GEOMETRY_EXPORT TypeConverter<PointPtr, gfx::Point> {
+ public:
+ static PointPtr ConvertFrom(const gfx::Point& input);
+ static gfx::Point ConvertTo(const PointPtr& input);
+};
+
+template<>
+class MOJO_GEOMETRY_EXPORT TypeConverter<SizePtr, gfx::Size> {
+ public:
+ static SizePtr ConvertFrom(const gfx::Size& input);
+ static gfx::Size ConvertTo(const SizePtr& input);
+};
+
+template<>
+class MOJO_GEOMETRY_EXPORT TypeConverter<RectPtr, gfx::Rect> {
+ public:
+ static RectPtr ConvertFrom(const gfx::Rect& input);
+ static gfx::Rect ConvertTo(const RectPtr& input);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_GEOMETRY_GEOMETRY_TYPE_CONVERTERS_H_
diff --git a/chromium/mojo/services/public/cpp/geometry/lib/geometry_type_converters.cc b/chromium/mojo/services/public/cpp/geometry/lib/geometry_type_converters.cc
new file mode 100644
index 00000000000..6d942376ac9
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/geometry/lib/geometry_type_converters.cc
@@ -0,0 +1,58 @@
+// Copyright 2014 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 "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+
+namespace mojo {
+
+// static
+PointPtr TypeConverter<PointPtr, gfx::Point>::ConvertFrom(
+ const gfx::Point& input) {
+ PointPtr point(Point::New());
+ point->x = input.x();
+ point->y = input.y();
+ return point.Pass();
+}
+
+// static
+gfx::Point TypeConverter<PointPtr, gfx::Point>::ConvertTo(
+ const PointPtr& input) {
+ if (!input)
+ return gfx::Point();
+ return gfx::Point(input->x, input->y);
+}
+
+// static
+SizePtr TypeConverter<SizePtr, gfx::Size>::ConvertFrom(const gfx::Size& input) {
+ SizePtr size(Size::New());
+ size->width = input.width();
+ size->height = input.height();
+ return size.Pass();
+}
+
+// static
+gfx::Size TypeConverter<SizePtr, gfx::Size>::ConvertTo(const SizePtr& input) {
+ if (!input)
+ return gfx::Size();
+ return gfx::Size(input->width, input->height);
+}
+
+// static
+RectPtr TypeConverter<RectPtr, gfx::Rect>::ConvertFrom(const gfx::Rect& input) {
+ RectPtr rect(Rect::New());
+ rect->x = input.x();
+ rect->y = input.y();
+ rect->width = input.width();
+ rect->height = input.height();
+ return rect.Pass();
+}
+
+// static
+gfx::Rect TypeConverter<RectPtr, gfx::Rect>::ConvertTo(const RectPtr& input) {
+ if (!input)
+ return gfx::Rect();
+ return gfx::Rect(input->x, input->y, input->width, input->height);
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/services/public/cpp/geometry/mojo_geometry_export.h b/chromium/mojo/services/public/cpp/geometry/mojo_geometry_export.h
new file mode 100644
index 00000000000..f21aebce133
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/geometry/mojo_geometry_export.h
@@ -0,0 +1,32 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_GEOMETRY_MOJO_GEOMETRY_EXPORT_H_
+#define MOJO_SERVICES_PUBLIC_CPP_GEOMETRY_MOJO_GEOMETRY_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_GEOMETRY_IMPLEMENTATION)
+#define MOJO_GEOMETRY_EXPORT __declspec(dllexport)
+#else
+#define MOJO_GEOMETRY_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_GEOMETRY_IMPLEMENTATION)
+#define MOJO_GEOMETRY_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_GEOMETRY_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#else // !defined(COMPONENT_BUILD)
+#define MOJO_GEOMETRY_EXPORT
+#endif
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_GEOMETRY_MOJO_GEOMETRY_EXPORT_H_
diff --git a/chromium/mojo/services/public/cpp/input_events/DEPS b/chromium/mojo/services/public/cpp/input_events/DEPS
new file mode 100644
index 00000000000..fe1d98e366d
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/input_events/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+ui/events",
+]
diff --git a/chromium/mojo/services/public/cpp/input_events/input_events_type_converters.h b/chromium/mojo/services/public/cpp/input_events/input_events_type_converters.h
new file mode 100644
index 00000000000..7882f62ea30
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/input_events/input_events_type_converters.h
@@ -0,0 +1,30 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_INPUT_EVENTS_TYPE_CONVERTERS_H_
+#define MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_INPUT_EVENTS_TYPE_CONVERTERS_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "mojo/services/public/cpp/input_events/mojo_input_events_export.h"
+#include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
+#include "ui/events/event.h"
+
+namespace mojo {
+
+template<>
+class MOJO_INPUT_EVENTS_EXPORT TypeConverter<EventPtr, ui::Event> {
+ public:
+ static EventPtr ConvertFrom(const ui::Event& input);
+};
+
+template<>
+class MOJO_INPUT_EVENTS_EXPORT TypeConverter<EventPtr,
+ scoped_ptr<ui::Event> > {
+ public:
+ static scoped_ptr<ui::Event> ConvertTo(const EventPtr& input);
+};
+
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_INPUT_EVENTS_TYPE_CONVERTERS_H_
diff --git a/chromium/mojo/services/public/cpp/input_events/lib/input_events_type_converters.cc b/chromium/mojo/services/public/cpp/input_events/lib/input_events_type_converters.cc
new file mode 100644
index 00000000000..5a47d07e720
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/input_events/lib/input_events_type_converters.cc
@@ -0,0 +1,68 @@
+// Copyright 2014 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 "mojo/services/public/cpp/input_events/input_events_type_converters.h"
+
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+
+namespace mojo {
+
+// static
+EventPtr TypeConverter<EventPtr, ui::Event>::ConvertFrom(
+ const ui::Event& input) {
+ EventPtr event(Event::New());
+ event->action = input.type();
+ event->flags = input.flags();
+ event->time_stamp = input.time_stamp().ToInternalValue();
+
+ if (input.IsMouseEvent() || input.IsTouchEvent()) {
+ const ui::LocatedEvent* located_event =
+ static_cast<const ui::LocatedEvent*>(&input);
+ event->location =
+ TypeConverter<PointPtr, gfx::Point>::ConvertFrom(
+ located_event->location());
+ }
+
+ if (input.IsTouchEvent()) {
+ const ui::TouchEvent* touch_event =
+ static_cast<const ui::TouchEvent*>(&input);
+ TouchDataPtr touch_data(TouchData::New());
+ touch_data->pointer_id = touch_event->touch_id();
+ event->touch_data = touch_data.Pass();
+ } else if (input.IsKeyEvent()) {
+ const ui::KeyEvent* key_event = static_cast<const ui::KeyEvent*>(&input);
+ KeyDataPtr key_data(KeyData::New());
+ key_data->key_code = key_event->key_code();
+ key_data->is_char = key_event->is_char();
+ event->key_data = key_data.Pass();
+ }
+ return event.Pass();
+}
+
+// static
+scoped_ptr<ui::Event>
+TypeConverter<EventPtr, scoped_ptr<ui::Event> >::ConvertTo(
+ const EventPtr& input) {
+ scoped_ptr<ui::Event> ui_event;
+ switch (input->action) {
+ case ui::ET_KEY_PRESSED:
+ case ui::ET_KEY_RELEASED:
+ ui_event.reset(new ui::KeyEvent(
+ static_cast<ui::EventType>(input->action),
+ static_cast<ui::KeyboardCode>(
+ input->key_data->key_code),
+ input->flags,
+ input->key_data->is_char));
+ break;
+ default:
+ // TODO: support other types.
+ // NOTIMPLEMENTED();
+ ;
+ }
+ // TODO: need to support time_stamp.
+ return ui_event.Pass();
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/services/public/cpp/input_events/mojo_input_events_export.h b/chromium/mojo/services/public/cpp/input_events/mojo_input_events_export.h
new file mode 100644
index 00000000000..d7b193e07cd
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/input_events/mojo_input_events_export.h
@@ -0,0 +1,32 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_MOJO_INPUT_EVENTS_EXPORT_H_
+#define MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_MOJO_INPUT_EVENTS_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_INPUT_EVENTS_IMPLEMENTATION)
+#define MOJO_INPUT_EVENTS_EXPORT __declspec(dllexport)
+#else
+#define MOJO_INPUT_EVENTS_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_INPUT_EVENTS_IMPLEMENTATION)
+#define MOJO_INPUT_EVENTS_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_INPUT_EVENTS_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#else // !defined(COMPONENT_BUILD)
+#define MOJO_INPUT_EVENTS_EXPORT
+#endif
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_INPUT_EVENTS_MOJO_INPUT_EVENTS_EXPORT_H_
diff --git a/chromium/mojo/services/public/cpp/view_manager/DEPS b/chromium/mojo/services/public/cpp/view_manager/DEPS
new file mode 100644
index 00000000000..4460c199e75
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+third_party/skia/include/core",
+ "+ui/gfx/geometry"
+]
diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/DEPS b/chromium/mojo/services/public/cpp/view_manager/lib/DEPS
new file mode 100644
index 00000000000..7ae7fe7dbae
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/lib/DEPS
@@ -0,0 +1,11 @@
+include_rules = [
+ "+mojo/geometry",
+ "+third_party/skia",
+ "+ui/gfx",
+]
+
+specific_include_rules = {
+ "view_manager_test_suite.cc": [
+ "+ui/gl",
+ ],
+}
diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/node.cc b/chromium/mojo/services/public/cpp/view_manager/lib/node.cc
new file mode 100644
index 00000000000..e3ce2c9b8dd
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/lib/node.cc
@@ -0,0 +1,432 @@
+// Copyright 2014 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 "mojo/services/public/cpp/view_manager/node.h"
+
+#include "mojo/services/public/cpp/view_manager/lib/node_private.h"
+#include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h"
+#include "mojo/services/public/cpp/view_manager/lib/view_private.h"
+#include "mojo/services/public/cpp/view_manager/node_observer.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+
+namespace mojo {
+namespace view_manager {
+
+namespace {
+
+void NotifyViewTreeChangeAtReceiver(
+ Node* receiver,
+ const NodeObserver::TreeChangeParams& params) {
+ NodeObserver::TreeChangeParams local_params = params;
+ local_params.receiver = receiver;
+ FOR_EACH_OBSERVER(NodeObserver,
+ *NodePrivate(receiver).observers(),
+ OnTreeChange(local_params));
+}
+
+void NotifyViewTreeChangeUp(
+ Node* start_at,
+ const NodeObserver::TreeChangeParams& params) {
+ for (Node* current = start_at; current; current = current->parent())
+ NotifyViewTreeChangeAtReceiver(current, params);
+}
+
+void NotifyViewTreeChangeDown(
+ Node* start_at,
+ const NodeObserver::TreeChangeParams& params) {
+ NotifyViewTreeChangeAtReceiver(start_at, params);
+ Node::Children::const_iterator it = start_at->children().begin();
+ for (; it != start_at->children().end(); ++it)
+ NotifyViewTreeChangeDown(*it, params);
+}
+
+void NotifyViewTreeChange(
+ const NodeObserver::TreeChangeParams& params) {
+ NotifyViewTreeChangeDown(params.target, params);
+ if (params.old_parent)
+ NotifyViewTreeChangeUp(params.old_parent, params);
+ if (params.new_parent)
+ NotifyViewTreeChangeUp(params.new_parent, params);
+}
+
+class ScopedTreeNotifier {
+ public:
+ ScopedTreeNotifier(Node* target, Node* old_parent, Node* new_parent) {
+ params_.target = target;
+ params_.old_parent = old_parent;
+ params_.new_parent = new_parent;
+ NotifyViewTreeChange(params_);
+ }
+ ~ScopedTreeNotifier() {
+ params_.phase = NodeObserver::DISPOSITION_CHANGED;
+ NotifyViewTreeChange(params_);
+ }
+
+ private:
+ NodeObserver::TreeChangeParams params_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedTreeNotifier);
+};
+
+void RemoveChildImpl(Node* child, Node::Children* children) {
+ Node::Children::iterator it =
+ std::find(children->begin(), children->end(), child);
+ if (it != children->end()) {
+ children->erase(it);
+ NodePrivate(child).ClearParent();
+ }
+}
+
+class ScopedOrderChangedNotifier {
+ public:
+ ScopedOrderChangedNotifier(Node* node,
+ Node* relative_node,
+ OrderDirection direction)
+ : node_(node),
+ relative_node_(relative_node),
+ direction_(direction) {
+ FOR_EACH_OBSERVER(
+ NodeObserver,
+ *NodePrivate(node_).observers(),
+ OnNodeReordered(node_,
+ relative_node_,
+ direction_,
+ NodeObserver::DISPOSITION_CHANGING));
+
+ }
+ ~ScopedOrderChangedNotifier() {
+ FOR_EACH_OBSERVER(
+ NodeObserver,
+ *NodePrivate(node_).observers(),
+ OnNodeReordered(node_,
+ relative_node_,
+ direction_,
+ NodeObserver::DISPOSITION_CHANGED));
+ }
+
+ private:
+ Node* node_;
+ Node* relative_node_;
+ OrderDirection direction_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedOrderChangedNotifier);
+};
+
+// Returns true if the order actually changed.
+bool ReorderImpl(Node::Children* children,
+ Node* node,
+ Node* relative,
+ OrderDirection direction) {
+ DCHECK(relative);
+ DCHECK_NE(node, relative);
+ DCHECK_EQ(node->parent(), relative->parent());
+
+ const size_t child_i =
+ std::find(children->begin(), children->end(), node) - children->begin();
+ const size_t target_i =
+ std::find(children->begin(), children->end(), relative) -
+ children->begin();
+ if ((direction == ORDER_ABOVE && child_i == target_i + 1) ||
+ (direction == ORDER_BELOW && child_i + 1 == target_i)) {
+ return false;
+ }
+
+ ScopedOrderChangedNotifier notifier(node, relative, direction);
+
+ const size_t dest_i =
+ direction == ORDER_ABOVE ?
+ (child_i < target_i ? target_i : target_i + 1) :
+ (child_i < target_i ? target_i - 1 : target_i);
+ children->erase(children->begin() + child_i);
+ children->insert(children->begin() + dest_i, node);
+
+ return true;
+}
+
+class ScopedSetActiveViewNotifier {
+ public:
+ ScopedSetActiveViewNotifier(Node* node, View* old_view, View* new_view)
+ : node_(node),
+ old_view_(old_view),
+ new_view_(new_view) {
+ FOR_EACH_OBSERVER(
+ NodeObserver,
+ *NodePrivate(node).observers(),
+ OnNodeActiveViewChange(node_,
+ old_view_,
+ new_view_,
+ NodeObserver::DISPOSITION_CHANGING));
+ }
+ ~ScopedSetActiveViewNotifier() {
+ FOR_EACH_OBSERVER(
+ NodeObserver,
+ *NodePrivate(node_).observers(),
+ OnNodeActiveViewChange(node_,
+ old_view_,
+ new_view_,
+ NodeObserver::DISPOSITION_CHANGED));
+ }
+
+ private:
+ Node* node_;
+ View* old_view_;
+ View* new_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSetActiveViewNotifier);
+};
+
+class ScopedSetBoundsNotifier {
+ public:
+ ScopedSetBoundsNotifier(Node* node,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds)
+ : node_(node),
+ old_bounds_(old_bounds),
+ new_bounds_(new_bounds) {
+ FOR_EACH_OBSERVER(
+ NodeObserver,
+ *NodePrivate(node_).observers(),
+ OnNodeBoundsChange(node_,
+ old_bounds_,
+ new_bounds_,
+ NodeObserver::DISPOSITION_CHANGING));
+ }
+ ~ScopedSetBoundsNotifier() {
+ FOR_EACH_OBSERVER(
+ NodeObserver,
+ *NodePrivate(node_).observers(),
+ OnNodeBoundsChange(node_,
+ old_bounds_,
+ new_bounds_,
+ NodeObserver::DISPOSITION_CHANGED));
+ }
+
+ private:
+ Node* node_;
+ const gfx::Rect old_bounds_;
+ const gfx::Rect new_bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSetBoundsNotifier);
+};
+
+class ScopedDestructionNotifier {
+ public:
+ explicit ScopedDestructionNotifier(Node* node)
+ : node_(node) {
+ FOR_EACH_OBSERVER(
+ NodeObserver,
+ *NodePrivate(node_).observers(),
+ OnNodeDestroy(node_, NodeObserver::DISPOSITION_CHANGING));
+ }
+ ~ScopedDestructionNotifier() {
+ FOR_EACH_OBSERVER(
+ NodeObserver,
+ *NodePrivate(node_).observers(),
+ OnNodeDestroy(node_, NodeObserver::DISPOSITION_CHANGED));
+ }
+
+ private:
+ Node* node_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedDestructionNotifier);
+};
+
+// Some operations are only permitted in the connection that created the node.
+bool OwnsNode(ViewManager* manager, Node* node) {
+ return !manager ||
+ static_cast<ViewManagerClientImpl*>(manager)->OwnsNode(node->id());
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// Node, public:
+
+// static
+Node* Node::Create(ViewManager* view_manager) {
+ Node* node = new Node(view_manager);
+ static_cast<ViewManagerClientImpl*>(view_manager)->AddNode(node);
+ return node;
+}
+
+void Node::Destroy() {
+ if (!OwnsNode(manager_, this))
+ return;
+
+ if (manager_)
+ static_cast<ViewManagerClientImpl*>(manager_)->DestroyNode(id_);
+ while (!children_.empty())
+ children_.front()->Destroy();
+ LocalDestroy();
+}
+
+void Node::SetBounds(const gfx::Rect& bounds) {
+ if (!OwnsNode(manager_, this))
+ return;
+
+ if (manager_)
+ static_cast<ViewManagerClientImpl*>(manager_)->SetBounds(id_, bounds);
+ LocalSetBounds(bounds_, bounds);
+}
+
+void Node::AddObserver(NodeObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void Node::RemoveObserver(NodeObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void Node::AddChild(Node* child) {
+ // TODO(beng): not necessarily valid to all connections, but possibly to the
+ // embeddee in an embedder-embeddee relationship.
+ if (manager_)
+ CHECK_EQ(NodePrivate(child).view_manager(), manager_);
+ LocalAddChild(child);
+ if (manager_)
+ static_cast<ViewManagerClientImpl*>(manager_)->AddChild(child->id(), id_);
+}
+
+void Node::RemoveChild(Node* child) {
+ // TODO(beng): not necessarily valid to all connections, but possibly to the
+ // embeddee in an embedder-embeddee relationship.
+ if (manager_)
+ CHECK_EQ(NodePrivate(child).view_manager(), manager_);
+ LocalRemoveChild(child);
+ if (manager_) {
+ static_cast<ViewManagerClientImpl*>(manager_)->RemoveChild(child->id(),
+ id_);
+ }
+}
+
+void Node::MoveToFront() {
+ Reorder(parent_->children_.back(), ORDER_ABOVE);
+}
+
+void Node::MoveToBack() {
+ Reorder(parent_->children_.front(), ORDER_BELOW);
+}
+
+void Node::Reorder(Node* relative, OrderDirection direction) {
+ if (!LocalReorder(relative, direction))
+ return;
+ if (manager_) {
+ static_cast<ViewManagerClientImpl*>(manager_)->Reorder(id_,
+ relative->id(),
+ direction);
+ }
+}
+
+bool Node::Contains(Node* child) const {
+ if (manager_)
+ CHECK_EQ(NodePrivate(child).view_manager(), manager_);
+ for (Node* p = child->parent(); p; p = p->parent()) {
+ if (p == this)
+ return true;
+ }
+ return false;
+}
+
+Node* Node::GetChildById(Id id) {
+ if (id == id_)
+ return this;
+ // TODO(beng): this could be improved depending on how we decide to own nodes.
+ Children::const_iterator it = children_.begin();
+ for (; it != children_.end(); ++it) {
+ Node* node = (*it)->GetChildById(id);
+ if (node)
+ return node;
+ }
+ return NULL;
+}
+
+void Node::SetActiveView(View* view) {
+ // TODO(beng): not necessarily valid to all connections, but possibly to the
+ // embeddee in an embedder-embeddee relationship.
+ if (manager_)
+ CHECK_EQ(ViewPrivate(view).view_manager(), manager_);
+ LocalSetActiveView(view);
+ if (manager_) {
+ static_cast<ViewManagerClientImpl*>(manager_)->SetActiveView(
+ id_, active_view_->id());
+ }
+}
+
+void Node::SetFocus() {
+ if (manager_)
+ static_cast<ViewManagerClientImpl*>(manager_)->SetFocus(id_);
+}
+
+void Node::Embed(const String& url) {
+ static_cast<ViewManagerClientImpl*>(manager_)->Embed(url, id_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Node, protected:
+
+Node::Node()
+ : manager_(NULL),
+ id_(-1),
+ parent_(NULL),
+ active_view_(NULL) {}
+
+Node::~Node() {
+ ScopedDestructionNotifier notifier(this);
+ if (parent_)
+ parent_->LocalRemoveChild(this);
+ // TODO(beng): It'd be better to do this via a destruction observer in the
+ // ViewManagerClientImpl.
+ if (manager_)
+ static_cast<ViewManagerClientImpl*>(manager_)->RemoveNode(id_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Node, private:
+
+Node::Node(ViewManager* manager)
+ : manager_(manager),
+ id_(static_cast<ViewManagerClientImpl*>(manager_)->CreateNode()),
+ parent_(NULL),
+ active_view_(NULL) {}
+
+void Node::LocalDestroy() {
+ delete this;
+}
+
+void Node::LocalAddChild(Node* child) {
+ ScopedTreeNotifier notifier(child, child->parent(), this);
+ if (child->parent())
+ RemoveChildImpl(child, &child->parent_->children_);
+ children_.push_back(child);
+ child->parent_ = this;
+}
+
+void Node::LocalRemoveChild(Node* child) {
+ DCHECK_EQ(this, child->parent());
+ ScopedTreeNotifier notifier(child, this, NULL);
+ RemoveChildImpl(child, &children_);
+}
+
+bool Node::LocalReorder(Node* relative, OrderDirection direction) {
+ return ReorderImpl(&parent_->children_, this, relative, direction);
+}
+
+void Node::LocalSetActiveView(View* view) {
+ ScopedSetActiveViewNotifier notifier(this, active_view_, view);
+ if (active_view_)
+ ViewPrivate(active_view_).set_node(NULL);
+ active_view_ = view;
+ if (active_view_)
+ ViewPrivate(active_view_).set_node(this);
+}
+
+void Node::LocalSetBounds(const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ DCHECK(old_bounds == bounds_);
+ ScopedSetBoundsNotifier notifier(this, old_bounds, new_bounds);
+ bounds_ = new_bounds;
+}
+
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/node_observer.cc b/chromium/mojo/services/public/cpp/view_manager/lib/node_observer.cc
new file mode 100644
index 00000000000..a0f9f4e67c2
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/lib/node_observer.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 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 "mojo/services/public/cpp/view_manager/node_observer.h"
+
+#include "base/basictypes.h"
+
+namespace mojo {
+namespace view_manager {
+
+////////////////////////////////////////////////////////////////////////////////
+// NodeObserver, public:
+
+NodeObserver::TreeChangeParams::TreeChangeParams()
+ : target(NULL),
+ old_parent(NULL),
+ new_parent(NULL),
+ receiver(NULL),
+ phase(NodeObserver::DISPOSITION_CHANGING) {}
+
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/node_private.cc b/chromium/mojo/services/public/cpp/view_manager/lib/node_private.cc
new file mode 100644
index 00000000000..92dfb537fee
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/lib/node_private.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 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 "mojo/services/public/cpp/view_manager/lib/node_private.h"
+
+namespace mojo {
+namespace view_manager {
+
+NodePrivate::NodePrivate(Node* node)
+ : node_(node) {
+}
+
+NodePrivate::~NodePrivate() {
+}
+
+// static
+Node* NodePrivate::LocalCreate() {
+ return new Node;
+}
+
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/node_private.h b/chromium/mojo/services/public/cpp/view_manager/lib/node_private.h
new file mode 100644
index 00000000000..6f634b3a95a
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/lib/node_private.h
@@ -0,0 +1,62 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_NODE_PRIVATE_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_NODE_PRIVATE_H_
+
+#include "base/basictypes.h"
+
+#include "mojo/services/public/cpp/view_manager/node.h"
+
+namespace mojo {
+namespace view_manager {
+
+class NodePrivate {
+ public:
+ explicit NodePrivate(Node* node);
+ ~NodePrivate();
+
+ static Node* LocalCreate();
+
+ ObserverList<NodeObserver>* observers() { return &node_->observers_; }
+
+ void ClearParent() { node_->parent_ = NULL; }
+
+ void set_id(Id id) { node_->id_ = id; }
+
+ ViewManager* view_manager() { return node_->manager_; }
+ void set_view_manager(ViewManager* manager) {
+ node_->manager_ = manager;
+ }
+
+ void LocalDestroy() {
+ node_->LocalDestroy();
+ }
+ void LocalAddChild(Node* child) {
+ node_->LocalAddChild(child);
+ }
+ void LocalRemoveChild(Node* child) {
+ node_->LocalRemoveChild(child);
+ }
+ void LocalReorder(Node* relative, OrderDirection direction) {
+ node_->LocalReorder(relative, direction);
+ }
+ void LocalSetActiveView(View* view) {
+ node_->LocalSetActiveView(view);
+ }
+ void LocalSetBounds(const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ node_->LocalSetBounds(old_bounds, new_bounds);
+ }
+
+ private:
+ Node* node_;
+
+ DISALLOW_COPY_AND_ASSIGN(NodePrivate);
+};
+
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_NODE_PRIVATE_H_
diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/view.cc b/chromium/mojo/services/public/cpp/view_manager/lib/view.cc
new file mode 100644
index 00000000000..4bcedfce080
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/lib/view.cc
@@ -0,0 +1,97 @@
+// Copyright 2014 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 "mojo/services/public/cpp/view_manager/view.h"
+
+#include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h"
+#include "mojo/services/public/cpp/view_manager/lib/view_private.h"
+#include "mojo/services/public/cpp/view_manager/node.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "ui/gfx/canvas.h"
+
+namespace mojo {
+namespace view_manager {
+
+namespace {
+class ScopedDestructionNotifier {
+ public:
+ explicit ScopedDestructionNotifier(View* view)
+ : view_(view) {
+ FOR_EACH_OBSERVER(
+ ViewObserver,
+ *ViewPrivate(view_).observers(),
+ OnViewDestroy(view_, ViewObserver::DISPOSITION_CHANGING));
+ }
+ ~ScopedDestructionNotifier() {
+ FOR_EACH_OBSERVER(
+ ViewObserver,
+ *ViewPrivate(view_).observers(),
+ OnViewDestroy(view_, ViewObserver::DISPOSITION_CHANGED));
+ }
+
+ private:
+ View* view_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedDestructionNotifier);
+};
+} // namespace
+
+// static
+View* View::Create(ViewManager* manager) {
+ View* view = new View(manager);
+ static_cast<ViewManagerClientImpl*>(manager)->AddView(view);
+ return view;
+}
+
+void View::Destroy() {
+ if (manager_)
+ static_cast<ViewManagerClientImpl*>(manager_)->DestroyView(id_);
+ LocalDestroy();
+}
+
+void View::AddObserver(ViewObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void View::RemoveObserver(ViewObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void View::SetContents(const SkBitmap& contents) {
+ if (manager_) {
+ static_cast<ViewManagerClientImpl*>(manager_)->SetViewContents(id_,
+ contents);
+ }
+}
+
+void View::SetColor(SkColor color) {
+ gfx::Canvas canvas(node_->bounds().size(), 1.0f, true);
+ canvas.DrawColor(color);
+ SetContents(skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(true));
+}
+
+View::View(ViewManager* manager)
+ : id_(static_cast<ViewManagerClientImpl*>(manager)->CreateView()),
+ node_(NULL),
+ manager_(manager) {}
+
+View::View()
+ : id_(-1),
+ node_(NULL),
+ manager_(NULL) {}
+
+View::~View() {
+ ScopedDestructionNotifier notifier(this);
+ // TODO(beng): It'd be better to do this via a destruction observer in the
+ // ViewManagerClientImpl.
+ if (manager_)
+ static_cast<ViewManagerClientImpl*>(manager_)->RemoveView(id_);
+}
+
+void View::LocalDestroy() {
+ delete this;
+}
+
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc
new file mode 100644
index 00000000000..cb7b07136af
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc
@@ -0,0 +1,841 @@
+// Copyright 2014 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 "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+#include "mojo/services/public/cpp/view_manager/lib/node_private.h"
+#include "mojo/services/public/cpp/view_manager/lib/view_private.h"
+#include "mojo/services/public/cpp/view_manager/node_observer.h"
+#include "mojo/services/public/cpp/view_manager/util.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/codec/png_codec.h"
+
+namespace mojo {
+namespace view_manager {
+
+Id MakeTransportId(ConnectionSpecificId connection_id,
+ ConnectionSpecificId local_id) {
+ return (connection_id << 16) | local_id;
+}
+
+// Helper called to construct a local node/view object from transport data.
+Node* AddNodeToViewManager(ViewManagerClientImpl* client,
+ Node* parent,
+ Id node_id,
+ Id view_id,
+ const gfx::Rect& bounds) {
+ // We don't use the ctor that takes a ViewManager here, since it will call
+ // back to the service and attempt to create a new node.
+ Node* node = NodePrivate::LocalCreate();
+ NodePrivate private_node(node);
+ private_node.set_view_manager(client);
+ private_node.set_id(node_id);
+ private_node.LocalSetBounds(gfx::Rect(), bounds);
+ if (parent)
+ NodePrivate(parent).LocalAddChild(node);
+ client->AddNode(node);
+
+ // View.
+ if (view_id != 0) {
+ View* view = ViewPrivate::LocalCreate();
+ ViewPrivate private_view(view);
+ private_view.set_view_manager(client);
+ private_view.set_id(view_id);
+ private_view.set_node(node);
+ // TODO(beng): this broadcasts notifications locally... do we want this? I
+ // don't think so. same story for LocalAddChild above!
+ private_node.LocalSetActiveView(view);
+ client->AddView(view);
+ }
+ return node;
+}
+
+Node* BuildNodeTree(ViewManagerClientImpl* client,
+ const Array<NodeDataPtr>& nodes) {
+ std::vector<Node*> parents;
+ Node* root = NULL;
+ Node* last_node = NULL;
+ for (size_t i = 0; i < nodes.size(); ++i) {
+ if (last_node && nodes[i]->parent_id == last_node->id()) {
+ parents.push_back(last_node);
+ } else if (!parents.empty()) {
+ while (parents.back()->id() != nodes[i]->parent_id)
+ parents.pop_back();
+ }
+ Node* node = AddNodeToViewManager(
+ client,
+ !parents.empty() ? parents.back() : NULL,
+ nodes[i]->node_id,
+ nodes[i]->view_id,
+ nodes[i]->bounds.To<gfx::Rect>());
+ if (!last_node)
+ root = node;
+ last_node = node;
+ }
+ return root;
+}
+
+// Responsible for removing a root from the ViewManager when that node is
+// destroyed.
+class RootObserver : public NodeObserver {
+ public:
+ explicit RootObserver(Node* root) : root_(root) {}
+ virtual ~RootObserver() {}
+
+ private:
+ // Overridden from NodeObserver:
+ virtual void OnNodeDestroy(Node* node,
+ DispositionChangePhase phase) OVERRIDE {
+ DCHECK_EQ(node, root_);
+ if (phase != NodeObserver::DISPOSITION_CHANGED)
+ return;
+ static_cast<ViewManagerClientImpl*>(
+ NodePrivate(root_).view_manager())->RemoveRoot(root_);
+ delete this;
+ }
+
+ Node* root_;
+
+ DISALLOW_COPY_AND_ASSIGN(RootObserver);
+};
+
+class ViewManagerTransaction {
+ public:
+ virtual ~ViewManagerTransaction() {}
+
+ void Commit() {
+ DCHECK(!committed_);
+ DoCommit();
+ committed_ = true;
+ }
+
+ bool committed() const { return committed_; }
+
+ protected:
+ explicit ViewManagerTransaction(ViewManagerClientImpl* client)
+ : committed_(false),
+ client_(client) {
+ }
+
+ // Overridden to perform transaction-specific commit actions.
+ virtual void DoCommit() = 0;
+
+ // Overridden to perform transaction-specific cleanup on commit ack from the
+ // service.
+ virtual void DoActionCompleted(bool success) = 0;
+
+ ViewManagerService* service() { return client_->service_; }
+
+ Id GetAndAdvanceNextServerChangeId() {
+ return client_->next_server_change_id_++;
+ }
+
+ base::Callback<void(bool)> ActionCompletedCallback() {
+ return base::Bind(&ViewManagerTransaction::OnActionCompleted,
+ base::Unretained(this));
+ }
+
+ private:
+ // General callback to be used for commits to the service.
+ void OnActionCompleted(bool success) {
+ DCHECK(success);
+ DoActionCompleted(success);
+ client_->RemoveFromPendingQueue(this);
+ }
+
+ bool committed_;
+ ViewManagerClientImpl* client_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerTransaction);
+};
+
+class CreateViewTransaction : public ViewManagerTransaction {
+ public:
+ CreateViewTransaction(Id view_id, ViewManagerClientImpl* client)
+ : ViewManagerTransaction(client),
+ view_id_(view_id) {}
+ virtual ~CreateViewTransaction() {}
+
+ private:
+ // Overridden from ViewManagerTransaction:
+ virtual void DoCommit() OVERRIDE {
+ service()->CreateView(view_id_, ActionCompletedCallback());
+ }
+ virtual void DoActionCompleted(bool success) OVERRIDE {
+ // TODO(beng): failure.
+ }
+
+ const Id view_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(CreateViewTransaction);
+};
+
+class DestroyViewTransaction : public ViewManagerTransaction {
+ public:
+ DestroyViewTransaction(Id view_id, ViewManagerClientImpl* client)
+ : ViewManagerTransaction(client),
+ view_id_(view_id) {}
+ virtual ~DestroyViewTransaction() {}
+
+ private:
+ // Overridden from ViewManagerTransaction:
+ virtual void DoCommit() OVERRIDE {
+ service()->DeleteView(view_id_, ActionCompletedCallback());
+ }
+ virtual void DoActionCompleted(bool success) OVERRIDE {
+ // TODO(beng): recovery?
+ }
+
+ const Id view_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(DestroyViewTransaction);
+};
+
+class CreateNodeTransaction : public ViewManagerTransaction {
+ public:
+ CreateNodeTransaction(Id node_id, ViewManagerClientImpl* client)
+ : ViewManagerTransaction(client),
+ node_id_(node_id) {}
+ virtual ~CreateNodeTransaction() {}
+
+ private:
+ // Overridden from ViewManagerTransaction:
+ virtual void DoCommit() OVERRIDE {
+ service()->CreateNode(node_id_, ActionCompletedCallback());
+ }
+ virtual void DoActionCompleted(bool success) OVERRIDE {
+ // TODO(beng): Failure means we tried to create with an extant id for this
+ // connection. It also could mean we tried to do something
+ // invalid, or we tried applying a change out of order. Figure
+ // out what to do.
+ }
+
+ const Id node_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(CreateNodeTransaction);
+};
+
+class DestroyNodeTransaction : public ViewManagerTransaction {
+ public:
+ DestroyNodeTransaction(Id node_id, ViewManagerClientImpl* client)
+ : ViewManagerTransaction(client),
+ node_id_(node_id) {}
+ virtual ~DestroyNodeTransaction() {}
+
+ private:
+ // Overridden from ViewManagerTransaction:
+ virtual void DoCommit() OVERRIDE {
+ service()->DeleteNode(node_id_,
+ GetAndAdvanceNextServerChangeId(),
+ ActionCompletedCallback());
+ }
+ virtual void DoActionCompleted(bool success) OVERRIDE {
+ // TODO(beng): recovery?
+ }
+
+ const Id node_id_;
+ DISALLOW_COPY_AND_ASSIGN(DestroyNodeTransaction);
+};
+
+class AddChildTransaction : public ViewManagerTransaction {
+ public:
+ AddChildTransaction(Id child_id,
+ Id parent_id,
+ ViewManagerClientImpl* client)
+ : ViewManagerTransaction(client),
+ child_id_(child_id),
+ parent_id_(parent_id) {}
+ virtual ~AddChildTransaction() {}
+
+ private:
+ // Overridden from ViewManagerTransaction:
+ virtual void DoCommit() OVERRIDE {
+ service()->AddNode(parent_id_,
+ child_id_,
+ GetAndAdvanceNextServerChangeId(),
+ ActionCompletedCallback());
+ }
+
+ virtual void DoActionCompleted(bool success) OVERRIDE {
+ // TODO(beng): recovery?
+ }
+
+ const Id child_id_;
+ const Id parent_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(AddChildTransaction);
+};
+
+class RemoveChildTransaction : public ViewManagerTransaction {
+ public:
+ RemoveChildTransaction(Id child_id, ViewManagerClientImpl* client)
+ : ViewManagerTransaction(client),
+ child_id_(child_id) {}
+ virtual ~RemoveChildTransaction() {}
+
+ private:
+ // Overridden from ViewManagerTransaction:
+ virtual void DoCommit() OVERRIDE {
+ service()->RemoveNodeFromParent(
+ child_id_,
+ GetAndAdvanceNextServerChangeId(),
+ ActionCompletedCallback());
+ }
+
+ virtual void DoActionCompleted(bool success) OVERRIDE {
+ // TODO(beng): recovery?
+ }
+
+ const Id child_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(RemoveChildTransaction);
+};
+
+class ReorderNodeTransaction : public ViewManagerTransaction {
+ public:
+ ReorderNodeTransaction(Id node_id,
+ Id relative_id,
+ OrderDirection direction,
+ ViewManagerClientImpl* client)
+ : ViewManagerTransaction(client),
+ node_id_(node_id),
+ relative_id_(relative_id),
+ direction_(direction) {}
+ virtual ~ReorderNodeTransaction() {}
+
+ private:
+ // Overridden from ViewManagerTransaction:
+ virtual void DoCommit() OVERRIDE {
+ service()->ReorderNode(node_id_,
+ relative_id_,
+ direction_,
+ GetAndAdvanceNextServerChangeId(),
+ ActionCompletedCallback());
+ }
+
+ virtual void DoActionCompleted(bool success) OVERRIDE {
+ // TODO(beng): recovery?
+ }
+
+ const Id node_id_;
+ const Id relative_id_;
+ const OrderDirection direction_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReorderNodeTransaction);
+};
+
+class SetActiveViewTransaction : public ViewManagerTransaction {
+ public:
+ SetActiveViewTransaction(Id node_id,
+ Id view_id,
+ ViewManagerClientImpl* client)
+ : ViewManagerTransaction(client),
+ node_id_(node_id),
+ view_id_(view_id) {}
+ virtual ~SetActiveViewTransaction() {}
+
+ private:
+ // Overridden from ViewManagerTransaction:
+ virtual void DoCommit() OVERRIDE {
+ service()->SetView(node_id_, view_id_, ActionCompletedCallback());
+ }
+ virtual void DoActionCompleted(bool success) OVERRIDE {
+ // TODO(beng): recovery?
+ }
+
+ const Id node_id_;
+ const Id view_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(SetActiveViewTransaction);
+};
+
+class SetBoundsTransaction : public ViewManagerTransaction {
+ public:
+ SetBoundsTransaction(Id node_id,
+ const gfx::Rect& bounds,
+ ViewManagerClientImpl* client)
+ : ViewManagerTransaction(client),
+ node_id_(node_id),
+ bounds_(bounds) {}
+ virtual ~SetBoundsTransaction() {}
+
+ private:
+ // Overridden from ViewManagerTransaction:
+ virtual void DoCommit() OVERRIDE {
+ service()->SetNodeBounds(
+ node_id_, Rect::From(bounds_), ActionCompletedCallback());
+ }
+ virtual void DoActionCompleted(bool success) OVERRIDE {
+ // TODO(beng): recovery?
+ }
+
+ const Id node_id_;
+ const gfx::Rect bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(SetBoundsTransaction);
+};
+
+class SetViewContentsTransaction : public ViewManagerTransaction {
+ public:
+ SetViewContentsTransaction(Id view_id,
+ const SkBitmap& contents,
+ ViewManagerClientImpl* client)
+ : ViewManagerTransaction(client),
+ view_id_(view_id),
+ contents_(contents) {}
+ virtual ~SetViewContentsTransaction() {}
+
+ private:
+ // Overridden from ViewManagerTransaction:
+ virtual void DoCommit() OVERRIDE {
+ std::vector<unsigned char> data;
+ gfx::PNGCodec::EncodeBGRASkBitmap(contents_, false, &data);
+
+ void* memory = NULL;
+ ScopedSharedBufferHandle duped;
+ bool result = CreateMapAndDupSharedBuffer(data.size(),
+ &memory,
+ &shared_state_handle_,
+ &duped);
+ if (!result)
+ return;
+
+ memcpy(memory, &data[0], data.size());
+
+ service()->SetViewContents(view_id_, duped.Pass(),
+ static_cast<uint32_t>(data.size()),
+ ActionCompletedCallback());
+ }
+ virtual void DoActionCompleted(bool success) OVERRIDE {
+ // TODO(beng): recovery?
+ }
+
+ bool CreateMapAndDupSharedBuffer(size_t size,
+ void** memory,
+ ScopedSharedBufferHandle* handle,
+ ScopedSharedBufferHandle* duped) {
+ MojoResult result = CreateSharedBuffer(NULL, size, handle);
+ if (result != MOJO_RESULT_OK)
+ return false;
+ DCHECK(handle->is_valid());
+
+ result = DuplicateBuffer(handle->get(), NULL, duped);
+ if (result != MOJO_RESULT_OK)
+ return false;
+ DCHECK(duped->is_valid());
+
+ result = MapBuffer(
+ handle->get(), 0, size, memory, MOJO_MAP_BUFFER_FLAG_NONE);
+ if (result != MOJO_RESULT_OK)
+ return false;
+ DCHECK(*memory);
+
+ return true;
+ }
+
+ const Id view_id_;
+ const SkBitmap contents_;
+ ScopedSharedBufferHandle shared_state_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(SetViewContentsTransaction);
+};
+
+class EmbedTransaction : public ViewManagerTransaction {
+ public:
+ EmbedTransaction(const String& url,
+ Id node_id,
+ ViewManagerClientImpl* client)
+ : ViewManagerTransaction(client),
+ url_(url),
+ node_id_(node_id) {}
+ virtual ~EmbedTransaction() {}
+
+ private:
+ // Overridden from ViewManagerTransaction:
+ virtual void DoCommit() OVERRIDE {
+ std::vector<Id> ids;
+ ids.push_back(node_id_);
+ service()->Embed(url_, Array<Id>::From(ids), ActionCompletedCallback());
+ }
+ virtual void DoActionCompleted(bool success) OVERRIDE {
+ // TODO(beng): recovery?
+ }
+
+ const String url_;
+ const Id node_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(EmbedTransaction);
+};
+
+class SetFocusTransaction : public ViewManagerTransaction {
+ public:
+ SetFocusTransaction(Id node_id, ViewManagerClientImpl* client)
+ : ViewManagerTransaction(client),
+ node_id_(node_id) {}
+ virtual ~SetFocusTransaction() {}
+
+ private:
+ // Overridden from ViewManagerTransaction:
+ virtual void DoCommit() OVERRIDE {
+ service()->SetFocus(node_id_, ActionCompletedCallback());
+ }
+ virtual void DoActionCompleted(bool success) OVERRIDE {
+ // TODO(beng): recovery?
+ }
+
+ const Id node_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(SetFocusTransaction);
+};
+
+ViewManagerClientImpl::ViewManagerClientImpl(ViewManagerDelegate* delegate)
+ : connected_(false),
+ connection_id_(0),
+ next_id_(1),
+ next_server_change_id_(0),
+ delegate_(delegate) {}
+
+ViewManagerClientImpl::~ViewManagerClientImpl() {
+ while (!nodes_.empty()) {
+ IdToNodeMap::iterator it = nodes_.begin();
+ if (OwnsNode(it->second->id()))
+ it->second->Destroy();
+ else
+ nodes_.erase(it);
+ }
+ while (!views_.empty()) {
+ IdToViewMap::iterator it = views_.begin();
+ if (OwnsView(it->second->id()))
+ it->second->Destroy();
+ else
+ views_.erase(it);
+ }
+}
+
+Id ViewManagerClientImpl::CreateNode() {
+ DCHECK(connected_);
+ const Id node_id(MakeTransportId(connection_id_, ++next_id_));
+ pending_transactions_.push_back(new CreateNodeTransaction(node_id, this));
+ Sync();
+ return node_id;
+}
+
+void ViewManagerClientImpl::DestroyNode(Id node_id) {
+ DCHECK(connected_);
+ pending_transactions_.push_back(new DestroyNodeTransaction(node_id, this));
+ Sync();
+}
+
+Id ViewManagerClientImpl::CreateView() {
+ DCHECK(connected_);
+ const Id view_id(MakeTransportId(connection_id_, ++next_id_));
+ pending_transactions_.push_back(new CreateViewTransaction(view_id, this));
+ Sync();
+ return view_id;
+}
+
+void ViewManagerClientImpl::DestroyView(Id view_id) {
+ DCHECK(connected_);
+ pending_transactions_.push_back(new DestroyViewTransaction(view_id, this));
+ Sync();
+}
+
+void ViewManagerClientImpl::AddChild(Id child_id,
+ Id parent_id) {
+ DCHECK(connected_);
+ pending_transactions_.push_back(
+ new AddChildTransaction(child_id, parent_id, this));
+ Sync();
+}
+
+void ViewManagerClientImpl::RemoveChild(Id child_id, Id parent_id) {
+ DCHECK(connected_);
+ pending_transactions_.push_back(new RemoveChildTransaction(child_id, this));
+ Sync();
+}
+
+void ViewManagerClientImpl::Reorder(
+ Id node_id,
+ Id relative_node_id,
+ OrderDirection direction) {
+ DCHECK(connected_);
+ pending_transactions_.push_back(
+ new ReorderNodeTransaction(node_id, relative_node_id, direction, this));
+ Sync();
+}
+
+bool ViewManagerClientImpl::OwnsNode(Id id) const {
+ return HiWord(id) == connection_id_;
+}
+
+bool ViewManagerClientImpl::OwnsView(Id id) const {
+ return HiWord(id) == connection_id_;
+}
+
+void ViewManagerClientImpl::SetActiveView(Id node_id, Id view_id) {
+ DCHECK(connected_);
+ pending_transactions_.push_back(
+ new SetActiveViewTransaction(node_id, view_id, this));
+ Sync();
+}
+
+void ViewManagerClientImpl::SetBounds(Id node_id, const gfx::Rect& bounds) {
+ DCHECK(connected_);
+ pending_transactions_.push_back(
+ new SetBoundsTransaction(node_id, bounds, this));
+ Sync();
+}
+
+void ViewManagerClientImpl::SetViewContents(Id view_id,
+ const SkBitmap& contents) {
+ DCHECK(connected_);
+ pending_transactions_.push_back(
+ new SetViewContentsTransaction(view_id, contents, this));
+ Sync();
+}
+
+void ViewManagerClientImpl::SetFocus(Id node_id) {
+ DCHECK(connected_);
+ pending_transactions_.push_back(new SetFocusTransaction(node_id, this));
+ Sync();
+}
+
+void ViewManagerClientImpl::Embed(const String& url, Id node_id) {
+ DCHECK(connected_);
+ pending_transactions_.push_back(new EmbedTransaction(url, node_id, this));
+ Sync();
+}
+
+void ViewManagerClientImpl::AddNode(Node* node) {
+ DCHECK(nodes_.find(node->id()) == nodes_.end());
+ nodes_[node->id()] = node;
+}
+
+void ViewManagerClientImpl::RemoveNode(Id node_id) {
+ IdToNodeMap::iterator it = nodes_.find(node_id);
+ if (it != nodes_.end())
+ nodes_.erase(it);
+}
+
+void ViewManagerClientImpl::AddView(View* view) {
+ DCHECK(views_.find(view->id()) == views_.end());
+ views_[view->id()] = view;
+}
+
+void ViewManagerClientImpl::RemoveView(Id view_id) {
+ IdToViewMap::iterator it = views_.find(view_id);
+ if (it != views_.end())
+ views_.erase(it);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewManagerClientImpl, ViewManager implementation:
+
+const std::string& ViewManagerClientImpl::GetEmbedderURL() const {
+ return creator_url_;
+}
+
+const std::vector<Node*>& ViewManagerClientImpl::GetRoots() const {
+ return roots_;
+}
+
+Node* ViewManagerClientImpl::GetNodeById(Id id) {
+ IdToNodeMap::const_iterator it = nodes_.find(id);
+ return it != nodes_.end() ? it->second : NULL;
+}
+
+View* ViewManagerClientImpl::GetViewById(Id id) {
+ IdToViewMap::const_iterator it = views_.find(id);
+ return it != views_.end() ? it->second : NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewManagerClientImpl, InterfaceImpl overrides:
+
+void ViewManagerClientImpl::OnConnectionEstablished() {
+ service_ = client();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewManagerClientImpl, ViewManagerClient implementation:
+
+void ViewManagerClientImpl::OnViewManagerConnectionEstablished(
+ ConnectionSpecificId connection_id,
+ const String& creator_url,
+ Id next_server_change_id,
+ Array<NodeDataPtr> nodes) {
+ connected_ = true;
+ connection_id_ = connection_id;
+ creator_url_ = TypeConverter<String, std::string>::ConvertFrom(creator_url);
+ next_server_change_id_ = next_server_change_id;
+
+ DCHECK(pending_transactions_.empty());
+ AddRoot(BuildNodeTree(this, nodes));
+}
+
+void ViewManagerClientImpl::OnRootsAdded(Array<NodeDataPtr> nodes) {
+ AddRoot(BuildNodeTree(this, nodes));
+}
+
+void ViewManagerClientImpl::OnServerChangeIdAdvanced(
+ Id next_server_change_id) {
+ next_server_change_id_ = next_server_change_id;
+}
+
+void ViewManagerClientImpl::OnNodeBoundsChanged(Id node_id,
+ RectPtr old_bounds,
+ RectPtr new_bounds) {
+ Node* node = GetNodeById(node_id);
+ NodePrivate(node).LocalSetBounds(old_bounds.To<gfx::Rect>(),
+ new_bounds.To<gfx::Rect>());
+}
+
+void ViewManagerClientImpl::OnNodeHierarchyChanged(
+ Id node_id,
+ Id new_parent_id,
+ Id old_parent_id,
+ Id server_change_id,
+ mojo::Array<NodeDataPtr> nodes) {
+ next_server_change_id_ = server_change_id + 1;
+
+ BuildNodeTree(this, nodes);
+
+ Node* new_parent = GetNodeById(new_parent_id);
+ Node* old_parent = GetNodeById(old_parent_id);
+ Node* node = GetNodeById(node_id);
+ if (new_parent)
+ NodePrivate(new_parent).LocalAddChild(node);
+ else
+ NodePrivate(old_parent).LocalRemoveChild(node);
+}
+
+void ViewManagerClientImpl::OnNodeReordered(Id node_id,
+ Id relative_node_id,
+ OrderDirection direction,
+ Id server_change_id) {
+ next_server_change_id_ = server_change_id + 1;
+
+ Node* node = GetNodeById(node_id);
+ Node* relative_node = GetNodeById(relative_node_id);
+ if (node && relative_node) {
+ NodePrivate(node).LocalReorder(relative_node, direction);
+ }
+}
+
+void ViewManagerClientImpl::OnNodeDeleted(Id node_id, Id server_change_id) {
+ next_server_change_id_ = server_change_id + 1;
+
+ Node* node = GetNodeById(node_id);
+ if (node)
+ NodePrivate(node).LocalDestroy();
+}
+
+void ViewManagerClientImpl::OnNodeViewReplaced(Id node_id,
+ Id new_view_id,
+ Id old_view_id) {
+ Node* node = GetNodeById(node_id);
+ View* new_view = GetViewById(new_view_id);
+ if (!new_view && new_view_id != 0) {
+ // This client wasn't aware of this View until now.
+ new_view = ViewPrivate::LocalCreate();
+ ViewPrivate private_view(new_view);
+ private_view.set_view_manager(this);
+ private_view.set_id(new_view_id);
+ private_view.set_node(node);
+ AddView(new_view);
+ }
+ View* old_view = GetViewById(old_view_id);
+ DCHECK_EQ(old_view, node->active_view());
+ NodePrivate(node).LocalSetActiveView(new_view);
+}
+
+void ViewManagerClientImpl::OnViewDeleted(Id view_id) {
+ View* view = GetViewById(view_id);
+ if (view)
+ ViewPrivate(view).LocalDestroy();
+}
+
+void ViewManagerClientImpl::OnViewInputEvent(
+ Id view_id,
+ EventPtr event,
+ const Callback<void()>& ack_callback) {
+ View* view = GetViewById(view_id);
+ if (view) {
+ FOR_EACH_OBSERVER(ViewObserver,
+ *ViewPrivate(view).observers(),
+ OnViewInputEvent(view, event));
+ }
+ ack_callback.Run();
+}
+
+void ViewManagerClientImpl::DispatchOnViewInputEvent(Id view_id,
+ EventPtr event) {
+ // For now blindly bounce the message back to the server. Doing this means the
+ // event is sent to the correct target (|view_id|).
+ // Note: This function is only invoked on the window manager.
+ service_->DispatchOnViewInputEvent(view_id, event.Pass());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewManagerClientImpl, private:
+
+void ViewManagerClientImpl::Sync() {
+ // The service connection may not be set up yet. OnConnectionEstablished()
+ // will schedule another sync when it is.
+ if (!connected_)
+ return;
+
+ Transactions::const_iterator it = pending_transactions_.begin();
+ for (; it != pending_transactions_.end(); ++it) {
+ if (!(*it)->committed())
+ (*it)->Commit();
+ }
+}
+
+void ViewManagerClientImpl::RemoveFromPendingQueue(
+ ViewManagerTransaction* transaction) {
+ DCHECK_EQ(transaction, pending_transactions_.front());
+ pending_transactions_.erase(pending_transactions_.begin());
+ if (pending_transactions_.empty() && !changes_acked_callback_.is_null())
+ changes_acked_callback_.Run();
+}
+
+void ViewManagerClientImpl::AddRoot(Node* root) {
+ // A new root must not already exist as a root or be contained by an existing
+ // hierarchy visible to this view manager.
+ std::vector<Node*>::const_iterator it = roots_.begin();
+ for (; it != roots_.end(); ++it) {
+ if (*it == root || (*it)->Contains(root))
+ return;
+ }
+ roots_.push_back(root);
+ root->AddObserver(new RootObserver(root));
+ delegate_->OnRootAdded(this, root);
+}
+
+void ViewManagerClientImpl::RemoveRoot(Node* root) {
+ std::vector<Node*>::iterator it =
+ std::find(roots_.begin(), roots_.end(), root);
+ if (it != roots_.end())
+ roots_.erase(it);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ViewManager, public:
+
+// static
+void ViewManager::Create(Application* application,
+ ViewManagerDelegate* delegate) {
+ application->AddService<ViewManagerClientImpl>(delegate);
+}
+
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h
new file mode 100644
index 00000000000..01cdc34110d
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h
@@ -0,0 +1,162 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_CLIENT_IMPL_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_CLIENT_IMPL_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/view_manager/node.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/cpp/view_manager/view_manager.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+
+class SkBitmap;
+
+namespace mojo {
+namespace view_manager {
+
+class ViewManager;
+class ViewManagerTransaction;
+
+// Manages the connection with the View Manager service.
+class ViewManagerClientImpl : public ViewManager,
+ public InterfaceImpl<ViewManagerClient> {
+ public:
+ explicit ViewManagerClientImpl(ViewManagerDelegate* delegate);
+ virtual ~ViewManagerClientImpl();
+
+ bool connected() const { return connected_; }
+ ConnectionSpecificId connection_id() const { return connection_id_; }
+
+ // API exposed to the node/view implementations that pushes local changes to
+ // the service.
+ Id CreateNode();
+ void DestroyNode(Id node_id);
+
+ Id CreateView();
+ void DestroyView(Id view_id);
+
+ // These methods take TransportIds. For views owned by the current connection,
+ // the connection id high word can be zero. In all cases, the TransportId 0x1
+ // refers to the root node.
+ void AddChild(Id child_id, Id parent_id);
+ void RemoveChild(Id child_id, Id parent_id);
+
+ void Reorder(Id node_id, Id relative_node_id, OrderDirection direction);
+
+ // Returns true if the specified node/view was created by this connection.
+ bool OwnsNode(Id id) const;
+ bool OwnsView(Id id) const;
+
+ void SetActiveView(Id node_id, Id view_id);
+ void SetBounds(Id node_id, const gfx::Rect& bounds);
+ void SetViewContents(Id view_id, const SkBitmap& contents);
+ void SetFocus(Id node_id);
+
+ void Embed(const String& url, Id node_id);
+
+ void set_changes_acked_callback(const base::Callback<void(void)>& callback) {
+ changes_acked_callback_ = callback;
+ }
+ void ClearChangesAckedCallback() {
+ changes_acked_callback_ = base::Callback<void(void)>();
+ }
+
+ // Start/stop tracking nodes & views. While tracked, they can be retrieved via
+ // ViewManager::GetNode/ViewById.
+ void AddNode(Node* node);
+ void RemoveNode(Id node_id);
+
+ void AddView(View* view);
+ void RemoveView(Id view_id);
+
+ private:
+ friend class RootObserver;
+ friend class ViewManagerTransaction;
+
+ typedef ScopedVector<ViewManagerTransaction> Transactions;
+ typedef std::map<Id, Node*> IdToNodeMap;
+ typedef std::map<Id, View*> IdToViewMap;
+
+ // Overridden from ViewManager:
+ virtual const std::string& GetEmbedderURL() const OVERRIDE;
+ virtual const std::vector<Node*>& GetRoots() const OVERRIDE;
+ virtual Node* GetNodeById(Id id) OVERRIDE;
+ virtual View* GetViewById(Id id) OVERRIDE;
+
+ // Overridden from InterfaceImpl:
+ virtual void OnConnectionEstablished() OVERRIDE;
+
+ // Overridden from ViewManagerClient:
+ virtual void OnViewManagerConnectionEstablished(
+ ConnectionSpecificId connection_id,
+ const String& creator_url,
+ Id next_server_change_id,
+ Array<NodeDataPtr> nodes) OVERRIDE;
+ virtual void OnRootsAdded(Array<NodeDataPtr> nodes) OVERRIDE;
+ virtual void OnServerChangeIdAdvanced(Id next_server_change_id) OVERRIDE;
+ virtual void OnNodeBoundsChanged(Id node_id,
+ RectPtr old_bounds,
+ RectPtr new_bounds) OVERRIDE;
+ virtual void OnNodeHierarchyChanged(Id node_id,
+ Id new_parent_id,
+ Id old_parent_id,
+ Id server_change_id,
+ Array<NodeDataPtr> nodes) OVERRIDE;
+ virtual void OnNodeReordered(Id node_id,
+ Id relative_node_id,
+ OrderDirection direction,
+ Id server_change_id) OVERRIDE;
+ virtual void OnNodeDeleted(Id node_id, Id server_change_id) OVERRIDE;
+ virtual void OnNodeViewReplaced(Id node,
+ Id new_view_id,
+ Id old_view_id) OVERRIDE;
+ virtual void OnViewDeleted(Id view_id) OVERRIDE;
+ virtual void OnViewInputEvent(Id view,
+ EventPtr event,
+ const Callback<void()>& callback) OVERRIDE;
+ virtual void DispatchOnViewInputEvent(Id view_id, EventPtr event) OVERRIDE;
+
+ // Sync the client model with the service by enumerating the pending
+ // transaction queue and applying them in order.
+ void Sync();
+
+ // Removes |transaction| from the pending queue. |transaction| must be at the
+ // front of the queue.
+ void RemoveFromPendingQueue(ViewManagerTransaction* transaction);
+
+ void AddRoot(Node* root);
+ void RemoveRoot(Node* root);
+
+ bool connected_;
+ ConnectionSpecificId connection_id_;
+ ConnectionSpecificId next_id_;
+ Id next_server_change_id_;
+
+ std::string creator_url_;
+
+ Transactions pending_transactions_;
+
+ base::Callback<void(void)> changes_acked_callback_;
+
+ ViewManagerDelegate* delegate_;
+
+ std::vector<Node*> roots_;
+
+ IdToNodeMap nodes_;
+ IdToViewMap views_;
+
+ ViewManagerService* service_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerClientImpl);
+};
+
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_CLIENT_IMPL_H_
diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.cc b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.cc
new file mode 100644
index 00000000000..565b469c5b6
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.cc
@@ -0,0 +1,24 @@
+// Copyright 2014 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 "mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.h"
+
+#include "ui/gl/gl_surface.h"
+
+namespace mojo {
+namespace view_manager {
+
+ViewManagerTestSuite::ViewManagerTestSuite(int argc, char** argv)
+ : TestSuite(argc, argv) {}
+
+ViewManagerTestSuite::~ViewManagerTestSuite() {
+}
+
+void ViewManagerTestSuite::Initialize() {
+ base::TestSuite::Initialize();
+ gfx::GLSurface::InitializeOneOffForTests();
+}
+
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.h b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.h
new file mode 100644
index 00000000000..d0744ade822
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.h
@@ -0,0 +1,28 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_TEST_SUITE_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_TEST_SUITE_H_
+
+#include "base/test/test_suite.h"
+
+namespace mojo {
+namespace view_manager {
+
+class ViewManagerTestSuite : public base::TestSuite {
+ public:
+ ViewManagerTestSuite(int argc, char** argv);
+ virtual ~ViewManagerTestSuite();
+
+ protected:
+ virtual void Initialize() OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerTestSuite);
+};
+
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_MANAGER_TEST_SUITE_H_
diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_unittests.cc b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_unittests.cc
new file mode 100644
index 00000000000..9a831397ba8
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/lib/view_manager_unittests.cc
@@ -0,0 +1,14 @@
+// Copyright 2014 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 "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "mojo/services/public/cpp/view_manager/lib/view_manager_test_suite.h"
+
+int main(int argc, char** argv) {
+ mojo::view_manager::ViewManagerTestSuite test_suite(argc, argv);
+
+ return base::LaunchUnitTests(
+ argc, argv, base::Bind(&TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/view_private.cc b/chromium/mojo/services/public/cpp/view_manager/lib/view_private.cc
new file mode 100644
index 00000000000..14ecf435f0b
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/lib/view_private.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 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 "mojo/services/public/cpp/view_manager/lib/view_private.h"
+
+namespace mojo {
+namespace view_manager {
+
+ViewPrivate::ViewPrivate(View* view)
+ : view_(view) {
+}
+
+ViewPrivate::~ViewPrivate() {
+}
+
+// static
+View* ViewPrivate::LocalCreate() {
+ return new View;
+}
+
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/public/cpp/view_manager/lib/view_private.h b/chromium/mojo/services/public/cpp/view_manager/lib/view_private.h
new file mode 100644
index 00000000000..2e38613dfad
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/lib/view_private.h
@@ -0,0 +1,44 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_PRIVATE_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_PRIVATE_H_
+
+#include "base/basictypes.h"
+
+#include "mojo/services/public/cpp/view_manager/view.h"
+
+namespace mojo {
+namespace view_manager {
+
+class ViewPrivate {
+ public:
+ explicit ViewPrivate(View* view);
+ ~ViewPrivate();
+
+ static View* LocalCreate();
+ void LocalDestroy() {
+ view_->LocalDestroy();
+ }
+
+ void set_id(Id id) { view_->id_ = id; }
+ void set_node(Node* node) { view_->node_ = node; }
+
+ ViewManager* view_manager() { return view_->manager_; }
+ void set_view_manager(ViewManager* manager) {
+ view_->manager_ = manager;
+ }
+
+ ObserverList<ViewObserver>* observers() { return &view_->observers_; }
+
+ private:
+ View* view_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewPrivate);
+};
+
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_VIEW_PRIVATE_H_
diff --git a/chromium/mojo/services/public/cpp/view_manager/node.h b/chromium/mojo/services/public/cpp/view_manager/node.h
new file mode 100644
index 00000000000..03dbd376def
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/node.h
@@ -0,0 +1,108 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_NODE_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_NODE_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/observer_list.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager_constants.mojom.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace mojo {
+namespace view_manager {
+
+class View;
+class ViewManager;
+class NodeObserver;
+
+// Nodes are owned by the ViewManager.
+// TODO(beng): Right now, you'll have to implement a NodeObserver to track
+// destruction and NULL any pointers you have.
+// Investigate some kind of smart pointer or weak pointer for these.
+class Node {
+ public:
+ typedef std::vector<Node*> Children;
+
+ static Node* Create(ViewManager* view_manager);
+
+ // Destroys this node and all its children.
+ void Destroy();
+
+ // Configuration.
+ Id id() const { return id_; }
+
+ // Geometric disposition.
+ const gfx::Rect& bounds() { return bounds_; }
+ void SetBounds(const gfx::Rect& bounds);
+
+ // Observation.
+ void AddObserver(NodeObserver* observer);
+ void RemoveObserver(NodeObserver* observer);
+
+ // Tree.
+ Node* parent() { return parent_; }
+ const Node* parent() const { return parent_; }
+ const Children& children() const { return children_; }
+
+ void AddChild(Node* child);
+ void RemoveChild(Node* child);
+
+ void Reorder(Node* relative, OrderDirection direction);
+ void MoveToFront();
+ void MoveToBack();
+
+ bool Contains(Node* child) const;
+
+ Node* GetChildById(Id id);
+
+ // View.
+ void SetActiveView(View* view);
+ View* active_view() { return active_view_; }
+
+ // Focus.
+ void SetFocus();
+
+ // Embedding.
+ void Embed(const String& url);
+
+ protected:
+ // This class is subclassed only by test classes that provide a public ctor.
+ Node();
+ ~Node();
+
+ private:
+ friend class NodePrivate;
+
+ explicit Node(ViewManager* manager);
+
+ void LocalDestroy();
+ void LocalAddChild(Node* child);
+ void LocalRemoveChild(Node* child);
+ // Returns true if the order actually changed.
+ bool LocalReorder(Node* relative, OrderDirection direction);
+ void LocalSetActiveView(View* view);
+ void LocalSetBounds(const gfx::Rect& old_bounds, const gfx::Rect& new_bounds);
+
+ ViewManager* manager_;
+ Id id_;
+ Node* parent_;
+ Children children_;
+
+ ObserverList<NodeObserver> observers_;
+
+ gfx::Rect bounds_;
+ View* active_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(Node);
+};
+
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_NODE_H_
diff --git a/chromium/mojo/services/public/cpp/view_manager/node_observer.h b/chromium/mojo/services/public/cpp/view_manager/node_observer.h
new file mode 100644
index 00000000000..7b0cdb010b9
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/node_observer.h
@@ -0,0 +1,66 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_NODE_OBSERVER_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_NODE_OBSERVER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+
+#include "mojo/services/public/cpp/view_manager/node.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace mojo {
+namespace view_manager {
+
+class Node;
+class View;
+
+class NodeObserver {
+ public:
+ enum DispositionChangePhase {
+ DISPOSITION_CHANGING,
+ DISPOSITION_CHANGED
+ };
+
+ struct TreeChangeParams {
+ TreeChangeParams();
+ Node* target;
+ Node* old_parent;
+ Node* new_parent;
+ Node* receiver;
+ DispositionChangePhase phase;
+ };
+
+ virtual void OnTreeChange(const TreeChangeParams& params) {}
+
+ virtual void OnNodeReordered(Node* node,
+ Node* relative_node,
+ OrderDirection direction,
+ DispositionChangePhase phase) {}
+
+ virtual void OnNodeDestroy(Node* node, DispositionChangePhase phase) {}
+
+ virtual void OnNodeActiveViewChange(Node* node,
+ View* old_view,
+ View* new_view,
+ DispositionChangePhase phase) {}
+
+ virtual void OnNodeBoundsChange(Node* node,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds,
+ DispositionChangePhase phase) {}
+
+ protected:
+ virtual ~NodeObserver() {}
+};
+
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_NODE_OBSERVER_H_
diff --git a/chromium/mojo/services/public/cpp/view_manager/types.h b/chromium/mojo/services/public/cpp/view_manager/types.h
new file mode 100644
index 00000000000..c1f33f30b8d
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/types.h
@@ -0,0 +1,27 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_TYPES_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_TYPES_H_
+
+#include "base/basictypes.h"
+
+// Typedefs for the transport types. These typedefs match that of the mojom
+// file, see it for specifics.
+
+namespace mojo {
+namespace view_manager {
+
+// Used to identify nodes, views and change ids.
+typedef uint32_t Id;
+
+// Used to identify a connection as well as a connection specific view or node
+// id. For example, the Id for a node consists of the ConnectionSpecificId of
+// the connection and the ConnectionSpecificId of the node.
+typedef uint16_t ConnectionSpecificId;
+
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_TYPES_H_
diff --git a/chromium/mojo/services/public/cpp/view_manager/util.h b/chromium/mojo/services/public/cpp/view_manager/util.h
new file mode 100644
index 00000000000..6716075afc3
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/util.h
@@ -0,0 +1,32 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_UTIL_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_UTIL_H_
+
+#include "mojo/services/public/cpp/view_manager/types.h"
+
+// TODO(beng): #$*&@#(@ MacOSX SDK!
+#if defined(HiWord)
+#undef HiWord
+#endif
+#if defined(LoWord)
+#undef LoWord
+#endif
+
+namespace mojo {
+namespace view_manager {
+
+inline uint16_t HiWord(uint32_t id) {
+ return static_cast<uint16_t>((id >> 16) & 0xFFFF);
+}
+
+inline uint16_t LoWord(uint32_t id) {
+ return static_cast<uint16_t>(id & 0xFFFF);
+}
+
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_UTIL_H_
diff --git a/chromium/mojo/services/public/cpp/view_manager/view.h b/chromium/mojo/services/public/cpp/view_manager/view.h
new file mode 100644
index 00000000000..b10417b21ba
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/view.h
@@ -0,0 +1,60 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_H_
+
+#include "base/basictypes.h"
+#include "base/observer_list.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+class SkBitmap;
+
+namespace mojo {
+namespace view_manager {
+
+class Node;
+class ViewManager;
+class ViewObserver;
+
+// Views are owned by the ViewManager.
+class View {
+ public:
+ static View* Create(ViewManager* manager);
+
+ void Destroy();
+
+ Id id() const { return id_; }
+ Node* node() { return node_; }
+
+ void AddObserver(ViewObserver* observer);
+ void RemoveObserver(ViewObserver* observer);
+
+ // TODO(beng): temporary only.
+ void SetContents(const SkBitmap& contents);
+ void SetColor(SkColor color);
+
+ private:
+ friend class ViewPrivate;
+
+ explicit View(ViewManager* manager);
+ View();
+ ~View();
+
+ void LocalDestroy();
+
+ Id id_;
+ Node* node_;
+ ViewManager* manager_;
+
+ ObserverList<ViewObserver> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(View);
+};
+
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_H_
diff --git a/chromium/mojo/services/public/cpp/view_manager/view_manager.h b/chromium/mojo/services/public/cpp/view_manager/view_manager.h
new file mode 100644
index 00000000000..6ed6e901d38
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/view_manager.h
@@ -0,0 +1,44 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include "mojo/services/public/cpp/view_manager/types.h"
+
+namespace mojo {
+class Application;
+namespace view_manager {
+
+class Node;
+class View;
+class ViewManagerDelegate;
+
+class ViewManager {
+ public:
+ // Delegate is owned by the caller.
+ static void Create(Application* application, ViewManagerDelegate* delegate);
+
+ // Returns the URL of the application that embedded this application.
+ virtual const std::string& GetEmbedderURL() const = 0;
+
+ // Returns all root nodes known to this connection.
+ virtual const std::vector<Node*>& GetRoots() const = 0;
+
+ // Returns a Node or View known to this connection.
+ virtual Node* GetNodeById(Id id) = 0;
+ virtual View* GetViewById(Id id) = 0;
+
+ protected:
+ virtual ~ViewManager() {}
+
+};
+
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_H_
diff --git a/chromium/mojo/services/public/cpp/view_manager/view_manager_delegate.h b/chromium/mojo/services/public/cpp/view_manager/view_manager_delegate.h
new file mode 100644
index 00000000000..bceee02d9be
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/view_manager_delegate.h
@@ -0,0 +1,25 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_DELEGATE_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_DELEGATE_H_
+
+namespace mojo {
+namespace view_manager {
+
+class Node;
+class ViewManager;
+
+class ViewManagerDelegate {
+ public:
+ virtual void OnRootAdded(ViewManager* view_manager, Node* root) {}
+
+ protected:
+ virtual ~ViewManagerDelegate() {}
+};
+
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_MANAGER_DELEGATE_H_
diff --git a/chromium/mojo/services/public/cpp/view_manager/view_observer.h b/chromium/mojo/services/public/cpp/view_manager/view_observer.h
new file mode 100644
index 00000000000..9e189fd609e
--- /dev/null
+++ b/chromium/mojo/services/public/cpp/view_manager/view_observer.h
@@ -0,0 +1,37 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_OBSERVER_H_
+#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_OBSERVER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+
+#include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
+
+namespace mojo {
+namespace view_manager {
+
+class View;
+
+class ViewObserver {
+ public:
+ enum DispositionChangePhase {
+ DISPOSITION_CHANGING,
+ DISPOSITION_CHANGED
+ };
+
+ virtual void OnViewDestroy(View* view, DispositionChangePhase phase) {}
+
+ virtual void OnViewInputEvent(View* view, const EventPtr& event) {}
+
+ protected:
+ virtual ~ViewObserver() {}
+};
+
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_OBSERVER_H_
diff --git a/chromium/mojo/services/public/interfaces/geometry/geometry.mojom b/chromium/mojo/services/public/interfaces/geometry/geometry.mojom
new file mode 100644
index 00000000000..ab68c8e61ce
--- /dev/null
+++ b/chromium/mojo/services/public/interfaces/geometry/geometry.mojom
@@ -0,0 +1,34 @@
+// Copyright 2014 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.
+
+module mojo {
+
+struct Point {
+ int32 x;
+ int32 y;
+};
+
+struct PointF {
+ float x;
+ float y;
+};
+
+struct Size {
+ int32 width;
+ int32 height;
+};
+
+struct Rect {
+ int32 x;
+ int32 y;
+ int32 width;
+ int32 height;
+};
+
+struct Transform {
+ // Should have exactly 16 entries.
+ float[] matrix;
+};
+
+}
diff --git a/chromium/mojo/services/public/interfaces/input_events/input_events.mojom b/chromium/mojo/services/public/interfaces/input_events/input_events.mojom
new file mode 100644
index 00000000000..255fe284fe0
--- /dev/null
+++ b/chromium/mojo/services/public/interfaces/input_events/input_events.mojom
@@ -0,0 +1,27 @@
+// Copyright 2014 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.
+
+import "../geometry/geometry.mojom"
+
+module mojo {
+
+struct KeyData {
+ int32 key_code;
+ bool is_char;
+};
+
+struct TouchData {
+ int32 pointer_id;
+};
+
+struct Event {
+ int32 action;
+ int32 flags;
+ int64 time_stamp;
+ Point location;
+ KeyData key_data;
+ TouchData touch_data;
+};
+
+}
diff --git a/chromium/mojo/services/public/interfaces/launcher/launcher.mojom b/chromium/mojo/services/public/interfaces/launcher/launcher.mojom
new file mode 100644
index 00000000000..e35eb6746ac
--- /dev/null
+++ b/chromium/mojo/services/public/interfaces/launcher/launcher.mojom
@@ -0,0 +1,36 @@
+// Copyright 2014 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.
+
+import "../navigation/navigation.mojom"
+
+module mojo.launcher {
+
+[Client=LauncherClient]
+interface Launcher {
+ // Determines the correct handler application that can render a given URL.
+ //
+ // Sometimes this is known statically from the URL (e.g., from its scheme, or
+ // because we already have an application locally that has said it can handle
+ // the URL. Other times, we will actually fetch |url| to examine its
+ // content-type and/or sniff it to determine the correct handler.
+ Launch(string url);
+};
+
+[Client=Launcher]
+interface LauncherClient {
+ // Called when a handler has been resolved in response to Launch(). Receiving
+ // application should connect to the application at |handler_url| and navigate
+ // it to |view_url|.
+ //
+ // Note: |view_url| might not be the same as the URL that was initially
+ // requested, e.g., in the case of redirects. Applications should always
+ // navigate to view_url, not the initial URL.
+ //
+ // Note: |response| can be NULL in the case where a request was not needed to
+ // determine the correct handler.
+ OnLaunch(string handler_url, string view_url,
+ mojo.navigation.ResponseDetails response);
+};
+
+}
diff --git a/chromium/mojo/services/public/interfaces/native_viewport/native_viewport.mojom b/chromium/mojo/services/public/interfaces/native_viewport/native_viewport.mojom
new file mode 100644
index 00000000000..e1ce173c08f
--- /dev/null
+++ b/chromium/mojo/services/public/interfaces/native_viewport/native_viewport.mojom
@@ -0,0 +1,28 @@
+// Copyright 2014 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.
+
+import "../../../gles2/command_buffer.mojom"
+import "../geometry/geometry.mojom"
+import "../input_events/input_events.mojom"
+
+module mojo {
+
+[Client=NativeViewportClient]
+interface NativeViewport {
+ Create(Rect bounds);
+ Show();
+ Hide();
+ Close();
+ SetBounds(Rect bounds);
+ CreateGLES2Context(CommandBuffer& gles2_client);
+};
+
+interface NativeViewportClient {
+ OnCreated();
+ OnBoundsChanged(Rect bounds);
+ OnDestroyed();
+ OnEvent(Event event) => ();
+};
+
+}
diff --git a/chromium/mojo/services/public/interfaces/navigation/navigation.mojom b/chromium/mojo/services/public/interfaces/navigation/navigation.mojom
new file mode 100644
index 00000000000..fbe082f06e4
--- /dev/null
+++ b/chromium/mojo/services/public/interfaces/navigation/navigation.mojom
@@ -0,0 +1,36 @@
+// Copyright 2014 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.
+
+import "../network/url_loader.mojom"
+
+module mojo.navigation {
+
+struct NavigationDetails {
+ string url;
+ // TODO(aa): method, data, etc.
+};
+
+struct ResponseDetails {
+ // TODO(beng): consider providing access to URLRequest too. Currently it is
+ // not possible to obtain from the URLLoader.
+ mojo.URLResponse response;
+ handle<data_pipe_consumer> response_body_stream;
+};
+
+// Embedders that support navigation of implement this interface.
+interface NavigatorHost {
+ RequestNavigate(uint32 source_node_id, NavigationDetails details);
+};
+
+// Applications implement this interface to support navigation of their views
+// by embedders.
+// |response_details| can be NULL when a navigation was not the result of a
+// network load.
+interface Navigator {
+ Navigate(uint32 node_id,
+ NavigationDetails navigation_details,
+ ResponseDetails response_details);
+};
+
+}
diff --git a/chromium/mojo/services/public/interfaces/network/network_error.mojom b/chromium/mojo/services/public/interfaces/network/network_error.mojom
new file mode 100644
index 00000000000..d345e935504
--- /dev/null
+++ b/chromium/mojo/services/public/interfaces/network/network_error.mojom
@@ -0,0 +1,12 @@
+// Copyright 2014 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.
+
+module mojo {
+
+struct NetworkError {
+ int32 code;
+ string description;
+};
+
+}
diff --git a/chromium/mojo/services/public/interfaces/network/network_service.mojom b/chromium/mojo/services/public/interfaces/network/network_service.mojom
new file mode 100644
index 00000000000..7d9cd178cf6
--- /dev/null
+++ b/chromium/mojo/services/public/interfaces/network/network_service.mojom
@@ -0,0 +1,15 @@
+// Copyright 2014 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.
+
+import "url_loader.mojom"
+
+module mojo {
+
+interface NetworkService {
+ CreateURLLoader(URLLoader& loader);
+
+ // TODO(darin): Add other methods here (e.g., cookies).
+};
+
+}
diff --git a/chromium/mojo/services/public/interfaces/network/url_loader.mojom b/chromium/mojo/services/public/interfaces/network/url_loader.mojom
new file mode 100644
index 00000000000..b4bcf09f89d
--- /dev/null
+++ b/chromium/mojo/services/public/interfaces/network/url_loader.mojom
@@ -0,0 +1,94 @@
+// Copyright 2014 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.
+
+import "network_error.mojom"
+
+module mojo {
+
+struct URLRequest {
+ // The URL to load.
+ string url;
+
+ // The HTTP method if applicable.
+ string method = "GET";
+
+ // Additional HTTP request headers.
+ string[] headers;
+
+ // The payload for the request body. For HTTP requests, the method must be
+ // set to "POST" or "PUT".
+ handle<data_pipe_consumer> body;
+
+ // The number of bytes to be read from |body|. A Content-Length header of
+ // this value will be sent. Set to -1 if length is unknown, which will cause
+ // |body| to be uploaded using a chunked encoding.
+ int64 body_length = 0;
+
+ // If set to true, then redirects will be automatically followed. Otherwise,
+ // when a redirect is encounterd, FollowRedirect must be called to proceed.
+ bool auto_follow_redirects = false;
+
+ // If set to true, then the HTTP request will bypass the local cache and will
+ // have a 'Cache-Control: nocache' header added in that causes any proxy
+ // servers to also not satisfy the request from their cache. This has the
+ // effect of forcing a full end-to-end fetch.
+ bool bypass_cache = false;
+};
+
+struct URLResponse {
+ // The final URL of the response, after redirects have been followed.
+ string url;
+
+ // The HTTP status code. 0 if not applicable.
+ uint32 status_code;
+
+ // The HTTP status line.
+ string status_line;
+
+ // The HTTP response headers.
+ string[] headers;
+};
+
+[Client=URLLoaderClient]
+interface URLLoader {
+ // Start loading the given |request|. When available, the response body will
+ // be copied to |response_body_stream|.
+ //
+ // The client's |OnReceivedResponse| method will run when response meta data
+ // becomes available, or if a redirect response is encountered and
+ // |auto_follow_redirects| is false, the client's |OnRecievedRedirect| method
+ // will called instead.
+ //
+ // NOTE: You may observe data being pushed to |response_body_stream| before
+ // you receive |OnReceivedResponse|.
+ Start(URLRequest request, handle<data_pipe_producer> response_body_stream);
+
+ // If the request passed to |Start| had |auto_follow_redirects| set to false,
+ // then upon receiving a redirect, |OnReceivedRedirect| will be called. To
+ // follow the indicated redirect, call the |FollowRedirect| method.
+ FollowRedirect();
+};
+
+interface URLLoaderClient {
+ // This method is called when a redirect is encountered, provided the
+ // request's |auto_follow_redirects| attribute was set to false.
+ OnReceivedRedirect(URLResponse response, string new_url, string new_method);
+
+ // This method is called when response meta data becomes available.
+ OnReceivedResponse(URLResponse response);
+
+ // This method is called when a network level error is encountered. This can
+ // happen before or after OnReceivedResponse, but cannot happen after
+ // OnReceivedEndOfResponseBody.
+ OnReceivedError(NetworkError error);
+
+ // This method is called when the response body has been successfully
+ // downloaded and copied in its entirety to |response_body_stream|.
+ //
+ // NOTE: Because |response_body_stream| is limited in size, this event may be
+ // delayed until |response_body_stream| is consumed.
+ OnReceivedEndOfResponseBody();
+};
+
+}
diff --git a/chromium/mojo/services/public/interfaces/surfaces/quads.mojom b/chromium/mojo/services/public/interfaces/surfaces/quads.mojom
new file mode 100644
index 00000000000..eb8898b0f36
--- /dev/null
+++ b/chromium/mojo/services/public/interfaces/surfaces/quads.mojom
@@ -0,0 +1,165 @@
+// Copyright 2014 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.
+
+import "../geometry/geometry.mojom"
+import "surface_id.mojom"
+
+module mojo.surfaces {
+
+struct Color {
+ uint32 rgba;
+};
+
+// TODO(jamesr): Populate subtype fields.
+struct CheckerboardQuadState {};
+
+struct DebugBorderQuadState {};
+
+struct IoSurfaceContentQuadState {};
+
+struct RenderPassQuadState {};
+
+struct SolidColorQuadState {
+ Color color;
+};
+
+struct SurfaceQuadState {
+ SurfaceId surface;
+};
+
+struct TextureQuadState {
+ uint32 resource_id;
+ bool premultiplied_alpha;
+ mojo.PointF uv_top_left;
+ mojo.PointF uv_bottom_right;
+ Color background_color;
+ // Should have exactly 4 entries.
+ float[] vertex_opacity;
+ bool flipped;
+};
+
+struct TiledContentQuadState {};
+
+struct StreamVideoQuadState {};
+
+struct YUVVideoQuadState {};
+
+enum Material {
+ CHECKERBOARD = 1,
+ DEBUG_BORDER,
+ IO_SURFACE_CONTENT,
+ RENDER_PASS,
+ SOLID_COLOR,
+ STREAM_VIDEO_CONTENT,
+ SURFACE_CONTENT,
+ TEXTURE_CONTENT,
+ TILED_CONTENT,
+ YUV_VIDEO_CONTENT,
+};
+
+struct Quad {
+ Material material;
+
+ // This rect, after applying the quad_transform(), gives the geometry that
+ // this quad should draw to. This rect lives in content space.
+ mojo.Rect rect;
+
+ // This specifies the region of the quad that is opaque. This rect lives in
+ // content space.
+ mojo.Rect opaque_rect;
+
+ // Allows changing the rect that gets drawn to make it smaller. This value
+ // should be clipped to |rect|. This rect lives in content space.
+ mojo.Rect visible_rect;
+
+ // Allows changing the rect that gets drawn to make it smaller. This value
+ // should be clipped to |rect|. This rect lives in content space.
+ bool needs_blending;
+
+ // Index into the containing pass' shared quad state array which has state
+ // (transforms etc) shared by multiple quads.
+ int32 shared_quad_state_index;
+
+ // Only one of the following will be set, depending on the material.
+ CheckerboardQuadState checkerboard_quad_state;
+ DebugBorderQuadState debug_border_quad_state;
+ IoSurfaceContentQuadState io_surface_quad_state;
+ RenderPassQuadState render_pass_quad_state;
+ SolidColorQuadState solid_color_quad_state;
+ SurfaceQuadState surface_quad_state;
+ TextureQuadState texture_quad_state;
+ TiledContentQuadState tiled_content_quad_state;
+ StreamVideoQuadState stream_video_quad_state;
+ YUVVideoQuadState yuv_video_quad_state;
+};
+
+enum SkXfermode {
+ kClear_Mode = 0, //!< [0, 0]
+ kSrc_Mode, //!< [Sa, Sc]
+ kDst_Mode, //!< [Da, Dc]
+ kSrcOver_Mode, //!< [Sa + Da - Sa*Da, Rc = Sc + (1 - Sa)*Dc]
+ kDstOver_Mode, //!< [Sa + Da - Sa*Da, Rc = Dc + (1 - Da)*Sc]
+ kSrcIn_Mode, //!< [Sa * Da, Sc * Da]
+ kDstIn_Mode, //!< [Sa * Da, Sa * Dc]
+ kSrcOut_Mode, //!< [Sa * (1 - Da), Sc * (1 - Da)]
+ kDstOut_Mode, //!< [Da * (1 - Sa), Dc * (1 - Sa)]
+ kSrcATop_Mode, //!< [Da, Sc * Da + (1 - Sa) * Dc]
+ kDstATop_Mode, //!< [Sa, Sa * Dc + Sc * (1 - Da)]
+ kXor_Mode, //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
+ kPlus_Mode, //!< [Sa + Da, Sc + Dc]
+ kModulate_Mode, // multiplies all components (= alpha and color)
+
+ // Following blend modes are defined in the CSS Compositing standard:
+ // https://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html#blending
+ kScreen_Mode,
+ kLastCoeffMode = kScreen_Mode,
+
+ kOverlay_Mode,
+ kDarken_Mode,
+ kLighten_Mode,
+ kColorDodge_Mode,
+ kColorBurn_Mode,
+ kHardLight_Mode,
+ kSoftLight_Mode,
+ kDifference_Mode,
+ kExclusion_Mode,
+ kMultiply_Mode,
+ kLastSeparableMode = kMultiply_Mode,
+
+ kHue_Mode,
+ kSaturation_Mode,
+ kColor_Mode,
+ kLuminosity_Mode,
+ kLastMode = kLuminosity_Mode
+};
+
+struct SharedQuadState {
+ // Transforms from quad's original content space to its target content space.
+ mojo.Transform content_to_target_transform;
+
+ // This size lives in the content space for the quad's originating layer.
+ mojo.Size content_bounds;
+
+ // This rect lives in the content space for the quad's originating layer.
+ mojo.Rect visible_content_rect;
+
+ // This rect lives in the target content space.
+ mojo.Rect clip_rect;
+
+ bool is_clipped;
+ float opacity;
+ SkXfermode blend_mode;
+};
+
+struct Pass {
+ int32 id;
+ mojo.Rect output_rect;
+ mojo.Rect damage_rect;
+ mojo.Transform transform_to_root_target;
+ bool has_transparent_pixels;
+ Quad[] quads;
+ SharedQuadState[] shared_quad_states;
+};
+
+}
diff --git a/chromium/mojo/services/public/interfaces/surfaces/surface_id.mojom b/chromium/mojo/services/public/interfaces/surfaces/surface_id.mojom
new file mode 100644
index 00000000000..42ab9924471
--- /dev/null
+++ b/chromium/mojo/services/public/interfaces/surfaces/surface_id.mojom
@@ -0,0 +1,11 @@
+// Copyright 2014 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.
+
+module mojo.surfaces {
+
+struct SurfaceId {
+ int32 id;
+};
+
+}
diff --git a/chromium/mojo/services/public/interfaces/surfaces/surfaces.mojom b/chromium/mojo/services/public/interfaces/surfaces/surfaces.mojom
new file mode 100644
index 00000000000..6bb38b7e0b3
--- /dev/null
+++ b/chromium/mojo/services/public/interfaces/surfaces/surfaces.mojom
@@ -0,0 +1,57 @@
+// Copyright 2014 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.
+
+import "../geometry/geometry.mojom"
+import "quads.mojom"
+import "surface_id.mojom"
+
+module mojo.surfaces {
+
+enum ResourceFormat {
+ RGBA_8888,
+ RGBA_4444,
+ BGRA_8888,
+ LUMINANCE_8,
+ RGB_565,
+ ETC1,
+};
+
+struct Mailbox {
+ // Should have exactly 64 entries.
+ int8[] name;
+};
+
+struct MailboxHolder {
+ Mailbox mailbox;
+ uint32 texture_target;
+ uint32 sync_point;
+};
+
+struct TransferableResource {
+ uint32 id;
+ ResourceFormat format;
+ uint32 filter;
+ mojo.Size size;
+ MailboxHolder mailbox_holder;
+ bool is_repeated;
+ bool is_software;
+};
+
+struct Frame {
+ TransferableResource[] resources;
+ Pass[] passes;
+};
+
+interface SurfaceClient {
+ ReturnResources(TransferableResource[] resources);
+};
+
+[client=SurfaceClient]
+interface Surface {
+ CreateSurface(SurfaceId id, mojo.Size size);
+ SubmitFrame(SurfaceId id, Frame frame);
+ DestroySurface(SurfaceId id);
+};
+
+}
diff --git a/chromium/mojo/services/public/interfaces/view_manager/view_manager.mojom b/chromium/mojo/services/public/interfaces/view_manager/view_manager.mojom
new file mode 100644
index 00000000000..ea1d3b56265
--- /dev/null
+++ b/chromium/mojo/services/public/interfaces/view_manager/view_manager.mojom
@@ -0,0 +1,193 @@
+// Copyright 2014 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.
+
+import "../geometry/geometry.mojom"
+import "../input_events/input_events.mojom"
+import "view_manager_constants.mojom"
+
+module mojo.view_manager {
+
+struct NodeData {
+ uint32 parent_id;
+ uint32 node_id;
+ uint32 view_id;
+ mojo.Rect bounds;
+};
+
+// ViewManagerInitService is responsible for launching the client that controls
+// the root node. mojo::view_manager returns an instance of this. All other
+// connections are established by the client this creates.
+interface ViewManagerInitService {
+ EmbedRoot(string url) => (bool success);
+};
+
+// Functions that mutate the hierarchy take a change id. This is an ever
+// increasing integer used to identify the change. Every hierarchy change
+// increases this value. The server only accepts changes where the supplied
+// |server_change_id| matches the expected next value. This ensures changes are
+// made in a well defined order.
+//
+// Nodes and Views are identified by a uint32. The upper 16 bits are the
+// connection id, and the lower 16 the id assigned by the client.
+//
+// The root node is identified with a connection id of 0, and value of 1.
+[Client=ViewManagerClient]
+interface ViewManagerService {
+ // Creates a new node with the specified id. It is up to the client to ensure
+ // the id is unique to the connection (the id need not be globally unique).
+ // Additionally the connection id (embedded in |node_id|) must match that of
+ // the connection.
+ CreateNode(uint32 node_id) => (bool success);
+
+ // Deletes a node. This does not recurse. No hierarchy change notifications
+ // are sent as a result of this. Only the connection that created the node can
+ // delete it.
+ DeleteNode(uint32 node_id, uint32 change_id) => (bool success);
+
+ // Sets the specified bounds of the specified node.
+ SetNodeBounds(uint32 node_id, mojo.Rect bounds) => (bool success);
+
+ // Reparents a node. See description above class for details of |change_id|.
+ // This fails for any of the following reasons:
+ // . |server_change_id| is not the expected id.
+ // . |parent| or |child| does not identify a valid node.
+ // . |child| is an ancestor of |parent|.
+ // . |child| is already a child of |parent|.
+ //
+ // This may result in a connection getting OnNodeDeleted(). See
+ // RemoveNodeFromParent for details.
+ AddNode(uint32 parent,
+ uint32 child,
+ uint32 server_change_id) => (bool success);
+
+ // Removes a view from its current parent. See description above class for
+ // details of |change_id|. This fails if the node is not valid,
+ // |server_change_id| doesn't match, or the node already has no parent.
+ //
+ // Removing a node from a parent may result in OnNodeDeleted() being sent to
+ // other connections. For example, connection A has nodes 1 and 2, with 2 a
+ // child of 1. Connection B has a root 1. If 2 is removed from 1 then B gets
+ // OnNodeDeleted(). This is done as node 2 is effectively no longer visible to
+ // connection B.
+ RemoveNodeFromParent(uint32 node_id,
+ uint32 server_change_id) => (bool success);
+
+ // Reorders a node in its parent, relative to |relative_node_id| according to
+ // |direction|.
+ // Only the connection that created the node's parent can reorder its
+ // children.
+ ReorderNode(uint32 node_id,
+ uint32 relative_node_id,
+ OrderDirection direction,
+ uint32 server_change_id) => (bool success);
+
+ // Returns the nodes comprising the tree starting at |node_id|. |node_id| is
+ // the first result in the return value, unless |node_id| is invalid, in which
+ // case an empty vector is returned. The nodes are visited using a depth first
+ // search (pre-order).
+ GetNodeTree(uint32 node_id) => (NodeData[] nodes);
+
+ // Creates a new view with the specified id. It is up to the client to ensure
+ // the id is unique to the connection (the id need not be globally unique).
+ // Additionally the connection id (embedded in |view_id|) must match that of
+ // the connection.
+ CreateView(uint32 view_id) => (bool success);
+
+ // Deletes the view with the specified id. Only the connection that created
+ // the view can delete it.
+ DeleteView(uint32 view_id) => (bool success);
+
+ // Sets the view a node is showing.
+ SetView(uint32 node_id, uint32 view_id) => (bool success);
+
+ // Shows the specified image (png encoded) in the specified view.
+ SetViewContents(uint32 view_id,
+ handle<shared_buffer> buffer,
+ uint32 buffer_size) => (bool success);
+
+ // Sets focus to the specified node.
+ SetFocus(uint32 node_id) => (bool success);
+
+ // Embeds the app at |url| in the specified nodes. More specifically this
+ // creates a new connection to the specified url, expecting to get an
+ // ViewManagerClient and configures it with the root nodes |nodes|. Fails
+ // if |nodes| is empty or contains nodes that were not created by this
+ // connection.
+ // If a particular client invokes Embed() multiple times with the same url,
+ // the connection is reused. When this happens the ViewManagerClient is
+ // notified of the additional roots by way of OnRootsAdded().
+ Embed(string url, uint32[] nodes) => (bool success);
+
+ // TODO(sky): move these to a separate interface when FIFO works.
+
+ // Sends OnViewInputEvent() to the owner of the specified view.
+ DispatchOnViewInputEvent(uint32 view_id, mojo.Event event);
+};
+
+// Changes to nodes/views are not sent to the connection that originated the
+// change. For example, if connection 1 attaches a view to a node (SetView())
+// connection 1 does not receive OnNodeViewReplaced().
+[Client=ViewManagerService]
+interface ViewManagerClient {
+ // Invoked once the connection has been established. |connection_id| is the id
+ // that uniquely identifies this connection. |next_server_change_id| is the
+ // id of the next change the server is expecting. |nodes| are the nodes
+ // parented to the root.
+ OnViewManagerConnectionEstablished(uint16 connection_id,
+ string creator_url,
+ uint32 next_server_change_id,
+ NodeData[] nodes);
+
+ // See description of ViewManagerService::Connect() for details as to when
+ // this is invoked.
+ OnRootsAdded(NodeData[] nodes);
+
+ // This is sent to clients when a change is made to the server that results
+ // in the |server_change_id| changing but the client isn't notified. This is
+ // not sent if the client receives a callback giving a new
+ // |server_change_id|. For example, if a client 1 changes the hierarchy in
+ // some way but client 2 isn't notified of the change, then client 2 gets
+ // OnServerChangeIdAdvanced().
+ OnServerChangeIdAdvanced(uint32 next_server_change_id);
+
+ // Invoked when a node's bounds have changed.
+ OnNodeBoundsChanged(uint32 node, mojo.Rect old_bounds, mojo.Rect new_bounds);
+
+ // Invoked when a change is done to the hierarchy. A value of 0 is used to
+ // identify a null node. For example, if the old_parent is NULL, 0 is
+ // supplied. See description above ViewManager for details on the change ids.
+ // |nodes| contains any nodes that are that the client has not been told
+ // about. This is not sent for hierarchy changes of nodes not known to this
+ // client or not attached to the tree.
+ OnNodeHierarchyChanged(uint32 node,
+ uint32 new_parent,
+ uint32 old_parent,
+ uint32 server_change_id,
+ NodeData[] nodes);
+
+ // Invoked when the order of nodes within a parent changes.
+ OnNodeReordered(uint32 node_id,
+ uint32 relative_node_id,
+ OrderDirection direction,
+ uint32 server_change_id);
+
+ // Invoked when a node is deleted.
+ OnNodeDeleted(uint32 node, uint32 server_change_id);
+
+ // Invoked when the view associated with a node is replaced by another view.
+ // 0 is used to identify a null view.
+ OnNodeViewReplaced(uint32 node, uint32 new_view_id, uint32 old_view_id);
+
+ // Invoked when a view is deleted.
+ OnViewDeleted(uint32 view);
+
+ // Invoked when an event is targeted at the specified view.
+ OnViewInputEvent(uint32 view, mojo.Event event) => ();
+
+ // TODO(sky): move to separate interface when FIFO sorted out.
+
+ DispatchOnViewInputEvent(uint32 view, mojo.Event event);
+};
+
+}
diff --git a/chromium/mojo/services/public/interfaces/view_manager/view_manager_constants.mojom b/chromium/mojo/services/public/interfaces/view_manager/view_manager_constants.mojom
new file mode 100644
index 00000000000..5c73190b70f
--- /dev/null
+++ b/chromium/mojo/services/public/interfaces/view_manager/view_manager_constants.mojom
@@ -0,0 +1,12 @@
+// Copyright 2014 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.
+
+module mojo.view_manager {
+
+enum OrderDirection {
+ ORDER_ABOVE = 1,
+ ORDER_BELOW,
+};
+
+}
diff --git a/chromium/mojo/services/test_service/test_service.mojom b/chromium/mojo/services/test_service/test_service.mojom
new file mode 100644
index 00000000000..1065b20ab30
--- /dev/null
+++ b/chromium/mojo/services/test_service/test_service.mojom
@@ -0,0 +1,11 @@
+// Copyright 2014 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.
+
+module mojo.test {
+
+interface ITestService {
+ Ping() => ();
+};
+
+} // module mojo.test
diff --git a/chromium/mojo/services/test_service/test_service_application.cc b/chromium/mojo/services/test_service/test_service_application.cc
new file mode 100644
index 00000000000..3737823c9d2
--- /dev/null
+++ b/chromium/mojo/services/test_service/test_service_application.cc
@@ -0,0 +1,44 @@
+// Copyright 2014 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 "mojo/services/test_service/test_service_application.h"
+
+#include <assert.h>
+
+#include "mojo/public/cpp/utility/run_loop.h"
+#include "mojo/services/test_service/test_service_impl.h"
+
+namespace mojo {
+namespace test {
+
+TestServiceApplication::TestServiceApplication() : ref_count_(0) {
+}
+
+TestServiceApplication::~TestServiceApplication() {
+}
+
+void TestServiceApplication::Initialize() {
+ AddService<TestServiceImpl>(this);
+}
+
+void TestServiceApplication::AddRef() {
+ assert(ref_count_ >= 0);
+ ref_count_++;
+}
+
+void TestServiceApplication::ReleaseRef() {
+ assert(ref_count_ > 0);
+ ref_count_--;
+ if (ref_count_ <= 0)
+ RunLoop::current()->Quit();
+}
+
+} // namespace test
+
+// static
+Application* Application::Create() {
+ return new mojo::test::TestServiceApplication();
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/services/test_service/test_service_application.h b/chromium/mojo/services/test_service/test_service_application.h
new file mode 100644
index 00000000000..9f042c1987a
--- /dev/null
+++ b/chromium/mojo/services/test_service/test_service_application.h
@@ -0,0 +1,33 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_TEST_SERVICE_TEST_SERVICE_APPLICATION_H_
+#define MOJO_SERVICES_TEST_SERVICE_TEST_SERVICE_APPLICATION_H_
+
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/public/cpp/system/macros.h"
+
+namespace mojo {
+namespace test {
+
+class TestServiceApplication : public Application {
+ public:
+ TestServiceApplication();
+ virtual ~TestServiceApplication();
+
+ virtual void Initialize() MOJO_OVERRIDE;
+
+ void AddRef();
+ void ReleaseRef();
+
+ private:
+ int ref_count_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(TestServiceApplication);
+};
+
+} // namespace test
+} // namespace mojo
+
+#endif // MOJO_SERVICES_TEST_SERVICE_TEST_SERVICE_APPLICATION_H_
diff --git a/chromium/mojo/services/test_service/test_service_impl.cc b/chromium/mojo/services/test_service/test_service_impl.cc
new file mode 100644
index 00000000000..a38d619d90e
--- /dev/null
+++ b/chromium/mojo/services/test_service/test_service_impl.cc
@@ -0,0 +1,32 @@
+// Copyright 2014 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 "mojo/services/test_service/test_service_impl.h"
+
+#include "mojo/services/test_service/test_service_application.h"
+
+namespace mojo {
+namespace test {
+
+TestServiceImpl::TestServiceImpl(TestServiceApplication* application)
+ : application_(application) {
+}
+
+TestServiceImpl::~TestServiceImpl() {
+}
+
+void TestServiceImpl::OnConnectionEstablished() {
+ application_->AddRef();
+}
+
+void TestServiceImpl::OnConnectionError() {
+ application_->ReleaseRef();
+}
+
+void TestServiceImpl::Ping(const mojo::Callback<void()>& callback) {
+ callback.Run();
+}
+
+} // namespace test
+} // namespace mojo
diff --git a/chromium/mojo/services/test_service/test_service_impl.h b/chromium/mojo/services/test_service/test_service_impl.h
new file mode 100644
index 00000000000..df2d4a160be
--- /dev/null
+++ b/chromium/mojo/services/test_service/test_service_impl.h
@@ -0,0 +1,35 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_TEST_SERVICE_TEST_SERVICE_IMPL_H_
+#define MOJO_SERVICES_TEST_SERVICE_TEST_SERVICE_IMPL_H_
+
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/test_service/test_service.mojom.h"
+
+namespace mojo {
+namespace test {
+
+class TestServiceApplication;
+
+class TestServiceImpl : public InterfaceImpl<ITestService> {
+ public:
+ explicit TestServiceImpl(TestServiceApplication* application);
+ virtual ~TestServiceImpl();
+
+ // |ITestService| methods:
+ virtual void OnConnectionEstablished() MOJO_OVERRIDE;
+ virtual void OnConnectionError() MOJO_OVERRIDE;
+ virtual void Ping(const mojo::Callback<void()>& callback) MOJO_OVERRIDE;
+
+ private:
+ TestServiceApplication* const application_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(TestServiceImpl);
+};
+
+} // namespace test
+} // namespace mojo
+
+#endif // MOJO_SERVICES_TEST_SERVICE_TEST_SERVICE_IMPL_H_
diff --git a/chromium/mojo/services/view_manager/DEPS b/chromium/mojo/services/view_manager/DEPS
new file mode 100644
index 00000000000..e6f1501ffe1
--- /dev/null
+++ b/chromium/mojo/services/view_manager/DEPS
@@ -0,0 +1,22 @@
+include_rules = [
+ "+cc",
+ "+mojo/aura",
+ "+mojo/cc",
+ "+mojo/geometry",
+ "+mojo/services",
+ "+third_party/skia",
+ "+ui/aura",
+ "+ui/base/cursor/cursor.h",
+ "+ui/base/hit_test.h",
+ "+ui/compositor",
+ "+ui/events",
+ "+ui/gfx",
+ "+ui/gl",
+ "+webkit/common/gpu",
+]
+
+specific_include_rules = {
+ "view_manager_unittest.cc": [
+ "+mojo/service_manager/service_manager.h",
+ ],
+}
diff --git a/chromium/mojo/services/view_manager/context_factory_impl.cc b/chromium/mojo/services/view_manager/context_factory_impl.cc
new file mode 100644
index 00000000000..c3977e7d2e4
--- /dev/null
+++ b/chromium/mojo/services/view_manager/context_factory_impl.cc
@@ -0,0 +1,75 @@
+// Copyright 2014 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 "mojo/services/view_manager/context_factory_impl.h"
+
+#include "cc/output/output_surface.h"
+#include "mojo/cc/context_provider_mojo.h"
+#include "ui/compositor/reflector.h"
+#include "ui/gl/gl_implementation.h"
+#include "ui/gl/gl_surface.h"
+#include "webkit/common/gpu/context_provider_in_process.h"
+#include "webkit/common/gpu/grcontext_for_webgraphicscontext3d.h"
+#include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h"
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+ContextFactoryImpl::ContextFactoryImpl(
+ ScopedMessagePipeHandle command_buffer_handle)
+ : command_buffer_handle_(command_buffer_handle.Pass()) {
+}
+
+ContextFactoryImpl::~ContextFactoryImpl() {
+}
+
+scoped_ptr<cc::OutputSurface> ContextFactoryImpl::CreateOutputSurface(
+ ui::Compositor* compositor, bool software_fallback) {
+ return make_scoped_ptr(
+ new cc::OutputSurface(
+ new ContextProviderMojo(command_buffer_handle_.Pass())));
+}
+
+scoped_refptr<ui::Reflector> ContextFactoryImpl::CreateReflector(
+ ui::Compositor* mirroed_compositor,
+ ui::Layer* mirroring_layer) {
+ return NULL;
+}
+
+void ContextFactoryImpl::RemoveReflector(
+ scoped_refptr<ui::Reflector> reflector) {
+}
+
+scoped_refptr<cc::ContextProvider>
+ContextFactoryImpl::SharedMainThreadContextProvider() {
+ if (!shared_main_thread_contexts_ ||
+ shared_main_thread_contexts_->DestroyedOnMainThread()) {
+ bool lose_context_when_out_of_memory = false;
+ shared_main_thread_contexts_ =
+ webkit::gpu::ContextProviderInProcess::CreateOffscreen(
+ lose_context_when_out_of_memory);
+ if (shared_main_thread_contexts_ &&
+ !shared_main_thread_contexts_->BindToCurrentThread())
+ shared_main_thread_contexts_ = NULL;
+ }
+ return shared_main_thread_contexts_;
+}
+
+void ContextFactoryImpl::RemoveCompositor(ui::Compositor* compositor) {
+}
+
+bool ContextFactoryImpl::DoesCreateTestContexts() { return false; }
+
+cc::SharedBitmapManager* ContextFactoryImpl::GetSharedBitmapManager() {
+ return NULL;
+}
+
+base::MessageLoopProxy* ContextFactoryImpl::GetCompositorMessageLoop() {
+ return NULL;
+}
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/view_manager/context_factory_impl.h b/chromium/mojo/services/view_manager/context_factory_impl.h
new file mode 100644
index 00000000000..30cd26754b8
--- /dev/null
+++ b/chromium/mojo/services/view_manager/context_factory_impl.h
@@ -0,0 +1,56 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_CONTEXT_FACTORY_IMPL_H_
+#define MOJO_SERVICES_VIEW_MANAGER_CONTEXT_FACTORY_IMPL_H_
+
+#include "mojo/public/cpp/system/core.h"
+#include "ui/compositor/compositor.h"
+
+namespace webkit {
+namespace gpu {
+class ContextProviderInProcess;
+}
+}
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+// The default factory that creates in-process contexts.
+class ContextFactoryImpl : public ui::ContextFactory {
+ public:
+ explicit ContextFactoryImpl(ScopedMessagePipeHandle command_buffer_handle);
+ virtual ~ContextFactoryImpl();
+
+ // ContextFactory implementation
+ virtual scoped_ptr<cc::OutputSurface> CreateOutputSurface(
+ ui::Compositor* compositor, bool software_fallback) OVERRIDE;
+
+ virtual scoped_refptr<ui::Reflector> CreateReflector(
+ ui::Compositor* compositor,
+ ui::Layer* layer) OVERRIDE;
+ virtual void RemoveReflector(scoped_refptr<ui::Reflector> reflector) OVERRIDE;
+
+ virtual scoped_refptr<cc::ContextProvider>
+ SharedMainThreadContextProvider() OVERRIDE;
+ virtual void RemoveCompositor(ui::Compositor* compositor) OVERRIDE;
+ virtual bool DoesCreateTestContexts() OVERRIDE;
+ virtual cc::SharedBitmapManager* GetSharedBitmapManager() OVERRIDE;
+ virtual base::MessageLoopProxy* GetCompositorMessageLoop() OVERRIDE;
+
+ private:
+ scoped_refptr<webkit::gpu::ContextProviderInProcess>
+ shared_main_thread_contexts_;
+
+ ScopedMessagePipeHandle command_buffer_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContextFactoryImpl);
+};
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_CONTEXT_FACTORY_IMPL_H_
diff --git a/chromium/mojo/services/view_manager/ids.h b/chromium/mojo/services/view_manager/ids.h
new file mode 100644
index 00000000000..8d906beecb6
--- /dev/null
+++ b/chromium/mojo/services/view_manager/ids.h
@@ -0,0 +1,94 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_IDS_H_
+#define MOJO_SERVICES_VIEW_MANAGER_IDS_H_
+
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/cpp/view_manager/util.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+// Connection id reserved for the root.
+const ConnectionSpecificId kRootConnection = 0;
+
+// TODO(sky): remove this, temporary while window manager API is in existing
+// api.
+const ConnectionSpecificId kWindowManagerConnection = 1;
+
+// Adds a bit of type safety to node ids.
+struct MOJO_VIEW_MANAGER_EXPORT NodeId {
+ NodeId(ConnectionSpecificId connection_id, ConnectionSpecificId node_id)
+ : connection_id(connection_id),
+ node_id(node_id) {}
+ NodeId() : connection_id(0), node_id(0) {}
+
+ bool operator==(const NodeId& other) const {
+ return other.connection_id == connection_id &&
+ other.node_id == node_id;
+ }
+
+ bool operator!=(const NodeId& other) const {
+ return !(*this == other);
+ }
+
+ ConnectionSpecificId connection_id;
+ ConnectionSpecificId node_id;
+};
+
+// Adds a bit of type safety to view ids.
+struct MOJO_VIEW_MANAGER_EXPORT ViewId {
+ ViewId(ConnectionSpecificId connection_id, ConnectionSpecificId view_id)
+ : connection_id(connection_id),
+ view_id(view_id) {}
+ ViewId() : connection_id(0), view_id(0) {}
+
+ bool operator==(const ViewId& other) const {
+ return other.connection_id == connection_id &&
+ other.view_id == view_id;
+ }
+
+ bool operator!=(const ViewId& other) const {
+ return !(*this == other);
+ }
+
+ ConnectionSpecificId connection_id;
+ ConnectionSpecificId view_id;
+};
+
+// Functions for converting to/from structs and transport values.
+inline NodeId NodeIdFromTransportId(Id id) {
+ return NodeId(HiWord(id), LoWord(id));
+}
+
+inline Id NodeIdToTransportId(const NodeId& id) {
+ return (id.connection_id << 16) | id.node_id;
+}
+
+inline ViewId ViewIdFromTransportId(Id id) {
+ return ViewId(HiWord(id), LoWord(id));
+}
+
+inline Id ViewIdToTransportId(const ViewId& id) {
+ return (id.connection_id << 16) | id.view_id;
+}
+
+inline NodeId RootNodeId() {
+ return NodeId(kRootConnection, 1);
+}
+
+// Returns a NodeId that is reserved to indicate no node. That is, no node will
+// ever be created with this id.
+inline NodeId InvalidNodeId() {
+ return NodeId(kRootConnection, 0);
+}
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_IDS_H_
diff --git a/chromium/mojo/services/view_manager/main.cc b/chromium/mojo/services/view_manager/main.cc
new file mode 100644
index 00000000000..de0a7684095
--- /dev/null
+++ b/chromium/mojo/services/view_manager/main.cc
@@ -0,0 +1,35 @@
+// Copyright 2014 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 "mojo/public/cpp/application/application.h"
+#include "mojo/services/view_manager/view_manager_init_service_impl.h"
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+class ViewManagerApp : public Application {
+ public:
+ ViewManagerApp() {}
+ virtual ~ViewManagerApp() {}
+
+ virtual void Initialize() MOJO_OVERRIDE {
+ // TODO(sky): this needs some sort of authentication as well as making sure
+ // we only ever have one active at a time.
+ AddService<ViewManagerInitServiceImpl>(service_provider());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerApp);
+};
+
+} // namespace service
+} // namespace view_manager
+
+// static
+Application* Application::Create() {
+ return new mojo::view_manager::service::ViewManagerApp();
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/services/view_manager/node.cc b/chromium/mojo/services/view_manager/node.cc
new file mode 100644
index 00000000000..cbdd16d712b
--- /dev/null
+++ b/chromium/mojo/services/view_manager/node.cc
@@ -0,0 +1,189 @@
+// Copyright 2014 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 "mojo/services/view_manager/node.h"
+
+#include "mojo/services/view_manager/node_delegate.h"
+#include "mojo/services/view_manager/view.h"
+#include "ui/aura/window_property.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/base/hit_test.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/native_widget_types.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(mojo::view_manager::service::Node*);
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+DEFINE_WINDOW_PROPERTY_KEY(Node*, kNodeKey, NULL);
+
+Node::Node(NodeDelegate* delegate, const NodeId& id)
+ : delegate_(delegate),
+ id_(id),
+ view_(NULL),
+ window_(this) {
+ DCHECK(delegate); // Must provide a delegate.
+ window_.set_owned_by_parent(false);
+ window_.AddObserver(this);
+ window_.SetProperty(kNodeKey, this);
+ window_.Init(aura::WINDOW_LAYER_TEXTURED);
+
+ // TODO(sky): this likely needs to be false and add a visibility API.
+ window_.Show();
+}
+
+Node::~Node() {
+ SetView(NULL);
+ // This is implicitly done during deletion of the window, but we do it here so
+ // that we're in a known state.
+ if (window_.parent())
+ window_.parent()->RemoveChild(&window_);
+}
+
+const Node* Node::GetParent() const {
+ if (!window_.parent())
+ return NULL;
+ return window_.parent()->GetProperty(kNodeKey);
+}
+
+void Node::Add(Node* child) {
+ window_.AddChild(&child->window_);
+}
+
+void Node::Remove(Node* child) {
+ window_.RemoveChild(&child->window_);
+}
+
+void Node::Reorder(Node* child, Node* relative, OrderDirection direction) {
+ if (direction == ORDER_ABOVE)
+ window_.StackChildAbove(child->window(), relative->window());
+ else if (direction == ORDER_BELOW)
+ window_.StackChildBelow(child->window(), relative->window());
+}
+
+const Node* Node::GetRoot() const {
+ const aura::Window* window = &window_;
+ while (window && window->parent())
+ window = window->parent();
+ return window->GetProperty(kNodeKey);
+}
+
+std::vector<const Node*> Node::GetChildren() const {
+ std::vector<const Node*> children;
+ children.reserve(window_.children().size());
+ for (size_t i = 0; i < window_.children().size(); ++i)
+ children.push_back(window_.children()[i]->GetProperty(kNodeKey));
+ return children;
+}
+
+std::vector<Node*> Node::GetChildren() {
+ std::vector<Node*> children;
+ children.reserve(window_.children().size());
+ for (size_t i = 0; i < window_.children().size(); ++i)
+ children.push_back(window_.children()[i]->GetProperty(kNodeKey));
+ return children;
+}
+
+bool Node::Contains(const Node* node) const {
+ return node && window_.Contains(&(node->window_));
+}
+
+void Node::SetView(View* view) {
+ if (view == view_)
+ return;
+
+ // Detach view from existing node. This way notifications are sent out.
+ if (view && view->node())
+ view->node()->SetView(NULL);
+
+ View* old_view = view_;
+ if (view_)
+ view_->set_node(NULL);
+ view_ = view;
+ if (view)
+ view->set_node(this);
+ delegate_->OnNodeViewReplaced(this, view, old_view);
+}
+
+void Node::OnWindowHierarchyChanged(
+ const aura::WindowObserver::HierarchyChangeParams& params) {
+ if (params.target != &window_ || params.receiver != &window_)
+ return;
+ const Node* new_parent = params.new_parent ?
+ params.new_parent->GetProperty(kNodeKey) : NULL;
+ const Node* old_parent = params.old_parent ?
+ params.old_parent->GetProperty(kNodeKey) : NULL;
+ delegate_->OnNodeHierarchyChanged(this, new_parent, old_parent);
+}
+
+gfx::Size Node::GetMinimumSize() const {
+ return gfx::Size();
+}
+
+gfx::Size Node::GetMaximumSize() const {
+ return gfx::Size();
+}
+
+void Node::OnBoundsChanged(const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+}
+
+gfx::NativeCursor Node::GetCursor(const gfx::Point& point) {
+ return gfx::kNullCursor;
+}
+
+int Node::GetNonClientComponent(const gfx::Point& point) const {
+ return HTCAPTION;
+}
+
+bool Node::ShouldDescendIntoChildForEventHandling(
+ aura::Window* child,
+ const gfx::Point& location) {
+ return true;
+}
+
+bool Node::CanFocus() {
+ return true;
+}
+
+void Node::OnCaptureLost() {
+}
+
+void Node::OnPaint(gfx::Canvas* canvas) {
+ if (view_) {
+ canvas->DrawImageInt(
+ gfx::ImageSkia::CreateFrom1xBitmap(view_->bitmap()), 0, 0);
+ }
+}
+
+void Node::OnDeviceScaleFactorChanged(float device_scale_factor) {
+}
+
+void Node::OnWindowDestroying(aura::Window* window) {
+}
+
+void Node::OnWindowDestroyed(aura::Window* window) {
+}
+
+void Node::OnWindowTargetVisibilityChanged(bool visible) {
+}
+
+bool Node::HasHitTestMask() const {
+ return false;
+}
+
+void Node::GetHitTestMask(gfx::Path* mask) const {
+}
+
+void Node::OnEvent(ui::Event* event) {
+ if (view_)
+ delegate_->OnViewInputEvent(view_, event);
+}
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/view_manager/node.h b/chromium/mojo/services/view_manager/node.h
new file mode 100644
index 00000000000..bfee6752378
--- /dev/null
+++ b/chromium/mojo/services/view_manager/node.h
@@ -0,0 +1,112 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_NODE_H_
+#define MOJO_SERVICES_VIEW_MANAGER_NODE_H_
+
+#include <vector>
+
+#include "base/logging.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "mojo/services/view_manager/ids.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_delegate.h"
+#include "ui/aura/window_observer.h"
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+class NodeDelegate;
+class View;
+
+// Represents a node in the graph. Delegate is informed of interesting events.
+class MOJO_VIEW_MANAGER_EXPORT Node
+ : public aura::WindowObserver,
+ public aura::WindowDelegate {
+ public:
+ Node(NodeDelegate* delegate, const NodeId& id);
+ virtual ~Node();
+
+ void set_view_id(const ViewId& view_id) { view_id_ = view_id; }
+ const ViewId& view_id() const { return view_id_; }
+
+ const NodeId& id() const { return id_; }
+
+ void Add(Node* child);
+ void Remove(Node* child);
+
+ void Reorder(Node* child, Node* relative, OrderDirection direction);
+
+ aura::Window* window() { return &window_; }
+
+ const gfx::Rect& bounds() const { return window_.bounds(); }
+
+ const Node* GetParent() const;
+ Node* GetParent() {
+ return const_cast<Node*>(const_cast<const Node*>(this)->GetParent());
+ }
+
+ const Node* GetRoot() const;
+ Node* GetRoot() {
+ return const_cast<Node*>(const_cast<const Node*>(this)->GetRoot());
+ }
+
+ std::vector<const Node*> GetChildren() const;
+ std::vector<Node*> GetChildren();
+
+ bool Contains(const Node* node) const;
+
+ // Sets the view associated with this node. Node does not own its View.
+ void SetView(View* view);
+ View* view() { return view_; }
+ const View* view() const { return view_; }
+
+ private:
+ // WindowObserver overrides:
+ virtual void OnWindowHierarchyChanged(
+ const aura::WindowObserver::HierarchyChangeParams& params) OVERRIDE;
+
+ // WindowDelegate overrides:
+ virtual gfx::Size GetMinimumSize() const OVERRIDE;
+ virtual gfx::Size GetMaximumSize() const OVERRIDE;
+ virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) OVERRIDE;
+ virtual gfx::NativeCursor GetCursor(const gfx::Point& point) OVERRIDE;
+ virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE;
+ virtual bool ShouldDescendIntoChildForEventHandling(
+ aura::Window* child,
+ const gfx::Point& location) OVERRIDE;
+ virtual bool CanFocus() OVERRIDE;
+ virtual void OnCaptureLost() OVERRIDE;
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+ virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE;
+ virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
+ virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
+ virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE;
+ virtual bool HasHitTestMask() const OVERRIDE;
+ virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE;
+
+ // ui::EventHandler overrides:
+ virtual void OnEvent(ui::Event* event) OVERRIDE;
+
+ NodeDelegate* delegate_;
+ const NodeId id_;
+
+ // Weak pointer to view associated with this node.
+ View* view_;
+
+ ViewId view_id_;
+
+ aura::Window window_;
+
+ DISALLOW_COPY_AND_ASSIGN(Node);
+};
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_NODE_H_
diff --git a/chromium/mojo/services/view_manager/node_delegate.h b/chromium/mojo/services/view_manager/node_delegate.h
new file mode 100644
index 00000000000..d63acc91998
--- /dev/null
+++ b/chromium/mojo/services/view_manager/node_delegate.h
@@ -0,0 +1,45 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_NODE_DELEGATE_H_
+#define MOJO_SERVICES_VIEW_MANAGER_NODE_DELEGATE_H_
+
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace ui {
+class Event;
+}
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+class Node;
+class View;
+
+class MOJO_VIEW_MANAGER_EXPORT NodeDelegate {
+ public:
+ // Invoked when the hierarchy has changed.
+ virtual void OnNodeHierarchyChanged(const Node* node,
+ const Node* new_parent,
+ const Node* old_parent) = 0;
+
+ // Invoked when the View associated with a node changes.
+ virtual void OnNodeViewReplaced(const Node* node,
+ const View* new_view,
+ const View* old_view) = 0;
+
+ // Invoked when an input event is received by the View at this node.
+ virtual void OnViewInputEvent(const View* view,
+ const ui::Event* event) = 0;
+
+ protected:
+ virtual ~NodeDelegate() {}
+};
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_NODE_DELEGATE_H_
diff --git a/chromium/mojo/services/view_manager/root_node_manager.cc b/chromium/mojo/services/view_manager/root_node_manager.cc
new file mode 100644
index 00000000000..f5c681fc7a1
--- /dev/null
+++ b/chromium/mojo/services/view_manager/root_node_manager.cc
@@ -0,0 +1,265 @@
+// Copyright 2014 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 "mojo/services/view_manager/root_node_manager.h"
+
+#include "base/logging.h"
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+#include "mojo/services/public/cpp/input_events/input_events_type_converters.h"
+#include "mojo/services/view_manager/view.h"
+#include "mojo/services/view_manager/view_manager_service_impl.h"
+#include "ui/aura/env.h"
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+RootNodeManager::ScopedChange::ScopedChange(
+ ViewManagerServiceImpl* connection,
+ RootNodeManager* root,
+ RootNodeManager::ChangeType change_type,
+ bool is_delete_node)
+ : root_(root),
+ connection_id_(connection->id()),
+ change_type_(change_type),
+ is_delete_node_(is_delete_node) {
+ root_->PrepareForChange(this);
+}
+
+RootNodeManager::ScopedChange::~ScopedChange() {
+ root_->FinishChange();
+}
+
+RootNodeManager::Context::Context() {
+ // Pass in false as native viewport creates the PlatformEventSource.
+ aura::Env::CreateInstance(false);
+}
+
+RootNodeManager::Context::~Context() {
+ aura::Env::DeleteInstance();
+}
+
+RootNodeManager::RootNodeManager(ServiceProvider* service_provider,
+ RootViewManagerDelegate* view_manager_delegate)
+ : service_provider_(service_provider),
+ next_connection_id_(1),
+ next_server_change_id_(1),
+ root_view_manager_(service_provider, this, view_manager_delegate),
+ root_(this, RootNodeId()),
+ current_change_(NULL) {
+}
+
+RootNodeManager::~RootNodeManager() {
+ while (!connections_created_by_connect_.empty())
+ delete *(connections_created_by_connect_.begin());
+ // All the connections should have been destroyed.
+ DCHECK(connection_map_.empty());
+}
+
+ConnectionSpecificId RootNodeManager::GetAndAdvanceNextConnectionId() {
+ const ConnectionSpecificId id = next_connection_id_++;
+ DCHECK_LT(id, next_connection_id_);
+ return id;
+}
+
+void RootNodeManager::AddConnection(ViewManagerServiceImpl* connection) {
+ DCHECK_EQ(0u, connection_map_.count(connection->id()));
+ connection_map_[connection->id()] = connection;
+}
+
+void RootNodeManager::RemoveConnection(ViewManagerServiceImpl* connection) {
+ connection_map_.erase(connection->id());
+ connections_created_by_connect_.erase(connection);
+
+ // Notify remaining connections so that they can cleanup.
+ for (ConnectionMap::const_iterator i = connection_map_.begin();
+ i != connection_map_.end(); ++i) {
+ i->second->OnViewManagerServiceImplDestroyed(connection->id());
+ }
+}
+
+void RootNodeManager::EmbedRoot(const std::string& url) {
+ CHECK(connection_map_.empty());
+ Array<Id> roots(0);
+ EmbedImpl(kRootConnection, String::From(url), roots);
+}
+
+void RootNodeManager::Embed(ConnectionSpecificId creator_id,
+ const String& url,
+ const Array<Id>& node_ids) {
+ CHECK_GT(node_ids.size(), 0u);
+ EmbedImpl(creator_id, url, node_ids)->set_delete_on_connection_error();
+}
+
+ViewManagerServiceImpl* RootNodeManager::GetConnection(
+ ConnectionSpecificId connection_id) {
+ ConnectionMap::iterator i = connection_map_.find(connection_id);
+ return i == connection_map_.end() ? NULL : i->second;
+}
+
+Node* RootNodeManager::GetNode(const NodeId& id) {
+ if (id == root_.id())
+ return &root_;
+ ConnectionMap::iterator i = connection_map_.find(id.connection_id);
+ return i == connection_map_.end() ? NULL : i->second->GetNode(id);
+}
+
+View* RootNodeManager::GetView(const ViewId& id) {
+ ConnectionMap::iterator i = connection_map_.find(id.connection_id);
+ return i == connection_map_.end() ? NULL : i->second->GetView(id);
+}
+
+void RootNodeManager::OnConnectionMessagedClient(ConnectionSpecificId id) {
+ if (current_change_)
+ current_change_->MarkConnectionAsMessaged(id);
+}
+
+bool RootNodeManager::DidConnectionMessageClient(
+ ConnectionSpecificId id) const {
+ return current_change_ && current_change_->DidMessageConnection(id);
+}
+
+ViewManagerServiceImpl* RootNodeManager::GetConnectionByCreator(
+ ConnectionSpecificId creator_id,
+ const std::string& url) const {
+ for (ConnectionMap::const_iterator i = connection_map_.begin();
+ i != connection_map_.end(); ++i) {
+ if (i->second->creator_id() == creator_id && i->second->url() == url)
+ return i->second;
+ }
+ return NULL;
+}
+
+void RootNodeManager::DispatchViewInputEventToWindowManager(
+ const View* view,
+ const ui::Event* event) {
+ // Input events are forwarded to the WindowManager. The WindowManager
+ // eventually calls back to us with DispatchOnViewInputEvent().
+ ViewManagerServiceImpl* connection = GetConnection(kWindowManagerConnection);
+ if (!connection)
+ return;
+ connection->client()->DispatchOnViewInputEvent(
+ ViewIdToTransportId(view->id()),
+ TypeConverter<EventPtr, ui::Event>::ConvertFrom(*event));
+}
+
+void RootNodeManager::ProcessNodeBoundsChanged(const Node* node,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) {
+ for (ConnectionMap::iterator i = connection_map_.begin();
+ i != connection_map_.end(); ++i) {
+ i->second->ProcessNodeBoundsChanged(node, old_bounds, new_bounds,
+ IsChangeSource(i->first));
+ }
+}
+
+void RootNodeManager::ProcessNodeHierarchyChanged(const Node* node,
+ const Node* new_parent,
+ const Node* old_parent) {
+ for (ConnectionMap::iterator i = connection_map_.begin();
+ i != connection_map_.end(); ++i) {
+ i->second->ProcessNodeHierarchyChanged(
+ node, new_parent, old_parent, next_server_change_id_,
+ IsChangeSource(i->first));
+ }
+}
+
+void RootNodeManager::ProcessNodeReorder(const Node* node,
+ const Node* relative_node,
+ const OrderDirection direction) {
+ for (ConnectionMap::iterator i = connection_map_.begin();
+ i != connection_map_.end(); ++i) {
+ i->second->ProcessNodeReorder(
+ node, relative_node, direction, next_server_change_id_,
+ IsChangeSource(i->first));
+ }
+}
+
+void RootNodeManager::ProcessNodeViewReplaced(const Node* node,
+ const View* new_view,
+ const View* old_view) {
+ for (ConnectionMap::iterator i = connection_map_.begin();
+ i != connection_map_.end(); ++i) {
+ i->second->ProcessNodeViewReplaced(node, new_view, old_view,
+ IsChangeSource(i->first));
+ }
+}
+
+void RootNodeManager::ProcessNodeDeleted(const NodeId& node) {
+ for (ConnectionMap::iterator i = connection_map_.begin();
+ i != connection_map_.end(); ++i) {
+ i->second->ProcessNodeDeleted(node, next_server_change_id_,
+ IsChangeSource(i->first));
+ }
+}
+
+void RootNodeManager::ProcessViewDeleted(const ViewId& view) {
+ for (ConnectionMap::iterator i = connection_map_.begin();
+ i != connection_map_.end(); ++i) {
+ i->second->ProcessViewDeleted(view, IsChangeSource(i->first));
+ }
+}
+
+void RootNodeManager::PrepareForChange(ScopedChange* change) {
+ // Should only ever have one change in flight.
+ CHECK(!current_change_);
+ current_change_ = change;
+}
+
+void RootNodeManager::FinishChange() {
+ // PrepareForChange/FinishChange should be balanced.
+ CHECK(current_change_);
+ if (current_change_->change_type() == CHANGE_TYPE_ADVANCE_SERVER_CHANGE_ID)
+ next_server_change_id_++;
+ current_change_ = NULL;
+}
+
+ViewManagerServiceImpl* RootNodeManager::EmbedImpl(
+ const ConnectionSpecificId creator_id,
+ const String& url,
+ const Array<Id>& node_ids) {
+ MessagePipe pipe;
+ service_provider_->ConnectToService(
+ url,
+ ViewManagerServiceImpl::Client::Name_,
+ pipe.handle1.Pass(),
+ String());
+
+ std::string creator_url;
+ ConnectionMap::const_iterator it = connection_map_.find(creator_id);
+ if (it != connection_map_.end())
+ creator_url = it->second->url();
+
+ ViewManagerServiceImpl* connection =
+ new ViewManagerServiceImpl(this,
+ creator_id,
+ creator_url,
+ url.To<std::string>());
+ connection->SetRoots(node_ids);
+ BindToPipe(connection, pipe.handle0.Pass());
+ connections_created_by_connect_.insert(connection);
+ return connection;
+}
+
+void RootNodeManager::OnNodeHierarchyChanged(const Node* node,
+ const Node* new_parent,
+ const Node* old_parent) {
+ if (!root_view_manager_.in_setup())
+ ProcessNodeHierarchyChanged(node, new_parent, old_parent);
+}
+
+void RootNodeManager::OnNodeViewReplaced(const Node* node,
+ const View* new_view,
+ const View* old_view) {
+ ProcessNodeViewReplaced(node, new_view, old_view);
+}
+
+void RootNodeManager::OnViewInputEvent(const View* view,
+ const ui::Event* event) {
+ DispatchViewInputEventToWindowManager(view, event);
+}
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/view_manager/root_node_manager.h b/chromium/mojo/services/view_manager/root_node_manager.h
new file mode 100644
index 00000000000..dba45d922c9
--- /dev/null
+++ b/chromium/mojo/services/view_manager/root_node_manager.h
@@ -0,0 +1,224 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_ROOT_NODE_MANAGER_H_
+#define MOJO_SERVICES_VIEW_MANAGER_ROOT_NODE_MANAGER_H_
+
+#include <map>
+#include <set>
+
+#include "base/basictypes.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/services/view_manager/ids.h"
+#include "mojo/services/view_manager/node.h"
+#include "mojo/services/view_manager/node_delegate.h"
+#include "mojo/services/view_manager/root_view_manager.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace ui {
+class Event;
+}
+
+namespace mojo {
+
+class ServiceProvider;
+
+namespace view_manager {
+namespace service {
+
+class RootViewManagerDelegate;
+class View;
+class ViewManagerServiceImpl;
+
+// RootNodeManager is responsible for managing the set of
+// ViewManagerServiceImpls as well as providing the root of the node hierarchy.
+class MOJO_VIEW_MANAGER_EXPORT RootNodeManager : public NodeDelegate {
+ public:
+ // Used to indicate if the server id should be incremented after notifiying
+ // clients of the change.
+ enum ChangeType {
+ CHANGE_TYPE_ADVANCE_SERVER_CHANGE_ID,
+ CHANGE_TYPE_DONT_ADVANCE_SERVER_CHANGE_ID,
+ };
+
+ // Create when a ViewManagerServiceImpl is about to make a change. Ensures
+ // clients are notified of the correct change id.
+ class ScopedChange {
+ public:
+ ScopedChange(ViewManagerServiceImpl* connection,
+ RootNodeManager* root,
+ RootNodeManager::ChangeType change_type,
+ bool is_delete_node);
+ ~ScopedChange();
+
+ ConnectionSpecificId connection_id() const { return connection_id_; }
+ ChangeType change_type() const { return change_type_; }
+ bool is_delete_node() const { return is_delete_node_; }
+
+ // Marks the connection with the specified id as having seen a message.
+ void MarkConnectionAsMessaged(ConnectionSpecificId connection_id) {
+ message_ids_.insert(connection_id);
+ }
+
+ // Returns true if MarkConnectionAsMessaged(connection_id) was invoked.
+ bool DidMessageConnection(ConnectionSpecificId connection_id) const {
+ return message_ids_.count(connection_id) > 0;
+ }
+
+ private:
+ RootNodeManager* root_;
+ const ConnectionSpecificId connection_id_;
+ const ChangeType change_type_;
+ const bool is_delete_node_;
+
+ // See description of MarkConnectionAsMessaged/DidMessageConnection.
+ std::set<ConnectionSpecificId> message_ids_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedChange);
+ };
+
+ RootNodeManager(ServiceProvider* service_provider,
+ RootViewManagerDelegate* view_manager_delegate);
+ virtual ~RootNodeManager();
+
+ // Returns the id for the next ViewManagerServiceImpl.
+ ConnectionSpecificId GetAndAdvanceNextConnectionId();
+
+ Id next_server_change_id() const {
+ return next_server_change_id_;
+ }
+
+ void AddConnection(ViewManagerServiceImpl* connection);
+ void RemoveConnection(ViewManagerServiceImpl* connection);
+
+ // Establishes the initial client. Similar to Connect(), but the resulting
+ // client is allowed to do anything.
+ void EmbedRoot(const std::string& url);
+
+ // See description of ViewManagerService::Embed() for details. This assumes
+ // |node_ids| has been validated.
+ void Embed(ConnectionSpecificId creator_id,
+ const String& url,
+ const Array<Id>& node_ids);
+
+ // Returns the connection by id.
+ ViewManagerServiceImpl* GetConnection(ConnectionSpecificId connection_id);
+
+ // Returns the Node identified by |id|.
+ Node* GetNode(const NodeId& id);
+
+ // Returns the View identified by |id|.
+ View* GetView(const ViewId& id);
+
+ Node* root() { return &root_; }
+
+ bool IsProcessingChange() const { return current_change_ != NULL; }
+
+ bool is_processing_delete_node() const {
+ return current_change_ && current_change_->is_delete_node(); }
+
+ // Invoked when a connection messages a client about the change. This is used
+ // to avoid sending ServerChangeIdAdvanced() unnecessarily.
+ void OnConnectionMessagedClient(ConnectionSpecificId id);
+
+ // Returns true if OnConnectionMessagedClient() was invoked for id.
+ bool DidConnectionMessageClient(ConnectionSpecificId id) const;
+
+ ViewManagerServiceImpl* GetConnectionByCreator(
+ ConnectionSpecificId creator_id,
+ const std::string& url) const;
+
+ void DispatchViewInputEventToWindowManager(const View* view,
+ const ui::Event* event);
+
+ // These functions trivially delegate to all ViewManagerServiceImpls, which in
+ // term notify their clients.
+ void ProcessNodeBoundsChanged(const Node* node,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds);
+ void ProcessNodeHierarchyChanged(const Node* node,
+ const Node* new_parent,
+ const Node* old_parent);
+ void ProcessNodeReorder(const Node* node,
+ const Node* relative_node,
+ const OrderDirection direction);
+ void ProcessNodeViewReplaced(const Node* node,
+ const View* new_view_id,
+ const View* old_view_id);
+ void ProcessNodeDeleted(const NodeId& node);
+ void ProcessViewDeleted(const ViewId& view);
+
+ private:
+ // Used to setup any static state needed by RootNodeManager.
+ struct Context {
+ Context();
+ ~Context();
+ };
+
+ typedef std::map<ConnectionSpecificId, ViewManagerServiceImpl*> ConnectionMap;
+
+ // Invoked when a connection is about to make a change. Subsequently followed
+ // by FinishChange() once the change is done.
+ //
+ // Changes should never nest, meaning each PrepareForChange() must be
+ // balanced with a call to FinishChange() with no PrepareForChange()
+ // in between.
+ void PrepareForChange(ScopedChange* change);
+
+ // Balances a call to PrepareForChange().
+ void FinishChange();
+
+ // Returns true if the specified connection originated the current change.
+ bool IsChangeSource(ConnectionSpecificId connection_id) const {
+ return current_change_ && current_change_->connection_id() == connection_id;
+ }
+
+ // Implementation of the two embed variants.
+ ViewManagerServiceImpl* EmbedImpl(ConnectionSpecificId creator_id,
+ const String& url,
+ const Array<Id>& node_ids);
+
+ // Overridden from NodeDelegate:
+ virtual void OnNodeHierarchyChanged(const Node* node,
+ const Node* new_parent,
+ const Node* old_parent) OVERRIDE;
+ virtual void OnNodeViewReplaced(const Node* node,
+ const View* new_view,
+ const View* old_view) OVERRIDE;
+ virtual void OnViewInputEvent(const View* view,
+ const ui::Event* event) OVERRIDE;
+
+ Context context_;
+
+ ServiceProvider* service_provider_;
+
+ // ID to use for next ViewManagerServiceImpl.
+ ConnectionSpecificId next_connection_id_;
+
+ Id next_server_change_id_;
+
+ // Set of ViewManagerServiceImpls.
+ ConnectionMap connection_map_;
+
+ RootViewManager root_view_manager_;
+
+ // Root node.
+ Node root_;
+
+ // Set of ViewManagerServiceImpls created by way of Connect(). These have to
+ // be explicitly destroyed.
+ std::set<ViewManagerServiceImpl*> connections_created_by_connect_;
+
+ // If non-null we're processing a change. The ScopedChange is not owned by us
+ // (it's created on the stack by ViewManagerServiceImpl).
+ ScopedChange* current_change_;
+
+ DISALLOW_COPY_AND_ASSIGN(RootNodeManager);
+};
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_ROOT_NODE_MANAGER_H_
diff --git a/chromium/mojo/services/view_manager/root_view_manager.cc b/chromium/mojo/services/view_manager/root_view_manager.cc
new file mode 100644
index 00000000000..0bcdbfbb20e
--- /dev/null
+++ b/chromium/mojo/services/view_manager/root_view_manager.cc
@@ -0,0 +1,159 @@
+// Copyright 2014 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 "mojo/services/view_manager/root_view_manager.h"
+
+#include "base/auto_reset.h"
+#include "base/scoped_observer.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/services/view_manager/root_node_manager.h"
+#include "mojo/services/view_manager/root_view_manager_delegate.h"
+#include "mojo/services/view_manager/screen_impl.h"
+#include "mojo/services/view_manager/window_tree_host_impl.h"
+#include "ui/aura/client/default_capture_client.h"
+#include "ui/aura/client/focus_change_observer.h"
+#include "ui/aura/client/focus_client.h"
+#include "ui/aura/client/window_tree_client.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_observer.h"
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+// TODO(sky): revisit this, we may need a more sophisticated FocusClient
+// implementation.
+class FocusClientImpl : public aura::client::FocusClient,
+ public aura::WindowObserver {
+ public:
+ FocusClientImpl()
+ : focused_window_(NULL),
+ observer_manager_(this) {
+ }
+ virtual ~FocusClientImpl() {}
+
+ private:
+ // Overridden from aura::client::FocusClient:
+ virtual void AddObserver(aura::client::FocusChangeObserver* observer)
+ OVERRIDE {
+ }
+ virtual void RemoveObserver(aura::client::FocusChangeObserver* observer)
+ OVERRIDE {
+ }
+ virtual void FocusWindow(aura::Window* window) OVERRIDE {
+ if (window && !window->CanFocus())
+ return;
+ if (focused_window_)
+ observer_manager_.Remove(focused_window_);
+ aura::Window* old_focused_window = focused_window_;
+ focused_window_ = window;
+ if (focused_window_)
+ observer_manager_.Add(focused_window_);
+
+ aura::client::FocusChangeObserver* observer =
+ aura::client::GetFocusChangeObserver(old_focused_window);
+ if (observer)
+ observer->OnWindowFocused(focused_window_, old_focused_window);
+ observer = aura::client::GetFocusChangeObserver(focused_window_);
+ if (observer)
+ observer->OnWindowFocused(focused_window_, old_focused_window);
+ }
+ virtual void ResetFocusWithinActiveWindow(aura::Window* window) OVERRIDE {
+ if (!window->Contains(focused_window_))
+ FocusWindow(window);
+ }
+ virtual aura::Window* GetFocusedWindow() OVERRIDE {
+ return focused_window_;
+ }
+
+ // Overridden from WindowObserver:
+ virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
+ DCHECK_EQ(window, focused_window_);
+ FocusWindow(NULL);
+ }
+
+ aura::Window* focused_window_;
+ ScopedObserver<aura::Window, aura::WindowObserver> observer_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusClientImpl);
+};
+
+class WindowTreeClientImpl : public aura::client::WindowTreeClient {
+ public:
+ explicit WindowTreeClientImpl(aura::Window* window) : window_(window) {
+ aura::client::SetWindowTreeClient(window_, this);
+ }
+
+ virtual ~WindowTreeClientImpl() {
+ aura::client::SetWindowTreeClient(window_, NULL);
+ }
+
+ // Overridden from aura::client::WindowTreeClient:
+ virtual aura::Window* GetDefaultParent(aura::Window* context,
+ aura::Window* window,
+ const gfx::Rect& bounds) OVERRIDE {
+ if (!capture_client_) {
+ capture_client_.reset(
+ new aura::client::DefaultCaptureClient(window_->GetRootWindow()));
+ }
+ return window_;
+ }
+
+ private:
+ aura::Window* window_;
+
+ scoped_ptr<aura::client::DefaultCaptureClient> capture_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowTreeClientImpl);
+};
+
+RootViewManager::RootViewManager(ServiceProvider* service_provider,
+ RootNodeManager* root_node,
+ RootViewManagerDelegate* delegate)
+ : delegate_(delegate),
+ root_node_manager_(root_node),
+ in_setup_(false) {
+ screen_.reset(ScreenImpl::Create());
+ gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, screen_.get());
+ NativeViewportPtr viewport;
+ ConnectToService(service_provider,
+ "mojo:mojo_native_viewport_service",
+ &viewport);
+ window_tree_host_.reset(new WindowTreeHostImpl(
+ viewport.Pass(),
+ gfx::Rect(800, 600),
+ base::Bind(&RootViewManager::OnCompositorCreated,
+ base::Unretained(this))));
+}
+
+RootViewManager::~RootViewManager() {
+ window_tree_client_.reset();
+ window_tree_host_.reset();
+ gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, NULL);
+}
+
+void RootViewManager::OnCompositorCreated() {
+ base::AutoReset<bool> resetter(&in_setup_, true);
+ window_tree_host_->InitHost();
+
+ aura::Window* root = root_node_manager_->root()->window();
+ window_tree_host_->window()->AddChild(root);
+ root->SetBounds(gfx::Rect(window_tree_host_->window()->bounds().size()));
+ root_node_manager_->root()->window()->Show();
+
+ window_tree_client_.reset(
+ new WindowTreeClientImpl(window_tree_host_->window()));
+
+ focus_client_.reset(new FocusClientImpl());
+ aura::client::SetFocusClient(window_tree_host_->window(),
+ focus_client_.get());
+
+ window_tree_host_->Show();
+
+ delegate_->OnRootViewManagerWindowTreeHostCreated();
+}
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/view_manager/root_view_manager.h b/chromium/mojo/services/view_manager/root_view_manager.h
new file mode 100644
index 00000000000..cfec2291f95
--- /dev/null
+++ b/chromium/mojo/services/view_manager/root_view_manager.h
@@ -0,0 +1,72 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_ROOT_VIEW_MANAGER_H_
+#define MOJO_SERVICES_VIEW_MANAGER_ROOT_VIEW_MANAGER_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/gles2/gles2.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace aura {
+namespace client {
+class FocusClient;
+class WindowTreeClient;
+}
+class WindowTreeHost;
+}
+
+namespace gfx {
+class Screen;
+}
+
+namespace mojo {
+
+class ServiceProvider;
+
+namespace view_manager {
+namespace service {
+
+class RootNodeManager;
+class RootViewManagerDelegate;
+
+// RootViewManager binds the root node to an actual display.
+class MOJO_VIEW_MANAGER_EXPORT RootViewManager {
+ public:
+ RootViewManager(ServiceProvider* service_provider,
+ RootNodeManager* root_node,
+ RootViewManagerDelegate* delegate);
+ virtual ~RootViewManager();
+
+ // See description above field for details.
+ bool in_setup() const { return in_setup_; }
+
+ private:
+ void OnCompositorCreated();
+
+ RootViewManagerDelegate* delegate_;
+
+ RootNodeManager* root_node_manager_;
+
+ GLES2Initializer gles_initializer_;
+
+ // Returns true if adding the root node's window to |window_tree_host_|.
+ bool in_setup_;
+
+ scoped_ptr<gfx::Screen> screen_;
+ scoped_ptr<aura::WindowTreeHost> window_tree_host_;
+ scoped_ptr<aura::client::WindowTreeClient> window_tree_client_;
+ scoped_ptr<aura::client::FocusClient> focus_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(RootViewManager);
+};
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_ROOT_VIEW_MANAGER_H_
diff --git a/chromium/mojo/services/view_manager/root_view_manager_delegate.h b/chromium/mojo/services/view_manager/root_view_manager_delegate.h
new file mode 100644
index 00000000000..2920f51c0d0
--- /dev/null
+++ b/chromium/mojo/services/view_manager/root_view_manager_delegate.h
@@ -0,0 +1,27 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_ROOT_VIEW_MANAGER_DELEGATE_H_
+#define MOJO_SERVICES_VIEW_MANAGER_ROOT_VIEW_MANAGER_DELEGATE_H_
+
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+class MOJO_VIEW_MANAGER_EXPORT RootViewManagerDelegate {
+ public:
+ // Invoked when the WindowTreeHost is ready.
+ virtual void OnRootViewManagerWindowTreeHostCreated() = 0;
+
+ protected:
+ virtual ~RootViewManagerDelegate() {}
+};
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_ROOT_VIEW_MANAGER_H_
diff --git a/chromium/mojo/services/view_manager/screen_impl.cc b/chromium/mojo/services/view_manager/screen_impl.cc
new file mode 100644
index 00000000000..6339626af97
--- /dev/null
+++ b/chromium/mojo/services/view_manager/screen_impl.cc
@@ -0,0 +1,80 @@
+// Copyright 2014 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 "mojo/services/view_manager/screen_impl.h"
+
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/rect_conversions.h"
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+// static
+gfx::Screen* ScreenImpl::Create() {
+ return new ScreenImpl(gfx::Rect(0, 0, 800, 600));
+}
+
+ScreenImpl::~ScreenImpl() {
+}
+
+bool ScreenImpl::IsDIPEnabled() {
+ NOTIMPLEMENTED();
+ return true;
+}
+
+gfx::Point ScreenImpl::GetCursorScreenPoint() {
+ NOTIMPLEMENTED();
+ return gfx::Point();
+}
+
+gfx::NativeWindow ScreenImpl::GetWindowUnderCursor() {
+ return GetWindowAtScreenPoint(GetCursorScreenPoint());
+}
+
+gfx::NativeWindow ScreenImpl::GetWindowAtScreenPoint(const gfx::Point& point) {
+ NOTIMPLEMENTED();
+ return NULL;
+}
+
+int ScreenImpl::GetNumDisplays() const {
+ return 1;
+}
+
+std::vector<gfx::Display> ScreenImpl::GetAllDisplays() const {
+ return std::vector<gfx::Display>(1, display_);
+}
+
+gfx::Display ScreenImpl::GetDisplayNearestWindow(
+ gfx::NativeWindow window) const {
+ return display_;
+}
+
+gfx::Display ScreenImpl::GetDisplayNearestPoint(const gfx::Point& point) const {
+ return display_;
+}
+
+gfx::Display ScreenImpl::GetDisplayMatching(const gfx::Rect& match_rect) const {
+ return display_;
+}
+
+gfx::Display ScreenImpl::GetPrimaryDisplay() const {
+ return display_;
+}
+
+void ScreenImpl::AddObserver(gfx::DisplayObserver* observer) {
+}
+
+void ScreenImpl::RemoveObserver(gfx::DisplayObserver* observer) {
+}
+
+ScreenImpl::ScreenImpl(const gfx::Rect& screen_bounds) {
+ static int64 synthesized_display_id = 2000;
+ display_.set_id(synthesized_display_id++);
+ display_.SetScaleAndBounds(1.0f, screen_bounds);
+}
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/view_manager/screen_impl.h b/chromium/mojo/services/view_manager/screen_impl.h
new file mode 100644
index 00000000000..08a6b04bb41
--- /dev/null
+++ b/chromium/mojo/services/view_manager/screen_impl.h
@@ -0,0 +1,58 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_SCREEN_IMPL_H_
+#define MOJO_SERVICES_VIEW_MANAGER_SCREEN_IMPL_H_
+
+#include "base/compiler_specific.h"
+#include "ui/gfx/display.h"
+#include "ui/gfx/screen.h"
+
+namespace gfx {
+class Rect;
+class Transform;
+}
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+// A minimal implementation of gfx::Screen for the view manager.
+class ScreenImpl : public gfx::Screen {
+ public:
+ static gfx::Screen* Create();
+ virtual ~ScreenImpl();
+
+ protected:
+ // gfx::Screen overrides:
+ virtual bool IsDIPEnabled() OVERRIDE;
+ virtual gfx::Point GetCursorScreenPoint() OVERRIDE;
+ virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE;
+ virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point)
+ OVERRIDE;
+ virtual int GetNumDisplays() const OVERRIDE;
+ virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE;
+ virtual gfx::Display GetDisplayNearestWindow(
+ gfx::NativeView view) const OVERRIDE;
+ virtual gfx::Display GetDisplayNearestPoint(
+ const gfx::Point& point) const OVERRIDE;
+ virtual gfx::Display GetDisplayMatching(
+ const gfx::Rect& match_rect) const OVERRIDE;
+ virtual gfx::Display GetPrimaryDisplay() const OVERRIDE;
+ virtual void AddObserver(gfx::DisplayObserver* observer) OVERRIDE;
+ virtual void RemoveObserver(gfx::DisplayObserver* observer) OVERRIDE;
+
+ private:
+ explicit ScreenImpl(const gfx::Rect& screen_bounds);
+
+ gfx::Display display_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScreenImpl);
+};
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_SCREEN_IMPL_H_
diff --git a/chromium/mojo/services/view_manager/test_change_tracker.cc b/chromium/mojo/services/view_manager/test_change_tracker.cc
new file mode 100644
index 00000000000..89e7fbb4445
--- /dev/null
+++ b/chromium/mojo/services/view_manager/test_change_tracker.cc
@@ -0,0 +1,263 @@
+// Copyright 2014 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 "mojo/services/view_manager/test_change_tracker.h"
+
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/view_manager/util.h"
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+std::string NodeIdToString(Id id) {
+ return (id == 0) ? "null" :
+ base::StringPrintf("%d,%d", HiWord(id), LoWord(id));
+}
+
+namespace {
+
+std::string RectToString(const gfx::Rect& rect) {
+ return base::StringPrintf("%d,%d %dx%d", rect.x(), rect.y(), rect.width(),
+ rect.height());
+}
+
+std::string DirectionToString(OrderDirection direction) {
+ return direction == ORDER_ABOVE ? "above" : "below";
+}
+
+std::string ChangeToDescription1(const Change& change) {
+ switch (change.type) {
+ case CHANGE_TYPE_CONNECTION_ESTABLISHED:
+ return base::StringPrintf("OnConnectionEstablished creator=%s",
+ change.creator_url.data());
+
+ case CHANGE_TYPE_ROOTS_ADDED:
+ return "OnRootsAdded";
+
+ case CHANGE_TYPE_SERVER_CHANGE_ID_ADVANCED:
+ return base::StringPrintf(
+ "ServerChangeIdAdvanced %d", static_cast<int>(change.change_id));
+
+
+ case CHANGE_TYPE_NODE_BOUNDS_CHANGED:
+ return base::StringPrintf(
+ "BoundsChanged node=%s old_bounds=%s new_bounds=%s",
+ NodeIdToString(change.node_id).c_str(),
+ RectToString(change.bounds).c_str(),
+ RectToString(change.bounds2).c_str());
+
+ case CHANGE_TYPE_NODE_HIERARCHY_CHANGED:
+ return base::StringPrintf(
+ "HierarchyChanged change_id=%d node=%s new_parent=%s old_parent=%s",
+ static_cast<int>(change.change_id),
+ NodeIdToString(change.node_id).c_str(),
+ NodeIdToString(change.node_id2).c_str(),
+ NodeIdToString(change.node_id3).c_str());
+
+ case CHANGE_TYPE_NODE_REORDERED:
+ return base::StringPrintf(
+ "Reordered change_id=%d node=%s relative=%s direction=%s",
+ static_cast<int>(change.change_id),
+ NodeIdToString(change.node_id).c_str(),
+ NodeIdToString(change.node_id2).c_str(),
+ DirectionToString(change.direction).c_str());
+
+ case CHANGE_TYPE_NODE_DELETED:
+ return base::StringPrintf("NodeDeleted change_id=%d node=%s",
+ static_cast<int>(change.change_id),
+ NodeIdToString(change.node_id).c_str());
+
+ case CHANGE_TYPE_VIEW_DELETED:
+ return base::StringPrintf("ViewDeleted view=%s",
+ NodeIdToString(change.view_id).c_str());
+
+ case CHANGE_TYPE_VIEW_REPLACED:
+ return base::StringPrintf(
+ "ViewReplaced node=%s new_view=%s old_view=%s",
+ NodeIdToString(change.node_id).c_str(),
+ NodeIdToString(change.view_id).c_str(),
+ NodeIdToString(change.view_id2).c_str());
+
+ case CHANGE_TYPE_INPUT_EVENT:
+ return base::StringPrintf(
+ "InputEvent view=%s event_action=%d",
+ NodeIdToString(change.view_id).c_str(),
+ change.event_action);
+ }
+ return std::string();
+}
+
+} // namespace
+
+std::vector<std::string> ChangesToDescription1(
+ const std::vector<Change>& changes) {
+ std::vector<std::string> strings(changes.size());
+ for (size_t i = 0; i < changes.size(); ++i)
+ strings[i] = ChangeToDescription1(changes[i]);
+ return strings;
+}
+
+std::string ChangeNodeDescription(const std::vector<Change>& changes) {
+ if (changes.size() != 1)
+ return std::string();
+ std::vector<std::string> node_strings(changes[0].nodes.size());
+ for (size_t i = 0; i < changes[0].nodes.size(); ++i)
+ node_strings[i] = "[" + changes[0].nodes[i].ToString() + "]";
+ return JoinString(node_strings, ',');
+}
+
+void NodeDatasToTestNodes(const Array<NodeDataPtr>& data,
+ std::vector<TestNode>* test_nodes) {
+ for (size_t i = 0; i < data.size(); ++i) {
+ TestNode node;
+ node.parent_id = data[i]->parent_id;
+ node.node_id = data[i]->node_id;
+ node.view_id = data[i]->view_id;
+ test_nodes->push_back(node);
+ }
+}
+
+Change::Change()
+ : type(CHANGE_TYPE_CONNECTION_ESTABLISHED),
+ connection_id(0),
+ change_id(0),
+ node_id(0),
+ node_id2(0),
+ node_id3(0),
+ view_id(0),
+ view_id2(0),
+ event_action(0),
+ direction(ORDER_ABOVE) {}
+
+Change::~Change() {
+}
+
+TestChangeTracker::TestChangeTracker()
+ : delegate_(NULL) {
+}
+
+TestChangeTracker::~TestChangeTracker() {
+}
+
+void TestChangeTracker::OnViewManagerConnectionEstablished(
+ ConnectionSpecificId connection_id,
+ const String& creator_url,
+ Id next_server_change_id,
+ Array<NodeDataPtr> nodes) {
+ Change change;
+ change.type = CHANGE_TYPE_CONNECTION_ESTABLISHED;
+ change.connection_id = connection_id;
+ change.change_id = next_server_change_id;
+ change.creator_url = creator_url;
+ NodeDatasToTestNodes(nodes, &change.nodes);
+ AddChange(change);
+}
+
+void TestChangeTracker::OnRootsAdded(Array<NodeDataPtr> nodes) {
+ Change change;
+ change.type = CHANGE_TYPE_ROOTS_ADDED;
+ NodeDatasToTestNodes(nodes, &change.nodes);
+ AddChange(change);
+}
+
+void TestChangeTracker::OnServerChangeIdAdvanced(Id change_id) {
+ Change change;
+ change.type = CHANGE_TYPE_SERVER_CHANGE_ID_ADVANCED;
+ change.change_id = change_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnNodeBoundsChanged(Id node_id,
+ RectPtr old_bounds,
+ RectPtr new_bounds) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_BOUNDS_CHANGED;
+ change.node_id = node_id;
+ change.bounds = old_bounds.To<gfx::Rect>();
+ change.bounds2 = new_bounds.To<gfx::Rect>();
+ AddChange(change);
+}
+
+void TestChangeTracker::OnNodeHierarchyChanged(Id node_id,
+ Id new_parent_id,
+ Id old_parent_id,
+ Id server_change_id,
+ Array<NodeDataPtr> nodes) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_HIERARCHY_CHANGED;
+ change.node_id = node_id;
+ change.node_id2 = new_parent_id;
+ change.node_id3 = old_parent_id;
+ change.change_id = server_change_id;
+ NodeDatasToTestNodes(nodes, &change.nodes);
+ AddChange(change);
+}
+
+void TestChangeTracker::OnNodeReordered(Id node_id,
+ Id relative_node_id,
+ OrderDirection direction,
+ Id server_change_id) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_REORDERED;
+ change.node_id = node_id;
+ change.node_id2 = relative_node_id;
+ change.direction = direction;
+ change.change_id = server_change_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnNodeDeleted(Id node_id, Id server_change_id) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_DELETED;
+ change.node_id = node_id;
+ change.change_id = server_change_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnViewDeleted(Id view_id) {
+ Change change;
+ change.type = CHANGE_TYPE_VIEW_DELETED;
+ change.view_id = view_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnNodeViewReplaced(Id node_id,
+ Id new_view_id,
+ Id old_view_id) {
+ Change change;
+ change.type = CHANGE_TYPE_VIEW_REPLACED;
+ change.node_id = node_id;
+ change.view_id = new_view_id;
+ change.view_id2 = old_view_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnViewInputEvent(Id view_id, EventPtr event) {
+ Change change;
+ change.type = CHANGE_TYPE_INPUT_EVENT;
+ change.view_id = view_id;
+ change.event_action = event->action;
+ AddChange(change);
+}
+
+void TestChangeTracker::AddChange(const Change& change) {
+ changes_.push_back(change);
+ if (delegate_)
+ delegate_->OnChangeAdded();
+}
+
+std::string TestNode::ToString() const {
+ return base::StringPrintf("node=%s parent=%s view=%s",
+ NodeIdToString(node_id).c_str(),
+ NodeIdToString(parent_id).c_str(),
+ NodeIdToString(view_id).c_str());
+}
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/view_manager/test_change_tracker.h b/chromium/mojo/services/view_manager/test_change_tracker.h
new file mode 100644
index 00000000000..5a5edf7d126
--- /dev/null
+++ b/chromium/mojo/services/view_manager/test_change_tracker.h
@@ -0,0 +1,134 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_TEST_CHANGE_TRACKER_H_
+#define MOJO_SERVICES_VIEW_MANAGER_TEST_CHANGE_TRACKER_H_
+
+#include <string>
+#include <vector>
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "ui/gfx/rect.h"
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+enum ChangeType {
+ CHANGE_TYPE_CONNECTION_ESTABLISHED,
+ CHANGE_TYPE_ROOTS_ADDED,
+ CHANGE_TYPE_SERVER_CHANGE_ID_ADVANCED,
+ CHANGE_TYPE_NODE_BOUNDS_CHANGED,
+ CHANGE_TYPE_NODE_HIERARCHY_CHANGED,
+ CHANGE_TYPE_NODE_REORDERED,
+ CHANGE_TYPE_NODE_DELETED,
+ CHANGE_TYPE_VIEW_DELETED,
+ CHANGE_TYPE_VIEW_REPLACED,
+ CHANGE_TYPE_INPUT_EVENT,
+};
+
+// TODO(sky): consider nuking and converting directly to NodeData.
+struct TestNode {
+ // Returns a string description of this.
+ std::string ToString() const;
+
+ Id parent_id;
+ Id node_id;
+ Id view_id;
+};
+
+// Tracks a call to ViewManagerClient. See the individual functions for the
+// fields that are used.
+struct Change {
+ Change();
+ ~Change();
+
+ ChangeType type;
+ ConnectionSpecificId connection_id;
+ Id change_id;
+ std::vector<TestNode> nodes;
+ Id node_id;
+ Id node_id2;
+ Id node_id3;
+ Id view_id;
+ Id view_id2;
+ gfx::Rect bounds;
+ gfx::Rect bounds2;
+ int32 event_action;
+ String creator_url;
+ OrderDirection direction;
+};
+
+// Converts Changes to string descriptions.
+std::vector<std::string> ChangesToDescription1(
+ const std::vector<Change>& changes);
+
+// Returns a string description of |changes[0].nodes|. Returns an empty string
+// if change.size() != 1.
+std::string ChangeNodeDescription(const std::vector<Change>& changes);
+
+// Converts NodeDatas to TestNodes.
+void NodeDatasToTestNodes(const Array<NodeDataPtr>& data,
+ std::vector<TestNode>* test_nodes);
+
+// TestChangeTracker is used to record ViewManagerClient functions. It notifies
+// a delegate any time a change is added.
+class TestChangeTracker {
+ public:
+ // Used to notify the delegate when a change is added. A change corresponds to
+ // a single ViewManagerClient function.
+ class Delegate {
+ public:
+ virtual void OnChangeAdded() = 0;
+
+ protected:
+ virtual ~Delegate() {}
+ };
+
+ TestChangeTracker();
+ ~TestChangeTracker();
+
+ void set_delegate(Delegate* delegate) { delegate_ = delegate; }
+
+ std::vector<Change>* changes() { return &changes_; }
+
+ // Each of these functions generate a Change. There is one per
+ // ViewManagerClient function.
+ void OnViewManagerConnectionEstablished(ConnectionSpecificId connection_id,
+ const String& creator_url,
+ Id next_server_change_id,
+ Array<NodeDataPtr> nodes);
+ void OnRootsAdded(Array<NodeDataPtr> nodes);
+ void OnServerChangeIdAdvanced(Id change_id);
+ void OnNodeBoundsChanged(Id node_id, RectPtr old_bounds, RectPtr new_bounds);
+ void OnNodeHierarchyChanged(Id node_id,
+ Id new_parent_id,
+ Id old_parent_id,
+ Id server_change_id,
+ Array<NodeDataPtr> nodes);
+ void OnNodeReordered(Id node_id,
+ Id relative_node_id,
+ OrderDirection direction,
+ Id server_change_id);
+ void OnNodeDeleted(Id node_id, Id server_change_id);
+ void OnViewDeleted(Id view_id);
+ void OnNodeViewReplaced(Id node_id, Id new_view_id, Id old_view_id);
+ void OnViewInputEvent(Id view_id, EventPtr event);
+
+ private:
+ void AddChange(const Change& change);
+
+ Delegate* delegate_;
+ std::vector<Change> changes_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestChangeTracker);
+};
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_TEST_CHANGE_TRACKER_H_
diff --git a/chromium/mojo/services/view_manager/view.cc b/chromium/mojo/services/view_manager/view.cc
new file mode 100644
index 00000000000..c33f5f847fc
--- /dev/null
+++ b/chromium/mojo/services/view_manager/view.cc
@@ -0,0 +1,28 @@
+// Copyright 2014 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 "mojo/services/view_manager/view.h"
+
+#include "mojo/services/view_manager/node.h"
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+View::View(const ViewId& id) : id_(id), node_(NULL) {}
+
+View::~View() {
+}
+
+void View::SetBitmap(const SkBitmap& bitmap) {
+ bitmap_ = bitmap;
+ if (node_) {
+ node_->window()->SchedulePaintInRect(
+ gfx::Rect(node_->window()->bounds().size()));
+ }
+}
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/view_manager/view.h b/chromium/mojo/services/view_manager/view.h
new file mode 100644
index 00000000000..c4b0c0521b6
--- /dev/null
+++ b/chromium/mojo/services/view_manager/view.h
@@ -0,0 +1,50 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_VIEW_H_
+#define MOJO_SERVICES_VIEW_MANAGER_VIEW_H_
+
+#include <vector>
+
+#include "base/logging.h"
+#include "mojo/services/view_manager/ids.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+class Node;
+
+// Represents a view. A view may be associated with a single Node.
+class MOJO_VIEW_MANAGER_EXPORT View {
+ public:
+ explicit View(const ViewId& id);
+ ~View();
+
+ const ViewId& id() const { return id_; }
+
+ Node* node() { return node_; }
+
+ void SetBitmap(const SkBitmap& contents);
+ const SkBitmap& bitmap() const { return bitmap_; }
+
+ private:
+ // Node is responsible for maintaining |node_|.
+ friend class Node;
+
+ void set_node(Node* node) { node_ = node; }
+
+ const ViewId id_;
+ Node* node_;
+ SkBitmap bitmap_;
+
+ DISALLOW_COPY_AND_ASSIGN(View);
+};
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_VIEW_H_
diff --git a/chromium/mojo/services/view_manager/view_manager_export.h b/chromium/mojo/services/view_manager/view_manager_export.h
new file mode 100644
index 00000000000..29b0e6219c9
--- /dev/null
+++ b/chromium/mojo/services/view_manager/view_manager_export.h
@@ -0,0 +1,31 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_EXPORT_H_
+#define MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(MOJO_VIEW_MANAGER_IMPLEMENTATION)
+#define MOJO_VIEW_MANAGER_EXPORT __declspec(dllexport)
+#else
+#define MOJO_VIEW_MANAGER_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_VIEW_MANAGER_IMPLEMENTATION)
+#define MOJO_VIEW_MANAGER_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_VIEW_MANAGER_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#else // defined(COMPONENT_BUILD)
+#define MOJO_VIEW_MANAGER_EXPORT
+#endif
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_EXPORT_H_
diff --git a/chromium/mojo/services/view_manager/view_manager_init_service_impl.cc b/chromium/mojo/services/view_manager/view_manager_init_service_impl.cc
new file mode 100644
index 00000000000..be2ce15935b
--- /dev/null
+++ b/chromium/mojo/services/view_manager/view_manager_init_service_impl.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 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 "mojo/services/view_manager/view_manager_init_service_impl.h"
+
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+#include "mojo/services/view_manager/ids.h"
+#include "mojo/services/view_manager/view_manager_service_impl.h"
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+ViewManagerInitServiceImpl::ConnectParams::ConnectParams() {}
+
+ViewManagerInitServiceImpl::ConnectParams::~ConnectParams() {}
+
+ViewManagerInitServiceImpl::ViewManagerInitServiceImpl(
+ ServiceProvider* service_provider)
+ : service_provider_(service_provider),
+ root_node_manager_(service_provider, this),
+ is_tree_host_ready_(false) {
+}
+
+ViewManagerInitServiceImpl::~ViewManagerInitServiceImpl() {
+}
+
+void ViewManagerInitServiceImpl::MaybeEmbedRoot(
+ const std::string& url,
+ const Callback<void(bool)>& callback) {
+ if (!is_tree_host_ready_)
+ return;
+
+ root_node_manager_.EmbedRoot(url);
+ callback.Run(true);
+}
+
+void ViewManagerInitServiceImpl::EmbedRoot(
+ const String& url,
+ const Callback<void(bool)>& callback) {
+ if (connect_params_.get()) {
+ DVLOG(1) << "Ignoring second connect";
+ callback.Run(false);
+ return;
+ }
+ connect_params_.reset(new ConnectParams);
+ connect_params_->url = url.To<std::string>();
+ connect_params_->callback = callback;
+ MaybeEmbedRoot(url.To<std::string>(), callback);
+}
+
+void ViewManagerInitServiceImpl::OnRootViewManagerWindowTreeHostCreated() {
+ DCHECK(!is_tree_host_ready_);
+ is_tree_host_ready_ = true;
+ if (connect_params_.get())
+ MaybeEmbedRoot(connect_params_->url, connect_params_->callback);
+}
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/view_manager/view_manager_init_service_impl.h b/chromium/mojo/services/view_manager/view_manager_init_service_impl.h
new file mode 100644
index 00000000000..59d438ef666
--- /dev/null
+++ b/chromium/mojo/services/view_manager/view_manager_init_service_impl.h
@@ -0,0 +1,79 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_INIT_SERVICE_IMPL_H_
+#define MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_INIT_SERVICE_IMPL_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "mojo/services/view_manager/root_node_manager.h"
+#include "mojo/services/view_manager/root_view_manager_delegate.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace mojo {
+
+class ServiceProvider;
+
+namespace view_manager {
+namespace service {
+
+#if defined(OS_WIN)
+// Equivalent of NON_EXPORTED_BASE which does not work with the template snafu
+// below.
+#pragma warning(push)
+#pragma warning(disable : 4275)
+#endif
+
+// Used to create the initial ViewManagerClient. Doesn't initiate the Connect()
+// until the WindowTreeHost has been created.
+class MOJO_VIEW_MANAGER_EXPORT ViewManagerInitServiceImpl
+ : public InterfaceImpl<ViewManagerInitService>,
+ public RootViewManagerDelegate {
+ public:
+ explicit ViewManagerInitServiceImpl(ServiceProvider* service_provider);
+ virtual ~ViewManagerInitServiceImpl();
+
+ private:
+ struct ConnectParams {
+ ConnectParams();
+ ~ConnectParams();
+
+ std::string url;
+ Callback<void(bool)> callback;
+ };
+
+ void MaybeEmbedRoot(const std::string& url,
+ const Callback<void(bool)>& callback);
+
+ // ViewManagerInitService overrides:
+ virtual void EmbedRoot(const String& url,
+ const Callback<void(bool)>& callback) OVERRIDE;
+
+ // RootViewManagerDelegate overrides:
+ virtual void OnRootViewManagerWindowTreeHostCreated() OVERRIDE;
+
+ ServiceProvider* service_provider_;
+
+ RootNodeManager root_node_manager_;
+
+ // Parameters passed to Connect(). If non-null Connect() has been invoked.
+ scoped_ptr<ConnectParams> connect_params_;
+
+ bool is_tree_host_ready_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerInitServiceImpl);
+};
+
+#if defined(OS_WIN)
+#pragma warning(pop)
+#endif
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_INIT_SERVICE_IMPL_H_
diff --git a/chromium/mojo/services/view_manager/view_manager_service_impl.cc b/chromium/mojo/services/view_manager/view_manager_service_impl.cc
new file mode 100644
index 00000000000..0af11c3f167
--- /dev/null
+++ b/chromium/mojo/services/view_manager/view_manager_service_impl.cc
@@ -0,0 +1,803 @@
+// Copyright 2014 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 "mojo/services/view_manager/view_manager_service_impl.h"
+
+#include "base/bind.h"
+#include "base/stl_util.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/input_events/input_events_type_converters.h"
+#include "mojo/services/view_manager/node.h"
+#include "mojo/services/view_manager/root_node_manager.h"
+#include "mojo/services/view_manager/view.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/aura/window.h"
+#include "ui/gfx/codec/png_codec.h"
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+namespace {
+
+// Places |node| in |nodes| and recurses through the children.
+void GetDescendants(const Node* node, std::vector<const Node*>* nodes) {
+ if (!node)
+ return;
+
+ nodes->push_back(node);
+
+ std::vector<const Node*> children(node->GetChildren());
+ for (size_t i = 0 ; i < children.size(); ++i)
+ GetDescendants(children[i], nodes);
+}
+
+} // namespace
+
+ViewManagerServiceImpl::ViewManagerServiceImpl(
+ RootNodeManager* root_node_manager,
+ ConnectionSpecificId creator_id,
+ const std::string& creator_url,
+ const std::string& url)
+ : root_node_manager_(root_node_manager),
+ id_(root_node_manager_->GetAndAdvanceNextConnectionId()),
+ url_(url),
+ creator_id_(creator_id),
+ creator_url_(creator_url),
+ delete_on_connection_error_(false) {
+}
+
+ViewManagerServiceImpl::~ViewManagerServiceImpl() {
+ // Delete any views we own.
+ while (!view_map_.empty()) {
+ bool result = DeleteViewImpl(this, view_map_.begin()->second->id());
+ DCHECK(result);
+ }
+
+ // We're about to destroy all our nodes. Detach any views from them.
+ for (NodeMap::iterator i = node_map_.begin(); i != node_map_.end(); ++i) {
+ if (i->second->view()) {
+ bool result = SetViewImpl(i->second, ViewId());
+ DCHECK(result);
+ }
+ }
+
+ if (!node_map_.empty()) {
+ RootNodeManager::ScopedChange change(
+ this, root_node_manager_,
+ RootNodeManager::CHANGE_TYPE_ADVANCE_SERVER_CHANGE_ID, true);
+ while (!node_map_.empty()) {
+ scoped_ptr<Node> node(node_map_.begin()->second);
+ Node* parent = node->GetParent();
+ const NodeId node_id(node->id());
+ if (parent)
+ parent->Remove(node.get());
+ root_node_manager_->ProcessNodeDeleted(node_id);
+ node_map_.erase(NodeIdToTransportId(node_id));
+ }
+ }
+
+ root_node_manager_->RemoveConnection(this);
+}
+
+const Node* ViewManagerServiceImpl::GetNode(const NodeId& id) const {
+ if (id_ == id.connection_id) {
+ NodeMap::const_iterator i = node_map_.find(id.node_id);
+ return i == node_map_.end() ? NULL : i->second;
+ }
+ return root_node_manager_->GetNode(id);
+}
+
+const View* ViewManagerServiceImpl::GetView(const ViewId& id) const {
+ if (id_ == id.connection_id) {
+ ViewMap::const_iterator i = view_map_.find(id.view_id);
+ return i == view_map_.end() ? NULL : i->second;
+ }
+ return root_node_manager_->GetView(id);
+}
+
+void ViewManagerServiceImpl::SetRoots(const Array<Id>& node_ids) {
+ DCHECK(roots_.empty());
+ NodeIdSet roots;
+ for (size_t i = 0; i < node_ids.size(); ++i) {
+ DCHECK(GetNode(NodeIdFromTransportId(node_ids[i])));
+ roots.insert(node_ids[i]);
+ }
+ roots_.swap(roots);
+}
+
+void ViewManagerServiceImpl::OnViewManagerServiceImplDestroyed(
+ ConnectionSpecificId id) {
+ if (creator_id_ == id)
+ creator_id_ = kRootConnection;
+}
+
+void ViewManagerServiceImpl::ProcessNodeBoundsChanged(
+ const Node* node,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds,
+ bool originated_change) {
+ if (originated_change)
+ return;
+ Id node_id = NodeIdToTransportId(node->id());
+ if (known_nodes_.count(node_id) > 0) {
+ client()->OnNodeBoundsChanged(node_id,
+ Rect::From(old_bounds),
+ Rect::From(new_bounds));
+ }
+}
+
+void ViewManagerServiceImpl::ProcessNodeHierarchyChanged(
+ const Node* node,
+ const Node* new_parent,
+ const Node* old_parent,
+ Id server_change_id,
+ bool originated_change) {
+ if (known_nodes_.count(NodeIdToTransportId(node->id())) > 0) {
+ if (originated_change)
+ return;
+ if (node->id().connection_id != id_ && !IsNodeDescendantOfRoots(node)) {
+ // Node was a descendant of roots and is no longer, treat it as though the
+ // node was deleted.
+ RemoveFromKnown(node);
+ client()->OnNodeDeleted(NodeIdToTransportId(node->id()),
+ server_change_id);
+ root_node_manager_->OnConnectionMessagedClient(id_);
+ return;
+ }
+ }
+
+ if (originated_change || root_node_manager_->is_processing_delete_node())
+ return;
+ std::vector<const Node*> to_send;
+ if (!ShouldNotifyOnHierarchyChange(node, &new_parent, &old_parent,
+ &to_send)) {
+ if (root_node_manager_->IsProcessingChange()) {
+ client()->OnServerChangeIdAdvanced(
+ root_node_manager_->next_server_change_id() + 1);
+ }
+ return;
+ }
+ const NodeId new_parent_id(new_parent ? new_parent->id() : NodeId());
+ const NodeId old_parent_id(old_parent ? old_parent->id() : NodeId());
+ DCHECK((node->id().connection_id == id_) ||
+ (roots_.count(NodeIdToTransportId(node->id())) > 0) ||
+ (new_parent && IsNodeDescendantOfRoots(new_parent)) ||
+ (old_parent && IsNodeDescendantOfRoots(old_parent)));
+ client()->OnNodeHierarchyChanged(NodeIdToTransportId(node->id()),
+ NodeIdToTransportId(new_parent_id),
+ NodeIdToTransportId(old_parent_id),
+ server_change_id,
+ NodesToNodeDatas(to_send));
+}
+
+void ViewManagerServiceImpl::ProcessNodeReorder(const Node* node,
+ const Node* relative_node,
+ OrderDirection direction,
+ Id server_change_id,
+ bool originated_change) {
+ if (originated_change ||
+ !known_nodes_.count(NodeIdToTransportId(node->id())) ||
+ !known_nodes_.count(NodeIdToTransportId(relative_node->id()))) {
+ return;
+ }
+
+ client()->OnNodeReordered(NodeIdToTransportId(node->id()),
+ NodeIdToTransportId(relative_node->id()),
+ direction,
+ server_change_id);
+}
+
+void ViewManagerServiceImpl::ProcessNodeViewReplaced(
+ const Node* node,
+ const View* new_view,
+ const View* old_view,
+ bool originated_change) {
+ if (originated_change || !known_nodes_.count(NodeIdToTransportId(node->id())))
+ return;
+ const Id new_view_id = new_view ?
+ ViewIdToTransportId(new_view->id()) : 0;
+ const Id old_view_id = old_view ?
+ ViewIdToTransportId(old_view->id()) : 0;
+ client()->OnNodeViewReplaced(NodeIdToTransportId(node->id()),
+ new_view_id, old_view_id);
+}
+
+void ViewManagerServiceImpl::ProcessNodeDeleted(const NodeId& node,
+ Id server_change_id,
+ bool originated_change) {
+ const bool in_known = known_nodes_.erase(NodeIdToTransportId(node)) > 0;
+ const bool in_roots = roots_.erase(NodeIdToTransportId(node)) > 0;
+
+ if (in_roots && roots_.empty())
+ roots_.insert(NodeIdToTransportId(InvalidNodeId()));
+
+ if (originated_change)
+ return;
+
+ if (in_known) {
+ client()->OnNodeDeleted(NodeIdToTransportId(node), server_change_id);
+ root_node_manager_->OnConnectionMessagedClient(id_);
+ } else if (root_node_manager_->IsProcessingChange() &&
+ !root_node_manager_->DidConnectionMessageClient(id_)) {
+ client()->OnServerChangeIdAdvanced(
+ root_node_manager_->next_server_change_id() + 1);
+ root_node_manager_->OnConnectionMessagedClient(id_);
+ }
+}
+
+void ViewManagerServiceImpl::ProcessViewDeleted(const ViewId& view,
+ bool originated_change) {
+ if (originated_change)
+ return;
+ client()->OnViewDeleted(ViewIdToTransportId(view));
+}
+
+void ViewManagerServiceImpl::OnConnectionError() {
+ if (delete_on_connection_error_)
+ delete this;
+}
+
+bool ViewManagerServiceImpl::CanRemoveNodeFromParent(const Node* node) const {
+ if (!node)
+ return false;
+
+ const Node* parent = node->GetParent();
+ if (!parent)
+ return false;
+
+ // Always allow the remove if there are no roots. Otherwise the remove is
+ // allowed if the parent is a descendant of the roots, or the node and its
+ // parent were created by this connection. We explicitly disallow removal of
+ // the node from its parent if the parent isn't visible to this connection
+ // (not in roots).
+ return (roots_.empty() ||
+ (IsNodeDescendantOfRoots(parent) ||
+ (node->id().connection_id == id_ &&
+ parent->id().connection_id == id_)));
+}
+
+bool ViewManagerServiceImpl::CanAddNode(const Node* parent,
+ const Node* child) const {
+ if (!parent || !child)
+ return false; // Both nodes must be valid.
+
+ if (child->GetParent() == parent || child->Contains(parent))
+ return false; // Would result in an invalid hierarchy.
+
+ if (roots_.empty())
+ return true; // No restriction if there are no roots.
+
+ if (!IsNodeDescendantOfRoots(parent) && parent->id().connection_id != id_)
+ return false; // |parent| is not visible to this connection.
+
+ // Allow the add if the child is already a descendant of the roots or was
+ // created by this connection.
+ return (IsNodeDescendantOfRoots(child) || child->id().connection_id == id_);
+}
+
+bool ViewManagerServiceImpl::CanReorderNode(const Node* node,
+ const Node* relative_node,
+ OrderDirection direction) const {
+ if (!node || !relative_node)
+ return false;
+
+ if (node->id().connection_id != id_)
+ return false;
+
+ const Node* parent = node->GetParent();
+ if (!parent || parent != relative_node->GetParent())
+ return false;
+
+ if (known_nodes_.count(NodeIdToTransportId(parent->id())) == 0)
+ return false;
+
+ std::vector<const Node*> children = parent->GetChildren();
+ const size_t child_i =
+ std::find(children.begin(), children.end(), node) - children.begin();
+ const size_t target_i =
+ std::find(children.begin(), children.end(), relative_node) -
+ children.begin();
+ if ((direction == ORDER_ABOVE && child_i == target_i + 1) ||
+ (direction == ORDER_BELOW && child_i + 1 == target_i)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ViewManagerServiceImpl::CanDeleteNode(const NodeId& node_id) const {
+ return node_id.connection_id == id_;
+}
+
+bool ViewManagerServiceImpl::CanDeleteView(const ViewId& view_id) const {
+ return view_id.connection_id == id_;
+}
+
+bool ViewManagerServiceImpl::CanSetView(const Node* node,
+ const ViewId& view_id) const {
+ if (!node || !IsNodeDescendantOfRoots(node))
+ return false;
+
+ const View* view = GetView(view_id);
+ return (view && view_id.connection_id == id_) || view_id == ViewId();
+}
+
+bool ViewManagerServiceImpl::CanSetFocus(const Node* node) const {
+ // TODO(beng): security.
+ return true;
+}
+
+bool ViewManagerServiceImpl::CanGetNodeTree(const Node* node) const {
+ return node &&
+ (IsNodeDescendantOfRoots(node) || node->id().connection_id == id_);
+}
+
+bool ViewManagerServiceImpl::CanEmbed(
+ const mojo::Array<uint32_t>& node_ids) const {
+ for (size_t i = 0; i < node_ids.size(); ++i) {
+ const Node* node = GetNode(NodeIdFromTransportId(node_ids[i]));
+ if (!node || node->id().connection_id != id_)
+ return false;
+ }
+ return node_ids.size() > 0;
+}
+
+bool ViewManagerServiceImpl::DeleteNodeImpl(ViewManagerServiceImpl* source,
+ const NodeId& node_id) {
+ DCHECK_EQ(node_id.connection_id, id_);
+ Node* node = GetNode(node_id);
+ if (!node)
+ return false;
+ RootNodeManager::ScopedChange change(
+ source, root_node_manager_,
+ RootNodeManager::CHANGE_TYPE_ADVANCE_SERVER_CHANGE_ID, true);
+ if (node->GetParent())
+ node->GetParent()->Remove(node);
+ std::vector<Node*> children(node->GetChildren());
+ for (size_t i = 0; i < children.size(); ++i)
+ node->Remove(children[i]);
+ DCHECK(node->GetChildren().empty());
+ node_map_.erase(node_id.node_id);
+ delete node;
+ node = NULL;
+ root_node_manager_->ProcessNodeDeleted(node_id);
+ return true;
+}
+
+bool ViewManagerServiceImpl::DeleteViewImpl(ViewManagerServiceImpl* source,
+ const ViewId& view_id) {
+ DCHECK_EQ(view_id.connection_id, id_);
+ View* view = GetView(view_id);
+ if (!view)
+ return false;
+ RootNodeManager::ScopedChange change(
+ source, root_node_manager_,
+ RootNodeManager::CHANGE_TYPE_DONT_ADVANCE_SERVER_CHANGE_ID, false);
+ if (view->node())
+ view->node()->SetView(NULL);
+ view_map_.erase(view_id.view_id);
+ // Make a copy of |view_id| as once we delete view |view_id| may no longer be
+ // valid.
+ const ViewId view_id_copy(view_id);
+ delete view;
+ root_node_manager_->ProcessViewDeleted(view_id_copy);
+ return true;
+}
+
+bool ViewManagerServiceImpl::SetViewImpl(Node* node, const ViewId& view_id) {
+ DCHECK(node); // CanSetView() should have verified node exists.
+ View* view = GetView(view_id);
+ RootNodeManager::ScopedChange change(
+ this, root_node_manager_,
+ RootNodeManager::CHANGE_TYPE_DONT_ADVANCE_SERVER_CHANGE_ID, false);
+ node->SetView(view);
+
+ // TODO(sky): this is temporary, need a real focus API.
+ if (view && root_node_manager_->root()->Contains(node))
+ node->window()->Focus();
+
+ return true;
+}
+
+void ViewManagerServiceImpl::GetUnknownNodesFrom(
+ const Node* node,
+ std::vector<const Node*>* nodes) {
+ const Id transport_id = NodeIdToTransportId(node->id());
+ if (known_nodes_.count(transport_id) == 1)
+ return;
+ nodes->push_back(node);
+ known_nodes_.insert(transport_id);
+ std::vector<const Node*> children(node->GetChildren());
+ for (size_t i = 0 ; i < children.size(); ++i)
+ GetUnknownNodesFrom(children[i], nodes);
+}
+
+void ViewManagerServiceImpl::RemoveFromKnown(const Node* node) {
+ if (node->id().connection_id == id_)
+ return;
+ known_nodes_.erase(NodeIdToTransportId(node->id()));
+ std::vector<const Node*> children = node->GetChildren();
+ for (size_t i = 0; i < children.size(); ++i)
+ RemoveFromKnown(children[i]);
+}
+
+bool ViewManagerServiceImpl::AddRoots(
+ const std::vector<Id>& node_ids) {
+ std::vector<const Node*> to_send;
+ bool did_add_root = false;
+ for (size_t i = 0; i < node_ids.size(); ++i) {
+ CHECK_EQ(creator_id_, NodeIdFromTransportId(node_ids[i]).connection_id);
+ if (roots_.count(node_ids[i]) > 0)
+ continue;
+
+ did_add_root = true;
+ roots_.insert(node_ids[i]);
+ Node* node = GetNode(NodeIdFromTransportId(node_ids[i]));
+ DCHECK(node);
+ if (known_nodes_.count(node_ids[i]) == 0) {
+ GetUnknownNodesFrom(node, &to_send);
+ } else {
+ // Even though the connection knows about the new root we need to tell it
+ // |node| is now a root.
+ to_send.push_back(node);
+ }
+ }
+
+ if (!did_add_root)
+ return false;
+
+ client()->OnRootsAdded(NodesToNodeDatas(to_send));
+ return true;
+}
+
+bool ViewManagerServiceImpl::IsNodeDescendantOfRoots(const Node* node) const {
+ if (roots_.empty())
+ return true;
+ if (!node)
+ return false;
+ const Id invalid_node_id =
+ NodeIdToTransportId(InvalidNodeId());
+ for (NodeIdSet::const_iterator i = roots_.begin(); i != roots_.end(); ++i) {
+ if (*i == invalid_node_id)
+ continue;
+ const Node* root = GetNode(NodeIdFromTransportId(*i));
+ DCHECK(root);
+ if (root->Contains(node))
+ return true;
+ }
+ return false;
+}
+
+bool ViewManagerServiceImpl::ShouldNotifyOnHierarchyChange(
+ const Node* node,
+ const Node** new_parent,
+ const Node** old_parent,
+ std::vector<const Node*>* to_send) {
+ // If the node is not in |roots_| or was never known to this connection then
+ // don't notify the client about it.
+ if (node->id().connection_id != id_ &&
+ known_nodes_.count(NodeIdToTransportId(node->id())) == 0 &&
+ !IsNodeDescendantOfRoots(node)) {
+ return false;
+ }
+ if (!IsNodeDescendantOfRoots(*new_parent))
+ *new_parent = NULL;
+ if (!IsNodeDescendantOfRoots(*old_parent))
+ *old_parent = NULL;
+
+ if (*new_parent) {
+ // On getting a new parent we may need to communicate new nodes to the
+ // client. We do that in the following cases:
+ // . New parent is a descendant of the roots. In this case the client
+ // already knows all ancestors, so we only have to communicate descendants
+ // of node the client doesn't know about.
+ // . If the client knew about the parent, we have to do the same.
+ // . If the client knows about the node and is added to a tree the client
+ // doesn't know about we have to communicate from the root down (the
+ // client is learning about a new root).
+ if (root_node_manager_->root()->Contains(*new_parent) ||
+ known_nodes_.count(NodeIdToTransportId((*new_parent)->id()))) {
+ GetUnknownNodesFrom(node, to_send);
+ return true;
+ }
+ // If parent wasn't known we have to communicate from the root down.
+ if (known_nodes_.count(NodeIdToTransportId(node->id()))) {
+ // No need to check against |roots_| as client should always know it's
+ // |roots_|.
+ GetUnknownNodesFrom((*new_parent)->GetRoot(), to_send);
+ return true;
+ }
+ }
+ // Otherwise only communicate the change if the node was known. We shouldn't
+ // need to communicate any nodes on a remove.
+ return known_nodes_.count(NodeIdToTransportId(node->id())) > 0;
+}
+
+Array<NodeDataPtr> ViewManagerServiceImpl::NodesToNodeDatas(
+ const std::vector<const Node*>& nodes) {
+ Array<NodeDataPtr> array(nodes.size());
+ for (size_t i = 0; i < nodes.size(); ++i) {
+ const Node* node = nodes[i];
+ DCHECK(known_nodes_.count(NodeIdToTransportId(node->id())) > 0);
+ const Node* parent = node->GetParent();
+ // If the parent isn't known, it means the parent is not visible to us (not
+ // in roots), and should not be sent over.
+ if (parent && known_nodes_.count(NodeIdToTransportId(parent->id())) == 0)
+ parent = NULL;
+ NodeDataPtr inode(NodeData::New());
+ inode->parent_id = NodeIdToTransportId(parent ? parent->id() : NodeId());
+ inode->node_id = NodeIdToTransportId(node->id());
+ inode->view_id =
+ ViewIdToTransportId(node->view() ? node->view()->id() : ViewId());
+ inode->bounds = Rect::From(node->bounds());
+ array[i] = inode.Pass();
+ }
+ return array.Pass();
+}
+
+void ViewManagerServiceImpl::CreateNode(
+ Id transport_node_id,
+ const Callback<void(bool)>& callback) {
+ const NodeId node_id(NodeIdFromTransportId(transport_node_id));
+ if (node_id.connection_id != id_ ||
+ node_map_.find(node_id.node_id) != node_map_.end()) {
+ callback.Run(false);
+ return;
+ }
+ node_map_[node_id.node_id] = new Node(this, node_id);
+ known_nodes_.insert(transport_node_id);
+ callback.Run(true);
+}
+
+void ViewManagerServiceImpl::DeleteNode(
+ Id transport_node_id,
+ Id server_change_id,
+ const Callback<void(bool)>& callback) {
+ const NodeId node_id(NodeIdFromTransportId(transport_node_id));
+ bool success = false;
+ if (server_change_id == root_node_manager_->next_server_change_id() &&
+ CanDeleteNode(node_id)) {
+ ViewManagerServiceImpl* connection = root_node_manager_->GetConnection(
+ node_id.connection_id);
+ success = connection && connection->DeleteNodeImpl(this, node_id);
+ }
+ callback.Run(success);
+}
+
+void ViewManagerServiceImpl::AddNode(
+ Id parent_id,
+ Id child_id,
+ Id server_change_id,
+ const Callback<void(bool)>& callback) {
+ bool success = false;
+ if (server_change_id == root_node_manager_->next_server_change_id()) {
+ Node* parent = GetNode(NodeIdFromTransportId(parent_id));
+ Node* child = GetNode(NodeIdFromTransportId(child_id));
+ if (CanAddNode(parent, child)) {
+ success = true;
+ RootNodeManager::ScopedChange change(
+ this, root_node_manager_,
+ RootNodeManager::CHANGE_TYPE_ADVANCE_SERVER_CHANGE_ID, false);
+ parent->Add(child);
+ }
+ }
+ callback.Run(success);
+}
+
+void ViewManagerServiceImpl::RemoveNodeFromParent(
+ Id node_id,
+ Id server_change_id,
+ const Callback<void(bool)>& callback) {
+ bool success = false;
+ if (server_change_id == root_node_manager_->next_server_change_id()) {
+ Node* node = GetNode(NodeIdFromTransportId(node_id));
+ if (CanRemoveNodeFromParent(node)) {
+ success = true;
+ RootNodeManager::ScopedChange change(
+ this, root_node_manager_,
+ RootNodeManager::CHANGE_TYPE_ADVANCE_SERVER_CHANGE_ID, false);
+ node->GetParent()->Remove(node);
+ }
+ }
+ callback.Run(success);
+}
+
+void ViewManagerServiceImpl::ReorderNode(Id node_id,
+ Id relative_node_id,
+ OrderDirection direction,
+ Id server_change_id,
+ const Callback<void(bool)>& callback) {
+ bool success = false;
+ if (server_change_id == root_node_manager_->next_server_change_id()) {
+ Node* node = GetNode(NodeIdFromTransportId(node_id));
+ Node* relative_node = GetNode(NodeIdFromTransportId(relative_node_id));
+ if (CanReorderNode(node, relative_node, direction)) {
+ success = true;
+ RootNodeManager::ScopedChange change(
+ this, root_node_manager_,
+ RootNodeManager::CHANGE_TYPE_ADVANCE_SERVER_CHANGE_ID, false);
+ node->GetParent()->Reorder(node, relative_node, direction);
+ root_node_manager_->ProcessNodeReorder(node, relative_node, direction);
+ }
+ }
+ callback.Run(success);
+}
+
+void ViewManagerServiceImpl::GetNodeTree(
+ Id node_id,
+ const Callback<void(Array<NodeDataPtr>)>& callback) {
+ Node* node = GetNode(NodeIdFromTransportId(node_id));
+ std::vector<const Node*> nodes;
+ if (CanGetNodeTree(node)) {
+ GetDescendants(node, &nodes);
+ for (size_t i = 0; i < nodes.size(); ++i)
+ known_nodes_.insert(NodeIdToTransportId(nodes[i]->id()));
+ }
+ callback.Run(NodesToNodeDatas(nodes));
+}
+
+void ViewManagerServiceImpl::CreateView(
+ Id transport_view_id,
+ const Callback<void(bool)>& callback) {
+ const ViewId view_id(ViewIdFromTransportId(transport_view_id));
+ if (view_id.connection_id != id_ || view_map_.count(view_id.view_id)) {
+ callback.Run(false);
+ return;
+ }
+ view_map_[view_id.view_id] = new View(view_id);
+ callback.Run(true);
+}
+
+void ViewManagerServiceImpl::DeleteView(
+ Id transport_view_id,
+ const Callback<void(bool)>& callback) {
+ const ViewId view_id(ViewIdFromTransportId(transport_view_id));
+ bool did_delete = CanDeleteView(view_id);
+ if (did_delete) {
+ ViewManagerServiceImpl* connection = root_node_manager_->GetConnection(
+ view_id.connection_id);
+ did_delete = (connection && connection->DeleteViewImpl(this, view_id));
+ }
+ callback.Run(did_delete);
+}
+
+void ViewManagerServiceImpl::SetView(Id transport_node_id,
+ Id transport_view_id,
+ const Callback<void(bool)>& callback) {
+ Node* node = GetNode(NodeIdFromTransportId(transport_node_id));
+ const ViewId view_id(ViewIdFromTransportId(transport_view_id));
+ callback.Run(CanSetView(node, view_id) && SetViewImpl(node, view_id));
+}
+
+void ViewManagerServiceImpl::SetViewContents(
+ Id view_id,
+ ScopedSharedBufferHandle buffer,
+ uint32_t buffer_size,
+ const Callback<void(bool)>& callback) {
+ View* view = GetView(ViewIdFromTransportId(view_id));
+ if (!view) {
+ callback.Run(false);
+ return;
+ }
+ void* handle_data;
+ if (MapBuffer(buffer.get(), 0, buffer_size, &handle_data,
+ MOJO_MAP_BUFFER_FLAG_NONE) != MOJO_RESULT_OK) {
+ callback.Run(false);
+ return;
+ }
+ SkBitmap bitmap;
+ gfx::PNGCodec::Decode(static_cast<const unsigned char*>(handle_data),
+ buffer_size, &bitmap);
+ view->SetBitmap(bitmap);
+ UnmapBuffer(handle_data);
+ callback.Run(true);
+}
+
+void ViewManagerServiceImpl::SetFocus(Id node_id,
+ const Callback<void(bool)> & callback) {
+ bool success = false;
+ Node* node = GetNode(NodeIdFromTransportId(node_id));
+ if (CanSetFocus(node)) {
+ success = true;
+ node->window()->Focus();
+ }
+ callback.Run(success);
+}
+
+void ViewManagerServiceImpl::SetNodeBounds(
+ Id node_id,
+ RectPtr bounds,
+ const Callback<void(bool)>& callback) {
+ if (NodeIdFromTransportId(node_id).connection_id != id_) {
+ callback.Run(false);
+ return;
+ }
+
+ Node* node = GetNode(NodeIdFromTransportId(node_id));
+ if (!node) {
+ callback.Run(false);
+ return;
+ }
+
+ RootNodeManager::ScopedChange change(
+ this, root_node_manager_,
+ RootNodeManager::CHANGE_TYPE_DONT_ADVANCE_SERVER_CHANGE_ID, false);
+ gfx::Rect old_bounds = node->window()->bounds();
+ node->window()->SetBounds(bounds.To<gfx::Rect>());
+ root_node_manager_->ProcessNodeBoundsChanged(
+ node, old_bounds, bounds.To<gfx::Rect>());
+ callback.Run(true);
+}
+
+void ViewManagerServiceImpl::Embed(const String& url,
+ Array<uint32_t> node_ids,
+ const Callback<void(bool)>& callback) {
+ bool success = CanEmbed(node_ids);
+ if (success) {
+ // We may already have this connection, if so reuse it.
+ ViewManagerServiceImpl* existing_connection =
+ root_node_manager_->GetConnectionByCreator(id_, url.To<std::string>());
+ if (existing_connection)
+ success = existing_connection->AddRoots(node_ids.storage());
+ else
+ root_node_manager_->Embed(id_, url, node_ids);
+ }
+ callback.Run(success);
+}
+
+void ViewManagerServiceImpl::DispatchOnViewInputEvent(Id transport_view_id,
+ EventPtr event) {
+ // We only allow the WM to dispatch events. At some point this function will
+ // move to a separate interface and the check can go away.
+ if (id_ != kWindowManagerConnection)
+ return;
+
+ const ViewId view_id(ViewIdFromTransportId(transport_view_id));
+ ViewManagerServiceImpl* connection = root_node_manager_->GetConnection(
+ view_id.connection_id);
+ if (connection)
+ connection->client()->OnViewInputEvent(
+ transport_view_id,
+ event.Pass(),
+ base::Bind(&base::DoNothing));
+}
+
+void ViewManagerServiceImpl::OnNodeHierarchyChanged(const Node* node,
+ const Node* new_parent,
+ const Node* old_parent) {
+ root_node_manager_->ProcessNodeHierarchyChanged(node, new_parent, old_parent);
+}
+
+void ViewManagerServiceImpl::OnNodeViewReplaced(const Node* node,
+ const View* new_view,
+ const View* old_view) {
+ root_node_manager_->ProcessNodeViewReplaced(node, new_view, old_view);
+}
+
+void ViewManagerServiceImpl::OnViewInputEvent(const View* view,
+ const ui::Event* event) {
+ root_node_manager_->DispatchViewInputEventToWindowManager(view, event);
+}
+
+void ViewManagerServiceImpl::OnConnectionEstablished() {
+ root_node_manager_->AddConnection(this);
+
+ std::vector<const Node*> to_send;
+ if (roots_.empty()) {
+ GetUnknownNodesFrom(root_node_manager_->root(), &to_send);
+ } else {
+ for (NodeIdSet::const_iterator i = roots_.begin(); i != roots_.end(); ++i)
+ GetUnknownNodesFrom(GetNode(NodeIdFromTransportId(*i)), &to_send);
+ }
+
+ client()->OnViewManagerConnectionEstablished(
+ id_,
+ creator_url_,
+ root_node_manager_->next_server_change_id(),
+ NodesToNodeDatas(to_send));
+}
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/view_manager/view_manager_service_impl.h b/chromium/mojo/services/view_manager/view_manager_service_impl.h
new file mode 100644
index 00000000000..53950dc3c11
--- /dev/null
+++ b/chromium/mojo/services/view_manager/view_manager_service_impl.h
@@ -0,0 +1,269 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_SERVICE_IMPL_H_
+#define MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_SERVICE_IMPL_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/containers/hash_tables.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "mojo/services/view_manager/ids.h"
+#include "mojo/services/view_manager/node_delegate.h"
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+class Node;
+class RootNodeManager;
+class View;
+
+#if defined(OS_WIN)
+// Equivalent of NON_EXPORTED_BASE which does not work with the template snafu
+// below.
+#pragma warning(push)
+#pragma warning(disable : 4275)
+#endif
+
+// Manages a connection from the client.
+class MOJO_VIEW_MANAGER_EXPORT ViewManagerServiceImpl
+ : public InterfaceImpl<ViewManagerService>,
+ public NodeDelegate {
+ public:
+ ViewManagerServiceImpl(RootNodeManager* root_node_manager,
+ ConnectionSpecificId creator_id,
+ const std::string& creator_url,
+ const std::string& url);
+ virtual ~ViewManagerServiceImpl();
+
+ // Used to mark this connection as originating from a call to
+ // ViewManagerService::Connect(). When set OnConnectionError() deletes |this|.
+ void set_delete_on_connection_error() { delete_on_connection_error_ = true; }
+
+ ConnectionSpecificId id() const { return id_; }
+ ConnectionSpecificId creator_id() const { return creator_id_; }
+ const std::string& url() const { return url_; }
+
+ // Returns the Node with the specified id.
+ Node* GetNode(const NodeId& id) {
+ return const_cast<Node*>(
+ const_cast<const ViewManagerServiceImpl*>(this)->GetNode(id));
+ }
+ const Node* GetNode(const NodeId& id) const;
+
+ // Returns the View with the specified id.
+ View* GetView(const ViewId& id) {
+ return const_cast<View*>(
+ const_cast<const ViewManagerServiceImpl*>(this)->GetView(id));
+ }
+ const View* GetView(const ViewId& id) const;
+
+ void SetRoots(const Array<Id>& node_ids);
+
+ // Invoked when a connection is destroyed.
+ void OnViewManagerServiceImplDestroyed(ConnectionSpecificId id);
+
+ // The following methods are invoked after the corresponding change has been
+ // processed. They do the appropriate bookkeeping and update the client as
+ // necessary.
+ void ProcessNodeBoundsChanged(const Node* node,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds,
+ bool originated_change);
+ void ProcessNodeHierarchyChanged(const Node* node,
+ const Node* new_parent,
+ const Node* old_parent,
+ Id server_change_id,
+ bool originated_change);
+ void ProcessNodeReorder(const Node* node,
+ const Node* relative_node,
+ OrderDirection direction,
+ Id server_change_id,
+ bool originated_change);
+ void ProcessNodeViewReplaced(const Node* node,
+ const View* new_view,
+ const View* old_view,
+ bool originated_change);
+ void ProcessNodeDeleted(const NodeId& node,
+ Id server_change_id,
+ bool originated_change);
+ void ProcessViewDeleted(const ViewId& view, bool originated_change);
+
+ // TODO(sky): move this to private section (currently can't because of
+ // bindings).
+ // InterfaceImp overrides:
+ virtual void OnConnectionError() MOJO_OVERRIDE;
+
+ private:
+ typedef std::map<ConnectionSpecificId, Node*> NodeMap;
+ typedef std::map<ConnectionSpecificId, View*> ViewMap;
+ typedef base::hash_set<Id> NodeIdSet;
+
+ // These functions return true if the corresponding mojom function is allowed
+ // for this connection.
+ bool CanRemoveNodeFromParent(const Node* node) const;
+ bool CanAddNode(const Node* parent, const Node* child) const;
+ bool CanReorderNode(const Node* node,
+ const Node* relative_node,
+ OrderDirection direction) const;
+ bool CanDeleteNode(const NodeId& node_id) const;
+ bool CanDeleteView(const ViewId& view_id) const;
+ bool CanSetView(const Node* node, const ViewId& view_id) const;
+ bool CanSetFocus(const Node* node) const;
+ bool CanGetNodeTree(const Node* node) const;
+ bool CanEmbed(const mojo::Array<uint32_t>& node_ids) const;
+
+ // Deletes a node owned by this connection. Returns true on success. |source|
+ // is the connection that originated the change.
+ bool DeleteNodeImpl(ViewManagerServiceImpl* source, const NodeId& node_id);
+
+ // Deletes a view owned by this connection. Returns true on success. |source|
+ // is the connection that originated the change.
+ bool DeleteViewImpl(ViewManagerServiceImpl* source, const ViewId& view_id);
+
+ // Sets the view associated with a node.
+ bool SetViewImpl(Node* node, const ViewId& view_id);
+
+ // If |node| is known (in |known_nodes_|) does nothing. Otherwise adds |node|
+ // to |nodes|, marks |node| as known and recurses.
+ void GetUnknownNodesFrom(const Node* node, std::vector<const Node*>* nodes);
+
+ // Removes |node| and all its descendants from |known_nodes_|. This does not
+ // recurse through nodes that were created by this connection.
+ void RemoveFromKnown(const Node* node);
+
+ // Adds |node_ids| to roots, returning true if at least one of the nodes was
+ // not already a root. If at least one of the nodes was not already a root
+ // the client is told of the new roots.
+ bool AddRoots(const std::vector<Id>& node_ids);
+
+ // Returns true if |node| is a non-null and a descendant of |roots_| (or
+ // |roots_| is empty).
+ bool IsNodeDescendantOfRoots(const Node* node) const;
+
+ // Returns true if notification should be sent of a hierarchy change. If true
+ // is returned, any nodes that need to be sent to the client are added to
+ // |to_send|.
+ bool ShouldNotifyOnHierarchyChange(const Node* node,
+ const Node** new_parent,
+ const Node** old_parent,
+ std::vector<const Node*>* to_send);
+
+ // Converts an array of Nodes to NodeDatas. This assumes all the nodes are
+ // valid for the client. The parent of nodes the client is not allowed to see
+ // are set to NULL (in the returned NodeDatas).
+ Array<NodeDataPtr> NodesToNodeDatas(const std::vector<const Node*>& nodes);
+
+ // Overridden from ViewManagerService:
+ virtual void CreateNode(Id transport_node_id,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void DeleteNode(Id transport_node_id,
+ Id server_change_id,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void AddNode(Id parent_id,
+ Id child_id,
+ Id server_change_id,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void RemoveNodeFromParent(
+ Id node_id,
+ Id server_change_id,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void ReorderNode(Id node_id,
+ Id relative_node_id,
+ OrderDirection direction,
+ Id server_change_id,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void GetNodeTree(
+ Id node_id,
+ const Callback<void(Array<NodeDataPtr>)>& callback) OVERRIDE;
+ virtual void CreateView(Id transport_view_id,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void DeleteView(Id transport_view_id,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void SetView(Id transport_node_id,
+ Id transport_view_id,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void SetViewContents(Id view_id,
+ ScopedSharedBufferHandle buffer,
+ uint32_t buffer_size,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void SetFocus(Id node_id,
+ const Callback<void(bool)> & callback) OVERRIDE;
+ virtual void SetNodeBounds(Id node_id,
+ RectPtr bounds,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void Embed(const mojo::String& url,
+ mojo::Array<uint32_t> node_ids,
+ const mojo::Callback<void(bool)>& callback) OVERRIDE;
+ virtual void DispatchOnViewInputEvent(Id transport_view_id,
+ EventPtr event) OVERRIDE;
+
+ // Overridden from NodeDelegate:
+ virtual void OnNodeHierarchyChanged(const Node* node,
+ const Node* new_parent,
+ const Node* old_parent) OVERRIDE;
+ virtual void OnNodeViewReplaced(const Node* node,
+ const View* new_view,
+ const View* old_view) OVERRIDE;
+ virtual void OnViewInputEvent(const View* view,
+ const ui::Event* event) OVERRIDE;
+
+ // InterfaceImp overrides:
+ virtual void OnConnectionEstablished() MOJO_OVERRIDE;
+
+ RootNodeManager* root_node_manager_;
+
+ // Id of this connection as assigned by RootNodeManager.
+ const ConnectionSpecificId id_;
+
+ // URL this connection was created for.
+ const std::string url_;
+
+ // ID of the connection that created us. If 0 it indicates either we were
+ // created by the root, or the connection that created us has been destroyed.
+ ConnectionSpecificId creator_id_;
+
+ // The URL of the app that embedded the app this connection was created for.
+ const std::string creator_url_;
+
+ NodeMap node_map_;
+
+ ViewMap view_map_;
+
+ // The set of nodes that has been communicated to the client.
+ NodeIdSet known_nodes_;
+
+ // This is the set of nodes the connection can parent nodes to (in addition to
+ // any nodes created by this connection). If empty the connection can
+ // manipulate any nodes (except for deleting other connections nodes/views).
+ // The connection can not delete or move these. If this is set to a non-empty
+ // value and all the nodes are deleted (by another connection), then an
+ // invalid node is added here to ensure this connection is still constrained.
+ NodeIdSet roots_;
+
+ // See description above setter.
+ bool delete_on_connection_error_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerServiceImpl);
+};
+
+#if defined(OS_WIN)
+#pragma warning(pop)
+#endif
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_SERVICE_IMPL_H_
diff --git a/chromium/mojo/services/view_manager/view_manager_unittest.cc b/chromium/mojo/services/view_manager/view_manager_unittest.cc
new file mode 100644
index 00000000000..ed540926a89
--- /dev/null
+++ b/chromium/mojo/services/view_manager/view_manager_unittest.cc
@@ -0,0 +1,1370 @@
+// Copyright 2014 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 <vector>
+
+#include "base/at_exit.h"
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/public/cpp/application/connect.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
+#include "mojo/service_manager/service_manager.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/view_manager/types.h"
+#include "mojo/services/public/cpp/view_manager/util.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "mojo/services/view_manager/ids.h"
+#include "mojo/services/view_manager/test_change_tracker.h"
+#include "mojo/shell/shell_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+namespace {
+
+const char kTestServiceURL[] = "mojo:test_url";
+
+// ViewManagerProxy is a proxy to an ViewManagerService. It handles invoking
+// ViewManagerService functions on the right thread in a synchronous manner
+// (each ViewManagerService cover function blocks until the response from the
+// ViewManagerService is returned). In addition it tracks the set of
+// ViewManagerClient messages received by way of a vector of Changes. Use
+// DoRunLoopUntilChangesCount() to wait for a certain number of messages to be
+// received.
+class ViewManagerProxy : public TestChangeTracker::Delegate {
+ public:
+ explicit ViewManagerProxy(TestChangeTracker* tracker)
+ : tracker_(tracker),
+ view_manager_(NULL),
+ quit_count_(0),
+ router_(NULL) {
+ SetInstance(this);
+ }
+
+ virtual ~ViewManagerProxy() {}
+
+ // Runs a message loop until the single instance has been created.
+ static ViewManagerProxy* WaitForInstance() {
+ if (!instance_)
+ RunMainLoop();
+ ViewManagerProxy* instance = instance_;
+ instance_ = NULL;
+ return instance;
+ }
+
+ ViewManagerService* view_manager() { return view_manager_; }
+
+ // Runs the main loop until |count| changes have been received.
+ std::vector<Change> DoRunLoopUntilChangesCount(size_t count) {
+ DCHECK_EQ(0u, quit_count_);
+ if (tracker_->changes()->size() >= count) {
+ CopyChangesFromTracker();
+ return changes_;
+ }
+ quit_count_ = count - tracker_->changes()->size();
+ // Run the current message loop. When |count| Changes have been received,
+ // we'll quit.
+ RunMainLoop();
+ return changes_;
+ }
+
+ const std::vector<Change>& changes() const { return changes_; }
+
+ // Destroys the connection, blocking until done.
+ void Destroy() {
+ router_->CloseMessagePipe();
+ }
+
+ // The following functions are cover methods for ViewManagerService. They
+ // block until the result is received.
+ bool CreateNode(Id node_id) {
+ changes_.clear();
+ bool result = false;
+ view_manager_->CreateNode(node_id,
+ base::Bind(&ViewManagerProxy::GotResult,
+ base::Unretained(this), &result));
+ RunMainLoop();
+ return result;
+ }
+ bool AddNode(Id parent, Id child, Id server_change_id) {
+ changes_.clear();
+ bool result = false;
+ view_manager_->AddNode(parent, child, server_change_id,
+ base::Bind(&ViewManagerProxy::GotResult,
+ base::Unretained(this), &result));
+ RunMainLoop();
+ return result;
+ }
+ bool RemoveNodeFromParent(Id node_id, Id server_change_id) {
+ changes_.clear();
+ bool result = false;
+ view_manager_->RemoveNodeFromParent(node_id, server_change_id,
+ base::Bind(&ViewManagerProxy::GotResult,
+ base::Unretained(this), &result));
+ RunMainLoop();
+ return result;
+ }
+ bool ReorderNode(Id node_id,
+ Id relative_node_id,
+ OrderDirection direction,
+ Id server_change_id) {
+ changes_.clear();
+ bool result = false;
+ view_manager_->ReorderNode(node_id, relative_node_id, direction,
+ server_change_id,
+ base::Bind(&ViewManagerProxy::GotResult,
+ base::Unretained(this), &result));
+ RunMainLoop();
+ return result;
+ }
+ bool SetView(Id node_id, Id view_id) {
+ changes_.clear();
+ bool result = false;
+ view_manager_->SetView(node_id, view_id,
+ base::Bind(&ViewManagerProxy::GotResult,
+ base::Unretained(this), &result));
+ RunMainLoop();
+ return result;
+ }
+ bool CreateView(Id view_id) {
+ changes_.clear();
+ bool result = false;
+ view_manager_->CreateView(view_id,
+ base::Bind(&ViewManagerProxy::GotResult,
+ base::Unretained(this), &result));
+ RunMainLoop();
+ return result;
+ }
+ void GetNodeTree(Id node_id, std::vector<TestNode>* nodes) {
+ changes_.clear();
+ view_manager_->GetNodeTree(node_id,
+ base::Bind(&ViewManagerProxy::GotNodeTree,
+ base::Unretained(this), nodes));
+ RunMainLoop();
+ }
+ bool Embed(const std::vector<Id>& nodes) {
+ changes_.clear();
+ base::AutoReset<bool> auto_reset(&in_embed_, true);
+ bool result = false;
+ view_manager_->Embed(kTestServiceURL, Array<Id>::From(nodes),
+ base::Bind(&ViewManagerProxy::GotResult,
+ base::Unretained(this), &result));
+ RunMainLoop();
+ return result;
+ }
+ bool DeleteNode(Id node_id, Id server_change_id) {
+ changes_.clear();
+ bool result = false;
+ view_manager_->DeleteNode(node_id,
+ server_change_id,
+ base::Bind(&ViewManagerProxy::GotResult,
+ base::Unretained(this), &result));
+ RunMainLoop();
+ return result;
+ }
+ bool DeleteView(Id view_id) {
+ changes_.clear();
+ bool result = false;
+ view_manager_->DeleteView(view_id,
+ base::Bind(&ViewManagerProxy::GotResult,
+ base::Unretained(this), &result));
+ RunMainLoop();
+ return result;
+ }
+ bool SetNodeBounds(Id node_id, const gfx::Rect& bounds) {
+ changes_.clear();
+ bool result = false;
+ view_manager_->SetNodeBounds(node_id, Rect::From(bounds),
+ base::Bind(&ViewManagerProxy::GotResult,
+ base::Unretained(this), &result));
+ RunMainLoop();
+ return result;
+ }
+
+ private:
+ friend class TestViewManagerClientConnection;
+
+ void set_router(mojo::internal::Router* router) { router_ = router; }
+
+ void set_view_manager(ViewManagerService* view_manager) {
+ view_manager_ = view_manager;
+ }
+
+ static void RunMainLoop() {
+ DCHECK(!main_run_loop_);
+ main_run_loop_ = new base::RunLoop;
+ main_run_loop_->Run();
+ delete main_run_loop_;
+ main_run_loop_ = NULL;
+ }
+
+ void QuitCountReached() {
+ CopyChangesFromTracker();
+ main_run_loop_->Quit();
+ }
+
+ void CopyChangesFromTracker() {
+ std::vector<Change> changes;
+ tracker_->changes()->swap(changes);
+ changes_.swap(changes);
+ }
+
+ static void SetInstance(ViewManagerProxy* instance) {
+ DCHECK(!instance_);
+ instance_ = instance;
+ // Embed() runs its own run loop that is quit when the result is
+ // received. Embed() also results in a new instance. If we quit here while
+ // waiting for a Embed() we would prematurely return before we got the
+ // result from Embed().
+ if (!in_embed_ && main_run_loop_)
+ main_run_loop_->Quit();
+ }
+
+ // Callbacks from the various ViewManagerService functions.
+ void GotResult(bool* result_cache, bool result) {
+ *result_cache = result;
+ DCHECK(main_run_loop_);
+ main_run_loop_->Quit();
+ }
+
+ void GotNodeTree(std::vector<TestNode>* nodes, Array<NodeDataPtr> results) {
+ NodeDatasToTestNodes(results, nodes);
+ DCHECK(main_run_loop_);
+ main_run_loop_->Quit();
+ }
+
+ // TestChangeTracker::Delegate:
+ virtual void OnChangeAdded() OVERRIDE {
+ if (quit_count_ > 0 && --quit_count_ == 0)
+ QuitCountReached();
+ }
+
+ static ViewManagerProxy* instance_;
+ static base::RunLoop* main_run_loop_;
+ static bool in_embed_;
+
+ TestChangeTracker* tracker_;
+
+ // MessageLoop of the test.
+ base::MessageLoop* main_loop_;
+
+ ViewManagerService* view_manager_;
+
+ // Number of changes we're waiting on until we quit the current loop.
+ size_t quit_count_;
+
+ std::vector<Change> changes_;
+
+ mojo::internal::Router* router_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerProxy);
+};
+
+// static
+ViewManagerProxy* ViewManagerProxy::instance_ = NULL;
+
+// static
+base::RunLoop* ViewManagerProxy::main_run_loop_ = NULL;
+
+// static
+bool ViewManagerProxy::in_embed_ = false;
+
+class TestViewManagerClientConnection
+ : public InterfaceImpl<ViewManagerClient> {
+ public:
+ TestViewManagerClientConnection() : connection_(&tracker_) {
+ tracker_.set_delegate(&connection_);
+ }
+
+ // InterfaceImp:
+ virtual void OnConnectionEstablished() OVERRIDE {
+ connection_.set_router(internal_state()->router());
+ connection_.set_view_manager(client());
+ }
+
+ // ViewMangerClient:
+ virtual void OnViewManagerConnectionEstablished(
+ ConnectionSpecificId connection_id,
+ const String& creator_url,
+ Id next_server_change_id,
+ Array<NodeDataPtr> nodes) OVERRIDE {
+ tracker_.OnViewManagerConnectionEstablished(
+ connection_id, creator_url, next_server_change_id, nodes.Pass());
+ }
+ virtual void OnRootsAdded(Array<NodeDataPtr> nodes) OVERRIDE {
+ tracker_.OnRootsAdded(nodes.Pass());
+ }
+ virtual void OnServerChangeIdAdvanced(
+ Id next_server_change_id) OVERRIDE {
+ tracker_.OnServerChangeIdAdvanced(next_server_change_id);
+ }
+ virtual void OnNodeBoundsChanged(Id node_id,
+ RectPtr old_bounds,
+ RectPtr new_bounds) OVERRIDE {
+ tracker_.OnNodeBoundsChanged(node_id, old_bounds.Pass(), new_bounds.Pass());
+ }
+ virtual void OnNodeHierarchyChanged(Id node,
+ Id new_parent,
+ Id old_parent,
+ Id server_change_id,
+ Array<NodeDataPtr> nodes) OVERRIDE {
+ tracker_.OnNodeHierarchyChanged(node, new_parent, old_parent,
+ server_change_id, nodes.Pass());
+ }
+ virtual void OnNodeReordered(Id node_id,
+ Id relative_node_id,
+ OrderDirection direction,
+ Id server_change_id) OVERRIDE {
+ tracker_.OnNodeReordered(node_id, relative_node_id, direction,
+ server_change_id);
+ }
+ virtual void OnNodeDeleted(Id node, Id server_change_id) OVERRIDE {
+ tracker_.OnNodeDeleted(node, server_change_id);
+ }
+ virtual void OnViewDeleted(Id view) OVERRIDE {
+ tracker_.OnViewDeleted(view);
+ }
+ virtual void OnNodeViewReplaced(Id node,
+ Id new_view_id,
+ Id old_view_id) OVERRIDE {
+ tracker_.OnNodeViewReplaced(node, new_view_id, old_view_id);
+ }
+ virtual void OnViewInputEvent(Id view_id,
+ EventPtr event,
+ const Callback<void()>& callback) OVERRIDE {
+ tracker_.OnViewInputEvent(view_id, event.Pass());
+ }
+ virtual void DispatchOnViewInputEvent(Id view_id,
+ mojo::EventPtr event) OVERRIDE {
+ }
+
+ private:
+ TestChangeTracker tracker_;
+ ViewManagerProxy connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestViewManagerClientConnection);
+};
+
+// Used with ViewManagerService::Embed(). Creates a
+// TestViewManagerClientConnection, which creates and owns the ViewManagerProxy.
+class EmbedServiceLoader : public ServiceLoader {
+ public:
+ EmbedServiceLoader() {}
+ virtual ~EmbedServiceLoader() {}
+
+ // ServiceLoader:
+ virtual void LoadService(ServiceManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle shell_handle) OVERRIDE {
+ scoped_ptr<Application> app(new Application(shell_handle.Pass()));
+ app->AddService<TestViewManagerClientConnection>();
+ apps_.push_back(app.release());
+ }
+ virtual void OnServiceError(ServiceManager* manager,
+ const GURL& url) OVERRIDE {
+ }
+
+ private:
+ ScopedVector<Application> apps_;
+
+ DISALLOW_COPY_AND_ASSIGN(EmbedServiceLoader);
+};
+
+// Creates an id used for transport from the specified parameters.
+Id BuildNodeId(ConnectionSpecificId connection_id,
+ ConnectionSpecificId node_id) {
+ return (connection_id << 16) | node_id;
+}
+
+// Creates an id used for transport from the specified parameters.
+Id BuildViewId(ConnectionSpecificId connection_id,
+ ConnectionSpecificId view_id) {
+ return (connection_id << 16) | view_id;
+}
+
+// Callback from EmbedRoot(). |result| is the result of the
+// Embed() call and |run_loop| the nested RunLoop.
+void EmbedRootCallback(bool* result_cache,
+ base::RunLoop* run_loop,
+ bool result) {
+ *result_cache = result;
+ run_loop->Quit();
+}
+
+// Resposible for establishing the initial ViewManagerService connection. Blocks
+// until result is determined.
+bool EmbedRoot(ViewManagerInitService* view_manager_init,
+ const std::string& url) {
+ bool result = false;
+ base::RunLoop run_loop;
+ view_manager_init->EmbedRoot(url, base::Bind(&EmbedRootCallback,
+ &result, &run_loop));
+ run_loop.Run();
+ return result;
+}
+
+} // namespace
+
+typedef std::vector<std::string> Changes;
+
+class ViewManagerTest : public testing::Test {
+ public:
+ ViewManagerTest() : connection_(NULL), connection2_(NULL) {}
+
+ virtual void SetUp() OVERRIDE {
+ test_helper_.Init();
+
+ test_helper_.SetLoaderForURL(
+ scoped_ptr<ServiceLoader>(new EmbedServiceLoader()),
+ GURL(kTestServiceURL));
+
+ ConnectToService(test_helper_.service_provider(),
+ "mojo:mojo_view_manager",
+ &view_manager_init_);
+ ASSERT_TRUE(EmbedRoot(view_manager_init_.get(), kTestServiceURL));
+
+ connection_ = ViewManagerProxy::WaitForInstance();
+ ASSERT_TRUE(connection_ != NULL);
+ connection_->DoRunLoopUntilChangesCount(1);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ if (connection2_)
+ connection2_->Destroy();
+ if (connection_)
+ connection_->Destroy();
+ }
+
+ protected:
+ void EstablishSecondConnectionWithRoots(Id id1, Id id2) {
+ std::vector<Id> node_ids;
+ node_ids.push_back(id1);
+ if (id2 != 0)
+ node_ids.push_back(id2);
+ ASSERT_TRUE(connection_->Embed(node_ids));
+ connection2_ = ViewManagerProxy::WaitForInstance();
+ ASSERT_TRUE(connection2_ != NULL);
+ connection2_->DoRunLoopUntilChangesCount(1);
+ ASSERT_EQ(1u, connection2_->changes().size());
+ }
+
+ // Creates a second connection to the viewmanager.
+ void EstablishSecondConnection(bool create_initial_node) {
+ if (create_initial_node)
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1)));
+ ASSERT_NO_FATAL_FAILURE(
+ EstablishSecondConnectionWithRoots(BuildNodeId(1, 1), 0));
+ const std::vector<Change>& changes(connection2_->changes());
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("OnConnectionEstablished creator=mojo:test_url",
+ ChangesToDescription1(changes)[0]);
+ if (create_initial_node) {
+ EXPECT_EQ("[node=1,1 parent=null view=null]",
+ ChangeNodeDescription(changes));
+ }
+ }
+
+ void DestroySecondConnection() {
+ connection2_->Destroy();
+ connection2_ = NULL;
+ }
+
+ base::ShadowingAtExitManager at_exit_;
+ base::MessageLoop loop_;
+ shell::ShellTestHelper test_helper_;
+
+ ViewManagerInitServicePtr view_manager_init_;
+
+ ViewManagerProxy* connection_;
+ ViewManagerProxy* connection2_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerTest);
+};
+
+// TODO(sky): reenable tests: http://crbug.com/385475
+
+// Verifies client gets a valid id.
+TEST_F(ViewManagerTest, DISABLED_ValidId) {
+ // TODO(beng): this should really have the URL of the application that
+ // connected to ViewManagerInit.
+ EXPECT_EQ("OnConnectionEstablished creator=",
+ ChangesToDescription1(connection_->changes())[0]);
+
+ // All these tests assume 1 for the client id. The only real assertion here is
+ // the client id is not zero, but adding this as rest of code here assumes 1.
+ EXPECT_EQ(1, connection_->changes()[0].connection_id);
+
+ // Change ids start at 1 as well.
+ EXPECT_EQ(static_cast<Id>(1), connection_->changes()[0].change_id);
+}
+
+// Verifies two clients/connections get different ids.
+TEST_F(ViewManagerTest, DISABLED_TwoClientsGetDifferentConnectionIds) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+ EXPECT_EQ("OnConnectionEstablished creator=mojo:test_url",
+ ChangesToDescription1(connection2_->changes())[0]);
+
+ // It isn't strickly necessary that the second connection gets 2, but these
+ // tests are written assuming that is the case. The key thing is the
+ // connection ids of |connection_| and |connection2_| differ.
+ EXPECT_EQ(2, connection2_->changes()[0].connection_id);
+
+ // Change ids start at 1 as well.
+ EXPECT_EQ(static_cast<Id>(1), connection2_->changes()[0].change_id);
+}
+
+// Verifies client gets a valid id.
+TEST_F(ViewManagerTest, DISABLED_CreateNode) {
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1)));
+ EXPECT_TRUE(connection_->changes().empty());
+
+ // Can't create a node with the same id.
+ ASSERT_FALSE(connection_->CreateNode(BuildNodeId(1, 1)));
+ EXPECT_TRUE(connection_->changes().empty());
+
+ // Can't create a node with a bogus connection id.
+ EXPECT_FALSE(connection_->CreateNode(BuildNodeId(2, 1)));
+ EXPECT_TRUE(connection_->changes().empty());
+}
+
+TEST_F(ViewManagerTest, DISABLED_CreateViewFailsWithBogusConnectionId) {
+ EXPECT_FALSE(connection_->CreateView(BuildViewId(2, 1)));
+ EXPECT_TRUE(connection_->changes().empty());
+}
+
+// Verifies hierarchy changes.
+TEST_F(ViewManagerTest, DISABLED_AddRemoveNotify) {
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 3)));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ // Make 3 a child of 2.
+ {
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 3), 1));
+ EXPECT_TRUE(connection_->changes().empty());
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("ServerChangeIdAdvanced 2", changes[0]);
+ }
+
+ // Remove 3 from its parent.
+ {
+ ASSERT_TRUE(connection_->RemoveNodeFromParent(BuildNodeId(1, 3), 2));
+ EXPECT_TRUE(connection_->changes().empty());
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("ServerChangeIdAdvanced 3", changes[0]);
+ }
+}
+
+// Verifies AddNode fails when node is already in position.
+TEST_F(ViewManagerTest, DISABLED_AddNodeWithNoChange) {
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 3)));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ // Make 3 a child of 2.
+ {
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 3), 1));
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("ServerChangeIdAdvanced 2", changes[0]);
+ }
+
+ // Try again, this should fail.
+ {
+ EXPECT_FALSE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 3), 2));
+ }
+}
+
+// Verifies AddNode fails when node is already in position.
+TEST_F(ViewManagerTest, DISABLED_AddAncestorFails) {
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 3)));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ // Make 3 a child of 2.
+ {
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 3), 1));
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("ServerChangeIdAdvanced 2", changes[0]);
+ }
+
+ // Try to make 2 a child of 3, this should fail since 2 is an ancestor of 3.
+ {
+ EXPECT_FALSE(connection_->AddNode(BuildNodeId(1, 3), BuildNodeId(1, 2), 2));
+ }
+}
+
+// Verifies adding with an invalid id fails.
+TEST_F(ViewManagerTest, DISABLED_AddWithInvalidServerId) {
+ // Create two nodes.
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+
+ // Make 2 a child of 1. Supply an invalid change id, which should fail.
+ ASSERT_FALSE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 0));
+}
+
+// Verifies adding to root sends right notifications.
+TEST_F(ViewManagerTest, DISABLED_AddToRoot) {
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 21)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 3)));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ // Make 3 a child of 21.
+ {
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 21), BuildNodeId(1, 3), 1));
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("ServerChangeIdAdvanced 2", changes[0]);
+ }
+
+ // Make 21 a child of 1.
+ {
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 21), 2));
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ(
+ "HierarchyChanged change_id=2 node=1,21 new_parent=1,1 old_parent=null",
+ changes[0]);
+ }
+}
+
+// Verifies HierarchyChanged is correctly sent for various adds/removes.
+TEST_F(ViewManagerTest, DISABLED_NodeHierarchyChangedNodes) {
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 11)));
+ // Make 11 a child of 2.
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 11), 1));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ // Make 2 a child of 1.
+ {
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 2));
+
+ // Client 2 should get a hierarchy change that includes the new nodes as it
+ // has not yet seen them.
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ(
+ "HierarchyChanged change_id=2 node=1,2 new_parent=1,1 old_parent=null",
+ changes[0]);
+ EXPECT_EQ("[node=1,2 parent=1,1 view=null],"
+ "[node=1,11 parent=1,2 view=null]",
+ ChangeNodeDescription(connection2_->changes()));
+ }
+
+ // Add 1 to the root.
+ {
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 3));
+
+ // Client 2 should get a hierarchy change that includes the new nodes as it
+ // has not yet seen them.
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ(
+ "HierarchyChanged change_id=3 node=1,1 new_parent=null old_parent=null",
+ changes[0]);
+ EXPECT_EQ(std::string(), ChangeNodeDescription(connection2_->changes()));
+ }
+
+ // Remove 1 from its parent.
+ {
+ ASSERT_TRUE(connection_->RemoveNodeFromParent(BuildNodeId(1, 1), 4));
+ EXPECT_TRUE(connection_->changes().empty());
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ(
+ "HierarchyChanged change_id=4 node=1,1 new_parent=null old_parent=null",
+ changes[0]);
+ }
+
+ // Create another node, 111, parent it to 11.
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 111)));
+ {
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 11), BuildNodeId(1, 111),
+ 5));
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ(
+ "HierarchyChanged change_id=5 node=1,111 new_parent=1,11 "
+ "old_parent=null", changes[0]);
+ EXPECT_EQ("[node=1,111 parent=1,11 view=null]",
+ ChangeNodeDescription(connection2_->changes()));
+ }
+
+ // Reattach 1 to the root.
+ {
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 6));
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ(
+ "HierarchyChanged change_id=6 node=1,1 new_parent=null old_parent=null",
+ changes[0]);
+ EXPECT_EQ(std::string(), ChangeNodeDescription(connection2_->changes()));
+ }
+}
+
+TEST_F(ViewManagerTest, DISABLED_NodeHierarchyChangedAddingKnownToUnknown) {
+ // Create the following structure: root -> 1 -> 11 and 2->21 (2 has no
+ // parent).
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 11)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 21)));
+
+ // Set up the hierarchy.
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 1));
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 11), 2));
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 21), 3));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+ {
+ EXPECT_EQ("[node=1,1 parent=null view=null],"
+ "[node=1,11 parent=1,1 view=null]",
+ ChangeNodeDescription(connection2_->changes()));
+ }
+
+ // Remove 11, should result in a delete (since 11 is no longer in connection
+ // 2's root).
+ {
+ ASSERT_TRUE(connection_->RemoveNodeFromParent(BuildNodeId(1, 11), 4));
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("NodeDeleted change_id=4 node=1,11", changes[0]);
+ }
+
+ // Add 2 to 1.
+ {
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 5));
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ(
+ "HierarchyChanged change_id=5 node=1,2 new_parent=1,1 old_parent=null",
+ changes[0]);
+ EXPECT_EQ("[node=1,2 parent=1,1 view=null],"
+ "[node=1,21 parent=1,2 view=null]",
+ ChangeNodeDescription(connection2_->changes()));
+ }
+}
+
+TEST_F(ViewManagerTest, DISABLED_ReorderNode) {
+ Id node1_id = BuildNodeId(1, 1);
+ Id node2_id = BuildNodeId(1, 2);
+ Id node3_id = BuildNodeId(1, 3);
+ Id node4_id = BuildNodeId(1, 4); // Peer to 1,1
+ Id node5_id = BuildNodeId(1, 5); // Peer to 1,1
+ Id node6_id = BuildNodeId(1, 6); // Child of 1,2.
+ Id node7_id = BuildNodeId(1, 7); // Unparented.
+ Id node8_id = BuildNodeId(1, 8); // Unparented.
+ ASSERT_TRUE(connection_->CreateNode(node1_id));
+ ASSERT_TRUE(connection_->CreateNode(node2_id));
+ ASSERT_TRUE(connection_->CreateNode(node3_id));
+ ASSERT_TRUE(connection_->CreateNode(node4_id));
+ ASSERT_TRUE(connection_->CreateNode(node5_id));
+ ASSERT_TRUE(connection_->CreateNode(node6_id));
+ ASSERT_TRUE(connection_->CreateNode(node7_id));
+ ASSERT_TRUE(connection_->CreateNode(node8_id));
+ ASSERT_TRUE(connection_->AddNode(node1_id, node2_id, 1));
+ ASSERT_TRUE(connection_->AddNode(node2_id, node6_id, 2));
+ ASSERT_TRUE(connection_->AddNode(node1_id, node3_id, 3));
+ ASSERT_TRUE(connection_->AddNode(
+ NodeIdToTransportId(RootNodeId()), node4_id, 4));
+ ASSERT_TRUE(connection_->AddNode(
+ NodeIdToTransportId(RootNodeId()), node5_id, 5));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+ {
+ connection_->ReorderNode(node2_id, node3_id, ORDER_ABOVE, 6);
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ(
+ "Reordered change_id=6 node=1,2 relative=1,3 direction=above",
+ changes[0]);
+ }
+
+ {
+ connection_->ReorderNode(node2_id, node3_id, ORDER_BELOW, 7);
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ(
+ "Reordered change_id=7 node=1,2 relative=1,3 direction=below",
+ changes[0]);
+ }
+
+ {
+ // node2 is already below node3.
+ EXPECT_FALSE(connection_->ReorderNode(node2_id, node3_id, ORDER_BELOW, 8));
+ }
+
+ {
+ // node4 & 5 are unknown to connection2_.
+ EXPECT_FALSE(connection2_->ReorderNode(node4_id, node5_id, ORDER_ABOVE, 8));
+ }
+
+ {
+ // node6 & node3 have different parents.
+ EXPECT_FALSE(connection_->ReorderNode(node3_id, node6_id, ORDER_ABOVE, 8));
+ }
+
+ {
+ // Non-existent node-ids
+ EXPECT_FALSE(connection_->ReorderNode(BuildNodeId(1, 27),
+ BuildNodeId(1, 28),
+ ORDER_ABOVE,
+ 8));
+ }
+
+ {
+ // node7 & node8 are un-parented.
+ EXPECT_FALSE(connection_->ReorderNode(node7_id, node8_id, ORDER_ABOVE, 8));
+ }
+}
+
+// Verifies DeleteNode works.
+TEST_F(ViewManagerTest, DISABLED_DeleteNode) {
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ // Make 2 a child of 1.
+ {
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 1));
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("HierarchyChanged change_id=1 node=1,2 new_parent=1,1 "
+ "old_parent=null", changes[0]);
+ }
+
+ // Delete 2.
+ {
+ ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 2), 2));
+ EXPECT_TRUE(connection_->changes().empty());
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("NodeDeleted change_id=2 node=1,2", changes[0]);
+ }
+}
+
+// Verifies DeleteNode isn't allowed from a separate connection.
+TEST_F(ViewManagerTest, DISABLED_DeleteNodeFromAnotherConnectionDisallowed) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+ EXPECT_FALSE(connection2_->DeleteNode(BuildNodeId(1, 1), 1));
+}
+
+// Verifies DeleteView isn't allowed from a separate connection.
+TEST_F(ViewManagerTest, DISABLED_DeleteViewFromAnotherConnectionDisallowed) {
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1)));
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+ EXPECT_FALSE(connection2_->DeleteView(BuildViewId(1, 1)));
+}
+
+// Verifies if a node was deleted and then reused that other clients are
+// properly notified.
+TEST_F(ViewManagerTest, DISABLED_ReuseDeletedNodeId) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+
+ // Add 2 to 1.
+ {
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 1));
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ EXPECT_EQ(
+ "HierarchyChanged change_id=1 node=1,2 new_parent=1,1 old_parent=null",
+ changes[0]);
+ EXPECT_EQ("[node=1,2 parent=1,1 view=null]",
+ ChangeNodeDescription(connection2_->changes()));
+ }
+
+ // Delete 2.
+ {
+ ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 2), 2));
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("NodeDeleted change_id=2 node=1,2", changes[0]);
+ }
+
+ // Create 2 again, and add it back to 1. Should get the same notification.
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+ {
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 3));
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ EXPECT_EQ(
+ "HierarchyChanged change_id=3 node=1,2 new_parent=1,1 old_parent=null",
+ changes[0]);
+ EXPECT_EQ("[node=1,2 parent=1,1 view=null]",
+ ChangeNodeDescription(connection2_->changes()));
+ }
+}
+
+// Assertions around setting a view.
+TEST_F(ViewManagerTest, DISABLED_SetView) {
+ // Create nodes 1, 2 and 3 and the view 11. Nodes 2 and 3 are parented to 1.
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 3)));
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 11)));
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 1));
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 3), 2));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+ // Set view 11 on node 1.
+ {
+ ASSERT_TRUE(connection_->SetView(BuildNodeId(1, 1),
+ BuildViewId(1, 11)));
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("ViewReplaced node=1,1 new_view=1,11 old_view=null",
+ changes[0]);
+ }
+
+ // Set view 11 on node 2.
+ {
+ ASSERT_TRUE(connection_->SetView(BuildNodeId(1, 2), BuildViewId(1, 11)));
+
+ connection2_->DoRunLoopUntilChangesCount(2);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(2u, changes.size());
+ EXPECT_EQ("ViewReplaced node=1,1 new_view=null old_view=1,11",
+ changes[0]);
+ EXPECT_EQ("ViewReplaced node=1,2 new_view=1,11 old_view=null",
+ changes[1]);
+ }
+}
+
+// Verifies deleting a node with a view sends correct notifications.
+TEST_F(ViewManagerTest, DISABLED_DeleteNodeWithView) {
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 3)));
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 11)));
+
+ // Set view 11 on node 2.
+ ASSERT_TRUE(connection_->SetView(BuildNodeId(1, 2), BuildViewId(1, 11)));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+ // Delete node 2. The second connection should not see this because the node
+ // was not known to it.
+ {
+ ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 2), 1));
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("ServerChangeIdAdvanced 2", changes[0]);
+ }
+
+ // Parent 3 to 1.
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 3), 2));
+ connection2_->DoRunLoopUntilChangesCount(1);
+
+ // Set view 11 on node 3.
+ {
+ ASSERT_TRUE(connection_->SetView(BuildNodeId(1, 3), BuildViewId(1, 11)));
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("ViewReplaced node=1,3 new_view=1,11 old_view=null", changes[0]);
+ }
+
+ // Delete 3.
+ {
+ ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 3), 3));
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("NodeDeleted change_id=3 node=1,3", changes[0]);
+ }
+}
+
+// Sets view from one connection on another.
+TEST_F(ViewManagerTest, DISABLED_SetViewFromSecondConnection) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+
+ // Create a view in the second connection.
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 51)));
+
+ // Attach view to node 1 in the first connection.
+ {
+ ASSERT_TRUE(connection2_->SetView(BuildNodeId(1, 1), BuildViewId(2, 51)));
+ connection_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("ViewReplaced node=1,1 new_view=2,51 old_view=null", changes[0]);
+ }
+
+ // Shutdown the second connection and verify view is removed.
+ {
+ DestroySecondConnection();
+ connection_->DoRunLoopUntilChangesCount(2);
+ const Changes changes(ChangesToDescription1(connection_->changes()));
+ ASSERT_EQ(2u, changes.size());
+ EXPECT_EQ("ViewReplaced node=1,1 new_view=null old_view=2,51", changes[0]);
+ EXPECT_EQ("ViewDeleted view=2,51", changes[1]);
+ }
+}
+
+// Assertions for GetNodeTree.
+TEST_F(ViewManagerTest, DISABLED_GetNodeTree) {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true));
+
+ // Create 11 in first connection and make it a child of 1.
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 11)));
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 1));
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 11), 2));
+
+ // Create two nodes in second connection, 2 and 3, both children of 1.
+ ASSERT_TRUE(connection2_->CreateNode(BuildNodeId(2, 2)));
+ ASSERT_TRUE(connection2_->CreateNode(BuildNodeId(2, 3)));
+ ASSERT_TRUE(connection2_->AddNode(BuildNodeId(1, 1), BuildNodeId(2, 2), 3));
+ ASSERT_TRUE(connection2_->AddNode(BuildNodeId(1, 1), BuildNodeId(2, 3), 4));
+
+ // Attach view to node 11 in the first connection.
+ ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 51)));
+ ASSERT_TRUE(connection_->SetView(BuildNodeId(1, 11), BuildViewId(1, 51)));
+
+ // Verifies GetNodeTree() on the root.
+ {
+ std::vector<TestNode> nodes;
+ connection_->GetNodeTree(BuildNodeId(0, 1), &nodes);
+ ASSERT_EQ(5u, nodes.size());
+ EXPECT_EQ("node=0,1 parent=null view=null", nodes[0].ToString());
+ EXPECT_EQ("node=1,1 parent=0,1 view=null", nodes[1].ToString());
+ EXPECT_EQ("node=1,11 parent=1,1 view=1,51", nodes[2].ToString());
+ EXPECT_EQ("node=2,2 parent=1,1 view=null", nodes[3].ToString());
+ EXPECT_EQ("node=2,3 parent=1,1 view=null", nodes[4].ToString());
+ }
+
+ // Verifies GetNodeTree() on the node 1,1.
+ {
+ std::vector<TestNode> nodes;
+ connection2_->GetNodeTree(BuildNodeId(1, 1), &nodes);
+ ASSERT_EQ(4u, nodes.size());
+ EXPECT_EQ("node=1,1 parent=null view=null", nodes[0].ToString());
+ EXPECT_EQ("node=1,11 parent=1,1 view=1,51", nodes[1].ToString());
+ EXPECT_EQ("node=2,2 parent=1,1 view=null", nodes[2].ToString());
+ EXPECT_EQ("node=2,3 parent=1,1 view=null", nodes[3].ToString());
+ }
+
+ // Connection 2 shouldn't be able to get the root tree.
+ {
+ std::vector<TestNode> nodes;
+ connection2_->GetNodeTree(BuildNodeId(0, 1), &nodes);
+ ASSERT_EQ(0u, nodes.size());
+ }
+}
+
+TEST_F(ViewManagerTest, DISABLED_SetNodeBounds) {
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1)));
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 1));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+ ASSERT_TRUE(connection_->SetNodeBounds(BuildNodeId(1, 1),
+ gfx::Rect(0, 0, 100, 100)));
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("BoundsChanged node=1,1 old_bounds=0,0 0x0 new_bounds=0,0 100x100",
+ changes[0]);
+
+ // Should not be possible to change the bounds of a node created by another
+ // connection.
+ ASSERT_FALSE(connection2_->SetNodeBounds(BuildNodeId(1, 1),
+ gfx::Rect(0, 0, 0, 0)));
+}
+
+// Various assertions around SetRoots.
+TEST_F(ViewManagerTest, DISABLED_SetRoots) {
+ // Create 1, 2, and 3 in the first connection.
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 3)));
+
+ // Parent 1 to the root.
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 1));
+
+ // Establish the second connection and give it the roots 1 and 3.
+ {
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnectionWithRoots(
+ BuildNodeId(1, 1), BuildNodeId(1, 3)));
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("OnConnectionEstablished creator=mojo:test_url", changes[0]);
+ EXPECT_EQ("[node=1,1 parent=null view=null],"
+ "[node=1,3 parent=null view=null]",
+ ChangeNodeDescription(connection2_->changes()));
+ }
+
+ // Create 4 and add it to the root, connection 2 should only get id advanced.
+ {
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 4)));
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 4), 2));
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("ServerChangeIdAdvanced 3", changes[0]);
+ }
+
+ // Move 4 under 3, this should expose 4 to the client.
+ {
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 3), BuildNodeId(1, 4), 3));
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ(
+ "HierarchyChanged change_id=3 node=1,4 new_parent=1,3 "
+ "old_parent=null", changes[0]);
+ EXPECT_EQ("[node=1,4 parent=1,3 view=null]",
+ ChangeNodeDescription(connection2_->changes()));
+ }
+
+ // Move 4 under 2, since 2 isn't a root client should get a delete.
+ {
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 4), 4));
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("NodeDeleted change_id=4 node=1,4", changes[0]);
+ }
+
+ // Delete 4, client shouldn't receive a delete since it should no longer know
+ // about 4.
+ {
+ ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 4), 5));
+
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("ServerChangeIdAdvanced 6", changes[0]);
+ }
+}
+
+// Verify AddNode fails when trying to manipulate nodes in other roots.
+TEST_F(ViewManagerTest, DISABLED_CantMoveNodesFromOtherRoot) {
+ // Create 1 and 2 in the first connection.
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+ // Try to move 2 to be a child of 1 from connection 2. This should fail as 2
+ // should not be able to access 1.
+ ASSERT_FALSE(connection2_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 1));
+
+ // Try to reparent 1 to the root. A connection is not allowed to reparent its
+ // roots.
+ ASSERT_FALSE(connection2_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 1));
+}
+
+// Verify RemoveNodeFromParent fails for nodes that are descendants of the
+// roots.
+TEST_F(ViewManagerTest, DISABLED_CantRemoveNodesInOtherRoots) {
+ // Create 1 and 2 in the first connection and parent both to the root.
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 1));
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 2), 2));
+
+ // Establish the second connection and give it the root 1.
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+ // Connection 2 should not be able to remove node 2 or 1 from its parent.
+ ASSERT_FALSE(connection2_->RemoveNodeFromParent(BuildNodeId(1, 2), 3));
+ ASSERT_FALSE(connection2_->RemoveNodeFromParent(BuildNodeId(1, 1), 3));
+
+ // Create nodes 10 and 11 in 2.
+ ASSERT_TRUE(connection2_->CreateNode(BuildNodeId(2, 10)));
+ ASSERT_TRUE(connection2_->CreateNode(BuildNodeId(2, 11)));
+
+ // Parent 11 to 10.
+ ASSERT_TRUE(connection2_->AddNode(BuildNodeId(2, 10), BuildNodeId(2, 11), 3));
+ // Remove 11 from 10.
+ ASSERT_TRUE(connection2_->RemoveNodeFromParent( BuildNodeId(2, 11), 4));
+
+ // Verify nothing was actually removed.
+ {
+ std::vector<TestNode> nodes;
+ connection_->GetNodeTree(BuildNodeId(0, 1), &nodes);
+ ASSERT_EQ(3u, nodes.size());
+ EXPECT_EQ("node=0,1 parent=null view=null", nodes[0].ToString());
+ EXPECT_EQ("node=1,1 parent=0,1 view=null", nodes[1].ToString());
+ EXPECT_EQ("node=1,2 parent=0,1 view=null", nodes[2].ToString());
+ }
+}
+
+// Verify SetView fails for nodes that are not descendants of the roots.
+TEST_F(ViewManagerTest, DISABLED_CantRemoveSetViewInOtherRoots) {
+ // Create 1 and 2 in the first connection and parent both to the root.
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 1));
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 2), 2));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+ // Create a view in the second connection.
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 51)));
+
+ // Connection 2 should be able to set the view on node 1 (it's root), but not
+ // on 2.
+ ASSERT_TRUE(connection2_->SetView(BuildNodeId(1, 1), BuildViewId(2, 51)));
+ ASSERT_FALSE(connection2_->SetView(BuildNodeId(1, 2), BuildViewId(2, 51)));
+}
+
+// Verify GetNodeTree fails for nodes that are not descendants of the roots.
+TEST_F(ViewManagerTest, DISABLED_CantGetNodeTreeOfOtherRoots) {
+ // Create 1 and 2 in the first connection and parent both to the root.
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 1));
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 2), 2));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+ std::vector<TestNode> nodes;
+
+ // Should get nothing for the root.
+ connection2_->GetNodeTree(BuildNodeId(0, 1), &nodes);
+ ASSERT_TRUE(nodes.empty());
+
+ // Should get nothing for node 2.
+ connection2_->GetNodeTree(BuildNodeId(1, 2), &nodes);
+ ASSERT_TRUE(nodes.empty());
+
+ // Should get node 1 if asked for.
+ connection2_->GetNodeTree(BuildNodeId(1, 1), &nodes);
+ ASSERT_EQ(1u, nodes.size());
+ EXPECT_EQ("node=1,1 parent=null view=null", nodes[0].ToString());
+}
+
+TEST_F(ViewManagerTest, DISABLED_ConnectTwice) {
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1)));
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2)));
+
+ ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 1));
+
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+
+ // Try to connect again to 1,1, this should fail as already connected to that
+ // root.
+ {
+ std::vector<Id> node_ids;
+ node_ids.push_back(BuildNodeId(1, 1));
+ ASSERT_FALSE(connection_->Embed(node_ids));
+ }
+
+ // Connecting to 1,2 should succeed and end up in connection2.
+ {
+ std::vector<Id> node_ids;
+ node_ids.push_back(BuildNodeId(1, 2));
+ ASSERT_TRUE(connection_->Embed(node_ids));
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("OnRootsAdded", changes[0]);
+ EXPECT_EQ("[node=1,2 parent=1,1 view=null]",
+ ChangeNodeDescription(connection2_->changes()));
+ }
+}
+
+TEST_F(ViewManagerTest, DISABLED_OnViewInput) {
+ // Create node 1 and assign a view from connection 2 to it.
+ ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1)));
+ ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false));
+ ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 11)));
+ ASSERT_TRUE(connection2_->SetView(BuildNodeId(1, 1), BuildViewId(2, 11)));
+
+ // Dispatch an event to the view and verify its received.
+ {
+ EventPtr event(Event::New());
+ event->action = 1;
+ connection_->view_manager()->DispatchOnViewInputEvent(
+ BuildViewId(2, 11),
+ event.Pass());
+ connection2_->DoRunLoopUntilChangesCount(1);
+ const Changes changes(ChangesToDescription1(connection2_->changes()));
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("InputEvent view=2,11 event_action=1", changes[0]);
+ }
+}
+
+// TODO(sky): add coverage of test that destroys connections and ensures other
+// connections get deletion notification (or advanced server id).
+
+// TODO(sky): need to better track changes to initial connection. For example,
+// that SetBounsdNodes/AddNode and the like don't result in messages to the
+// originating connection.
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/view_manager/window_tree_host_impl.cc b/chromium/mojo/services/view_manager/window_tree_host_impl.cc
new file mode 100644
index 00000000000..c344ba4bfe2
--- /dev/null
+++ b/chromium/mojo/services/view_manager/window_tree_host_impl.cc
@@ -0,0 +1,177 @@
+// Copyright (c) 2013 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 "mojo/services/view_manager/window_tree_host_impl.h"
+
+#include "mojo/public/c/gles2/gles2.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/view_manager/context_factory_impl.h"
+#include "ui/aura/env.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/compositor/compositor.h"
+#include "ui/events/event.h"
+#include "ui/events/event_constants.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+// TODO(sky): nuke this. It shouldn't be static.
+// static
+ContextFactoryImpl* WindowTreeHostImpl::context_factory_ = NULL;
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowTreeHostImpl, public:
+
+WindowTreeHostImpl::WindowTreeHostImpl(
+ NativeViewportPtr viewport,
+ const gfx::Rect& bounds,
+ const base::Callback<void()>& compositor_created_callback)
+ : native_viewport_(viewport.Pass()),
+ compositor_created_callback_(compositor_created_callback),
+ bounds_(bounds) {
+ native_viewport_.set_client(this);
+ native_viewport_->Create(Rect::From(bounds));
+
+ MessagePipe pipe;
+ native_viewport_->CreateGLES2Context(
+ MakeRequest<CommandBuffer>(pipe.handle0.Pass()));
+
+ // The ContextFactory must exist before any Compositors are created.
+ if (context_factory_) {
+ delete context_factory_;
+ context_factory_ = NULL;
+ }
+ context_factory_ = new ContextFactoryImpl(pipe.handle1.Pass());
+ aura::Env::GetInstance()->set_context_factory(context_factory_);
+ CHECK(context_factory_) << "No GL bindings.";
+}
+
+WindowTreeHostImpl::~WindowTreeHostImpl() {
+ DestroyCompositor();
+ DestroyDispatcher();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowTreeHostImpl, aura::WindowTreeHost implementation:
+
+ui::EventSource* WindowTreeHostImpl::GetEventSource() {
+ return this;
+}
+
+gfx::AcceleratedWidget WindowTreeHostImpl::GetAcceleratedWidget() {
+ NOTIMPLEMENTED() << "GetAcceleratedWidget";
+ return gfx::kNullAcceleratedWidget;
+}
+
+void WindowTreeHostImpl::Show() {
+ window()->Show();
+ native_viewport_->Show();
+}
+
+void WindowTreeHostImpl::Hide() {
+ native_viewport_->Hide();
+ window()->Hide();
+}
+
+gfx::Rect WindowTreeHostImpl::GetBounds() const {
+ return bounds_;
+}
+
+void WindowTreeHostImpl::SetBounds(const gfx::Rect& bounds) {
+ native_viewport_->SetBounds(Rect::From(bounds));
+}
+
+gfx::Point WindowTreeHostImpl::GetLocationOnNativeScreen() const {
+ return gfx::Point(0, 0);
+}
+
+void WindowTreeHostImpl::SetCapture() {
+ NOTIMPLEMENTED();
+}
+
+void WindowTreeHostImpl::ReleaseCapture() {
+ NOTIMPLEMENTED();
+}
+
+void WindowTreeHostImpl::PostNativeEvent(
+ const base::NativeEvent& native_event) {
+ NOTIMPLEMENTED();
+}
+
+void WindowTreeHostImpl::OnDeviceScaleFactorChanged(float device_scale_factor) {
+ NOTIMPLEMENTED();
+}
+
+void WindowTreeHostImpl::SetCursorNative(gfx::NativeCursor cursor) {
+ NOTIMPLEMENTED();
+}
+
+void WindowTreeHostImpl::MoveCursorToNative(const gfx::Point& location) {
+ NOTIMPLEMENTED();
+}
+
+void WindowTreeHostImpl::OnCursorVisibilityChangedNative(bool show) {
+ NOTIMPLEMENTED();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowTreeHostImpl, ui::EventSource implementation:
+
+ui::EventProcessor* WindowTreeHostImpl::GetEventProcessor() {
+ return dispatcher();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowTreeHostImpl, NativeViewportClient implementation:
+
+void WindowTreeHostImpl::OnCreated() {
+ CreateCompositor(GetAcceleratedWidget());
+ compositor_created_callback_.Run();
+}
+
+void WindowTreeHostImpl::OnBoundsChanged(RectPtr bounds) {
+ bounds_ = bounds.To<gfx::Rect>();
+ OnHostResized(bounds_.size());
+}
+
+void WindowTreeHostImpl::OnDestroyed() {
+ base::MessageLoop::current()->Quit();
+}
+
+void WindowTreeHostImpl::OnEvent(EventPtr event,
+ const mojo::Callback<void()>& callback) {
+ switch (event->action) {
+ case ui::ET_MOUSE_PRESSED:
+ case ui::ET_MOUSE_DRAGGED:
+ case ui::ET_MOUSE_RELEASED:
+ case ui::ET_MOUSE_MOVED:
+ case ui::ET_MOUSE_ENTERED:
+ case ui::ET_MOUSE_EXITED: {
+ gfx::Point location(event->location->x, event->location->y);
+ ui::MouseEvent ev(static_cast<ui::EventType>(event->action), location,
+ location, event->flags, 0);
+ SendEventToProcessor(&ev);
+ break;
+ }
+ case ui::ET_KEY_PRESSED:
+ case ui::ET_KEY_RELEASED: {
+ ui::KeyEvent ev(
+ static_cast<ui::EventType>(event->action),
+ static_cast<ui::KeyboardCode>(event->key_data->key_code),
+ event->flags, event->key_data->is_char);
+ SendEventToProcessor(&ev);
+ break;
+ }
+ // TODO(beng): touch, etc.
+ }
+ callback.Run();
+};
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
diff --git a/chromium/mojo/services/view_manager/window_tree_host_impl.h b/chromium/mojo/services/view_manager/window_tree_host_impl.h
new file mode 100644
index 00000000000..6c11f9a9441
--- /dev/null
+++ b/chromium/mojo/services/view_manager/window_tree_host_impl.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2013 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.
+
+#ifndef MOJO_AURA_WINDOW_TREE_HOST_MOJO_H_
+#define MOJO_AURA_WINDOW_TREE_HOST_MOJO_H_
+
+#include "base/bind.h"
+#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/events/event_source.h"
+#include "ui/gfx/rect.h"
+
+namespace ui {
+class ContextFactory;
+}
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+class ContextFactoryImpl;
+
+class WindowTreeHostImpl : public aura::WindowTreeHost,
+ public ui::EventSource,
+ public NativeViewportClient {
+ public:
+ WindowTreeHostImpl(NativeViewportPtr viewport,
+ const gfx::Rect& bounds,
+ const base::Callback<void()>& compositor_created_callback);
+ virtual ~WindowTreeHostImpl();
+
+ gfx::Rect bounds() const { return bounds_; }
+
+ private:
+ // WindowTreeHost:
+ virtual ui::EventSource* GetEventSource() OVERRIDE;
+ virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE;
+ virtual void Show() OVERRIDE;
+ virtual void Hide() OVERRIDE;
+ virtual gfx::Rect GetBounds() const OVERRIDE;
+ virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE;
+ virtual gfx::Point GetLocationOnNativeScreen() const OVERRIDE;
+ virtual void SetCapture() OVERRIDE;
+ virtual void ReleaseCapture() OVERRIDE;
+ virtual void PostNativeEvent(const base::NativeEvent& native_event) OVERRIDE;
+ virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE;
+ virtual void SetCursorNative(gfx::NativeCursor cursor) OVERRIDE;
+ virtual void MoveCursorToNative(const gfx::Point& location) OVERRIDE;
+ virtual void OnCursorVisibilityChangedNative(bool show) OVERRIDE;
+
+ // ui::EventSource:
+ virtual ui::EventProcessor* GetEventProcessor() OVERRIDE;
+
+ // Overridden from NativeViewportClient:
+ virtual void OnCreated() OVERRIDE;
+ virtual void OnDestroyed() OVERRIDE;
+ virtual void OnBoundsChanged(RectPtr bounds) OVERRIDE;
+ virtual void OnEvent(EventPtr event,
+ const mojo::Callback<void()>& callback) OVERRIDE;
+
+ static ContextFactoryImpl* context_factory_;
+
+ NativeViewportPtr native_viewport_;
+ base::Callback<void()> compositor_created_callback_;
+
+ gfx::Rect bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowTreeHostImpl);
+};
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_AURA_WINDOW_TREE_HOST_MOJO_H_
diff --git a/chromium/mojo/shell/DEPS b/chromium/mojo/shell/DEPS
new file mode 100644
index 00000000000..7b96bb6884b
--- /dev/null
+++ b/chromium/mojo/shell/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "+dbus",
+ "+mojo/embedder",
+ "+net",
+ "+ui/gl",
+]
diff --git a/chromium/mojo/shell/android/DEPS b/chromium/mojo/shell/android/DEPS
new file mode 100644
index 00000000000..c80012b5621
--- /dev/null
+++ b/chromium/mojo/shell/android/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+jni",
+]
diff --git a/chromium/mojo/shell/android/apk/AndroidManifest.xml b/chromium/mojo/shell/android/apk/AndroidManifest.xml
new file mode 100644
index 00000000000..325901817cc
--- /dev/null
+++ b/chromium/mojo/shell/android/apk/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright 2013 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.chromium.mojo_shell_apk">
+
+ <application android:name="MojoShellApplication"
+ android:label="Mojo Shell">
+ <activity android:name="MojoShellActivity"
+ android:launchMode="singleTask"
+ android:theme="@android:style/Theme.Holo.Light.NoActionBar"
+ android:configChanges="orientation|keyboardHidden|keyboard|screenSize"
+ android:hardwareAccelerated="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+ <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" />
+ <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>
diff --git a/chromium/mojo/shell/android/apk/res/layout/mojo_shell_activity.xml b/chromium/mojo/shell/android/apk/res/layout/mojo_shell_activity.xml
new file mode 100644
index 00000000000..d319941501b
--- /dev/null
+++ b/chromium/mojo/shell/android/apk/res/layout/mojo_shell_activity.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright 2013 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+</LinearLayout>
diff --git a/chromium/mojo/shell/android/apk/res/values/strings.xml b/chromium/mojo/shell/android/apk/res/values/strings.xml
new file mode 100644
index 00000000000..ff3f8bb8221
--- /dev/null
+++ b/chromium/mojo/shell/android/apk/res/values/strings.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright 2013 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.
+ -->
+
+<resources>
+</resources>
diff --git a/chromium/mojo/shell/android/library_loader.cc b/chromium/mojo/shell/android/library_loader.cc
new file mode 100644
index 00000000000..37d6592521f
--- /dev/null
+++ b/chromium/mojo/shell/android/library_loader.cc
@@ -0,0 +1,46 @@
+// Copyright 2013 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 "base/android/base_jni_registrar.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_registrar.h"
+#include "base/android/library_loader/library_loader_hooks.h"
+#include "base/logging.h"
+#include "mojo/services/native_viewport/native_viewport_android.h"
+#include "mojo/shell/android/mojo_main.h"
+#include "net/android/net_jni_registrar.h"
+
+namespace {
+
+base::android::RegistrationMethod kMojoRegisteredMethods[] = {
+ { "MojoMain", mojo::RegisterMojoMain },
+ { "NativeViewportAndroid", mojo::services::NativeViewportAndroid::Register },
+};
+
+bool RegisterMojoJni(JNIEnv* env) {
+ return RegisterNativeMethods(env, kMojoRegisteredMethods,
+ arraysize(kMojoRegisteredMethods));
+}
+
+} // namespace
+
+// This is called by the VM when the shared library is first loaded.
+JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+ base::android::InitVM(vm);
+ JNIEnv* env = base::android::AttachCurrentThread();
+
+ if (!base::android::RegisterLibraryLoaderEntryHook(env))
+ return -1;
+
+ if (!base::android::RegisterJni(env))
+ return -1;
+
+ if (!net::android::RegisterJni(env))
+ return -1;
+
+ if (!RegisterMojoJni(env))
+ return -1;
+
+ return JNI_VERSION_1_4;
+}
diff --git a/chromium/mojo/shell/android/mojo_main.cc b/chromium/mojo/shell/android/mojo_main.cc
new file mode 100644
index 00000000000..c2656321d19
--- /dev/null
+++ b/chromium/mojo/shell/android/mojo_main.cc
@@ -0,0 +1,80 @@
+// Copyright 2013 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 "mojo/shell/android/mojo_main.h"
+
+#include "base/android/jni_string.h"
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "jni/MojoMain_jni.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/service_manager/service_loader.h"
+#include "mojo/service_manager/service_manager.h"
+#include "mojo/shell/context.h"
+#include "mojo/shell/init.h"
+#include "mojo/shell/run.h"
+#include "ui/gl/gl_surface_egl.h"
+
+using base::LazyInstance;
+
+namespace mojo {
+
+namespace {
+
+LazyInstance<scoped_ptr<base::MessageLoop> > g_java_message_loop =
+ LAZY_INSTANCE_INITIALIZER;
+
+LazyInstance<scoped_ptr<shell::Context> > g_context =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+static void Init(JNIEnv* env, jclass clazz, jobject context) {
+ base::android::ScopedJavaLocalRef<jobject> scoped_context(env, context);
+
+ base::android::InitApplicationContext(env, scoped_context);
+
+ base::CommandLine::Init(0, 0);
+ mojo::shell::InitializeLogging();
+
+ g_java_message_loop.Get().reset(new base::MessageLoopForUI);
+ base::MessageLoopForUI::current()->Start();
+
+ // TODO(abarth): At which point should we switch to cross-platform
+ // initialization?
+
+ gfx::GLSurface::InitializeOneOff();
+}
+
+static void Start(JNIEnv* env, jclass clazz, jobject context, jstring jurl) {
+ std::vector<GURL> app_urls;
+#if defined(MOJO_SHELL_DEBUG_URL)
+ app_urls.push_back(GURL(MOJO_SHELL_DEBUG_URL));
+ // Sleep for 5 seconds to give the debugger a chance to attach.
+ sleep(5);
+#else
+ if (jurl)
+ app_urls.push_back(GURL(base::android::ConvertJavaStringToUTF8(env, jurl)));
+#endif
+
+ base::android::ScopedJavaGlobalRef<jobject> activity;
+ activity.Reset(env, context);
+
+ shell::Context* shell_context = new shell::Context();
+ shell_context->set_activity(activity.obj());
+
+ g_context.Get().reset(shell_context);
+ shell::Run(shell_context, app_urls);
+}
+
+bool RegisterMojoMain(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/shell/android/mojo_main.h b/chromium/mojo/shell/android/mojo_main.h
new file mode 100644
index 00000000000..161ca71d684
--- /dev/null
+++ b/chromium/mojo/shell/android/mojo_main.h
@@ -0,0 +1,16 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SHELL_ANDROID_MOJO_MAIN_H_
+#define MOJO_SHELL_ANDROID_MOJO_MAIN_H_
+
+#include <jni.h>
+
+namespace mojo {
+
+bool RegisterMojoMain(JNIEnv* env);
+
+} // namespace mojo
+
+#endif // MOJO_SHELL_ANDROID_MOJO_MAIN_H_
diff --git a/chromium/mojo/shell/app_child_process.cc b/chromium/mojo/shell/app_child_process.cc
new file mode 100644
index 00000000000..8cc4d81e69f
--- /dev/null
+++ b/chromium/mojo/shell/app_child_process.cc
@@ -0,0 +1,289 @@
+// Copyright 2014 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 "mojo/shell/app_child_process.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/scoped_native_library.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/common/message_pump_mojo.h"
+#include "mojo/embedder/embedder.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/shell/app_child_process.mojom.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+
+// Blocker ---------------------------------------------------------------------
+
+// Blocks a thread until another thread unblocks it, at which point it unblocks
+// and runs a closure provided by that thread.
+class Blocker {
+ public:
+ class Unblocker {
+ public:
+ ~Unblocker() {}
+
+ void Unblock(base::Closure run_after) {
+ DCHECK(blocker_);
+ DCHECK(blocker_->run_after_.is_null());
+ blocker_->run_after_ = run_after;
+ blocker_->event_.Signal();
+ blocker_ = NULL;
+ }
+
+ private:
+ friend class Blocker;
+ Unblocker(Blocker* blocker) : blocker_(blocker) {
+ DCHECK(blocker_);
+ }
+
+ Blocker* blocker_;
+
+ // Copy and assign allowed.
+ };
+
+ Blocker() : event_(true, false) {}
+ ~Blocker() {}
+
+ void Block() {
+ DCHECK(run_after_.is_null());
+ event_.Wait();
+ run_after_.Run();
+ }
+
+ Unblocker GetUnblocker() {
+ return Unblocker(this);
+ }
+
+ private:
+ base::WaitableEvent event_;
+ base::Closure run_after_;
+
+ DISALLOW_COPY_AND_ASSIGN(Blocker);
+};
+
+// AppContext ------------------------------------------------------------------
+
+class AppChildControllerImpl;
+
+static void DestroyController(scoped_ptr<AppChildControllerImpl> controller) {
+}
+
+// Should be created and initialized on the main thread.
+class AppContext {
+ public:
+ AppContext()
+ : io_thread_("io_thread"),
+ controller_thread_("controller_thread") {}
+ ~AppContext() {}
+
+ void Init() {
+ // Initialize Mojo before starting any threads.
+ embedder::Init();
+
+ // Create and start our I/O thread.
+ base::Thread::Options io_thread_options(base::MessageLoop::TYPE_IO, 0);
+ CHECK(io_thread_.StartWithOptions(io_thread_options));
+ io_runner_ = io_thread_.message_loop_proxy().get();
+ CHECK(io_runner_);
+
+ // Create and start our controller thread.
+ base::Thread::Options controller_thread_options;
+ controller_thread_options.message_loop_type =
+ base::MessageLoop::TYPE_CUSTOM;
+ controller_thread_options.message_pump_factory =
+ base::Bind(&common::MessagePumpMojo::Create);
+ CHECK(controller_thread_.StartWithOptions(controller_thread_options));
+ controller_runner_ = controller_thread_.message_loop_proxy().get();
+ CHECK(controller_runner_);
+ }
+
+ void Shutdown() {
+ controller_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&DestroyController, base::Passed(&controller_)));
+ }
+
+ base::SingleThreadTaskRunner* io_runner() const {
+ return io_runner_.get();
+ }
+
+ base::SingleThreadTaskRunner* controller_runner() const {
+ return controller_runner_.get();
+ }
+
+ AppChildControllerImpl* controller() const {
+ return controller_.get();
+ }
+
+ void set_controller(scoped_ptr<AppChildControllerImpl> controller) {
+ controller_ = controller.Pass();
+ }
+
+ private:
+ // Accessed only on the controller thread.
+ // IMPORTANT: This must be BEFORE |controller_thread_|, so that the controller
+ // thread gets joined (and thus |controller_| reset) before |controller_| is
+ // destroyed.
+ scoped_ptr<AppChildControllerImpl> controller_;
+
+ base::Thread io_thread_;
+ scoped_refptr<base::SingleThreadTaskRunner> io_runner_;
+
+ base::Thread controller_thread_;
+ scoped_refptr<base::SingleThreadTaskRunner> controller_runner_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppContext);
+};
+
+// AppChildControllerImpl ------------------------------------------------------
+
+class AppChildControllerImpl : public InterfaceImpl<AppChildController> {
+ public:
+ virtual ~AppChildControllerImpl() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // TODO(vtl): Pass in the result from |MainMain()|.
+ client()->AppCompleted(MOJO_RESULT_UNIMPLEMENTED);
+ }
+
+ // To be executed on the controller thread. Creates the |AppChildController|,
+ // etc.
+ static void Init(
+ AppContext* app_context,
+ embedder::ScopedPlatformHandle platform_channel,
+ const Blocker::Unblocker& unblocker) {
+ DCHECK(app_context);
+ DCHECK(platform_channel.is_valid());
+
+ DCHECK(!app_context->controller());
+
+ scoped_ptr<AppChildControllerImpl> impl(
+ new AppChildControllerImpl(app_context, unblocker));
+
+ ScopedMessagePipeHandle host_message_pipe(embedder::CreateChannel(
+ platform_channel.Pass(),
+ app_context->io_runner(),
+ base::Bind(&AppChildControllerImpl::DidCreateChannel,
+ base::Unretained(impl.get())),
+ base::MessageLoopProxy::current()));
+
+ BindToPipe(impl.get(), host_message_pipe.Pass());
+
+ app_context->set_controller(impl.Pass());
+ }
+
+ virtual void OnConnectionError() OVERRIDE {
+ // TODO(darin): How should we handle a connection error here?
+ }
+
+ // |AppChildController| methods:
+ virtual void StartApp(const String& app_path,
+ ScopedMessagePipeHandle service) OVERRIDE {
+ DVLOG(2) << "AppChildControllerImpl::StartApp(" << app_path << ", ...)";
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ unblocker_.Unblock(base::Bind(&AppChildControllerImpl::StartAppOnMainThread,
+ base::FilePath::FromUTF8Unsafe(app_path),
+ base::Passed(&service)));
+ }
+
+ private:
+ AppChildControllerImpl(AppContext* app_context,
+ const Blocker::Unblocker& unblocker)
+ : app_context_(app_context),
+ unblocker_(unblocker),
+ channel_info_(NULL) {
+ }
+
+ // Callback for |embedder::CreateChannel()|.
+ void DidCreateChannel(embedder::ChannelInfo* channel_info) {
+ DVLOG(2) << "AppChildControllerImpl::DidCreateChannel()";
+ DCHECK(thread_checker_.CalledOnValidThread());
+ channel_info_ = channel_info;
+ }
+
+ static void StartAppOnMainThread(const base::FilePath& app_path,
+ ScopedMessagePipeHandle service) {
+ // TODO(vtl): This is copied from in_process_dynamic_service_runner.cc.
+ DVLOG(2) << "Loading/running Mojo app from " << app_path.value()
+ << " out of process";
+
+ do {
+ base::NativeLibraryLoadError load_error;
+ base::ScopedNativeLibrary app_library(
+ base::LoadNativeLibrary(app_path, &load_error));
+ if (!app_library.is_valid()) {
+ LOG(ERROR) << "Failed to load library (error: " << load_error.ToString()
+ << ")";
+ break;
+ }
+
+ typedef MojoResult (*MojoMainFunction)(MojoHandle);
+ MojoMainFunction main_function = reinterpret_cast<MojoMainFunction>(
+ app_library.GetFunctionPointer("MojoMain"));
+ if (!main_function) {
+ LOG(ERROR) << "Entrypoint MojoMain not found";
+ break;
+ }
+
+ // TODO(vtl): Report the result back to our parent process.
+ // |MojoMain()| takes ownership of the service handle.
+ MojoResult result = main_function(service.release().value());
+ if (result < MOJO_RESULT_OK)
+ LOG(ERROR) << "MojoMain returned an error: " << result;
+ } while (false);
+ }
+
+ base::ThreadChecker thread_checker_;
+ AppContext* const app_context_;
+ Blocker::Unblocker unblocker_;
+
+ embedder::ChannelInfo* channel_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppChildControllerImpl);
+};
+
+} // namespace
+
+// AppChildProcess -------------------------------------------------------------
+
+AppChildProcess::AppChildProcess() {
+}
+
+AppChildProcess::~AppChildProcess() {
+}
+
+void AppChildProcess::Main() {
+ DVLOG(2) << "AppChildProcess::Main()";
+
+ AppContext app_context;
+ app_context.Init();
+
+ Blocker blocker;
+ app_context.controller_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&AppChildControllerImpl::Init, base::Unretained(&app_context),
+ base::Passed(platform_channel()), blocker.GetUnblocker()));
+ // This will block, then run whatever the controller wants.
+ blocker.Block();
+
+ app_context.Shutdown();
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/app_child_process.h b/chromium/mojo/shell/app_child_process.h
new file mode 100644
index 00000000000..b5b1eb6660e
--- /dev/null
+++ b/chromium/mojo/shell/app_child_process.h
@@ -0,0 +1,30 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SHELL_APP_CHILD_PROCESS_H_
+#define MOJO_SHELL_APP_CHILD_PROCESS_H_
+
+#include "base/macros.h"
+#include "mojo/shell/child_process.h"
+
+namespace mojo {
+namespace shell {
+
+// An implementation of |ChildProcess| for a |TYPE_APP| child process, which
+// runs a single app (loaded from the file system) on its main thread.
+class AppChildProcess : public ChildProcess {
+ public:
+ AppChildProcess();
+ virtual ~AppChildProcess();
+
+ virtual void Main() OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AppChildProcess);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_APP_CHILD_PROCESS_H_
diff --git a/chromium/mojo/shell/app_child_process.mojom b/chromium/mojo/shell/app_child_process.mojom
new file mode 100644
index 00000000000..5b0f180c496
--- /dev/null
+++ b/chromium/mojo/shell/app_child_process.mojom
@@ -0,0 +1,17 @@
+// Copyright 2014 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.
+
+module mojo.shell {
+
+[Client=AppChildControllerClient]
+interface AppChildController {
+ // TODO(vtl): |service| should be of a more specific type.
+ StartApp(string app_path, handle<message_pipe> service);
+};
+
+interface AppChildControllerClient {
+ AppCompleted(int32 result);
+};
+
+} // module mojo.shell
diff --git a/chromium/mojo/shell/app_child_process_host.cc b/chromium/mojo/shell/app_child_process_host.cc
new file mode 100644
index 00000000000..0740608789a
--- /dev/null
+++ b/chromium/mojo/shell/app_child_process_host.cc
@@ -0,0 +1,63 @@
+// Copyright 2014 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 "mojo/shell/app_child_process_host.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/embedder/embedder.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/shell/context.h"
+#include "mojo/shell/task_runners.h"
+
+namespace mojo {
+namespace shell {
+
+AppChildProcessHost::AppChildProcessHost(
+ Context* context,
+ AppChildControllerClient* controller_client)
+ : ChildProcessHost(context, this, ChildProcess::TYPE_APP),
+ controller_client_(controller_client),
+ channel_info_(NULL) {
+}
+
+AppChildProcessHost::~AppChildProcessHost() {
+}
+
+void AppChildProcessHost::WillStart() {
+ DCHECK(platform_channel()->is_valid());
+
+ mojo::ScopedMessagePipeHandle handle(embedder::CreateChannel(
+ platform_channel()->Pass(),
+ context()->task_runners()->io_runner(),
+ base::Bind(&AppChildProcessHost::DidCreateChannel,
+ base::Unretained(this)),
+ base::MessageLoop::current()->message_loop_proxy()));
+
+ controller_.Bind(handle.Pass());
+ controller_.set_client(controller_client_);
+}
+
+void AppChildProcessHost::DidStart(bool success) {
+ DVLOG(2) << "AppChildProcessHost::DidStart()";
+
+ if (!success) {
+ LOG(ERROR) << "Failed to start app child process";
+ controller_client_->AppCompleted(MOJO_RESULT_UNKNOWN);
+ return;
+ }
+}
+
+// Callback for |embedder::CreateChannel()|.
+void AppChildProcessHost::DidCreateChannel(
+ embedder::ChannelInfo* channel_info) {
+ DVLOG(2) << "AppChildProcessHost::DidCreateChannel()";
+
+ CHECK(channel_info);
+ channel_info_ = channel_info;
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/app_child_process_host.h b/chromium/mojo/shell/app_child_process_host.h
new file mode 100644
index 00000000000..30e6b798a24
--- /dev/null
+++ b/chromium/mojo/shell/app_child_process_host.h
@@ -0,0 +1,55 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SHELL_APP_CHILD_PROCESS_HOST_H_
+#define MOJO_SHELL_APP_CHILD_PROCESS_HOST_H_
+
+#include "base/macros.h"
+#include "mojo/shell/app_child_process.mojom.h"
+#include "mojo/shell/child_process_host.h"
+
+namespace mojo {
+
+namespace embedder {
+struct ChannelInfo;
+}
+
+namespace shell {
+
+// A subclass of |ChildProcessHost| to host a |TYPE_APP| child process, which
+// runs a single app (loaded from the file system).
+//
+// Note: After |Start()|, this object must remain alive until the controller
+// client's |AppCompleted()| is called.
+class AppChildProcessHost : public ChildProcessHost,
+ public ChildProcessHost::Delegate {
+ public:
+ AppChildProcessHost(Context* context,
+ AppChildControllerClient* controller_client);
+ virtual ~AppChildProcessHost();
+
+ AppChildController* controller() {
+ return controller_.get();
+ }
+
+ private:
+ // |ChildProcessHost::Delegate| methods:
+ virtual void WillStart() OVERRIDE;
+ virtual void DidStart(bool success) OVERRIDE;
+
+ // Callback for |embedder::CreateChannel()|.
+ void DidCreateChannel(embedder::ChannelInfo* channel_info);
+
+ AppChildControllerClient* const controller_client_;
+
+ AppChildControllerPtr controller_;
+ embedder::ChannelInfo* channel_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppChildProcessHost);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_APP_CHILD_PROCESS_HOST_H_
diff --git a/chromium/mojo/shell/child_process.cc b/chromium/mojo/shell/child_process.cc
new file mode 100644
index 00000000000..24728af482e
--- /dev/null
+++ b/chromium/mojo/shell/child_process.cc
@@ -0,0 +1,58 @@
+// Copyright 2014 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 "mojo/shell/child_process.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "mojo/embedder/platform_channel_pair.h"
+#include "mojo/shell/app_child_process.h"
+#include "mojo/shell/switches.h"
+#include "mojo/shell/test_child_process.h"
+
+namespace mojo {
+namespace shell {
+
+ChildProcess::~ChildProcess() {
+}
+
+// static
+scoped_ptr<ChildProcess> ChildProcess::Create(
+ const base::CommandLine& command_line) {
+ if (!command_line.HasSwitch(switches::kChildProcessType))
+ return scoped_ptr<ChildProcess>();
+
+ int type_as_int;
+ CHECK(base::StringToInt(command_line.GetSwitchValueASCII(
+ switches::kChildProcessType), &type_as_int));
+
+ scoped_ptr<ChildProcess> rv;
+ switch (type_as_int) {
+ case TYPE_TEST:
+ rv.reset(new TestChildProcess());
+ break;
+ case TYPE_APP:
+ rv.reset(new AppChildProcess());
+ break;
+ default:
+ CHECK(false) << "Invalid child process type";
+ break;
+ }
+
+ if (rv) {
+ rv->platform_channel_ =
+ embedder::PlatformChannelPair::PassClientHandleFromParentProcess(
+ command_line);
+ CHECK(rv->platform_channel_.is_valid());
+ }
+
+ return rv.Pass();
+}
+
+ChildProcess::ChildProcess() {
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/child_process.h b/chromium/mojo/shell/child_process.h
new file mode 100644
index 00000000000..d794a4ea948
--- /dev/null
+++ b/chromium/mojo/shell/child_process.h
@@ -0,0 +1,57 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SHELL_CHILD_PROCESS_H_
+#define MOJO_SHELL_CHILD_PROCESS_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/embedder/scoped_platform_handle.h"
+
+namespace base {
+class CommandLine;
+}
+
+namespace mojo {
+namespace shell {
+
+// A base class for child processes -- i.e., code that is actually run within
+// the child process. (Instances are manufactured by |Create()|.)
+class ChildProcess {
+ public:
+ enum Type {
+ TYPE_TEST,
+ // Hosts a single app (see app_child_process(_host).*).
+ TYPE_APP
+ };
+
+ virtual ~ChildProcess();
+
+ // Returns null if the command line doesn't indicate that this is a child
+ // process. |main()| should call this, and if it returns non-null it should
+ // call |Main()| (without a message loop on the current thread).
+ static scoped_ptr<ChildProcess> Create(const base::CommandLine& command_line);
+
+ // To be implemented by subclasses. This is the "entrypoint" for a child
+ // process. Run with no message loop for the main thread.
+ virtual void Main() = 0;
+
+ protected:
+ ChildProcess();
+
+ embedder::ScopedPlatformHandle* platform_channel() {
+ return &platform_channel_;
+ }
+
+ private:
+ // Available in |Main()| (after a successful |Create()|).
+ embedder::ScopedPlatformHandle platform_channel_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChildProcess);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_CHILD_PROCESS_H_
diff --git a/chromium/mojo/shell/child_process_host.cc b/chromium/mojo/shell/child_process_host.cc
new file mode 100644
index 00000000000..e743522e5ff
--- /dev/null
+++ b/chromium/mojo/shell/child_process_host.cc
@@ -0,0 +1,105 @@
+// Copyright 2014 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 "mojo/shell/child_process_host.h"
+
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/process/kill.h"
+#include "base/process/launch.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task_runner.h"
+#include "base/task_runner_util.h"
+#include "mojo/shell/context.h"
+#include "mojo/shell/switches.h"
+
+namespace mojo {
+namespace shell {
+
+ChildProcessHost::ChildProcessHost(Context* context,
+ Delegate* delegate,
+ ChildProcess::Type type)
+ : context_(context),
+ delegate_(delegate),
+ type_(type),
+ child_process_handle_(base::kNullProcessHandle) {
+ DCHECK(delegate);
+ platform_channel_ = platform_channel_pair_.PassServerHandle();
+ CHECK(platform_channel_.is_valid());
+}
+
+ChildProcessHost::~ChildProcessHost() {
+ if (child_process_handle_ != base::kNullProcessHandle) {
+ LOG(WARNING) << "Destroying ChildProcessHost with unjoined child";
+ base::CloseProcessHandle(child_process_handle_);
+ child_process_handle_ = base::kNullProcessHandle;
+ }
+}
+
+void ChildProcessHost::Start() {
+ DCHECK_EQ(child_process_handle_, base::kNullProcessHandle);
+
+ delegate_->WillStart();
+
+ CHECK(base::PostTaskAndReplyWithResult(
+ context_->task_runners()->blocking_pool(),
+ FROM_HERE,
+ base::Bind(&ChildProcessHost::DoLaunch, base::Unretained(this)),
+ base::Bind(&ChildProcessHost::DidLaunch, base::Unretained(this))));
+}
+
+int ChildProcessHost::Join() {
+ DCHECK_NE(child_process_handle_, base::kNullProcessHandle);
+ int rv = -1;
+ LOG_IF(ERROR, !base::WaitForExitCode(child_process_handle_, &rv))
+ << "Failed to wait for child process";
+ base::CloseProcessHandle(child_process_handle_);
+ child_process_handle_ = base::kNullProcessHandle;
+ return rv;
+}
+
+bool ChildProcessHost::DoLaunch() {
+ static const char* kForwardSwitches[] = {
+ switches::kTraceToConsole,
+ switches::kV,
+ switches::kVModule,
+ };
+
+ const base::CommandLine* parent_command_line =
+ base::CommandLine::ForCurrentProcess();
+ base::CommandLine child_command_line(parent_command_line->GetProgram());
+ child_command_line.CopySwitchesFrom(*parent_command_line, kForwardSwitches,
+ arraysize(kForwardSwitches));
+ child_command_line.AppendSwitchASCII(
+ switches::kChildProcessType, base::IntToString(static_cast<int>(type_)));
+
+ embedder::HandlePassingInformation handle_passing_info;
+ platform_channel_pair_.PrepareToPassClientHandleToChildProcess(
+ &child_command_line, &handle_passing_info);
+
+ base::LaunchOptions options;
+#if defined(OS_WIN)
+ options.start_hidden = true;
+ options.handles_to_inherit = &handle_passing_info;
+#elif defined(OS_POSIX)
+ options.fds_to_remap = &handle_passing_info;
+#endif
+
+ if (!base::LaunchProcess(child_command_line, options, &child_process_handle_))
+ return false;
+
+ platform_channel_pair_.ChildProcessLaunched();
+ return true;
+}
+
+void ChildProcessHost::DidLaunch(bool success) {
+ delegate_->DidStart(success);
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/child_process_host.h b/chromium/mojo/shell/child_process_host.h
new file mode 100644
index 00000000000..d3c7669a1b4
--- /dev/null
+++ b/chromium/mojo/shell/child_process_host.h
@@ -0,0 +1,84 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SHELL_CHILD_PROCESS_HOST_H_
+#define MOJO_SHELL_CHILD_PROCESS_HOST_H_
+
+#include "base/macros.h"
+#include "base/process/process_handle.h"
+#include "mojo/embedder/platform_channel_pair.h"
+#include "mojo/embedder/scoped_platform_handle.h"
+#include "mojo/shell/child_process.h" // For |ChildProcess::Type|.
+
+namespace mojo {
+namespace shell {
+
+class Context;
+
+// (Base) class for a "child process host". Handles launching and connecting a
+// platform-specific "pipe" to the child, and supports joining the child
+// process. Intended for use as a base class, but may be used on its own in
+// simple cases.
+//
+// This class is not thread-safe. It should be created/used/destroyed on a
+// single thread.
+//
+// Note: Does not currently work on Windows before Vista.
+class ChildProcessHost {
+ public:
+ class Delegate {
+ public:
+ virtual void WillStart() = 0;
+ virtual void DidStart(bool success) = 0;
+ };
+
+ ChildProcessHost(Context* context,
+ Delegate* delegate,
+ ChildProcess::Type type);
+ virtual ~ChildProcessHost();
+
+ // |Start()|s the child process; calls the delegate's |DidStart()| (on the
+ // thread on which |Start()| was called) when the child has been started (or
+ // failed to start). After calling |Start()|, this object must not be
+ // destroyed until |DidStart()| has been called.
+ // TODO(vtl): Consider using weak pointers and removing this requirement.
+ void Start();
+
+ // Waits for the child process to terminate, and returns its exit code.
+ // Note: If |Start()| has been called, this must not be called until the
+ // callback has been called.
+ int Join();
+
+ embedder::ScopedPlatformHandle* platform_channel() {
+ return &platform_channel_;
+ }
+
+ protected:
+ Context* context() const {
+ return context_;
+ }
+
+ private:
+ bool DoLaunch();
+ void DidLaunch(bool success);
+
+ Context* const context_;
+ Delegate* const delegate_;
+ const ChildProcess::Type type_;
+
+ base::ProcessHandle child_process_handle_;
+
+ embedder::PlatformChannelPair platform_channel_pair_;
+
+ // Platform-specific "pipe" to the child process. Valid immediately after
+ // creation.
+ embedder::ScopedPlatformHandle platform_channel_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChildProcessHost);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_CHILD_PROCESS_HOST_H_
diff --git a/chromium/mojo/shell/child_process_host_unittest.cc b/chromium/mojo/shell/child_process_host_unittest.cc
new file mode 100644
index 00000000000..37f06c3a174
--- /dev/null
+++ b/chromium/mojo/shell/child_process_host_unittest.cc
@@ -0,0 +1,56 @@
+// Copyright 2014 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.
+
+// Note: This file also tests child_process.*.
+
+#include "mojo/shell/child_process_host.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/common/message_pump_mojo.h"
+#include "mojo/shell/context.h"
+#include "mojo/shell/shell_test_base.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace shell {
+namespace test {
+namespace {
+
+class TestChildProcessHostDelegate : public ChildProcessHost::Delegate {
+ public:
+ TestChildProcessHostDelegate() {}
+ virtual ~TestChildProcessHostDelegate() {}
+ virtual void WillStart() OVERRIDE {
+ VLOG(2) << "TestChildProcessHostDelegate::WillStart()";
+ }
+ virtual void DidStart(bool success) OVERRIDE {
+ VLOG(2) << "TestChildProcessHostDelegate::DidStart(" << success << ")";
+ base::MessageLoop::current()->QuitWhenIdle();
+ }
+};
+
+typedef ShellTestBase ChildProcessHostTest;
+
+TEST_F(ChildProcessHostTest, Basic) {
+ base::MessageLoop message_loop(
+ scoped_ptr<base::MessagePump>(new common::MessagePumpMojo()));
+
+ Context context;
+ TestChildProcessHostDelegate child_process_host_delegate;
+ ChildProcessHost child_process_host(&context,
+ &child_process_host_delegate,
+ ChildProcess::TYPE_TEST);
+ child_process_host.Start();
+ message_loop.Run();
+ int exit_code = child_process_host.Join();
+ VLOG(2) << "Joined child: exit_code = " << exit_code;
+ EXPECT_EQ(0, exit_code);
+}
+
+} // namespace
+} // namespace test
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/context.cc b/chromium/mojo/shell/context.cc
new file mode 100644
index 00000000000..b47967e59c7
--- /dev/null
+++ b/chromium/mojo/shell/context.cc
@@ -0,0 +1,141 @@
+// Copyright 2013 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 "mojo/shell/context.h"
+
+#include "build/build_config.h"
+#include "base/command_line.h"
+#include "base/lazy_instance.h"
+#include "base/memory/scoped_vector.h"
+#include "mojo/embedder/embedder.h"
+#include "mojo/gles2/gles2_support_impl.h"
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/service_manager/background_service_loader.h"
+#include "mojo/service_manager/service_loader.h"
+#include "mojo/service_manager/service_manager.h"
+#include "mojo/services/native_viewport/native_viewport_service.h"
+#include "mojo/shell/dynamic_service_loader.h"
+#include "mojo/shell/in_process_dynamic_service_runner.h"
+#include "mojo/shell/out_of_process_dynamic_service_runner.h"
+#include "mojo/shell/switches.h"
+#include "mojo/spy/spy.h"
+
+#if defined(OS_LINUX)
+#include "mojo/shell/dbus_service_loader_linux.h"
+#endif // defined(OS_LINUX)
+
+#if defined(USE_AURA)
+#include "mojo/shell/view_manager_loader.h"
+#endif
+
+namespace mojo {
+namespace shell {
+namespace {
+
+// These mojo: URLs are loaded directly from the local filesystem. They
+// correspond to shared libraries bundled alongside the mojo_shell.
+const char* kLocalMojoURLs[] = {
+ "mojo:mojo_network_service",
+};
+
+// Used to ensure we only init once.
+class Setup {
+ public:
+ Setup() {
+ embedder::Init();
+ gles2::GLES2SupportImpl::Init();
+ }
+
+ ~Setup() {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Setup);
+};
+
+static base::LazyInstance<Setup>::Leaky setup = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+class Context::NativeViewportServiceLoader : public ServiceLoader {
+ public:
+ explicit NativeViewportServiceLoader(Context* context) : context_(context) {}
+ virtual ~NativeViewportServiceLoader() {}
+
+ private:
+ virtual void LoadService(ServiceManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle service_handle) OVERRIDE {
+ app_.reset(::CreateNativeViewportService(context_, service_handle.Pass()));
+ }
+
+ virtual void OnServiceError(ServiceManager* manager,
+ const GURL& url) OVERRIDE {
+ }
+
+ Context* context_;
+ scoped_ptr<Application> app_;
+ DISALLOW_COPY_AND_ASSIGN(NativeViewportServiceLoader);
+};
+
+Context::Context()
+ : task_runners_(base::MessageLoop::current()->message_loop_proxy()) {
+ setup.Get();
+
+ for (size_t i = 0; i < arraysize(kLocalMojoURLs); ++i)
+ mojo_url_resolver_.AddLocalFileMapping(GURL(kLocalMojoURLs[i]));
+
+ base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+ scoped_ptr<DynamicServiceRunnerFactory> runner_factory;
+ if (cmdline->HasSwitch(switches::kEnableMultiprocess))
+ runner_factory.reset(new OutOfProcessDynamicServiceRunnerFactory());
+ else
+ runner_factory.reset(new InProcessDynamicServiceRunnerFactory());
+
+ service_manager_.set_default_loader(
+ scoped_ptr<ServiceLoader>(
+ new DynamicServiceLoader(this, runner_factory.Pass())));
+ // The native viewport service synchronously waits for certain messages. If we
+ // don't run it on its own thread we can easily deadlock. Long term native
+ // viewport should run its own process so that this isn't an issue.
+ service_manager_.SetLoaderForURL(
+ scoped_ptr<ServiceLoader>(
+ new BackgroundServiceLoader(
+ scoped_ptr<ServiceLoader>(new NativeViewportServiceLoader(this)),
+ "native_viewport",
+ base::MessageLoop::TYPE_UI)),
+ GURL("mojo:mojo_native_viewport_service"));
+#if defined(USE_AURA)
+ // TODO(sky): need a better way to find this. It shouldn't be linked in.
+ service_manager_.SetLoaderForURL(
+ scoped_ptr<ServiceLoader>(new ViewManagerLoader()),
+ GURL("mojo:mojo_view_manager"));
+#endif
+
+#if defined(OS_LINUX)
+ service_manager_.SetLoaderForScheme(
+ scoped_ptr<ServiceLoader>(new DBusServiceLoader(this)),
+ "dbus");
+#endif // defined(OS_LINUX)
+
+ if (cmdline->HasSwitch(switches::kSpy)) {
+ spy_.reset(new mojo::Spy(&service_manager_,
+ cmdline->GetSwitchValueASCII(switches::kSpy)));
+ }
+}
+
+Context::~Context() {
+ // mojo_view_manager uses native_viewport. Destroy mojo_view_manager first so
+ // that there aren't shutdown ordering issues. Once native viewport service is
+ // moved into its own process this can likely be nuked.
+#if defined(USE_AURA)
+ service_manager_.SetLoaderForURL(
+ scoped_ptr<ServiceLoader>(),
+ GURL("mojo:mojo_view_manager"));
+#endif
+ service_manager_.set_default_loader(scoped_ptr<ServiceLoader>());
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/context.h b/chromium/mojo/shell/context.h
new file mode 100644
index 00000000000..c643cd93317
--- /dev/null
+++ b/chromium/mojo/shell/context.h
@@ -0,0 +1,62 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SHELL_CONTEXT_H_
+#define MOJO_SHELL_CONTEXT_H_
+
+#include <string>
+
+#include "mojo/service_manager/service_manager.h"
+#include "mojo/shell/keep_alive.h"
+#include "mojo/shell/mojo_url_resolver.h"
+#include "mojo/shell/task_runners.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/scoped_java_ref.h"
+#endif // defined(OS_ANDROID)
+
+namespace mojo {
+
+class Spy;
+
+namespace shell {
+
+class DynamicServiceLoader;
+
+// The "global" context for the shell's main process.
+class Context {
+ public:
+ Context();
+ ~Context();
+
+ TaskRunners* task_runners() { return &task_runners_; }
+ ServiceManager* service_manager() { return &service_manager_; }
+ KeepAliveCounter* keep_alive_counter() { return &keep_alive_counter_; }
+ MojoURLResolver* mojo_url_resolver() { return &mojo_url_resolver_; }
+
+#if defined(OS_ANDROID)
+ jobject activity() const { return activity_.obj(); }
+ void set_activity(jobject activity) { activity_.Reset(NULL, activity); }
+#endif // defined(OS_ANDROID)
+
+ private:
+ class NativeViewportServiceLoader;
+
+ TaskRunners task_runners_;
+ ServiceManager service_manager_;
+ MojoURLResolver mojo_url_resolver_;
+ scoped_ptr<Spy> spy_;
+#if defined(OS_ANDROID)
+ base::android::ScopedJavaGlobalRef<jobject> activity_;
+#endif // defined(OS_ANDROID)
+
+ KeepAliveCounter keep_alive_counter_;
+
+ DISALLOW_COPY_AND_ASSIGN(Context);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_CONTEXT_H_
diff --git a/chromium/mojo/shell/dbus_service_loader_linux.cc b/chromium/mojo/shell/dbus_service_loader_linux.cc
new file mode 100644
index 00000000000..bd4255c2fab
--- /dev/null
+++ b/chromium/mojo/shell/dbus_service_loader_linux.cc
@@ -0,0 +1,181 @@
+// Copyright 2014 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 "mojo/shell/dbus_service_loader_linux.h"
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/task_runner_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "dbus/bus.h"
+#include "dbus/file_descriptor.h"
+#include "dbus/message.h"
+#include "dbus/object_path.h"
+#include "dbus/object_proxy.h"
+#include "mojo/dbus/dbus_external_service.h"
+#include "mojo/embedder/channel_init.h"
+#include "mojo/embedder/platform_channel_pair.h"
+#include "mojo/shell/context.h"
+#include "mojo/shell/external_service.mojom.h"
+#include "mojo/shell/keep_alive.h"
+
+namespace mojo {
+namespace shell {
+
+// Manages the connection to a single externally-running service.
+class DBusServiceLoader::LoadContext {
+ public:
+ // Kicks off the attempt to bootstrap a connection to the externally-running
+ // service specified by url_.
+ // Creates a MessagePipe and passes one end over DBus to the service. Then,
+ // calls ExternalService::Activate(ShellHandle) over the now-shared pipe.
+ LoadContext(DBusServiceLoader* loader,
+ const scoped_refptr<dbus::Bus>& bus,
+ const GURL& url,
+ ScopedMessagePipeHandle service_provider_handle)
+ : loader_(loader),
+ bus_(bus),
+ service_dbus_proxy_(NULL),
+ url_(url),
+ service_provider_handle_(service_provider_handle.Pass()),
+ keep_alive_(loader->context_) {
+ base::PostTaskAndReplyWithResult(
+ loader_->context_->task_runners()->io_runner(),
+ FROM_HERE,
+ base::Bind(&LoadContext::CreateChannelOnIOThread,
+ base::Unretained(this)),
+ base::Bind(&LoadContext::ConnectChannel, base::Unretained(this)));
+ }
+
+ virtual ~LoadContext() {
+ }
+
+ private:
+ // Sets up a pipe to share with the externally-running service and returns
+ // the endpoint that should be sent over DBus.
+ // The FD for the endpoint must be validated on an IO thread.
+ scoped_ptr<dbus::FileDescriptor> CreateChannelOnIOThread() {
+ base::ThreadRestrictions::AssertIOAllowed();
+ CHECK(bus_->Connect());
+ CHECK(bus_->SetUpAsyncOperations());
+
+ embedder::PlatformChannelPair channel_pair;
+ channel_init_.reset(new embedder::ChannelInit);
+ mojo::ScopedMessagePipeHandle bootstrap_message_pipe =
+ channel_init_->Init(channel_pair.PassServerHandle().release().fd,
+ loader_->context_->task_runners()->io_runner());
+ CHECK(bootstrap_message_pipe.is_valid());
+
+ external_service_.Bind(bootstrap_message_pipe.Pass());
+
+ scoped_ptr<dbus::FileDescriptor> client_fd(new dbus::FileDescriptor);
+ client_fd->PutValue(channel_pair.PassClientHandle().release().fd);
+ client_fd->CheckValidity(); // Must be run on an IO thread.
+ return client_fd.Pass();
+ }
+
+ // Sends client_fd over to the externally-running service. If that
+ // attempt is successful, the service will then be "activated" by
+ // sending it a ShellHandle.
+ void ConnectChannel(scoped_ptr<dbus::FileDescriptor> client_fd) {
+ size_t first_slash = url_.path().find_first_of('/');
+ DCHECK_NE(first_slash, std::string::npos);
+
+ const std::string service_name = url_.path().substr(0, first_slash);
+ const std::string object_path = url_.path().substr(first_slash);
+ service_dbus_proxy_ =
+ bus_->GetObjectProxy(service_name, dbus::ObjectPath(object_path));
+
+ dbus::MethodCall call(kMojoDBusInterface, kMojoDBusConnectMethod);
+ dbus::MessageWriter writer(&call);
+ writer.AppendFileDescriptor(*client_fd.get());
+
+ // TODO(cmasone): handle errors!
+ service_dbus_proxy_->CallMethod(
+ &call,
+ dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ base::Bind(&LoadContext::ActivateService, base::Unretained(this)));
+ }
+
+ // Sends a ShellHandle over to the now-connected externally-running service,
+ // using the Mojo ExternalService API.
+ void ActivateService(dbus::Response* response) {
+ external_service_->Activate(
+ mojo::ScopedMessagePipeHandle(
+ mojo::MessagePipeHandle(
+ service_provider_handle_.release().value())));
+ }
+
+ // Should the ExternalService disappear completely, destroy connection state.
+ // NB: This triggers off of the service disappearing from
+ // DBus. Perhaps there's a way to watch at the Mojo layer instead,
+ // and that would be superior?
+ void HandleNameOwnerChanged(const std::string& old_owner,
+ const std::string& new_owner) {
+ DCHECK(loader_->context_->task_runners()->ui_runner()->
+ BelongsToCurrentThread());
+
+ if (new_owner.empty()) {
+ loader_->context_->task_runners()->ui_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&DBusServiceLoader::ForgetService,
+ base::Unretained(loader_), url_));
+ }
+ }
+
+ DBusServiceLoader* const loader_;
+ scoped_refptr<dbus::Bus> bus_;
+ dbus::ObjectProxy* service_dbus_proxy_; // Owned by bus_;
+ const GURL url_;
+ ScopedMessagePipeHandle service_provider_handle_;
+ KeepAlive keep_alive_;
+ scoped_ptr<embedder::ChannelInit> channel_init_;
+ ExternalServicePtr external_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoadContext);
+};
+
+DBusServiceLoader::DBusServiceLoader(Context* context) : context_(context) {
+ dbus::Bus::Options options;
+ options.bus_type = dbus::Bus::SESSION;
+ options.dbus_task_runner = context_->task_runners()->io_runner();
+ bus_ = new dbus::Bus(options);
+}
+
+DBusServiceLoader::~DBusServiceLoader() {
+ DCHECK(url_to_load_context_.empty());
+}
+
+void DBusServiceLoader::LoadService(ServiceManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle service_handle) {
+ DCHECK(url.SchemeIs("dbus"));
+ DCHECK(url_to_load_context_.find(url) == url_to_load_context_.end());
+ url_to_load_context_[url] =
+ new LoadContext(this, bus_, url, service_handle.Pass());
+}
+
+void DBusServiceLoader::OnServiceError(ServiceManager* manager,
+ const GURL& url) {
+ // TODO(cmasone): Anything at all in this method here.
+}
+
+void DBusServiceLoader::ForgetService(const GURL& url) {
+ DCHECK(context_->task_runners()->ui_runner()->BelongsToCurrentThread());
+ DVLOG(2) << "Forgetting service (url: " << url << ")";
+
+ LoadContextMap::iterator it = url_to_load_context_.find(url);
+ DCHECK(it != url_to_load_context_.end()) << url;
+
+ LoadContext* doomed = it->second;
+ url_to_load_context_.erase(it);
+
+ delete doomed;
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/dbus_service_loader_linux.h b/chromium/mojo/shell/dbus_service_loader_linux.h
new file mode 100644
index 00000000000..5bc24cab5fe
--- /dev/null
+++ b/chromium/mojo/shell/dbus_service_loader_linux.h
@@ -0,0 +1,89 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SHELL_DBUS_SERVICE_LOADER_H_
+#define MOJO_SHELL_DBUS_SERVICE_LOADER_H_
+
+#include <map>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/service_manager/service_loader.h"
+#include "mojo/shell/keep_alive.h"
+#include "url/gurl.h"
+
+namespace dbus {
+class Bus;
+} // namespace dbus
+
+namespace mojo {
+namespace shell {
+
+class Context;
+
+// An implementation of ServiceLoader that contacts a system service
+// and bootstraps a Mojo connection to it over DBus.
+//
+// In order to allow the externally-running service to accept connections from
+// a Mojo shell, we need to get it a ShellHandle. This class creates a
+// dedicated MessagePipe, passes a handle to one end to the desired service
+// over DBus, and then passes the ShellHandle over that pipe.
+//
+// This class assumes the following:
+// 1) Your service is already running.
+// 2) Your service implements the Mojo ExternalService API
+// (from external_service.mojom).
+// 3) Your service exports an object that implements the org.chromium.Mojo DBus
+// interface:
+// <interface name="org.chromium.Mojo">
+// <method name="ConnectChannel">
+// <arg type="h" name="file_descriptor" direction="in" />
+// </method>
+// </interface>
+class DBusServiceLoader : public ServiceLoader {
+ public:
+ DBusServiceLoader(Context* context);
+ virtual ~DBusServiceLoader();
+
+ // URL for DBus services are of the following format:
+ // dbus:tld.domain.ServiceName/path/to/DBusObject
+ //
+ // This is simply the scheme (dbus:) and then the DBus service name followed
+ // by the DBus object path of an object that implements the org.chromium.Mojo
+ // interface as discussed above.
+ //
+ // Example:
+ // dbus:org.chromium.EchoService/org/chromium/MojoImpl
+ //
+ // This will tell DBusServiceLoader to reach out to a service with
+ // the name "org.chromium.EchoService" and invoke the method
+ // "org.chromium.Mojo.ConnectChannel" on the object exported at
+ // "/org/chromium/MojoImpl".
+ virtual void LoadService(ServiceManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle service_handle) OVERRIDE;
+
+ virtual void OnServiceError(ServiceManager* manager, const GURL& url)
+ OVERRIDE;
+
+ private:
+ class LoadContext;
+
+ // Tosses out connection-related state to service at given URL.
+ void ForgetService(const GURL& url);
+
+ Context* const context_;
+ scoped_refptr<dbus::Bus> bus_;
+
+ typedef std::map<GURL, LoadContext*> LoadContextMap;
+ LoadContextMap url_to_load_context_;
+
+ DISALLOW_COPY_AND_ASSIGN(DBusServiceLoader);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_DBUS_SERVICE_LOADER_H_
diff --git a/chromium/mojo/shell/desktop/mojo_main.cc b/chromium/mojo/shell/desktop/mojo_main.cc
new file mode 100644
index 00000000000..de2d0d4906e
--- /dev/null
+++ b/chromium/mojo/shell/desktop/mojo_main.cc
@@ -0,0 +1,56 @@
+// Copyright 2013 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 "base/at_exit.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/shell/child_process.h"
+#include "mojo/shell/context.h"
+#include "mojo/shell/init.h"
+#include "mojo/shell/run.h"
+#include "mojo/shell/switches.h"
+#include "ui/gl/gl_surface.h"
+
+int main(int argc, char** argv) {
+ base::AtExitManager at_exit;
+ base::CommandLine::Init(argc, argv);
+ mojo::shell::InitializeLogging();
+
+ // TODO(vtl): Unify parent and child process cases to the extent possible.
+ if (scoped_ptr<mojo::shell::ChildProcess> child_process =
+ mojo::shell::ChildProcess::Create(
+ *base::CommandLine::ForCurrentProcess())) {
+ child_process->Main();
+ } else {
+ gfx::GLSurface::InitializeOneOff();
+
+ base::MessageLoop message_loop;
+ mojo::shell::Context shell_context;
+
+ const base::CommandLine& command_line =
+ *base::CommandLine::ForCurrentProcess();
+ if (command_line.HasSwitch(switches::kOrigin)) {
+ shell_context.mojo_url_resolver()->set_origin(
+ command_line.GetSwitchValueASCII(switches::kOrigin));
+ }
+
+ std::vector<GURL> app_urls;
+ base::CommandLine::StringVector args = command_line.GetArgs();
+ for (base::CommandLine::StringVector::const_iterator it = args.begin();
+ it != args.end();
+ ++it)
+ app_urls.push_back(GURL(*it));
+
+ message_loop.PostTask(FROM_HERE,
+ base::Bind(mojo::shell::Run,
+ &shell_context,
+ app_urls));
+
+ message_loop.Run();
+ }
+
+ return 0;
+}
diff --git a/chromium/mojo/shell/dynamic_service_loader.cc b/chromium/mojo/shell/dynamic_service_loader.cc
new file mode 100644
index 00000000000..9def450ed9b
--- /dev/null
+++ b/chromium/mojo/shell/dynamic_service_loader.cc
@@ -0,0 +1,186 @@
+// Copyright 2014 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 "mojo/shell/dynamic_service_loader.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/common/data_pipe_utils.h"
+#include "mojo/services/public/interfaces/network/url_loader.mojom.h"
+#include "mojo/shell/context.h"
+#include "mojo/shell/keep_alive.h"
+#include "mojo/shell/switches.h"
+#include "net/base/filename_util.h"
+
+namespace mojo {
+namespace shell {
+namespace {
+
+class Loader {
+ public:
+ explicit Loader(scoped_ptr<DynamicServiceRunner> runner)
+ : runner_(runner.Pass()) {
+ }
+
+ virtual void Start(const GURL& url,
+ ScopedMessagePipeHandle service_handle,
+ Context* context) = 0;
+
+ void StartService(const base::FilePath& path,
+ ScopedMessagePipeHandle service_handle,
+ bool path_is_valid) {
+ if (path_is_valid) {
+ runner_->Start(path, service_handle.Pass(),
+ base::Bind(&Loader::AppCompleted, base::Unretained(this)));
+ } else {
+ AppCompleted();
+ }
+ }
+
+ protected:
+ virtual ~Loader() {}
+
+ private:
+ void AppCompleted() {
+ delete this;
+ }
+
+ scoped_ptr<DynamicServiceRunner> runner_;
+};
+
+// For loading services via file:// URLs.
+class LocalLoader : public Loader {
+ public:
+ explicit LocalLoader(scoped_ptr<DynamicServiceRunner> runner)
+ : Loader(runner.Pass()) {
+ }
+
+ virtual void Start(const GURL& url,
+ ScopedMessagePipeHandle service_handle,
+ Context* context) OVERRIDE {
+ base::FilePath path;
+ net::FileURLToFilePath(url, &path);
+
+ // TODO(darin): Check if the given file path exists.
+
+ // Complete asynchronously for consistency with NetworkServiceLoader.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&Loader::StartService,
+ base::Unretained(this),
+ path,
+ base::Passed(&service_handle),
+ true));
+ }
+};
+
+// For loading services via the network stack.
+class NetworkLoader : public Loader, public URLLoaderClient {
+ public:
+ explicit NetworkLoader(scoped_ptr<DynamicServiceRunner> runner,
+ NetworkService* network_service)
+ : Loader(runner.Pass()) {
+ network_service->CreateURLLoader(Get(&url_loader_));
+ url_loader_.set_client(this);
+ }
+
+ virtual void Start(const GURL& url,
+ ScopedMessagePipeHandle service_handle,
+ Context* context) OVERRIDE {
+ service_handle_ = service_handle.Pass();
+
+ URLRequestPtr request(URLRequest::New());
+ request->url = url.spec();
+ request->auto_follow_redirects = true;
+
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableCache)) {
+ request->bypass_cache = true;
+ }
+
+ DataPipe data_pipe;
+ url_loader_->Start(request.Pass(), data_pipe.producer_handle.Pass());
+
+ base::CreateTemporaryFile(&file_);
+ common::CopyToFile(data_pipe.consumer_handle.Pass(),
+ file_,
+ context->task_runners()->blocking_pool(),
+ base::Bind(&Loader::StartService,
+ base::Unretained(this),
+ file_,
+ base::Passed(&service_handle_)));
+ }
+
+ private:
+ virtual ~NetworkLoader() {
+ if (!file_.empty())
+ base::DeleteFile(file_, false);
+ }
+
+ // URLLoaderClient methods:
+ virtual void OnReceivedRedirect(URLResponsePtr response,
+ const String& new_url,
+ const String& new_method) OVERRIDE {
+ // TODO(darin): Handle redirects properly!
+ }
+ virtual void OnReceivedResponse(URLResponsePtr response) OVERRIDE {}
+ virtual void OnReceivedError(NetworkErrorPtr error) OVERRIDE {}
+ virtual void OnReceivedEndOfResponseBody() OVERRIDE {}
+
+ NetworkServicePtr network_service_;
+ URLLoaderPtr url_loader_;
+ ScopedMessagePipeHandle service_handle_;
+ base::FilePath file_;
+};
+
+} // namespace
+
+DynamicServiceLoader::DynamicServiceLoader(
+ Context* context,
+ scoped_ptr<DynamicServiceRunnerFactory> runner_factory)
+ : context_(context),
+ runner_factory_(runner_factory.Pass()) {
+}
+
+DynamicServiceLoader::~DynamicServiceLoader() {
+}
+
+void DynamicServiceLoader::LoadService(ServiceManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle service_handle) {
+ scoped_ptr<DynamicServiceRunner> runner = runner_factory_->Create(context_);
+
+ GURL resolved_url;
+ if (url.SchemeIs("mojo")) {
+ resolved_url = context_->mojo_url_resolver()->Resolve(url);
+ } else {
+ resolved_url = url;
+ }
+
+ Loader* loader;
+ if (resolved_url.SchemeIsFile()) {
+ loader = new LocalLoader(runner.Pass());
+ } else {
+ if (!network_service_.get()) {
+ context_->service_manager()->ConnectTo(GURL("mojo:mojo_network_service"),
+ &network_service_,
+ GURL());
+ }
+ loader = new NetworkLoader(runner.Pass(), network_service_.get());
+ }
+ loader->Start(resolved_url, service_handle.Pass(), context_);
+}
+
+void DynamicServiceLoader::OnServiceError(ServiceManager* manager,
+ const GURL& url) {
+ // TODO(darin): What should we do about service errors? This implies that
+ // the app closed its handle to the service manager. Maybe we don't care?
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/dynamic_service_loader.h b/chromium/mojo/shell/dynamic_service_loader.h
new file mode 100644
index 00000000000..72bf093e56e
--- /dev/null
+++ b/chromium/mojo/shell/dynamic_service_loader.h
@@ -0,0 +1,51 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SHELL_DYNAMIC_SERVICE_LOADER_H_
+#define MOJO_SHELL_DYNAMIC_SERVICE_LOADER_H_
+
+#include <map>
+
+#include "base/macros.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/service_manager/service_loader.h"
+#include "mojo/services/public/interfaces/network/network_service.mojom.h"
+#include "mojo/shell/dynamic_service_runner.h"
+#include "mojo/shell/keep_alive.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+class Context;
+class DynamicServiceRunnerFactory;
+
+// An implementation of ServiceLoader that retrieves a dynamic library
+// containing the implementation of the service and loads/runs it (via a
+// DynamicServiceRunner).
+class DynamicServiceLoader : public ServiceLoader {
+ public:
+ DynamicServiceLoader(Context* context,
+ scoped_ptr<DynamicServiceRunnerFactory> runner_factory);
+ virtual ~DynamicServiceLoader();
+
+ // ServiceLoader methods:
+ virtual void LoadService(ServiceManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle service_handle) OVERRIDE;
+ virtual void OnServiceError(ServiceManager* manager, const GURL& url)
+ OVERRIDE;
+
+ private:
+ Context* const context_;
+ scoped_ptr<DynamicServiceRunnerFactory> runner_factory_;
+ NetworkServicePtr network_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(DynamicServiceLoader);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_DYNAMIC_SERVICE_LOADER_H_
diff --git a/chromium/mojo/shell/dynamic_service_runner.h b/chromium/mojo/shell/dynamic_service_runner.h
new file mode 100644
index 00000000000..1409fb0e5f7
--- /dev/null
+++ b/chromium/mojo/shell/dynamic_service_runner.h
@@ -0,0 +1,57 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SHELL_DYNAMIC_SERVICE_RUNNER_H_
+#define MOJO_SHELL_DYNAMIC_SERVICE_RUNNER_H_
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace mojo {
+namespace shell {
+
+class Context;
+
+// Abstraction for loading a service (from the file system) and running it (on
+// another thread or in a separate process).
+class DynamicServiceRunner {
+ public:
+ virtual ~DynamicServiceRunner() {}
+
+ // Takes ownership of the file at |app_path|. Loads the app in that file and
+ // runs it on some other thread/process. |app_completed_callback| is posted
+ // (to the thread on which |Start()| was called) after |MojoMain()| completes.
+ virtual void Start(const base::FilePath& app_path,
+ ScopedMessagePipeHandle service_handle,
+ const base::Closure& app_completed_callback) = 0;
+};
+
+class DynamicServiceRunnerFactory {
+ public:
+ virtual ~DynamicServiceRunnerFactory() {}
+ virtual scoped_ptr<DynamicServiceRunner> Create(Context* context) = 0;
+};
+
+// A generic factory.
+template <class DynamicServiceRunnerImpl>
+class DynamicServiceRunnerFactoryImpl : public DynamicServiceRunnerFactory {
+ public:
+ DynamicServiceRunnerFactoryImpl() {}
+ virtual ~DynamicServiceRunnerFactoryImpl() {}
+ virtual scoped_ptr<DynamicServiceRunner> Create(Context* context) OVERRIDE {
+ return scoped_ptr<DynamicServiceRunner>(
+ new DynamicServiceRunnerImpl(context));
+ }
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_DYNAMIC_SERVICE_RUNNER_H_
diff --git a/chromium/mojo/shell/external_service.mojom b/chromium/mojo/shell/external_service.mojom
new file mode 100644
index 00000000000..e08f71fe504
--- /dev/null
+++ b/chromium/mojo/shell/external_service.mojom
@@ -0,0 +1,11 @@
+// Copyright 2014 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.
+
+module mojo {
+
+interface ExternalService {
+ Activate(handle<message_pipe> service_provider_handle);
+};
+
+}
diff --git a/chromium/mojo/shell/in_process_dynamic_service_runner.cc b/chromium/mojo/shell/in_process_dynamic_service_runner.cc
new file mode 100644
index 00000000000..6bcaa5c67a8
--- /dev/null
+++ b/chromium/mojo/shell/in_process_dynamic_service_runner.cc
@@ -0,0 +1,102 @@
+// Copyright 2014 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 "mojo/shell/in_process_dynamic_service_runner.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/scoped_native_library.h"
+#include "mojo/public/platform/native/system_thunks.h"
+
+namespace mojo {
+namespace shell {
+
+InProcessDynamicServiceRunner::InProcessDynamicServiceRunner(
+ Context* context)
+ : keep_alive_(context),
+ thread_(this, "app_thread") {
+}
+
+InProcessDynamicServiceRunner::~InProcessDynamicServiceRunner() {
+ if (thread_.HasBeenStarted()) {
+ DCHECK(!thread_.HasBeenJoined());
+ thread_.Join();
+ }
+}
+
+void InProcessDynamicServiceRunner::Start(
+ const base::FilePath& app_path,
+ ScopedMessagePipeHandle service_handle,
+ const base::Closure& app_completed_callback) {
+ app_path_ = app_path;
+
+ DCHECK(!service_handle_.is_valid());
+ service_handle_ = service_handle.Pass();
+
+ DCHECK(app_completed_callback_runner_.is_null());
+ app_completed_callback_runner_ = base::Bind(&base::TaskRunner::PostTask,
+ base::MessageLoopProxy::current(),
+ FROM_HERE,
+ app_completed_callback);
+
+ DCHECK(!thread_.HasBeenStarted());
+ thread_.Start();
+}
+
+void InProcessDynamicServiceRunner::Run() {
+ DVLOG(2) << "Loading/running Mojo app in process from library: "
+ << app_path_.value();
+
+ do {
+ base::NativeLibraryLoadError error;
+ base::ScopedNativeLibrary app_library(
+ base::LoadNativeLibrary(app_path_, &error));
+ if (!app_library.is_valid()) {
+ LOG(ERROR) << "Failed to load app library (error: " << error.ToString()
+ << ")";
+ break;
+ }
+
+ MojoSetSystemThunksFn mojo_set_system_thunks_fn =
+ reinterpret_cast<MojoSetSystemThunksFn>(app_library.GetFunctionPointer(
+ "MojoSetSystemThunks"));
+ if (mojo_set_system_thunks_fn) {
+ MojoSystemThunks system_thunks = MojoMakeSystemThunks();
+ size_t expected_size = mojo_set_system_thunks_fn(&system_thunks);
+ if (expected_size > sizeof(MojoSystemThunks)) {
+ LOG(ERROR)
+ << "Invalid app library: expected MojoSystemThunks size: "
+ << expected_size;
+ break;
+ }
+ } else {
+ // Strictly speaking this is not required, but it's very unusual to have
+ // an app that doesn't require the basic system library.
+ LOG(WARNING) << "MojoSetSystemThunks not found in app library";
+ }
+
+ typedef MojoResult (*MojoMainFunction)(MojoHandle);
+ MojoMainFunction main_function = reinterpret_cast<MojoMainFunction>(
+ app_library.GetFunctionPointer("MojoMain"));
+ if (!main_function) {
+ LOG(ERROR) << "Entrypoint MojoMain not found";
+ break;
+ }
+
+ // |MojoMain()| takes ownership of the service handle.
+ MojoResult result = main_function(service_handle_.release().value());
+ if (result < MOJO_RESULT_OK)
+ LOG(ERROR) << "MojoMain returned an error: " << result;
+ } while (false);
+
+ bool success = app_completed_callback_runner_.Run();
+ app_completed_callback_runner_.Reset();
+ LOG_IF(ERROR, !success) << "Failed post run app_completed_callback";
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/in_process_dynamic_service_runner.h b/chromium/mojo/shell/in_process_dynamic_service_runner.h
new file mode 100644
index 00000000000..ce6b029c782
--- /dev/null
+++ b/chromium/mojo/shell/in_process_dynamic_service_runner.h
@@ -0,0 +1,52 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SHELL_IN_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
+#define MOJO_SHELL_IN_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/threading/simple_thread.h"
+#include "mojo/shell/dynamic_service_runner.h"
+#include "mojo/shell/keep_alive.h"
+
+namespace mojo {
+namespace shell {
+
+// An implementation of |DynamicServiceRunner| that loads/runs the given app
+// (from the file system) on a separate thread (in the current process).
+class InProcessDynamicServiceRunner
+ : public DynamicServiceRunner,
+ public base::DelegateSimpleThread::Delegate {
+ public:
+ explicit InProcessDynamicServiceRunner(Context* context);
+ virtual ~InProcessDynamicServiceRunner();
+
+ // |DynamicServiceRunner| method:
+ virtual void Start(const base::FilePath& app_path,
+ ScopedMessagePipeHandle service_handle,
+ const base::Closure& app_completed_callback) OVERRIDE;
+
+ private:
+ // |base::DelegateSimpleThread::Delegate| method:
+ virtual void Run() OVERRIDE;
+
+ KeepAlive keep_alive_;
+ base::FilePath app_path_;
+ ScopedMessagePipeHandle service_handle_;
+ base::Callback<bool(void)> app_completed_callback_runner_;
+
+ base::DelegateSimpleThread thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(InProcessDynamicServiceRunner);
+};
+
+typedef DynamicServiceRunnerFactoryImpl<InProcessDynamicServiceRunner>
+ InProcessDynamicServiceRunnerFactory;
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_IN_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
diff --git a/chromium/mojo/shell/init.cc b/chromium/mojo/shell/init.cc
new file mode 100644
index 00000000000..481e5ee4a4d
--- /dev/null
+++ b/chromium/mojo/shell/init.cc
@@ -0,0 +1,24 @@
+// Copyright 2013 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 "mojo/shell/init.h"
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace shell {
+
+void InitializeLogging() {
+ logging::LoggingSettings settings;
+ settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+ logging::InitLogging(settings);
+ // To view log output with IDs and timestamps use "adb logcat -v threadtime".
+ logging::SetLogItems(false, // Process ID
+ false, // Thread ID
+ false, // Timestamp
+ false); // Tick count
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/init.h b/chromium/mojo/shell/init.h
new file mode 100644
index 00000000000..c83134aa88c
--- /dev/null
+++ b/chromium/mojo/shell/init.h
@@ -0,0 +1,18 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SHELL_INIT_H_
+#define MOJO_SHELL_INIT_H_
+
+namespace mojo {
+namespace shell {
+
+// Initialization routines shared by desktop and Android main functions.
+
+void InitializeLogging();
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_INIT_H_
diff --git a/chromium/mojo/shell/keep_alive.cc b/chromium/mojo/shell/keep_alive.cc
new file mode 100644
index 00000000000..387c6a790c3
--- /dev/null
+++ b/chromium/mojo/shell/keep_alive.cc
@@ -0,0 +1,34 @@
+// Copyright 2014 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 "mojo/shell/keep_alive.h"
+
+#include "base/bind.h"
+#include "mojo/shell/context.h"
+
+namespace mojo {
+namespace shell {
+
+KeepAlive::KeepAlive(Context* context) : context_(context) {
+ DCHECK(context_->task_runners()->ui_runner()->RunsTasksOnCurrentThread());
+ ++context_->keep_alive_counter()->count_;
+}
+
+KeepAlive::~KeepAlive() {
+ DCHECK(context_->task_runners()->ui_runner()->RunsTasksOnCurrentThread());
+ if (--context_->keep_alive_counter()->count_ == 0) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&KeepAlive::MaybeQuit, context_));
+ }
+}
+
+// static
+void KeepAlive::MaybeQuit(Context* context) {
+ if (context->keep_alive_counter()->count_ == 0)
+ base::MessageLoop::current()->Quit();
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/keep_alive.h b/chromium/mojo/shell/keep_alive.h
new file mode 100644
index 00000000000..d9b61275ca8
--- /dev/null
+++ b/chromium/mojo/shell/keep_alive.h
@@ -0,0 +1,44 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SHELL_KEEP_ALIVE_H_
+#define MOJO_SHELL_KEEP_ALIVE_H_
+
+#include "base/basictypes.h"
+
+namespace mojo {
+namespace shell {
+
+class Context;
+class KeepAlive;
+
+class KeepAliveCounter {
+ public:
+ KeepAliveCounter() : count_(0) {
+ }
+ private:
+ friend class KeepAlive;
+ int count_;
+};
+
+// Instantiate this class to extend the lifetime of the thread associated
+// with |context| (i.e., the shell's UI thread). Must only be used from
+// the shell's UI thread.
+class KeepAlive {
+ public:
+ explicit KeepAlive(Context* context);
+ ~KeepAlive();
+
+ private:
+ static void MaybeQuit(Context* context);
+
+ Context* context_;
+
+ DISALLOW_COPY_AND_ASSIGN(KeepAlive);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_KEEP_ALIVE_H_
diff --git a/chromium/mojo/shell/mojo_url_resolver.cc b/chromium/mojo/shell/mojo_url_resolver.cc
new file mode 100644
index 00000000000..35059547e94
--- /dev/null
+++ b/chromium/mojo/shell/mojo_url_resolver.cc
@@ -0,0 +1,78 @@
+// Copyright 2014 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 "mojo/shell/mojo_url_resolver.h"
+
+#include "base/base_paths.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "net/base/filename_util.h"
+#include "url/url_util.h"
+
+namespace mojo {
+namespace shell {
+namespace {
+
+std::string MakeSharedLibraryName(const std::string& host_name) {
+#if defined(OS_WIN)
+ return host_name + ".dll";
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
+ return "lib" + host_name + ".so";
+#elif defined(OS_MACOSX)
+ return "lib" + host_name + ".dylib";
+#else
+ NOTREACHED() << "dynamic loading of services not supported";
+ return std::string();
+#endif
+}
+
+} // namespace
+
+MojoURLResolver::MojoURLResolver() {
+ // Needed to treat first component of mojo URLs as host, not path.
+ url::AddStandardScheme("mojo");
+}
+
+MojoURLResolver::~MojoURLResolver() {
+}
+
+void MojoURLResolver::AddCustomMapping(const GURL& mojo_url,
+ const GURL& resolved_url) {
+ url_map_[mojo_url] = resolved_url;
+}
+
+void MojoURLResolver::AddLocalFileMapping(const GURL& mojo_url) {
+ local_file_set_.insert(mojo_url);
+}
+
+GURL MojoURLResolver::Resolve(const GURL& mojo_url) const {
+ std::map<GURL, GURL>::const_iterator it = url_map_.find(mojo_url);
+ if (it != url_map_.end())
+ return it->second;
+
+ std::string lib = MakeSharedLibraryName(mojo_url.host());
+
+ if (local_file_set_.find(mojo_url) != local_file_set_.end()) {
+ // Resolve to a local file URL.
+ base::FilePath path;
+#if defined(OS_ANDROID)
+ // On Android, additional lib are bundled.
+ PathService::Get(base::DIR_MODULE, &path);
+#else
+ PathService::Get(base::DIR_EXE, &path);
+#if !defined(OS_WIN)
+ path = path.Append(FILE_PATH_LITERAL("lib"));
+#endif // !defined(OS_WIN)
+#endif // defined(OS_ANDROID)
+ path = path.Append(base::FilePath::FromUTF8Unsafe(lib));
+ return net::FilePathToFileURL(path);
+ }
+
+ // Otherwise, resolve to an URL relative to origin_.
+ return GURL(origin_ + "/" + lib);
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/mojo_url_resolver.h b/chromium/mojo/shell/mojo_url_resolver.h
new file mode 100644
index 00000000000..d232a407035
--- /dev/null
+++ b/chromium/mojo/shell/mojo_url_resolver.h
@@ -0,0 +1,49 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SHELL_MOJO_URL_RESOLVER_H_
+#define MOJO_SHELL_MOJO_URL_RESOLVER_H_
+
+#include <map>
+#include <set>
+
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+// This class resolves "mojo:" URLs to physical URLs (e.g., "file:" and
+// "https:" URLs). By default, "mojo:" URLs resolve to a file location, but
+// that resolution can be customized via the AddCustomMapping method.
+class MojoURLResolver {
+ public:
+ MojoURLResolver();
+ ~MojoURLResolver();
+
+ // If specified, then unknown "mojo:" URLs will be resolved relative to this
+ // origin. That is, the portion after the colon will be appeneded to origin
+ // with platform-specific shared library prefix and suffix inserted.
+ void set_origin(const std::string& origin) { origin_ = origin; }
+
+ // Add a custom mapping for a particular "mojo:" URL.
+ void AddCustomMapping(const GURL& mojo_url, const GURL& resolved_url);
+
+ // Add a local file mapping for a particular "mojo:" URL. This causes the
+ // "mojo:" URL to be resolved to an base::DIR_EXE-relative shared library.
+ void AddLocalFileMapping(const GURL& mojo_url);
+
+ // Resolve the given "mojo:" URL to the URL that should be used to fetch the
+ // code for the corresponding Mojo App.
+ GURL Resolve(const GURL& mojo_url) const;
+
+ private:
+ std::map<GURL, GURL> url_map_;
+ std::set<GURL> local_file_set_;
+ std::string origin_;
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_MOJO_URL_RESOLVER_H_
diff --git a/chromium/mojo/shell/out_of_process_dynamic_service_runner.cc b/chromium/mojo/shell/out_of_process_dynamic_service_runner.cc
new file mode 100644
index 00000000000..9b2e3cfac72
--- /dev/null
+++ b/chromium/mojo/shell/out_of_process_dynamic_service_runner.cc
@@ -0,0 +1,64 @@
+// Copyright 2014 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 "mojo/shell/out_of_process_dynamic_service_runner.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/scoped_native_library.h"
+
+namespace mojo {
+namespace shell {
+
+OutOfProcessDynamicServiceRunner::OutOfProcessDynamicServiceRunner(
+ Context* context)
+ : context_(context),
+ keep_alive_(context) {
+}
+
+OutOfProcessDynamicServiceRunner::~OutOfProcessDynamicServiceRunner() {
+ if (app_child_process_host_) {
+ // TODO(vtl): Race condition: If |AppChildProcessHost::DidStart()| hasn't
+ // been called yet, we shouldn't call |Join()| here. (Until |DidStart()|, we
+ // may not have a child process to wait on.) Probably we should fix
+ // |Join()|.
+ app_child_process_host_->Join();
+ }
+}
+
+void OutOfProcessDynamicServiceRunner::Start(
+ const base::FilePath& app_path,
+ ScopedMessagePipeHandle service_handle,
+ const base::Closure& app_completed_callback) {
+ app_path_ = app_path;
+
+ DCHECK(!service_handle_.is_valid());
+ service_handle_ = service_handle.Pass();
+
+ DCHECK(app_completed_callback_.is_null());
+ app_completed_callback_ = app_completed_callback;
+
+ app_child_process_host_.reset(new AppChildProcessHost(context_, this));
+ app_child_process_host_->Start();
+
+ // TODO(vtl): |app_path.AsUTF8Unsafe()| is unsafe.
+ app_child_process_host_->controller()->StartApp(
+ app_path.AsUTF8Unsafe(),
+ ScopedMessagePipeHandle(MessagePipeHandle(
+ service_handle.release().value())));
+}
+
+void OutOfProcessDynamicServiceRunner::AppCompleted(int32_t result) {
+ DVLOG(2) << "OutOfProcessDynamicServiceRunner::AppCompleted(" << result
+ << ")";
+
+ app_completed_callback_.Run();
+ app_completed_callback_.Reset();
+ app_child_process_host_.reset();
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/out_of_process_dynamic_service_runner.h b/chromium/mojo/shell/out_of_process_dynamic_service_runner.h
new file mode 100644
index 00000000000..da07ff24fee
--- /dev/null
+++ b/chromium/mojo/shell/out_of_process_dynamic_service_runner.h
@@ -0,0 +1,55 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SHELL_OUT_OF_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
+#define MOJO_SHELL_OUT_OF_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "mojo/shell/app_child_process.mojom.h"
+#include "mojo/shell/app_child_process_host.h"
+#include "mojo/shell/dynamic_service_runner.h"
+#include "mojo/shell/keep_alive.h"
+
+namespace mojo {
+namespace shell {
+
+// An implementation of |DynamicServiceRunner| that loads/runs the given app
+// (from the file system) in a separate process (of its own).
+class OutOfProcessDynamicServiceRunner
+ : public DynamicServiceRunner,
+ public AppChildControllerClient {
+ public:
+ explicit OutOfProcessDynamicServiceRunner(Context* context);
+ virtual ~OutOfProcessDynamicServiceRunner();
+
+ // |DynamicServiceRunner| method:
+ virtual void Start(const base::FilePath& app_path,
+ ScopedMessagePipeHandle service_handle,
+ const base::Closure& app_completed_callback) OVERRIDE;
+
+ private:
+ // |AppChildControllerClient| method:
+ virtual void AppCompleted(int32_t result) OVERRIDE;
+
+ Context* const context_;
+
+ KeepAlive keep_alive_;
+ base::FilePath app_path_;
+ ScopedMessagePipeHandle service_handle_;
+ base::Closure app_completed_callback_;
+
+ scoped_ptr<AppChildProcessHost> app_child_process_host_;
+
+ DISALLOW_COPY_AND_ASSIGN(OutOfProcessDynamicServiceRunner);
+};
+
+typedef DynamicServiceRunnerFactoryImpl<OutOfProcessDynamicServiceRunner>
+ OutOfProcessDynamicServiceRunnerFactory;
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_OUT_OF_PROCESS_DYNAMIC_SERVICE_RUNNER_H_
diff --git a/chromium/mojo/shell/run.cc b/chromium/mojo/shell/run.cc
new file mode 100644
index 00000000000..3d6840442ba
--- /dev/null
+++ b/chromium/mojo/shell/run.cc
@@ -0,0 +1,33 @@
+// Copyright 2013 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 "mojo/shell/run.h"
+
+#include "base/logging.h"
+#include "mojo/service_manager/service_manager.h"
+#include "mojo/shell/context.h"
+#include "mojo/shell/keep_alive.h"
+
+namespace mojo {
+namespace shell {
+
+void Run(Context* context, const std::vector<GURL>& app_urls) {
+ KeepAlive keep_alive(context);
+
+ if (app_urls.empty()) {
+ LOG(ERROR) << "No app path specified";
+ return;
+ }
+
+ for (std::vector<GURL>::const_iterator it = app_urls.begin();
+ it != app_urls.end();
+ ++it) {
+ ScopedMessagePipeHandle no_handle;
+ context->service_manager()->ConnectToService(
+ *it, std::string(), no_handle.Pass(), GURL());
+ }
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/run.h b/chromium/mojo/shell/run.h
new file mode 100644
index 00000000000..2b60b4bd7aa
--- /dev/null
+++ b/chromium/mojo/shell/run.h
@@ -0,0 +1,22 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SHELL_RUN_H_
+#define MOJO_SHELL_RUN_H_
+
+#include <vector>
+
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+
+class Context;
+
+void Run(Context* context, const std::vector<GURL>& app_urls);
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_RUN_H_
diff --git a/chromium/mojo/shell/shell_test_base.cc b/chromium/mojo/shell/shell_test_base.cc
new file mode 100644
index 00000000000..947e70efdc1
--- /dev/null
+++ b/chromium/mojo/shell/shell_test_base.cc
@@ -0,0 +1,63 @@
+// Copyright 2014 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 "mojo/shell/shell_test_base.h"
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "build/build_config.h"
+#include "mojo/shell/context.h"
+#include "net/base/filename_util.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+namespace test {
+
+ShellTestBase::ShellTestBase() {
+}
+
+ShellTestBase::~ShellTestBase() {
+}
+
+void ShellTestBase::InitMojo() {
+ DCHECK(!message_loop_);
+ DCHECK(!shell_context_);
+ message_loop_.reset(new base::MessageLoop());
+ shell_context_.reset(new Context());
+}
+
+void ShellTestBase::LaunchServiceInProcess(
+ const GURL& service_url,
+ const std::string& service_name,
+ ScopedMessagePipeHandle client_handle) {
+ DCHECK(message_loop_);
+ DCHECK(shell_context_);
+
+ base::FilePath base_dir;
+ CHECK(PathService::Get(base::DIR_EXE, &base_dir));
+ // On android, the library is bundled with the app.
+#if defined(OS_ANDROID)
+ base::FilePath service_dir;
+ CHECK(PathService::Get(base::DIR_MODULE, &service_dir));
+ // On Mac and Windows, libraries are dumped beside the executables.
+#elif defined(OS_MACOSX) || defined(OS_WIN)
+ base::FilePath service_dir(base_dir);
+#else
+ // On Linux, they're under lib/.
+ base::FilePath service_dir(base_dir.AppendASCII("lib"));
+#endif
+ shell_context_->mojo_url_resolver()->set_origin(
+ net::FilePathToFileURL(service_dir).spec());
+
+ shell_context_->service_manager()->ConnectToService(
+ service_url, service_name, client_handle.Pass(), GURL());
+}
+
+} // namespace test
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/shell_test_base.h b/chromium/mojo/shell/shell_test_base.h
new file mode 100644
index 00000000000..84864b7300f
--- /dev/null
+++ b/chromium/mojo/shell/shell_test_base.h
@@ -0,0 +1,58 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SHELL_SHELL_TEST_BASE_H_
+#define MOJO_SHELL_SHELL_TEST_BASE_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/cpp/system/core.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class GURL;
+
+namespace base {
+class MessageLoop;
+}
+
+namespace mojo {
+namespace shell {
+
+class Context;
+
+namespace test {
+
+class ShellTestBase : public testing::Test {
+ public:
+ ShellTestBase();
+ virtual ~ShellTestBase();
+
+ // Should be called before any of the methods below are called.
+ void InitMojo();
+
+ // Launches the given service in-process; |service_url| should typically be a
+ // mojo: URL (the origin will be set to an "appropriate" file: URL).
+ void LaunchServiceInProcess(const GURL& service_url,
+ const std::string& service_name,
+ ScopedMessagePipeHandle client_handle);
+
+ base::MessageLoop* message_loop() { return message_loop_.get(); }
+ Context* shell_context() { return shell_context_.get(); }
+
+ private:
+ // Only set if/when |InitMojo()| is called.
+ scoped_ptr<base::MessageLoop> message_loop_;
+ scoped_ptr<Context> shell_context_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShellTestBase);
+};
+
+} // namespace test
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_SHELL_TEST_BASE_H_
diff --git a/chromium/mojo/shell/shell_test_base_unittest.cc b/chromium/mojo/shell/shell_test_base_unittest.cc
new file mode 100644
index 00000000000..126249c06ad
--- /dev/null
+++ b/chromium/mojo/shell/shell_test_base_unittest.cc
@@ -0,0 +1,110 @@
+// Copyright 2014 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 "mojo/shell/shell_test_base.h"
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/services/test_service/test_service.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace shell {
+namespace test {
+namespace {
+
+typedef ShellTestBase ShellTestBaseTest;
+
+class QuitMessageLoopErrorHandler : public ErrorHandler {
+ public:
+ QuitMessageLoopErrorHandler() {}
+ virtual ~QuitMessageLoopErrorHandler() {}
+
+ // |ErrorHandler| implementation:
+ virtual void OnConnectionError() OVERRIDE {
+ base::MessageLoop::current()->QuitWhenIdle();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuitMessageLoopErrorHandler);
+};
+
+void PingCallback(base::MessageLoop* message_loop, bool* was_run) {
+ *was_run = true;
+ VLOG(2) << "Ping callback";
+ message_loop->QuitWhenIdle();
+}
+
+TEST_F(ShellTestBaseTest, LaunchServiceInProcess) {
+ InitMojo();
+
+ InterfacePtr<mojo::test::ITestService> test_service;
+
+ {
+ MessagePipe mp;
+ test_service.Bind(mp.handle0.Pass());
+ LaunchServiceInProcess(GURL("mojo:mojo_test_service"),
+ mojo::test::ITestService::Name_,
+ mp.handle1.Pass());
+ }
+
+ bool was_run = false;
+ test_service->Ping(base::Bind(&PingCallback,
+ base::Unretained(message_loop()),
+ base::Unretained(&was_run)));
+ message_loop()->Run();
+ EXPECT_TRUE(was_run);
+ EXPECT_FALSE(test_service.encountered_error());
+
+ test_service.reset();
+
+ // This will run until the test service has actually quit (which it will,
+ // since we killed the only connection to it).
+ message_loop()->Run();
+}
+
+// Tests that launching a service in process fails properly if the service
+// doesn't exist.
+TEST_F(ShellTestBaseTest, LaunchServiceInProcessInvalidService) {
+ InitMojo();
+
+ InterfacePtr<mojo::test::ITestService> test_service;
+
+ {
+ MessagePipe mp;
+ test_service.Bind(mp.handle0.Pass());
+ LaunchServiceInProcess(GURL("mojo:non_existent_service"),
+ mojo::test::ITestService::Name_,
+ mp.handle1.Pass());
+ }
+
+ bool was_run = false;
+ test_service->Ping(base::Bind(&PingCallback,
+ base::Unretained(message_loop()),
+ base::Unretained(&was_run)));
+
+ // This will quit because there's nothing running.
+ message_loop()->Run();
+ EXPECT_FALSE(was_run);
+
+ // It may have quit before an error was processed.
+ if (!test_service.encountered_error()) {
+ QuitMessageLoopErrorHandler quitter;
+ test_service.set_error_handler(&quitter);
+ message_loop()->Run();
+ EXPECT_TRUE(test_service.encountered_error());
+ }
+
+ test_service.reset();
+}
+
+} // namespace
+} // namespace test
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/shell_test_helper.cc b/chromium/mojo/shell/shell_test_helper.cc
new file mode 100644
index 00000000000..cec75ce47d4
--- /dev/null
+++ b/chromium/mojo/shell/shell_test_helper.cc
@@ -0,0 +1,52 @@
+// Copyright 2014 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 "mojo/shell/shell_test_helper.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "mojo/shell/init.h"
+
+namespace mojo {
+namespace shell {
+
+class ShellTestHelper::TestServiceProvider : public ServiceProvider {
+ public:
+ TestServiceProvider() {}
+ virtual ~TestServiceProvider() {}
+
+ // ServiceProvider:
+ virtual void ConnectToService(
+ const mojo::String& service_url,
+ const mojo::String& service_name,
+ ScopedMessagePipeHandle client_handle,
+ const mojo::String& requestor_url) OVERRIDE {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestServiceProvider);
+};
+
+ShellTestHelper::ShellTestHelper() {
+ base::CommandLine::Init(0, NULL);
+ mojo::shell::InitializeLogging();
+}
+
+ShellTestHelper::~ShellTestHelper() {
+}
+
+void ShellTestHelper::Init() {
+ context_.reset(new Context);
+ test_api_.reset(new ServiceManager::TestAPI(context_->service_manager()));
+ local_service_provider_.reset(new TestServiceProvider);
+ service_provider_.Bind(test_api_->GetServiceProviderHandle().Pass());
+ service_provider_.set_client(local_service_provider_.get());
+}
+
+void ShellTestHelper::SetLoaderForURL(scoped_ptr<ServiceLoader> loader,
+ const GURL& url) {
+ context_->service_manager()->SetLoaderForURL(loader.Pass(), url);
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/shell_test_helper.h b/chromium/mojo/shell/shell_test_helper.h
new file mode 100644
index 00000000000..ef8af899e11
--- /dev/null
+++ b/chromium/mojo/shell/shell_test_helper.h
@@ -0,0 +1,61 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SHELL_SHELL_TEST_HELPER_
+#define MOJO_SHELL_SHELL_TEST_HELPER_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/run_loop.h"
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+#include "mojo/service_manager/service_loader.h"
+#include "mojo/shell/context.h"
+
+class GURL;
+
+namespace mojo {
+
+class ServiceLoader;
+
+namespace shell {
+
+// ShellTestHelper is useful for tests to establish a connection to the
+// ServiceProvider. Invoke Init() to establish the connection. Once done,
+// service_provider() returns the handle to the ServiceProvider.
+class ShellTestHelper {
+ public:
+ ShellTestHelper();
+ ~ShellTestHelper();
+
+ void Init();
+
+ // Returns a handle to the ServiceProvider. ShellTestHelper owns the
+ // ServiceProvider.
+ ServiceProvider* service_provider() { return service_provider_.get(); }
+
+ // Sets a ServiceLoader for the specified URL. |loader| is ultimately used on
+ // the thread this class spawns.
+ void SetLoaderForURL(scoped_ptr<ServiceLoader> loader, const GURL& url);
+
+ private:
+ class TestServiceProvider;
+
+ scoped_ptr<Context> context_;
+
+ scoped_ptr<ServiceManager::TestAPI> test_api_;
+
+ // ScopedMessagePipeHandle service_provider_handle_;
+
+ // Client interface for the shell.
+ scoped_ptr<TestServiceProvider> local_service_provider_;
+
+ ServiceProviderPtr service_provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShellTestHelper);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_SHELL_TEST_HELPER_
diff --git a/chromium/mojo/shell/shell_test_main.cc b/chromium/mojo/shell/shell_test_main.cc
new file mode 100644
index 00000000000..7f057f707ce
--- /dev/null
+++ b/chromium/mojo/shell/shell_test_main.cc
@@ -0,0 +1,31 @@
+// Copyright 2014 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 "base/bind.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "mojo/shell/child_process.h"
+#include "mojo/shell/switches.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+int main(int argc, char** argv) {
+ base::CommandLine::Init(argc, argv);
+ const base::CommandLine& command_line =
+ *base::CommandLine::ForCurrentProcess();
+
+ if (command_line.HasSwitch(switches::kChildProcessType)) {
+ scoped_ptr<mojo::shell::ChildProcess> child_process =
+ mojo::shell::ChildProcess::Create(command_line);
+ CHECK(child_process);
+ child_process->Main();
+ return 0;
+ }
+
+ base::TestSuite test_suite(argc, argv);
+ return base::LaunchUnitTests(
+ argc, argv, base::Bind(&base::TestSuite::Run,
+ base::Unretained(&test_suite)));
+}
diff --git a/chromium/mojo/shell/switches.cc b/chromium/mojo/shell/switches.cc
new file mode 100644
index 00000000000..c597218f412
--- /dev/null
+++ b/chromium/mojo/shell/switches.cc
@@ -0,0 +1,30 @@
+// Copyright 2013 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 "mojo/shell/switches.h"
+
+namespace switches {
+
+// Used to specify the type of child process (switch values from
+// |ChildProcess::Type|).
+const char kChildProcessType[] = "child-process-type";
+
+// Force dynamically loaded apps / services to be loaded irrespective of cache
+// instructions.
+const char kDisableCache[] = "disable-cache";
+
+// Load apps in separate processes.
+// TODO(vtl): Work in progress; doesn't work. Flip this to "disable" (or maybe
+// change it to "single-process") when it works.
+const char kEnableMultiprocess[] = "enable-multiprocess";
+
+// Map mojo: URLs to a shared library of similar name at this origin. See
+// mojo_url_resolver.cc for details.
+const char kOrigin[] = "origin";
+
+// Enables the mojo spy, which acts as a man-in-the-middle inspector for
+// message pipes and other activities. This is work in progress.
+const char kSpy[] = "spy";
+
+} // namespace switches
diff --git a/chromium/mojo/shell/switches.h b/chromium/mojo/shell/switches.h
new file mode 100644
index 00000000000..a1d4ac578f9
--- /dev/null
+++ b/chromium/mojo/shell/switches.h
@@ -0,0 +1,19 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SHELL_SWITCHES_H_
+#define MOJO_SHELL_SWITCHES_H_
+
+namespace switches {
+
+// All switches in alphabetical order. The switches should be documented
+// alongside the definition of their values in the .cc file.
+extern const char kChildProcessType[];
+extern const char kDisableCache[];
+extern const char kEnableMultiprocess[];
+extern const char kOrigin[];
+extern const char kSpy[];
+} // namespace switches
+
+#endif // MOJO_SHELL_SWITCHES_H_
diff --git a/chromium/mojo/shell/task_runners.cc b/chromium/mojo/shell/task_runners.cc
new file mode 100644
index 00000000000..573dca6ea29
--- /dev/null
+++ b/chromium/mojo/shell/task_runners.cc
@@ -0,0 +1,38 @@
+// Copyright 2013 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 "mojo/shell/task_runners.h"
+
+#include "base/threading/sequenced_worker_pool.h"
+
+namespace mojo {
+namespace shell {
+
+namespace {
+
+const size_t kMaxBlockingPoolThreads = 3;
+
+scoped_ptr<base::Thread> CreateIOThread(const char* name) {
+ scoped_ptr<base::Thread> thread(new base::Thread(name));
+ base::Thread::Options options;
+ options.message_loop_type = base::MessageLoop::TYPE_IO;
+ thread->StartWithOptions(options);
+ return thread.Pass();
+}
+
+} // namespace
+
+TaskRunners::TaskRunners(base::SingleThreadTaskRunner* ui_runner)
+ : ui_runner_(ui_runner),
+ io_thread_(CreateIOThread("io_thread")),
+ blocking_pool_(new base::SequencedWorkerPool(kMaxBlockingPoolThreads,
+ "blocking_pool")) {
+}
+
+TaskRunners::~TaskRunners() {
+ blocking_pool_->Shutdown();
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/task_runners.h b/chromium/mojo/shell/task_runners.h
new file mode 100644
index 00000000000..7331fa8e524
--- /dev/null
+++ b/chromium/mojo/shell/task_runners.h
@@ -0,0 +1,53 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SHELL_TASK_RUNNERS_H_
+#define MOJO_SHELL_TASK_RUNNERS_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/threading/thread.h"
+
+namespace base {
+class SequencedWorkerPool;
+}
+
+namespace mojo {
+namespace shell {
+
+// A context object that contains the common task runners for the shell's main
+// process.
+class TaskRunners {
+ public:
+ explicit TaskRunners(base::SingleThreadTaskRunner* ui_runner);
+ ~TaskRunners();
+
+ base::SingleThreadTaskRunner* ui_runner() const {
+ return ui_runner_.get();
+ }
+
+ base::SingleThreadTaskRunner* io_runner() const {
+ return io_thread_->message_loop_proxy();
+ }
+
+ base::SequencedWorkerPool* blocking_pool() const {
+ return blocking_pool_.get();
+ }
+
+ private:
+ // TODO(beng): should this be named shell_runner_?
+ scoped_refptr<base::SingleThreadTaskRunner> ui_runner_;
+ scoped_ptr<base::Thread> io_thread_;
+
+ scoped_refptr<base::SequencedWorkerPool> blocking_pool_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskRunners);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_TASK_RUNNERS_H_
diff --git a/chromium/mojo/shell/test_child_process.cc b/chromium/mojo/shell/test_child_process.cc
new file mode 100644
index 00000000000..fe187a546dc
--- /dev/null
+++ b/chromium/mojo/shell/test_child_process.cc
@@ -0,0 +1,28 @@
+// Copyright 2014 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 "mojo/shell/test_child_process.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+
+namespace mojo {
+namespace shell {
+
+TestChildProcess::TestChildProcess() {
+}
+
+TestChildProcess::~TestChildProcess() {
+}
+
+void TestChildProcess::Main() {
+ VLOG(2) << "TestChildProcess::Main()";
+
+ CHECK(!base::MessageLoop::current());
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/test_child_process.h b/chromium/mojo/shell/test_child_process.h
new file mode 100644
index 00000000000..c2d440b8295
--- /dev/null
+++ b/chromium/mojo/shell/test_child_process.h
@@ -0,0 +1,28 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SHELL_TEST_CHILD_PROCESS_H_
+#define MOJO_SHELL_TEST_CHILD_PROCESS_H_
+
+#include "base/macros.h"
+#include "mojo/shell/child_process.h"
+
+namespace mojo {
+namespace shell {
+
+class TestChildProcess : public ChildProcess {
+ public:
+ TestChildProcess();
+ virtual ~TestChildProcess();
+
+ virtual void Main() OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestChildProcess);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_TEST_CHILD_PROCESS_H_
diff --git a/chromium/mojo/shell/view_manager_loader.cc b/chromium/mojo/shell/view_manager_loader.cc
new file mode 100644
index 00000000000..278ab81e05b
--- /dev/null
+++ b/chromium/mojo/shell/view_manager_loader.cc
@@ -0,0 +1,36 @@
+// Copyright 2014 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 "mojo/shell/view_manager_loader.h"
+
+#include "mojo/public/cpp/application/application.h"
+#include "mojo/services/view_manager/view_manager_init_service_impl.h"
+
+namespace mojo {
+namespace shell {
+
+ViewManagerLoader::ViewManagerLoader() {
+}
+
+ViewManagerLoader::~ViewManagerLoader() {
+}
+
+void ViewManagerLoader::LoadService(
+ ServiceManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle service_provider_handle) {
+ // TODO(sky): this needs some sort of authentication as well as making sure
+ // we only ever have one active at a time.
+ scoped_ptr<Application> app(new Application(service_provider_handle.Pass()));
+ app->AddService<view_manager::service::ViewManagerInitServiceImpl>(
+ app->service_provider());
+ apps_.push_back(app.release());
+}
+
+void ViewManagerLoader::OnServiceError(ServiceManager* manager,
+ const GURL& url) {
+}
+
+} // namespace shell
+} // namespace mojo
diff --git a/chromium/mojo/shell/view_manager_loader.h b/chromium/mojo/shell/view_manager_loader.h
new file mode 100644
index 00000000000..8bebc5742cb
--- /dev/null
+++ b/chromium/mojo/shell/view_manager_loader.h
@@ -0,0 +1,41 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SHELL_VIEW_MANAGER_LOADER_H_
+#define MOJO_SHELL_VIEW_MANAGER_LOADER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "mojo/service_manager/service_loader.h"
+
+namespace mojo {
+
+class Application;
+
+namespace shell {
+
+// ServiceLoader responsible for creating connections to the ViewManager.
+class ViewManagerLoader : public ServiceLoader {
+ public:
+ ViewManagerLoader();
+ virtual ~ViewManagerLoader();
+
+ private:
+ // ServiceLoader overrides:
+ virtual void LoadService(
+ ServiceManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle service_provider_handle) OVERRIDE;
+ virtual void OnServiceError(ServiceManager* manager,
+ const GURL& url) OVERRIDE;
+
+ ScopedVector<Application> apps_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerLoader);
+};
+
+} // namespace shell
+} // namespace mojo
+
+#endif // MOJO_SHELL_VIEW_MANAGER_LOADER_H_
diff --git a/chromium/mojo/spy/DEPS b/chromium/mojo/spy/DEPS
new file mode 100644
index 00000000000..8fa9d48d882
--- /dev/null
+++ b/chromium/mojo/spy/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+net",
+]
diff --git a/chromium/mojo/spy/PRESUBMIT.py b/chromium/mojo/spy/PRESUBMIT.py
new file mode 100644
index 00000000000..5090fb8a1db
--- /dev/null
+++ b/chromium/mojo/spy/PRESUBMIT.py
@@ -0,0 +1,40 @@
+# Copyright 2014 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.
+import os
+import sys
+
+def _CommonChecks(input_api, output_api):
+ results = []
+ # Importing ui actually brings tvcm into the path.
+ import ui
+ from tvcm import presubmit_checker
+ checker = presubmit_checker.PresubmitChecker(input_api, output_api)
+ results += checker.RunChecks()
+ return results
+
+def GetPathsToPrepend(input_api):
+ return [input_api.PresubmitLocalPath()]
+
+def RunWithPrependedPath(prepended_path, fn, *args):
+ old_path = sys.path
+
+ try:
+ sys.path = prepended_path + old_path
+ return fn(*args)
+ finally:
+ sys.path = old_path
+
+def CheckChangeOnUpload(input_api, output_api):
+ def go():
+ results = []
+ results.extend(_CommonChecks(input_api, output_api))
+ return results
+ return RunWithPrependedPath(GetPathsToPrepend(input_api), go)
+
+def CheckChangeOnCommit(input_api, output_api):
+ def go():
+ results = []
+ results.extend(_CommonChecks(input_api, output_api))
+ return results
+ return RunWithPrependedPath(GetPathsToPrepend(input_api), go)
diff --git a/chromium/mojo/spy/run_ui_dev_server b/chromium/mojo/spy/run_ui_dev_server
new file mode 100755
index 00000000000..5dd294f0514
--- /dev/null
+++ b/chromium/mojo/spy/run_ui_dev_server
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+# Copyright 2014 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.
+"""Starts the mojo spy dev server.
+
+During normal usage of mojo spy, the spy files are compiled into standalone
+HTML+JS+CSS snippets that are then embedded in the mojo shell.
+
+The dev server allows edit-reload style development of the spy UI in isolation
+of the c++ bits. To use, start the dev server, navigate to the URL the script
+prints, and run any of the tests listed. Reloading in the browser loads the
+latest content from disk, enabling a traditional web development workflow.
+"""
+import sys
+
+from ui import dev_server
+
+COMPONENTS_PORT = 8015
+
+if __name__ == '__main__':
+ sys.exit(dev_server.Main(COMPONENTS_PORT, sys.argv[1:]))
diff --git a/chromium/mojo/spy/run_ui_tests b/chromium/mojo/spy/run_ui_tests
new file mode 100755
index 00000000000..00f8ba1bb99
--- /dev/null
+++ b/chromium/mojo/spy/run_ui_tests
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+# Copyright 2014 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.
+"""Starts the mojo spy dev server.
+
+During normal usage of mojo spy, the spy files are compiled into standalone
+HTML+JS+CSS snippets that are then embedded in the mojo shell.
+
+The dev server allows edit-reload style development of the spy UI in isolation
+of the c++ bits. To use, start the dev server, navigate to the URL the script
+prints, and run any of the tests listed. Reloading in the browser loads the
+latest content from disk, enabling a traditional web development workflow.
+"""
+import sys
+
+import ui
+from tvcm import test_runner
+
+if __name__ == '__main__':
+ runner = test_runner.TestRunner()
+ runner.AddModule(ui)
+ sys.exit(runner.Main())
diff --git a/chromium/mojo/spy/spy.cc b/chromium/mojo/spy/spy.cc
new file mode 100644
index 00000000000..3fcc49c3d66
--- /dev/null
+++ b/chromium/mojo/spy/spy.cc
@@ -0,0 +1,215 @@
+// Copyright 2014 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 "mojo/spy/spy.h"
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/threading/thread.h"
+#include "base/threading/worker_pool.h"
+
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/service_manager/service_manager.h"
+#include "mojo/spy/websocket_server.h"
+
+namespace {
+
+const size_t kMessageBufSize = 2 * 1024;
+const size_t kHandleBufSize = 64;
+const int kDefaultWebSocketPort = 42424;
+
+void CloseHandles(MojoHandle* handles, size_t count) {
+ for (size_t ix = 0; ix != count; ++count)
+ MojoClose(handles[ix]);
+}
+
+// In charge of processing messages that flow over a
+// single message pipe.
+class MessageProcessor :
+ public base::RefCountedThreadSafe<MessageProcessor> {
+ public:
+
+ MessageProcessor()
+ : last_result_(MOJO_RESULT_OK),
+ bytes_transfered_(0) {
+
+ message_count_[0] = 0;
+ message_count_[1] = 0;
+ handle_count_[0] = 0;
+ handle_count_[1] = 0;
+ }
+
+ void Start(mojo::ScopedMessagePipeHandle client,
+ mojo::ScopedMessagePipeHandle interceptor) {
+ std::vector<mojo::MessagePipeHandle> pipes;
+ pipes.push_back(client.get());
+ pipes.push_back(interceptor.get());
+ std::vector<MojoHandleSignals> handle_signals;
+ handle_signals.push_back(MOJO_HANDLE_SIGNAL_READABLE);
+ handle_signals.push_back(MOJO_HANDLE_SIGNAL_READABLE);
+
+ scoped_ptr<char[]> mbuf(new char[kMessageBufSize]);
+ scoped_ptr<MojoHandle[]> hbuf(new MojoHandle[kHandleBufSize]);
+
+ // Main processing loop:
+ // 1- Wait for an endpoint to have a message.
+ // 2- Read the message
+ // 3- Log data
+ // 4- Wait until the opposite port is ready for writting
+ // 4- Write the message to opposite port.
+
+ for (;;) {
+ int r = WaitMany(pipes, handle_signals, MOJO_DEADLINE_INDEFINITE);
+ if ((r < 0) || (r > 1)) {
+ last_result_ = r;
+ break;
+ }
+
+ uint32_t bytes_read = kMessageBufSize;
+ uint32_t handles_read = kHandleBufSize;
+
+ if (!CheckResult(ReadMessageRaw(pipes[r],
+ mbuf.get(), &bytes_read,
+ hbuf.get(), &handles_read,
+ MOJO_READ_MESSAGE_FLAG_NONE)))
+ break;
+
+ if (!bytes_read && !handles_read)
+ continue;
+
+ if (handles_read)
+ handle_count_[r] += handles_read;
+
+ ++message_count_[r];
+ bytes_transfered_ += bytes_read;
+
+ mojo::MessagePipeHandle write_handle = (r == 0) ? pipes[1] : pipes[0];
+ if (!CheckResult(Wait(write_handle,
+ MOJO_HANDLE_SIGNAL_WRITABLE,
+ MOJO_DEADLINE_INDEFINITE)))
+ break;
+
+ if (!CheckResult(WriteMessageRaw(write_handle,
+ mbuf.get(), bytes_read,
+ hbuf.get(), handles_read,
+ MOJO_WRITE_MESSAGE_FLAG_NONE))) {
+ // On failure we own the handles. For now just close them.
+ if (handles_read)
+ CloseHandles(hbuf.get(), handles_read);
+ break;
+ }
+ }
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<MessageProcessor>;
+ virtual ~MessageProcessor() {}
+
+ bool CheckResult(MojoResult mr) {
+ if (mr == MOJO_RESULT_OK)
+ return true;
+ last_result_ = mr;
+ return false;
+ }
+
+ MojoResult last_result_;
+ uint32_t bytes_transfered_;
+ uint32_t message_count_[2];
+ uint32_t handle_count_[2];
+};
+
+// In charge of intercepting access to the service manager.
+class SpyInterceptor : public mojo::ServiceManager::Interceptor {
+ private:
+ virtual mojo::ScopedMessagePipeHandle OnConnectToClient(
+ const GURL& url, mojo::ScopedMessagePipeHandle real_client) OVERRIDE {
+ if (!MustIntercept(url))
+ return real_client.Pass();
+
+ // You can get an invalid handle if the app (or service) is
+ // created by unconventional means, for example the command line.
+ if (!real_client.is_valid())
+ return real_client.Pass();
+
+ mojo::ScopedMessagePipeHandle faux_client;
+ mojo::ScopedMessagePipeHandle interceptor;
+ CreateMessagePipe(NULL, &faux_client, &interceptor);
+
+ scoped_refptr<MessageProcessor> processor = new MessageProcessor();
+ base::WorkerPool::PostTask(
+ FROM_HERE,
+ base::Bind(&MessageProcessor::Start,
+ processor,
+ base::Passed(&real_client), base::Passed(&interceptor)),
+ true);
+
+ return faux_client.Pass();
+ }
+
+ bool MustIntercept(const GURL& url) {
+ // TODO(cpu): manage who and when to intercept.
+ return true;
+ }
+};
+
+spy::WebSocketServer* ws_server = NULL;
+
+void StartServer(int port) {
+ // TODO(cpu) figure out lifetime of the server. See Spy() dtor.
+ ws_server = new spy::WebSocketServer(port);
+ ws_server->Start();
+}
+
+struct SpyOptions {
+ int websocket_port;
+
+ SpyOptions()
+ : websocket_port(kDefaultWebSocketPort) {
+ }
+};
+
+SpyOptions ProcessOptions(const std::string& options) {
+ SpyOptions spy_options;
+ if (options.empty())
+ return spy_options;
+ base::StringPairs kv_pairs;
+ base::SplitStringIntoKeyValuePairs(options, ':', ',', &kv_pairs);
+ base::StringPairs::iterator it = kv_pairs.begin();
+ for (; it != kv_pairs.end(); ++it) {
+ if (it->first == "port") {
+ int port;
+ if (base::StringToInt(it->second, &port))
+ spy_options.websocket_port = port;
+ }
+ }
+ return spy_options;
+}
+
+} // namespace
+
+namespace mojo {
+
+Spy::Spy(mojo::ServiceManager* service_manager, const std::string& options) {
+ SpyOptions spy_options = ProcessOptions(options);
+ // Start the tread what will accept commands from the frontend.
+ control_thread_.reset(new base::Thread("mojo_spy_control_thread"));
+ base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
+ control_thread_->StartWithOptions(thread_options);
+ control_thread_->message_loop_proxy()->PostTask(
+ FROM_HERE, base::Bind(&StartServer, spy_options.websocket_port));
+
+ // Start intercepting mojo services.
+ service_manager->SetInterceptor(new SpyInterceptor());
+}
+
+Spy::~Spy(){
+ // TODO(cpu): Do not leak the interceptor. Lifetime between the
+ // service_manager and the spy is still unclear hence the leak.
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/spy/spy.h b/chromium/mojo/spy/spy.h
new file mode 100644
index 00000000000..c3673f453f4
--- /dev/null
+++ b/chromium/mojo/spy/spy.h
@@ -0,0 +1,40 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SPY_SPY_H_
+#define MOJO_SPY_SPY_H_
+
+#include <string>
+#include "base/memory/scoped_ptr.h"
+
+namespace base {
+ class Thread;
+}
+
+namespace mojo {
+
+class ServiceManager;
+
+// mojo::Spy is a troubleshooting and debugging aid. It helps tracking
+// the mojo system core activities like messages, service creation, etc.
+//
+// The |options| parameter in the constructor comes from the command
+// line of the mojo_shell. Which takes --spy=<options>. Each option is
+// separated by ',' and each option is a key+ value pair separated by ':'.
+//
+// For example --spy=port:13333
+//
+class Spy {
+ public:
+ Spy(mojo::ServiceManager* service_manager, const std::string& options);
+ ~Spy();
+
+ private:
+ // This thread runs the code that talks to the frontend.
+ scoped_ptr<base::Thread> control_thread_;
+};
+
+} // namespace mojo
+
+#endif // MOJO_SPY_SPY_H_
diff --git a/chromium/mojo/spy/ui/__init__.py b/chromium/mojo/spy/ui/__init__.py
new file mode 100644
index 00000000000..bfecdb26df6
--- /dev/null
+++ b/chromium/mojo/spy/ui/__init__.py
@@ -0,0 +1,5 @@
+# Copyright 2014 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.
+
+from ui import tvcm_stub
diff --git a/chromium/mojo/spy/ui/dev_server.py b/chromium/mojo/spy/ui/dev_server.py
new file mode 100644
index 00000000000..2a830324b2d
--- /dev/null
+++ b/chromium/mojo/spy/ui/dev_server.py
@@ -0,0 +1,23 @@
+# Copyright 2014 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.
+
+import optparse
+import tvcm
+
+from ui import spy_project
+
+
+def Main(port, args):
+ parser = optparse.OptionParser()
+ _, args = parser.parse_args(args)
+
+ project = spy_project.SpyProject()
+ server = tvcm.DevServer(
+ port=port, project=project)
+
+ def IsTestModuleResourcePartOfSpy(module_resource):
+ return module_resource.absolute_path.startswith(project.spy_path)
+
+ server.test_module_resource_filter = IsTestModuleResourcePartOfSpy
+ return server.serve_forever()
diff --git a/chromium/mojo/spy/ui/spy.html b/chromium/mojo/spy/ui/spy.html
new file mode 100644
index 00000000000..79aeb7f8d88
--- /dev/null
+++ b/chromium/mojo/spy/ui/spy.html
@@ -0,0 +1,35 @@
+<!--
+Copyright 2014 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.
+-->
+<style>
+ x-spy-log-message {
+ display: block;
+ border-style: 1px solid black;
+ margin-top: 4px;
+ margin-bottom: 4px;
+ }
+
+ x-spy {
+ display: -webkit-flex;
+ -webkit-flex-direction: column;
+ }
+ x-spy > messages {
+ display: block;
+ -webkit-flex: 1 1 auto;
+ }
+ x-spy > form {
+ -webkit-flex: 0 0 auto;
+ }
+ x-spy > form > input {
+ width: 300px;
+ }
+</style>
+
+
+<template id="x-spy-template">
+ <messages>
+ </messages>
+ <input type="text" id="command" placeholder="enter spy command + enter" />
+</template>
diff --git a/chromium/mojo/spy/ui/spy.js b/chromium/mojo/spy/ui/spy.js
new file mode 100644
index 00000000000..e3f47e1f01a
--- /dev/null
+++ b/chromium/mojo/spy/ui/spy.js
@@ -0,0 +1,96 @@
+// Copyright 2014 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.
+
+'use strict';
+
+tvcm.require('tvcm.utils');
+tvcm.require('tvcm.ui');
+tvcm.requireTemplate('ui.spy');
+
+tvcm.exportTo('ui', function() {
+ /**
+ * @constructor
+ */
+ var LogMessage = tvcm.ui.define('x-spy-log-message');
+
+ LogMessage.prototype = {
+ __proto__: HTMLUnknownElement.prototype,
+
+ decorate: function() {
+ },
+
+ get message() {
+ return message_;
+ },
+
+ set message(message) {
+ this.message_ = message;
+ this.textContent = JSON.stringify(message);
+ }
+ };
+
+
+ /**
+ * @constructor
+ */
+ var Spy = tvcm.ui.define('x-spy');
+
+ Spy.prototype = {
+ __proto__: HTMLUnknownElement.prototype,
+
+ decorate: function() {
+ var node = tvcm.instantiateTemplate('#x-spy-template');
+ this.appendChild(node);
+
+ this.channel_ = undefined;
+ this.onMessage_ = this.onMessage_.bind(this);
+
+ var commandEl = this.querySelector('#command');
+ commandEl.addEventListener('keydown', function(e) {
+ if (e.keyCode == 13) {
+ e.stopPropagation();
+ this.onCommandEntered_();
+ }
+ }.bind(this));
+
+ this.updateDisabledStates_();
+ },
+
+ get channel() {
+ return channel_;
+ },
+
+ set channel(channel) {
+ if (this.channel_)
+ this.channel_.removeEventListener('message', this.onMessage_);
+ this.channel_ = channel;
+ if (this.channel_)
+ this.channel_.addEventListener('message', this.onMessage_);
+ this.updateDisabledStates_();
+ },
+
+ updateDisabledStates_: function() {
+ var connected = this.channel_ !== undefined;
+
+ this.querySelector('#command').disabled = !connected;
+ },
+
+ onCommandEntered_: function(cmd) {
+ var commandEl = this.querySelector('#command');
+ this.channel_.send(JSON.stringify(commandEl.value));
+ commandEl.value = '';
+ },
+
+ onMessage_: function(message) {
+ var messageEl = new LogMessage();
+ messageEl.message = message.data;
+ this.querySelector('messages').appendChild(messageEl);
+ }
+
+ };
+
+ return {
+ Spy: Spy
+ };
+});
diff --git a/chromium/mojo/spy/ui/spy_project.py b/chromium/mojo/spy/ui/spy_project.py
new file mode 100644
index 00000000000..409ecdfd5d1
--- /dev/null
+++ b/chromium/mojo/spy/ui/spy_project.py
@@ -0,0 +1,18 @@
+# Copyright 2014 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.
+
+import os
+
+import tvcm_stub
+
+from trace_viewer import trace_viewer_project
+
+
+class SpyProject(trace_viewer_project.TraceViewerProject):
+ spy_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
+ '..'))
+
+ def __init__(self):
+ super(SpyProject, self).__init__(
+ [self.spy_path])
diff --git a/chromium/mojo/spy/ui/spy_shell.html b/chromium/mojo/spy/ui/spy_shell.html
new file mode 100644
index 00000000000..c46a24ff0fb
--- /dev/null
+++ b/chromium/mojo/spy/ui/spy_shell.html
@@ -0,0 +1,38 @@
+<!--
+Copyright 2014 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.
+-->
+<style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ height: 100%;
+ display: -webkit-flex;
+ -webkit-flex-direction: column;
+ }
+ body > x-spy-shell {
+ -webkit-flex: 1 1 auto;
+ }
+
+ x-spy-shell {
+ display: -webkit-flex;
+ -webkit-flex-direction: column;
+ }
+
+ x-spy-shell > #status {
+ -webkit-flex: 0 0 auto;
+ border-bottom: 1px solid black;
+ }
+
+ x-spy-shell > x-spy {
+ -webkit-flex: 1 1 auto;
+ }
+</style>
+
+
+<template id="x-spy-shell-template">
+ <div id="status"></div>
+ <x-spy></x-spy>
+</template>
diff --git a/chromium/mojo/spy/ui/spy_shell.js b/chromium/mojo/spy/ui/spy_shell.js
new file mode 100644
index 00000000000..bcf0430a37d
--- /dev/null
+++ b/chromium/mojo/spy/ui/spy_shell.js
@@ -0,0 +1,68 @@
+// Copyright 2014 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.
+
+'use strict';
+
+tvcm.require('ui.spy');
+tvcm.require('tvcm.ui');
+tvcm.require('tvcm.ui.dom_helpers');
+tvcm.requireTemplate('ui.spy_shell');
+
+tvcm.exportTo('ui', function() {
+ /**
+ * @constructor
+ */
+ var SpyShell = tvcm.ui.define('x-spy-shell');
+
+ SpyShell.prototype = {
+ __proto__: HTMLUnknownElement.prototype,
+
+ decorate: function(socketURL) {
+ var node = tvcm.instantiateTemplate('#x-spy-shell-template');
+ this.appendChild(node);
+
+ this.socketURL_ = socketURL;
+ this.conn_ = undefined;
+
+ this.statusEl_ = this.querySelector('#status');
+ this.statusEl_.textContent = 'Not connected';
+
+ this.spy_ = this.querySelector('x-spy');
+ tvcm.ui.decorate(this.spy_, ui.Spy);
+
+ this.openConnection_();
+ },
+
+ get socketURL() {
+ return this.socketURL_;
+ },
+
+ openConnection_: function() {
+ if (!(this.conn_ == undefined ||
+ this.conn_.readyState === undefined ||
+ conn.readyState > 1)) {
+ return;
+ }
+
+ this.conn_ = new WebSocket(this.socketURL_);
+ this.conn_.onopen = function() {
+ this.statusEl_.textContent = 'connected at ' + this.socketURL_;
+ this.spy_.connection = this.conn_;
+ }.bind(this);
+
+ this.conn_.onclose = function(event) {
+ this.statusEl_.textContent = 'connection closed';
+ this.spy_.connection = undefined;
+ }.bind(this);
+ this.conn_.onerror = function(event) {
+ this.statusEl_.innerHTML = 'got error';
+ }.bind(this);
+ }
+
+ };
+
+ return {
+ SpyShell: SpyShell
+ };
+});
diff --git a/chromium/mojo/spy/ui/spy_shell_to_html b/chromium/mojo/spy/ui/spy_shell_to_html
new file mode 100755
index 00000000000..68ba0899ab5
--- /dev/null
+++ b/chromium/mojo/spy/ui/spy_shell_to_html
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+# Copyright 2014 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.
+
+import os
+import sys
+
+if __name__ == '__main__':
+ top_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+ sys.path.append(top_dir)
+ from ui import spy_shell_to_html
+ sys.exit(spy_shell_to_html.Main(sys.argv))
diff --git a/chromium/mojo/spy/ui/spy_shell_to_html.py b/chromium/mojo/spy/ui/spy_shell_to_html.py
new file mode 100644
index 00000000000..293b9fb0baf
--- /dev/null
+++ b/chromium/mojo/spy/ui/spy_shell_to_html.py
@@ -0,0 +1,39 @@
+# Copyright 2014 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.
+import sys
+import os
+import optparse
+
+from ui import spy_project
+from tvcm import generate
+
+def Main(args):
+ parser = optparse.OptionParser()
+ parser.add_option('--output-file', '-o')
+ options,args = parser.parse_args(args)
+
+ if options.output_file:
+ ofile = open(options.output_file, 'w')
+ else:
+ ofile = sys.stdout
+ GenerateHTML(ofile)
+ if ofile != sys.stdout:
+ ofile.close()
+
+def GenerateHTML(ofile):
+ project = spy_project.SpyProject()
+ load_sequence = project.CalcLoadSequenceForModuleNames(
+ ['ui.spy_shell'])
+ bootstrap_js = """
+
+ document.addEventListener('DOMContentLoaded', function() {
+ document.body.appendChild(new ui.SpyShell('ws://127.0.0.1:42424'));
+
+ });
+"""
+ bootstrap_script = generate.ExtraScript(text_content=bootstrap_js)
+ generate.GenerateStandaloneHTMLToFile(
+ ofile, load_sequence,
+ title='Mojo spy',
+ extra_scripts=[bootstrap_script])
diff --git a/chromium/mojo/spy/ui/spy_test.js b/chromium/mojo/spy/ui/spy_test.js
new file mode 100644
index 00000000000..b70ecd9d63d
--- /dev/null
+++ b/chromium/mojo/spy/ui/spy_test.js
@@ -0,0 +1,51 @@
+// Copyright 2014 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.
+
+'use strict';
+
+tvcm.require('ui.spy');
+tvcm.require('tvcm.event_target');
+
+tvcm.unittest.testSuite('ui.spy_test', function() {
+ /**
+ * @constructor
+ */
+ function FakeChannel() {
+ tvcm.EventTarget.call(this);
+ }
+
+ FakeChannel.prototype = {
+ __proto__: tvcm.EventTarget.prototype,
+
+ send: function(msg) {
+ },
+
+ dispatchMessage: function(msg) {
+ var event = new Event('message', false, false);
+ event.data = msg;
+ this.dispatchEvent(event);
+ }
+ };
+
+ test('basic', function() {
+ var channel = new FakeChannel();
+
+ var spy = new ui.Spy();
+ spy.style.width = '600px';
+ spy.style.height = '400px';
+ spy.style.border = '1px solid black';
+ this.addHTMLOutput(spy);
+ spy.channel = channel;
+
+ channel.dispatchMessage({data: 'alo there'});
+
+ // Fake out echo reply
+ channel.send = function(msg) {
+ setTimeout(function() {
+ channel.dispatchMessage({data: {type: 'reply', msg: msg}});
+ }, 10);
+ }
+ });
+
+});
diff --git a/chromium/mojo/spy/ui/tvcm_stub.py b/chromium/mojo/spy/ui/tvcm_stub.py
new file mode 100644
index 00000000000..8c7c0842dad
--- /dev/null
+++ b/chromium/mojo/spy/ui/tvcm_stub.py
@@ -0,0 +1,18 @@
+# Copyright 2014 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.
+
+import os
+import sys
+
+_CHROME_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__),
+ '..', '..', '..'))
+
+# Bring in tvcm module for basic JS components capabilities.
+sys.path.append(os.path.join(_CHROME_PATH,
+ 'third_party', 'trace-viewer', 'third_party', 'tvcm'))
+
+# Bring in trace_viewer module for the UI features that are part of the trace
+# viewer.
+sys.path.append(os.path.join(_CHROME_PATH,
+ 'third_party', 'trace-viewer'))
diff --git a/chromium/mojo/spy/ui/ui_unittest.py b/chromium/mojo/spy/ui/ui_unittest.py
new file mode 100644
index 00000000000..ffaffad6710
--- /dev/null
+++ b/chromium/mojo/spy/ui/ui_unittest.py
@@ -0,0 +1,15 @@
+# Copyright 2014 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.
+
+from ui import spy_project
+from tvcm import module_test_case
+
+
+def load_tests(_, _2, _3):
+ project = spy_project.SpyProject()
+ suite = module_test_case.DiscoverTestsInModule(
+ project,
+ project.spy_path)
+ assert suite.countTestCases() > 0, 'Expected to find at least one test.'
+ return suite
diff --git a/chromium/mojo/spy/websocket_server.cc b/chromium/mojo/spy/websocket_server.cc
new file mode 100644
index 00000000000..649a135c13e
--- /dev/null
+++ b/chromium/mojo/spy/websocket_server.cc
@@ -0,0 +1,70 @@
+// Copyright 2014 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 "mojo/spy/websocket_server.h"
+
+#include "base/bind.h"
+#include "base/strings/stringprintf.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/server/http_server_request_info.h"
+#include "net/server/http_server_response_info.h"
+#include "net/socket/tcp_listen_socket.h"
+
+namespace spy {
+
+const int kNotConnected = -1;
+
+WebSocketServer::WebSocketServer(int port)
+ : port_(port), connection_id_(kNotConnected) {
+}
+
+WebSocketServer::~WebSocketServer() {
+}
+
+bool WebSocketServer::Start() {
+ net::TCPListenSocketFactory factory("0.0.0.0", port_);
+ server_ = new net::HttpServer(factory, this);
+ net::IPEndPoint address;
+ int error = server_->GetLocalAddress(&address);
+ port_ = address.port();
+ return (error == net::OK);
+}
+
+void WebSocketServer::OnHttpRequest(
+ int connection_id,
+ const net::HttpServerRequestInfo& info) {
+ server_->Send500(connection_id, "websockets protocol only");
+}
+
+void WebSocketServer::OnWebSocketRequest(
+ int connection_id,
+ const net::HttpServerRequestInfo& info) {
+ if (connection_id_ != kNotConnected) {
+ // Reject connection since we already have our client.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&net::HttpServer::Close, server_, connection_id));
+ return;
+ }
+ // Accept the connection.
+ server_->AcceptWebSocket(connection_id, info);
+ connection_id_ = connection_id;
+}
+
+void WebSocketServer::OnWebSocketMessage(
+ int connection_id,
+ const std::string& data) {
+ // TODO(cpu): remove this test code soon.
+ if (data == "\"hello\"")
+ server_->SendOverWebSocket(connection_id, "\"hi there!\"");
+}
+
+void WebSocketServer::OnClose(
+ int connection_id) {
+ if (connection_id == connection_id_)
+ connection_id_ = kNotConnected;
+}
+
+} // namespace spy
diff --git a/chromium/mojo/spy/websocket_server.h b/chromium/mojo/spy/websocket_server.h
new file mode 100644
index 00000000000..84830b8b8da
--- /dev/null
+++ b/chromium/mojo/spy/websocket_server.h
@@ -0,0 +1,44 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SPY_WEBSOCKET_SERVER_H_
+#define MOJO_SPY_WEBSOCKET_SERVER_H_
+
+#include "net/server/http_server.h"
+
+namespace spy {
+
+class WebSocketServer : public net::HttpServer::Delegate {
+ public:
+ // Pass 0 in |port| to listen in one available port.
+ explicit WebSocketServer(int port);
+ virtual ~WebSocketServer();
+ // Begin accepting HTTP requests. Must be called from an IO MessageLoop.
+ bool Start();
+ // Returns the listening port, useful if 0 was passed to the contructor.
+ int port() const { return port_; }
+
+ protected:
+ // Overridden from net::HttpServer::Delegate.
+ virtual void OnHttpRequest(
+ int connection_id,
+ const net::HttpServerRequestInfo& info) OVERRIDE;
+ virtual void OnWebSocketRequest(
+ int connection_id,
+ const net::HttpServerRequestInfo& info) OVERRIDE;
+ virtual void OnWebSocketMessage(
+ int connection_id,
+ const std::string& data) OVERRIDE;
+ virtual void OnClose(int connection_id) OVERRIDE;
+
+ private:
+ int port_;
+ int connection_id_;
+ scoped_refptr<net::HttpServer> server_;
+ DISALLOW_COPY_AND_ASSIGN(WebSocketServer);
+};
+
+} // namespace spy
+
+#endif // MOJO_SPY_WEBSOCKET_SERVER_H_
diff --git a/chromium/mojo/system/BUILD.gn b/chromium/mojo/system/BUILD.gn
new file mode 100644
index 00000000000..2ad4679d4c8
--- /dev/null
+++ b/chromium/mojo/system/BUILD.gn
@@ -0,0 +1,109 @@
+# Copyright 2014 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.
+
+config("system_config") {
+ defines = [
+ # Ensures that dependent projects import the core functions on Windows.
+ "MOJO_USE_SYSTEM_IMPL",
+ ]
+}
+
+component("system") {
+ output_name = "mojo_system_impl"
+
+ sources = [
+ # Should there be a separate "embedder" target?
+ "../embedder/embedder.cc",
+ "../embedder/embedder.h",
+ "../embedder/platform_channel_pair.cc",
+ "../embedder/platform_channel_pair.h",
+ "../embedder/platform_channel_pair_posix.cc",
+ "../embedder/platform_channel_pair_win.cc",
+ "../embedder/platform_channel_utils_posix.cc",
+ "../embedder/platform_channel_utils_posix.h",
+ "../embedder/platform_handle.cc",
+ "../embedder/platform_handle.h",
+ "../embedder/platform_handle_utils.h",
+ "../embedder/platform_handle_utils_posix.cc",
+ "../embedder/platform_handle_utils_win.cc",
+ "../embedder/platform_handle_vector.h",
+ "../embedder/scoped_platform_handle.h",
+ "channel.cc",
+ "channel.h",
+ "constants.h",
+ "core.cc",
+ "core.h",
+ "data_pipe.cc",
+ "data_pipe.h",
+ "data_pipe_consumer_dispatcher.cc",
+ "data_pipe_consumer_dispatcher.h",
+ "data_pipe_producer_dispatcher.cc",
+ "data_pipe_producer_dispatcher.h",
+ "dispatcher.cc",
+ "dispatcher.h",
+ "entrypoints.cc",
+ "handle_signals_state.h",
+ "handle_table.cc",
+ "handle_table.h",
+ "local_data_pipe.cc",
+ "local_data_pipe.h",
+ "local_message_pipe_endpoint.cc",
+ "local_message_pipe_endpoint.h",
+ "mapping_table.cc",
+ "mapping_table.h",
+ "memory.cc",
+ "memory.h",
+ "message_in_transit.cc",
+ "message_in_transit.h",
+ "message_in_transit_queue.cc",
+ "message_in_transit_queue.h",
+ "message_pipe.cc",
+ "message_pipe.h",
+ "message_pipe_dispatcher.cc",
+ "message_pipe_dispatcher.h",
+ "message_pipe_endpoint.cc",
+ "message_pipe_endpoint.h",
+ "options_validation.h",
+ "platform_handle_dispatcher.cc",
+ "platform_handle_dispatcher.h",
+ "proxy_message_pipe_endpoint.cc",
+ "proxy_message_pipe_endpoint.h",
+ "raw_channel.cc",
+ "raw_channel.h",
+ "raw_channel_posix.cc",
+ "raw_channel_win.cc",
+ "raw_shared_buffer.cc",
+ "raw_shared_buffer.h",
+ "raw_shared_buffer_posix.cc",
+ "raw_shared_buffer_win.cc",
+ "shared_buffer_dispatcher.cc",
+ "shared_buffer_dispatcher.h",
+ "simple_dispatcher.cc",
+ "simple_dispatcher.h",
+ "transport_data.cc",
+ "transport_data.h",
+ "waiter.cc",
+ "waiter.h",
+ "waiter_list.cc",
+ "waiter_list.h",
+ # Test-only code:
+ # TODO(vtl): It's a little unfortunate that these end up in the same
+ # component as non-test-only code. In the static build, this code should
+ # hopefully be dead-stripped.
+ "../embedder/test_embedder.cc",
+ "../embedder/test_embedder.h",
+ ]
+
+ defines = [
+ "MOJO_SYSTEM_IMPL_IMPLEMENTATION",
+ "MOJO_SYSTEM_IMPLEMENTATION",
+ ]
+
+ all_dependent_configs = [ ":system_config" ]
+
+ deps = [
+ "//base",
+ "//base/third_party/dynamic_annotations",
+ ]
+}
diff --git a/chromium/mojo/system/channel.cc b/chromium/mojo/system/channel.cc
new file mode 100644
index 00000000000..a311695b233
--- /dev/null
+++ b/chromium/mojo/system/channel.cc
@@ -0,0 +1,505 @@
+// Copyright 2013 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 "mojo/system/channel.h"
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/embedder/platform_handle_vector.h"
+#include "mojo/system/message_pipe_endpoint.h"
+#include "mojo/system/transport_data.h"
+
+namespace mojo {
+namespace system {
+
+COMPILE_ASSERT(Channel::kBootstrapEndpointId !=
+ MessageInTransit::kInvalidEndpointId,
+ kBootstrapEndpointId_is_invalid);
+
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::EndpointId
+ Channel::kBootstrapEndpointId;
+
+Channel::EndpointInfo::EndpointInfo()
+ : state(STATE_NORMAL),
+ port() {
+}
+
+Channel::EndpointInfo::EndpointInfo(scoped_refptr<MessagePipe> message_pipe,
+ unsigned port)
+ : state(STATE_NORMAL),
+ message_pipe(message_pipe),
+ port(port) {
+}
+
+Channel::EndpointInfo::~EndpointInfo() {
+}
+
+Channel::Channel()
+ : is_running_(false),
+ next_local_id_(kBootstrapEndpointId) {
+}
+
+bool Channel::Init(scoped_ptr<RawChannel> raw_channel) {
+ DCHECK(creation_thread_checker_.CalledOnValidThread());
+ DCHECK(raw_channel);
+
+ // No need to take |lock_|, since this must be called before this object
+ // becomes thread-safe.
+ DCHECK(!is_running_no_lock());
+ raw_channel_ = raw_channel.Pass();
+
+ if (!raw_channel_->Init(this)) {
+ raw_channel_.reset();
+ return false;
+ }
+
+ is_running_ = true;
+ return true;
+}
+
+void Channel::Shutdown() {
+ DCHECK(creation_thread_checker_.CalledOnValidThread());
+
+ IdToEndpointInfoMap to_destroy;
+ {
+ base::AutoLock locker(lock_);
+ if (!is_running_no_lock())
+ return;
+
+ // Note: Don't reset |raw_channel_|, in case we're being called from within
+ // |OnReadMessage()| or |OnFatalError()|.
+ raw_channel_->Shutdown();
+ is_running_ = false;
+
+ // We need to deal with it outside the lock.
+ std::swap(to_destroy, local_id_to_endpoint_info_map_);
+ }
+
+ size_t num_live = 0;
+ size_t num_zombies = 0;
+ for (IdToEndpointInfoMap::iterator it = to_destroy.begin();
+ it != to_destroy.end();
+ ++it) {
+ if (it->second.state == EndpointInfo::STATE_NORMAL) {
+ it->second.message_pipe->OnRemove(it->second.port);
+ num_live++;
+ } else {
+ DCHECK(!it->second.message_pipe);
+ num_zombies++;
+ }
+ }
+ DVLOG_IF(2, num_live || num_zombies)
+ << "Shut down Channel with " << num_live << " live endpoints and "
+ << num_zombies << " zombies";
+}
+
+MessageInTransit::EndpointId Channel::AttachMessagePipeEndpoint(
+ scoped_refptr<MessagePipe> message_pipe,
+ unsigned port) {
+ DCHECK(message_pipe);
+ DCHECK(port == 0 || port == 1);
+
+ MessageInTransit::EndpointId local_id;
+ {
+ base::AutoLock locker(lock_);
+
+ while (next_local_id_ == MessageInTransit::kInvalidEndpointId ||
+ local_id_to_endpoint_info_map_.find(next_local_id_) !=
+ local_id_to_endpoint_info_map_.end())
+ next_local_id_++;
+
+ local_id = next_local_id_;
+ next_local_id_++;
+
+ // TODO(vtl): Use emplace when we move to C++11 unordered_maps. (It'll avoid
+ // some expensive reference count increment/decrements.) Once this is done,
+ // we should be able to delete |EndpointInfo|'s default constructor.
+ local_id_to_endpoint_info_map_[local_id] = EndpointInfo(message_pipe, port);
+ }
+
+ // This might fail if that port got an |OnPeerClose()| before attaching.
+ if (message_pipe->Attach(port, scoped_refptr<Channel>(this), local_id))
+ return local_id;
+
+ // Note: If it failed, quite possibly the endpoint info was removed from that
+ // map (there's a race between us adding it to the map above and calling
+ // |Attach()|). And even if an entry exists for |local_id|, we need to check
+ // that it's the one we added (and not some other one that was added since).
+ {
+ base::AutoLock locker(lock_);
+ IdToEndpointInfoMap::iterator it =
+ local_id_to_endpoint_info_map_.find(local_id);
+ if (it != local_id_to_endpoint_info_map_.end() &&
+ it->second.message_pipe.get() == message_pipe.get() &&
+ it->second.port == port) {
+ DCHECK_EQ(it->second.state, EndpointInfo::STATE_NORMAL);
+ // TODO(vtl): FIXME -- This is wrong. We need to specify (to
+ // |AttachMessagePipeEndpoint()| who's going to be responsible for calling
+ // |RunMessagePipeEndpoint()| ("us", or the remote by sending us a
+ // |kSubtypeChannelRunMessagePipeEndpoint|). If the remote is going to
+ // run, then we'll get messages to an "invalid" local ID (for running, for
+ // removal).
+ local_id_to_endpoint_info_map_.erase(it);
+ }
+ }
+ return MessageInTransit::kInvalidEndpointId;
+}
+
+bool Channel::RunMessagePipeEndpoint(MessageInTransit::EndpointId local_id,
+ MessageInTransit::EndpointId remote_id) {
+ EndpointInfo endpoint_info;
+ {
+ base::AutoLock locker(lock_);
+
+ IdToEndpointInfoMap::const_iterator it =
+ local_id_to_endpoint_info_map_.find(local_id);
+ if (it == local_id_to_endpoint_info_map_.end())
+ return false;
+ endpoint_info = it->second;
+ }
+
+ // Assume that this was in response to |kSubtypeChannelRunMessagePipeEndpoint|
+ // and ignore it.
+ if (endpoint_info.state != EndpointInfo::STATE_NORMAL) {
+ DVLOG(2) << "Ignoring run message pipe endpoint for zombie endpoint "
+ "(local ID " << local_id << ", remote ID " << remote_id << ")";
+ return true;
+ }
+
+ // TODO(vtl): FIXME -- We need to handle the case that message pipe is already
+ // running when we're here due to |kSubtypeChannelRunMessagePipeEndpoint|).
+ endpoint_info.message_pipe->Run(endpoint_info.port, remote_id);
+ return true;
+}
+
+void Channel::RunRemoteMessagePipeEndpoint(
+ MessageInTransit::EndpointId local_id,
+ MessageInTransit::EndpointId remote_id) {
+#if DCHECK_IS_ON
+ {
+ base::AutoLock locker(lock_);
+ DCHECK(local_id_to_endpoint_info_map_.find(local_id) !=
+ local_id_to_endpoint_info_map_.end());
+ }
+#endif
+
+ if (!SendControlMessage(
+ MessageInTransit::kSubtypeChannelRunMessagePipeEndpoint,
+ local_id, remote_id)) {
+ HandleLocalError(base::StringPrintf(
+ "Failed to send message to run remote message pipe endpoint (local ID "
+ "%u, remote ID %u)",
+ static_cast<unsigned>(local_id), static_cast<unsigned>(remote_id)));
+ }
+}
+
+bool Channel::WriteMessage(scoped_ptr<MessageInTransit> message) {
+ base::AutoLock locker(lock_);
+ if (!is_running_no_lock()) {
+ // TODO(vtl): I think this is probably not an error condition, but I should
+ // think about it (and the shutdown sequence) more carefully.
+ LOG(WARNING) << "WriteMessage() after shutdown";
+ return false;
+ }
+
+ return raw_channel_->WriteMessage(message.Pass());
+}
+
+bool Channel::IsWriteBufferEmpty() {
+ base::AutoLock locker(lock_);
+ if (!is_running_no_lock())
+ return true;
+ return raw_channel_->IsWriteBufferEmpty();
+}
+
+void Channel::DetachMessagePipeEndpoint(
+ MessageInTransit::EndpointId local_id,
+ MessageInTransit::EndpointId remote_id) {
+ DCHECK_NE(local_id, MessageInTransit::kInvalidEndpointId);
+
+ bool should_send_remove_message = false;
+ {
+ base::AutoLock locker_(lock_);
+ if (!is_running_no_lock())
+ return;
+
+ IdToEndpointInfoMap::iterator it =
+ local_id_to_endpoint_info_map_.find(local_id);
+ DCHECK(it != local_id_to_endpoint_info_map_.end());
+
+ switch (it->second.state) {
+ case EndpointInfo::STATE_NORMAL:
+ it->second.state = EndpointInfo::STATE_WAIT_REMOTE_REMOVE_ACK;
+ it->second.message_pipe = NULL;
+ should_send_remove_message =
+ (remote_id != MessageInTransit::kInvalidEndpointId);
+ break;
+ case EndpointInfo::STATE_WAIT_LOCAL_DETACH:
+ local_id_to_endpoint_info_map_.erase(it);
+ break;
+ case EndpointInfo::STATE_WAIT_REMOTE_REMOVE_ACK:
+ NOTREACHED();
+ break;
+ case EndpointInfo::STATE_WAIT_LOCAL_DETACH_AND_REMOTE_REMOVE_ACK:
+ it->second.state = EndpointInfo::STATE_WAIT_REMOTE_REMOVE_ACK;
+ break;
+ }
+ }
+ if (!should_send_remove_message)
+ return;
+
+ if (!SendControlMessage(
+ MessageInTransit::kSubtypeChannelRemoveMessagePipeEndpoint,
+ local_id, remote_id)) {
+ HandleLocalError(base::StringPrintf(
+ "Failed to send message to remove remote message pipe endpoint (local "
+ "ID %u, remote ID %u)",
+ static_cast<unsigned>(local_id), static_cast<unsigned>(remote_id)));
+ }
+}
+
+size_t Channel::GetSerializedPlatformHandleSize() const {
+ return raw_channel_->GetSerializedPlatformHandleSize();
+}
+
+Channel::~Channel() {
+ // The channel should have been shut down first.
+ DCHECK(!is_running_no_lock());
+}
+
+void Channel::OnReadMessage(
+ const MessageInTransit::View& message_view,
+ embedder::ScopedPlatformHandleVectorPtr platform_handles) {
+ switch (message_view.type()) {
+ case MessageInTransit::kTypeMessagePipeEndpoint:
+ case MessageInTransit::kTypeMessagePipe:
+ OnReadMessageForDownstream(message_view, platform_handles.Pass());
+ break;
+ case MessageInTransit::kTypeChannel:
+ OnReadMessageForChannel(message_view, platform_handles.Pass());
+ break;
+ default:
+ HandleRemoteError(base::StringPrintf(
+ "Received message of invalid type %u",
+ static_cast<unsigned>(message_view.type())));
+ break;
+ }
+}
+
+void Channel::OnFatalError(FatalError fatal_error) {
+ switch (fatal_error) {
+ case FATAL_ERROR_READ:
+ // Most read errors aren't notable: they just reflect that the other side
+ // tore down the channel.
+ DVLOG(1) << "RawChannel fatal error (read)";
+ break;
+ case FATAL_ERROR_WRITE:
+ // Write errors are slightly notable: they probably shouldn't happen under
+ // normal operation (but maybe the other side crashed).
+ LOG(WARNING) << "RawChannel fatal error (write)";
+ break;
+ }
+ Shutdown();
+}
+
+void Channel::OnReadMessageForDownstream(
+ const MessageInTransit::View& message_view,
+ embedder::ScopedPlatformHandleVectorPtr platform_handles) {
+ DCHECK(message_view.type() == MessageInTransit::kTypeMessagePipeEndpoint ||
+ message_view.type() == MessageInTransit::kTypeMessagePipe);
+
+ MessageInTransit::EndpointId local_id = message_view.destination_id();
+ if (local_id == MessageInTransit::kInvalidEndpointId) {
+ HandleRemoteError("Received message with no destination ID");
+ return;
+ }
+
+ EndpointInfo endpoint_info;
+ {
+ base::AutoLock locker(lock_);
+
+ // Since we own |raw_channel_|, and this method and |Shutdown()| should only
+ // be called from the creation thread, |raw_channel_| should never be null
+ // here.
+ DCHECK(is_running_no_lock());
+
+ IdToEndpointInfoMap::const_iterator it =
+ local_id_to_endpoint_info_map_.find(local_id);
+ if (it == local_id_to_endpoint_info_map_.end()) {
+ HandleRemoteError(base::StringPrintf(
+ "Received a message for nonexistent local destination ID %u",
+ static_cast<unsigned>(local_id)));
+ // This is strongly indicative of some problem. However, it's not a fatal
+ // error, since it may indicate a bug (or hostile) remote process. Don't
+ // die even for Debug builds, since handling this properly needs to be
+ // tested (TODO(vtl)).
+ DLOG(ERROR) << "This should not happen under normal operation.";
+ return;
+ }
+ endpoint_info = it->second;
+ }
+
+ // Ignore messages for zombie endpoints (not an error).
+ if (endpoint_info.state != EndpointInfo::STATE_NORMAL) {
+ DVLOG(2) << "Ignoring downstream message for zombie endpoint (local ID = "
+ << local_id << ", remote ID = " << message_view.source_id() << ")";
+ return;
+ }
+
+ // We need to duplicate the message (data), because |EnqueueMessage()| will
+ // take ownership of it.
+ scoped_ptr<MessageInTransit> message(new MessageInTransit(message_view));
+ if (message_view.transport_data_buffer_size() > 0) {
+ DCHECK(message_view.transport_data_buffer());
+ message->SetDispatchers(
+ TransportData::DeserializeDispatchers(
+ message_view.transport_data_buffer(),
+ message_view.transport_data_buffer_size(),
+ platform_handles.Pass(),
+ this));
+ }
+ MojoResult result = endpoint_info.message_pipe->EnqueueMessage(
+ MessagePipe::GetPeerPort(endpoint_info.port), message.Pass());
+ if (result != MOJO_RESULT_OK) {
+ // TODO(vtl): This might be a "non-error", e.g., if the destination endpoint
+ // has been closed (in an unavoidable race). This might also be a "remote"
+ // error, e.g., if the remote side is sending invalid control messages (to
+ // the message pipe).
+ HandleLocalError(base::StringPrintf(
+ "Failed to enqueue message to local ID %u (result %d)",
+ static_cast<unsigned>(local_id), static_cast<int>(result)));
+ return;
+ }
+}
+
+void Channel::OnReadMessageForChannel(
+ const MessageInTransit::View& message_view,
+ embedder::ScopedPlatformHandleVectorPtr platform_handles) {
+ DCHECK_EQ(message_view.type(), MessageInTransit::kTypeChannel);
+
+ // Currently, no channel messages take platform handles.
+ if (platform_handles) {
+ HandleRemoteError(
+ "Received invalid channel message (has platform handles)");
+ NOTREACHED();
+ return;
+ }
+
+ switch (message_view.subtype()) {
+ case MessageInTransit::kSubtypeChannelRunMessagePipeEndpoint:
+ DVLOG(2) << "Handling channel message to run message pipe (local ID "
+ << message_view.destination_id() << ", remote ID "
+ << message_view.source_id() << ")";
+ if (!RunMessagePipeEndpoint(message_view.destination_id(),
+ message_view.source_id())) {
+ HandleRemoteError(
+ "Received invalid channel message to run message pipe");
+ }
+ break;
+ case MessageInTransit::kSubtypeChannelRemoveMessagePipeEndpoint:
+ DVLOG(2) << "Handling channel message to remove message pipe (local ID "
+ << message_view.destination_id() << ", remote ID "
+ << message_view.source_id() << ")";
+ if (!RemoveMessagePipeEndpoint(message_view.destination_id(),
+ message_view.source_id())) {
+ HandleRemoteError(
+ "Received invalid channel message to remove message pipe");
+ }
+ break;
+ case MessageInTransit::kSubtypeChannelRemoveMessagePipeEndpointAck:
+ DVLOG(2) << "Handling channel message to ack remove message pipe (local "
+ "ID "
+ << message_view.destination_id() << ", remote ID "
+ << message_view.source_id() << ")";
+ if (!RemoveMessagePipeEndpoint(message_view.destination_id(),
+ message_view.source_id())) {
+ HandleRemoteError(
+ "Received invalid channel message to ack remove message pipe");
+ }
+ break;
+ default:
+ HandleRemoteError("Received invalid channel message");
+ NOTREACHED();
+ break;
+ }
+}
+
+bool Channel::RemoveMessagePipeEndpoint(
+ MessageInTransit::EndpointId local_id,
+ MessageInTransit::EndpointId remote_id) {
+ EndpointInfo endpoint_info;
+ {
+ base::AutoLock locker(lock_);
+
+ IdToEndpointInfoMap::iterator it =
+ local_id_to_endpoint_info_map_.find(local_id);
+ if (it == local_id_to_endpoint_info_map_.end()) {
+ DVLOG(2) << "Remove message pipe error: not found";
+ return false;
+ }
+
+ // If it's waiting for the remove ack, just do it and return.
+ if (it->second.state == EndpointInfo::STATE_WAIT_REMOTE_REMOVE_ACK) {
+ local_id_to_endpoint_info_map_.erase(it);
+ return true;
+ }
+
+ if (it->second.state != EndpointInfo::STATE_NORMAL) {
+ DVLOG(2) << "Remove message pipe error: wrong state";
+ return false;
+ }
+
+ it->second.state = EndpointInfo::STATE_WAIT_LOCAL_DETACH;
+ endpoint_info = it->second;
+ it->second.message_pipe = NULL;
+ }
+
+ if (!SendControlMessage(
+ MessageInTransit::kSubtypeChannelRemoveMessagePipeEndpointAck,
+ local_id, remote_id)) {
+ HandleLocalError(base::StringPrintf(
+ "Failed to send message to remove remote message pipe endpoint ack "
+ "(local ID %u, remote ID %u)",
+ static_cast<unsigned>(local_id), static_cast<unsigned>(remote_id)));
+ }
+
+ endpoint_info.message_pipe->OnRemove(endpoint_info.port);
+
+ return true;
+}
+
+bool Channel::SendControlMessage(MessageInTransit::Subtype subtype,
+ MessageInTransit::EndpointId local_id,
+ MessageInTransit::EndpointId remote_id) {
+ DVLOG(2) << "Sending channel control message: subtype " << subtype
+ << ", local ID " << local_id << ", remote ID " << remote_id;
+ scoped_ptr<MessageInTransit> message(new MessageInTransit(
+ MessageInTransit::kTypeChannel, subtype, 0, NULL));
+ message->set_source_id(local_id);
+ message->set_destination_id(remote_id);
+ return WriteMessage(message.Pass());
+}
+
+void Channel::HandleRemoteError(const base::StringPiece& error_message) {
+ // TODO(vtl): Is this how we really want to handle this? Probably we want to
+ // terminate the connection, since it's spewing invalid stuff.
+ LOG(WARNING) << error_message;
+}
+
+void Channel::HandleLocalError(const base::StringPiece& error_message) {
+ // TODO(vtl): Is this how we really want to handle this?
+ // Sometimes we'll want to propagate the error back to the message pipe
+ // (endpoint), and notify it that the remote is (effectively) closed.
+ // Sometimes we'll want to kill the channel (and notify all the endpoints that
+ // their remotes are dead.
+ LOG(WARNING) << error_message;
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/channel.h b/chromium/mojo/system/channel.h
new file mode 100644
index 00000000000..859e242ec94
--- /dev/null
+++ b/chromium/mojo/system/channel.h
@@ -0,0 +1,202 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_CHANNEL_H_
+#define MOJO_SYSTEM_CHANNEL_H_
+
+#include <stdint.h>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/embedder/scoped_platform_handle.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/system/message_in_transit.h"
+#include "mojo/system/message_pipe.h"
+#include "mojo/system/raw_channel.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+// This class is mostly thread-safe. It must be created on an I/O thread.
+// |Init()| must be called on that same thread before it becomes thread-safe (in
+// particular, before references are given to any other thread) and |Shutdown()|
+// must be called on that same thread before destruction. Its public methods are
+// otherwise thread-safe. It may be destroyed on any thread, in the sense that
+// the last reference to it may be released on any thread, with the proviso that
+// |Shutdown()| must have been called first (so the pattern is that a "main"
+// reference is kept on its creation thread and is released after |Shutdown()|
+// is called, but other threads may have temporarily "dangling" references).
+//
+// Note that |MessagePipe| calls into |Channel| and the former's |lock_| must be
+// acquired before the latter's. When |Channel| wants to call into a
+// |MessagePipe|, it must obtain a reference to the |MessagePipe| (from
+// |local_id_to_endpoint_info_map_|) under |Channel::lock_| and then release the
+// lock.
+//
+// Also, care must be taken with respect to references: While a |Channel| has
+// references to |MessagePipe|s, |MessagePipe|s (via |ProxyMessagePipeEndpoint|)
+// may also have references to |Channel|s. These references are set up by
+// calling |AttachMessagePipeEndpoint()|. The reference to |MessagePipe| owned
+// by |Channel| must be removed by calling |DetachMessagePipeEndpoint()| (which
+// is done by |MessagePipe|/|ProxyMessagePipeEndpoint|, which simultaneously
+// removes its reference to |Channel|).
+class MOJO_SYSTEM_IMPL_EXPORT Channel
+ : public base::RefCountedThreadSafe<Channel>,
+ public RawChannel::Delegate {
+ public:
+ // The first message pipe endpoint attached will have this as its local ID.
+ static const MessageInTransit::EndpointId kBootstrapEndpointId = 1;
+
+ Channel();
+
+ // This must be called on the creation thread before any other methods are
+ // called, and before references to this object are given to any other
+ // threads. |raw_channel| should be uninitialized. Returns true on success. On
+ // failure, no other methods should be called (including |Shutdown()|).
+ bool Init(scoped_ptr<RawChannel> raw_channel);
+
+ // This must be called on the creation thread before destruction (which can
+ // happen on any thread).
+ void Shutdown();
+
+ // Attaches the given message pipe/port's endpoint (which must be a
+ // |ProxyMessagePipeEndpoint|) to this channel. This assigns it a local ID,
+ // which it returns. The first message pipe endpoint attached will always have
+ // |kBootstrapEndpointId| as its local ID. (For bootstrapping, this occurs on
+ // both sides, so one should use |kBootstrapEndpointId| for the remote ID for
+ // the first message pipe across a channel.) Returns |kInvalidEndpointId| on
+ // failure.
+ // TODO(vtl): Maybe limit the number of attached message pipes.
+ MessageInTransit::EndpointId AttachMessagePipeEndpoint(
+ scoped_refptr<MessagePipe> message_pipe,
+ unsigned port);
+
+ // Runs the message pipe with the given |local_id| (previously attached), with
+ // the given |remote_id| (negotiated using some other means, e.g., over an
+ // existing message pipe; see comments above for the bootstrap case). Returns
+ // false on failure, in particular if no message pipe with |local_id| is
+ // attached.
+ bool RunMessagePipeEndpoint(MessageInTransit::EndpointId local_id,
+ MessageInTransit::EndpointId remote_id);
+
+ // Tells the other side of the channel to run a message pipe endpoint (which
+ // must already be attached); |local_id| and |remote_id| are relative to this
+ // channel (i.e., |local_id| is the other side's remote ID and |remote_id| is
+ // its local ID).
+ // TODO(vtl): Maybe we should just have a flag argument to
+ // |RunMessagePipeEndpoint()| that tells it to do this.
+ void RunRemoteMessagePipeEndpoint(MessageInTransit::EndpointId local_id,
+ MessageInTransit::EndpointId remote_id);
+
+ // This forwards |message| verbatim to |raw_channel_|.
+ bool WriteMessage(scoped_ptr<MessageInTransit> message);
+
+ // See |RawChannel::IsWriteBufferEmpty()|.
+ // TODO(vtl): Maybe we shouldn't expose this, and instead have a
+ // |FlushWriteBufferAndShutdown()| or something like that.
+ bool IsWriteBufferEmpty();
+
+ // This removes the message pipe/port's endpoint (with the given local ID and
+ // given remote ID, which should be |kInvalidEndpointId| if not yet running),
+ // returned by |AttachMessagePipeEndpoint()| from this channel. After this is
+ // called, |local_id| may be reused for another message pipe.
+ void DetachMessagePipeEndpoint(MessageInTransit::EndpointId local_id,
+ MessageInTransit::EndpointId remote_id);
+
+ // See |RawChannel::GetSerializedPlatformHandleSize()|.
+ size_t GetSerializedPlatformHandleSize() const;
+
+ private:
+ struct EndpointInfo {
+ enum State {
+ // Attached, possibly running or not.
+ STATE_NORMAL,
+ // "Zombie" states:
+ // Waiting for |DetachMessagePipeEndpoint()| before removing.
+ STATE_WAIT_LOCAL_DETACH,
+ // Waiting for a |kSubtypeChannelRemoveMessagePipeEndpointAck| before
+ // removing.
+ STATE_WAIT_REMOTE_REMOVE_ACK,
+ // Waiting for both of the above conditions before removing.
+ STATE_WAIT_LOCAL_DETACH_AND_REMOTE_REMOVE_ACK,
+ };
+
+ EndpointInfo();
+ EndpointInfo(scoped_refptr<MessagePipe> message_pipe, unsigned port);
+ ~EndpointInfo();
+
+ State state;
+ scoped_refptr<MessagePipe> message_pipe;
+ unsigned port;
+ };
+
+ friend class base::RefCountedThreadSafe<Channel>;
+ virtual ~Channel();
+
+ // |RawChannel::Delegate| implementation:
+ virtual void OnReadMessage(
+ const MessageInTransit::View& message_view,
+ embedder::ScopedPlatformHandleVectorPtr platform_handles) OVERRIDE;
+ virtual void OnFatalError(FatalError fatal_error) OVERRIDE;
+
+ // Helpers for |OnReadMessage|:
+ void OnReadMessageForDownstream(
+ const MessageInTransit::View& message_view,
+ embedder::ScopedPlatformHandleVectorPtr platform_handles);
+ void OnReadMessageForChannel(
+ const MessageInTransit::View& message_view,
+ embedder::ScopedPlatformHandleVectorPtr platform_handles);
+
+ // Removes the message pipe endpoint with the given local ID, which must exist
+ // and be a zombie, and given remote ID. Returns false on failure, in
+ // particular if no message pipe with |local_id| is attached.
+ bool RemoveMessagePipeEndpoint(MessageInTransit::EndpointId local_id,
+ MessageInTransit::EndpointId remote_id);
+
+ // Handles errors (e.g., invalid messages) from the remote side.
+ void HandleRemoteError(const base::StringPiece& error_message);
+ // Handles internal errors/failures from the local side.
+ void HandleLocalError(const base::StringPiece& error_message);
+
+ // Helper to send channel control messages. Returns true on success. Should be
+ // called *without* |lock_| held.
+ bool SendControlMessage(MessageInTransit::Subtype subtype,
+ MessageInTransit::EndpointId source_id,
+ MessageInTransit::EndpointId destination_id);
+
+ bool is_running_no_lock() const { return is_running_; }
+
+ base::ThreadChecker creation_thread_checker_;
+
+ // Note: |MessagePipe|s MUST NOT be used under |lock_|. I.e., |lock_| can only
+ // be acquired after |MessagePipe::lock_|, never before. Thus to call into a
+ // |MessagePipe|, a reference should be acquired from
+ // |local_id_to_endpoint_info_map_| under |lock_| (e.g., by copying the
+ // |EndpointInfo|) and then the lock released.
+ base::Lock lock_; // Protects the members below.
+
+ scoped_ptr<RawChannel> raw_channel_;
+ bool is_running_;
+
+ typedef base::hash_map<MessageInTransit::EndpointId, EndpointInfo>
+ IdToEndpointInfoMap;
+ IdToEndpointInfoMap local_id_to_endpoint_info_map_;
+ // The next local ID to try (when allocating new local IDs). Note: It should
+ // be checked for existence before use.
+ MessageInTransit::EndpointId next_local_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(Channel);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_CHANNEL_H_
diff --git a/chromium/mojo/system/channel_unittest.cc b/chromium/mojo/system/channel_unittest.cc
new file mode 100644
index 00000000000..1c40d024796
--- /dev/null
+++ b/chromium/mojo/system/channel_unittest.cc
@@ -0,0 +1,329 @@
+// Copyright 2014 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 "mojo/system/channel.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/embedder/platform_channel_pair.h"
+#include "mojo/system/local_message_pipe_endpoint.h"
+#include "mojo/system/message_in_transit.h"
+#include "mojo/system/message_pipe.h"
+#include "mojo/system/proxy_message_pipe_endpoint.h"
+#include "mojo/system/raw_channel.h"
+#include "mojo/system/test_utils.h"
+#include "mojo/system/waiter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+enum Tristate {
+ TRISTATE_UNKNOWN = -1,
+ TRISTATE_FALSE = 0,
+ TRISTATE_TRUE = 1
+};
+
+Tristate BoolToTristate(bool b) {
+ return b ? TRISTATE_TRUE : TRISTATE_FALSE;
+}
+
+class ChannelTest : public testing::Test {
+ public:
+ ChannelTest()
+ : io_thread_(test::TestIOThread::kAutoStart),
+ init_result_(TRISTATE_UNKNOWN) {}
+ virtual ~ChannelTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ io_thread_.PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&ChannelTest::SetUpOnIOThread, base::Unretained(this)));
+ }
+
+ void CreateChannelOnIOThread() {
+ CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+ channel_ = new Channel();
+ }
+
+ void InitChannelOnIOThread() {
+ CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+
+ CHECK(raw_channel_);
+ CHECK(channel_);
+ CHECK_EQ(init_result_, TRISTATE_UNKNOWN);
+
+ init_result_ = BoolToTristate(channel_->Init(raw_channel_.Pass()));
+ }
+
+ void ShutdownChannelOnIOThread() {
+ CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+
+ CHECK(channel_);
+ channel_->Shutdown();
+ }
+
+ test::TestIOThread* io_thread() { return &io_thread_; }
+ RawChannel* raw_channel() { return raw_channel_.get(); }
+ scoped_ptr<RawChannel>* mutable_raw_channel() { return &raw_channel_; }
+ Channel* channel() { return channel_.get(); }
+ scoped_refptr<Channel>* mutable_channel() { return &channel_; }
+ Tristate init_result() const { return init_result_; }
+
+ private:
+ void SetUpOnIOThread() {
+ CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+
+ embedder::PlatformChannelPair channel_pair;
+ raw_channel_ = RawChannel::Create(channel_pair.PassServerHandle()).Pass();
+ other_platform_handle_ = channel_pair.PassClientHandle();
+ }
+
+ test::TestIOThread io_thread_;
+ scoped_ptr<RawChannel> raw_channel_;
+ embedder::ScopedPlatformHandle other_platform_handle_;
+ scoped_refptr<Channel> channel_;
+
+ Tristate init_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChannelTest);
+};
+
+// ChannelTest.InitShutdown ----------------------------------------------------
+
+TEST_F(ChannelTest, InitShutdown) {
+ io_thread()->PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&ChannelTest::CreateChannelOnIOThread,
+ base::Unretained(this)));
+ ASSERT_TRUE(channel());
+
+ io_thread()->PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&ChannelTest::InitChannelOnIOThread,
+ base::Unretained(this)));
+ EXPECT_EQ(TRISTATE_TRUE, init_result());
+
+ io_thread()->PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&ChannelTest::ShutdownChannelOnIOThread,
+ base::Unretained(this)));
+
+ // Okay to destroy |Channel| on not-the-I/O-thread.
+ EXPECT_TRUE(channel()->HasOneRef());
+ *mutable_channel() = NULL;
+}
+
+// ChannelTest.InitFails -------------------------------------------------------
+
+class MockRawChannelOnInitFails : public RawChannel {
+ public:
+ MockRawChannelOnInitFails() : on_init_called_(false) {}
+ virtual ~MockRawChannelOnInitFails() {}
+
+ // |RawChannel| public methods:
+ virtual size_t GetSerializedPlatformHandleSize() const OVERRIDE {
+ return 0;
+ }
+
+ private:
+ // |RawChannel| protected methods:
+ virtual IOResult Read(size_t*) OVERRIDE {
+ CHECK(false);
+ return IO_FAILED;
+ }
+ virtual IOResult ScheduleRead() OVERRIDE {
+ CHECK(false);
+ return IO_FAILED;
+ }
+ virtual embedder::ScopedPlatformHandleVectorPtr GetReadPlatformHandles(
+ size_t, const void*) OVERRIDE {
+ CHECK(false);
+ return embedder::ScopedPlatformHandleVectorPtr();
+ }
+ virtual IOResult WriteNoLock(size_t*, size_t*) OVERRIDE {
+ CHECK(false);
+ return IO_FAILED;
+ }
+ virtual IOResult ScheduleWriteNoLock() OVERRIDE {
+ CHECK(false);
+ return IO_FAILED;
+ }
+ virtual bool OnInit() OVERRIDE {
+ EXPECT_FALSE(on_init_called_);
+ on_init_called_ = true;
+ return false;
+ }
+ virtual void OnShutdownNoLock(scoped_ptr<ReadBuffer>,
+ scoped_ptr<WriteBuffer>) OVERRIDE {
+ CHECK(false);
+ }
+
+ bool on_init_called_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockRawChannelOnInitFails);
+};
+
+TEST_F(ChannelTest, InitFails) {
+ io_thread()->PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&ChannelTest::CreateChannelOnIOThread,
+ base::Unretained(this)));
+ ASSERT_TRUE(channel());
+
+ ASSERT_TRUE(raw_channel());
+ mutable_raw_channel()->reset(new MockRawChannelOnInitFails());
+
+ io_thread()->PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&ChannelTest::InitChannelOnIOThread,
+ base::Unretained(this)));
+ EXPECT_EQ(TRISTATE_FALSE, init_result());
+
+ // Should destroy |Channel| with no |Shutdown()| (on not-the-I/O-thread).
+ EXPECT_TRUE(channel()->HasOneRef());
+ *mutable_channel() = NULL;
+}
+
+// ChannelTest.CloseBeforeRun --------------------------------------------------
+
+TEST_F(ChannelTest, CloseBeforeRun) {
+ io_thread()->PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&ChannelTest::CreateChannelOnIOThread,
+ base::Unretained(this)));
+ ASSERT_TRUE(channel());
+
+ io_thread()->PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&ChannelTest::InitChannelOnIOThread,
+ base::Unretained(this)));
+ EXPECT_EQ(TRISTATE_TRUE, init_result());
+
+ scoped_refptr<MessagePipe> mp(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+
+ MessageInTransit::EndpointId local_id =
+ channel()->AttachMessagePipeEndpoint(mp, 1);
+ EXPECT_EQ(Channel::kBootstrapEndpointId, local_id);
+
+ mp->Close(0);
+
+ // TODO(vtl): Currently, the |Close()| above won't detach (since it thinks
+ // we're still expecting a "run" message from the other side), so the
+ // |RunMessagePipeEndpoint()| below will return true. We need to refactor
+ // |AttachMessagePipeEndpoint()| to indicate whether |Run...()| will
+ // necessarily be called or not. (Then, in the case that it may not be called,
+ // this will return false.)
+ EXPECT_TRUE(channel()->RunMessagePipeEndpoint(local_id,
+ Channel::kBootstrapEndpointId));
+
+ io_thread()->PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&ChannelTest::ShutdownChannelOnIOThread,
+ base::Unretained(this)));
+
+ EXPECT_TRUE(channel()->HasOneRef());
+}
+
+// ChannelTest.ShutdownAfterAttachAndRun ---------------------------------------
+
+TEST_F(ChannelTest, ShutdownAfterAttach) {
+ io_thread()->PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&ChannelTest::CreateChannelOnIOThread,
+ base::Unretained(this)));
+ ASSERT_TRUE(channel());
+
+ io_thread()->PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&ChannelTest::InitChannelOnIOThread,
+ base::Unretained(this)));
+ EXPECT_EQ(TRISTATE_TRUE, init_result());
+
+ scoped_refptr<MessagePipe> mp(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+
+ MessageInTransit::EndpointId local_id =
+ channel()->AttachMessagePipeEndpoint(mp, 1);
+ EXPECT_EQ(Channel::kBootstrapEndpointId, local_id);
+
+ // TODO(vtl): Currently, we always "expect" a |RunMessagePipeEndpoint()| after
+ // an |AttachMessagePipeEndpoint()| (which is actually incorrect). We need to
+ // refactor |AttachMessagePipeEndpoint()| to indicate whether |Run...()| will
+ // necessarily be called or not. (Then, in the case that it may not be called,
+ // we should test a |Shutdown()| without the |Run...()|.)
+ EXPECT_TRUE(channel()->RunMessagePipeEndpoint(local_id,
+ Channel::kBootstrapEndpointId));
+
+ Waiter waiter;
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->AddWaiter(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123));
+
+ // Don't wait for the shutdown to run ...
+ io_thread()->PostTask(FROM_HERE,
+ base::Bind(&ChannelTest::ShutdownChannelOnIOThread,
+ base::Unretained(this)));
+
+ // ... since this |Wait()| should fail once the channel is shut down.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ waiter.Wait(MOJO_DEADLINE_INDEFINITE, NULL));
+ mp->RemoveWaiter(0, &waiter);
+
+ mp->Close(0);
+
+ EXPECT_TRUE(channel()->HasOneRef());
+}
+
+// ChannelTest.WaitAfterAttachRunAndShutdown -----------------------------------
+
+TEST_F(ChannelTest, WaitAfterAttachRunAndShutdown) {
+ io_thread()->PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&ChannelTest::CreateChannelOnIOThread,
+ base::Unretained(this)));
+ ASSERT_TRUE(channel());
+
+ io_thread()->PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&ChannelTest::InitChannelOnIOThread,
+ base::Unretained(this)));
+ EXPECT_EQ(TRISTATE_TRUE, init_result());
+
+ scoped_refptr<MessagePipe> mp(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+
+ MessageInTransit::EndpointId local_id =
+ channel()->AttachMessagePipeEndpoint(mp, 1);
+ EXPECT_EQ(Channel::kBootstrapEndpointId, local_id);
+
+ EXPECT_TRUE(channel()->RunMessagePipeEndpoint(local_id,
+ Channel::kBootstrapEndpointId));
+
+ io_thread()->PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&ChannelTest::ShutdownChannelOnIOThread,
+ base::Unretained(this)));
+
+ Waiter waiter;
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ mp->AddWaiter(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123));
+
+ mp->Close(0);
+
+ EXPECT_TRUE(channel()->HasOneRef());
+}
+
+// TODO(vtl): More. ------------------------------------------------------------
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/constants.h b/chromium/mojo/system/constants.h
new file mode 100644
index 00000000000..b92eef10221
--- /dev/null
+++ b/chromium/mojo/system/constants.h
@@ -0,0 +1,48 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_CONSTANTS_H_
+#define MOJO_SYSTEM_CONSTANTS_H_
+
+#include <stddef.h>
+
+namespace mojo {
+namespace system {
+
+// Maximum number of open (Mojo) handles.
+// TODO(vtl): This doesn't count "live" handles, some of which may live in
+// messages.
+const size_t kMaxHandleTableSize = 1000000;
+
+// Maximum number of active memory mappings.
+const size_t kMaxMappingTableSize = 1000000;
+
+const size_t kMaxWaitManyNumHandles = kMaxHandleTableSize;
+
+const size_t kMaxMessageNumBytes = 4 * 1024 * 1024;
+
+const size_t kMaxMessageNumHandles = 10000;
+
+// Maximum capacity of a data pipe, in bytes. This value must fit into a
+// |uint32_t|.
+// WARNING: If you bump it closer to 2^32, you must audit all the code to check
+// that we don't overflow (2^31 would definitely be risky; up to 2^30 is
+// probably okay).
+const size_t kMaxDataPipeCapacityBytes = 256 * 1024 * 1024; // 256 MB.
+
+const size_t kDefaultDataPipeCapacityBytes = 1024 * 1024; // 1 MB.
+
+// Alignment for the "start" of the data buffer used by data pipes. (The
+// alignment of elements will depend on this and the element size.)
+const size_t kDataPipeBufferAlignmentBytes = 16;
+
+// TODO(vtl): Set this hard limit appropriately (e.g., higher on 64-bit). (This
+// will also entail some auditing to make sure I'm not messing up my checks
+// anywhere.)
+const size_t kMaxSharedMemoryNumBytes = 1024 * 1024 * 1024; // 1 GB.
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_CONSTANTS_H_
diff --git a/chromium/mojo/system/core.cc b/chromium/mojo/system/core.cc
new file mode 100644
index 00000000000..d72198f1610
--- /dev/null
+++ b/chromium/mojo/system/core.cc
@@ -0,0 +1,561 @@
+// Copyright 2013 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 "mojo/system/core.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "mojo/public/c/system/macros.h"
+#include "mojo/system/constants.h"
+#include "mojo/system/data_pipe.h"
+#include "mojo/system/data_pipe_consumer_dispatcher.h"
+#include "mojo/system/data_pipe_producer_dispatcher.h"
+#include "mojo/system/dispatcher.h"
+#include "mojo/system/local_data_pipe.h"
+#include "mojo/system/memory.h"
+#include "mojo/system/message_pipe.h"
+#include "mojo/system/message_pipe_dispatcher.h"
+#include "mojo/system/raw_shared_buffer.h"
+#include "mojo/system/shared_buffer_dispatcher.h"
+#include "mojo/system/waiter.h"
+
+namespace mojo {
+namespace system {
+
+// Implementation notes
+//
+// Mojo primitives are implemented by the singleton |Core| object. Most calls
+// are for a "primary" handle (the first argument). |Core::GetDispatcher()| is
+// used to look up a |Dispatcher| object for a given handle. That object
+// implements most primitives for that object. The wait primitives are not
+// attached to objects and are implemented by |Core| itself.
+//
+// Some objects have multiple handles associated to them, e.g., message pipes
+// (which have two). In such a case, there is still a |Dispatcher| (e.g.,
+// |MessagePipeDispatcher|) for each handle, with each handle having a strong
+// reference to the common "secondary" object (e.g., |MessagePipe|). This
+// secondary object does NOT have any references to the |Dispatcher|s (even if
+// it did, it wouldn't be able to do anything with them due to lock order
+// requirements -- see below).
+//
+// Waiting is implemented by having the thread that wants to wait call the
+// |Dispatcher|s for the handles that it wants to wait on with a |Waiter|
+// object; this |Waiter| object may be created on the stack of that thread or be
+// kept in thread local storage for that thread (TODO(vtl): future improvement).
+// The |Dispatcher| then adds the |Waiter| to a |WaiterList| that's either owned
+// by that |Dispatcher| (see |SimpleDispatcher|) or by a secondary object (e.g.,
+// |MessagePipe|). To signal/wake a |Waiter|, the object in question -- either a
+// |SimpleDispatcher| or a secondary object -- talks to its |WaiterList|.
+
+// Thread-safety notes
+//
+// Mojo primitives calls are thread-safe. We achieve this with relatively
+// fine-grained locking. There is a global handle table lock. This lock should
+// be held as briefly as possible (TODO(vtl): a future improvement would be to
+// switch it to a reader-writer lock). Each |Dispatcher| object then has a lock
+// (which subclasses can use to protect their data).
+//
+// The lock ordering is as follows:
+// 1. global handle table lock, global mapping table lock
+// 2. |Dispatcher| locks
+// 3. secondary object locks
+// ...
+// INF. |Waiter| locks
+//
+// Notes:
+// - While holding a |Dispatcher| lock, you may not unconditionally attempt
+// to take another |Dispatcher| lock. (This has consequences on the
+// concurrency semantics of |MojoWriteMessage()| when passing handles.)
+// Doing so would lead to deadlock.
+// - Locks at the "INF" level may not have any locks taken while they are
+// held.
+
+Core::Core() {
+}
+
+Core::~Core() {
+}
+
+MojoHandle Core::AddDispatcher(
+ const scoped_refptr<Dispatcher>& dispatcher) {
+ base::AutoLock locker(handle_table_lock_);
+ return handle_table_.AddDispatcher(dispatcher);
+}
+
+scoped_refptr<Dispatcher> Core::GetDispatcher(MojoHandle handle) {
+ if (handle == MOJO_HANDLE_INVALID)
+ return NULL;
+
+ base::AutoLock locker(handle_table_lock_);
+ return handle_table_.GetDispatcher(handle);
+}
+
+MojoTimeTicks Core::GetTimeTicksNow() {
+ return base::TimeTicks::Now().ToInternalValue();
+}
+
+MojoResult Core::Close(MojoHandle handle) {
+ if (handle == MOJO_HANDLE_INVALID)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ scoped_refptr<Dispatcher> dispatcher;
+ {
+ base::AutoLock locker(handle_table_lock_);
+ MojoResult result = handle_table_.GetAndRemoveDispatcher(handle,
+ &dispatcher);
+ if (result != MOJO_RESULT_OK)
+ return result;
+ }
+
+ // The dispatcher doesn't have a say in being closed, but gets notified of it.
+ // Note: This is done outside of |handle_table_lock_|. As a result, there's a
+ // race condition that the dispatcher must handle; see the comment in
+ // |Dispatcher| in dispatcher.h.
+ return dispatcher->Close();
+}
+
+MojoResult Core::Wait(MojoHandle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline) {
+ return WaitManyInternal(&handle, &signals, 1, deadline);
+}
+
+MojoResult Core::WaitMany(const MojoHandle* handles,
+ const MojoHandleSignals* signals,
+ uint32_t num_handles,
+ MojoDeadline deadline) {
+ if (!VerifyUserPointerWithCount<MojoHandle>(handles, num_handles))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (!VerifyUserPointerWithCount<MojoHandleSignals>(signals, num_handles))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (num_handles < 1)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (num_handles > kMaxWaitManyNumHandles)
+ return MOJO_RESULT_RESOURCE_EXHAUSTED;
+ return WaitManyInternal(handles, signals, num_handles, deadline);
+}
+
+MojoResult Core::CreateMessagePipe(const MojoCreateMessagePipeOptions* options,
+ MojoHandle* message_pipe_handle0,
+ MojoHandle* message_pipe_handle1) {
+ MojoCreateMessagePipeOptions validated_options = {};
+ // This will verify the |options| pointer.
+ MojoResult result = MessagePipeDispatcher::ValidateCreateOptions(
+ options, &validated_options);
+ if (result != MOJO_RESULT_OK)
+ return result;
+ if (!VerifyUserPointer<MojoHandle>(message_pipe_handle0))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (!VerifyUserPointer<MojoHandle>(message_pipe_handle1))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ scoped_refptr<MessagePipeDispatcher> dispatcher0(
+ new MessagePipeDispatcher(validated_options));
+ scoped_refptr<MessagePipeDispatcher> dispatcher1(
+ new MessagePipeDispatcher(validated_options));
+
+ std::pair<MojoHandle, MojoHandle> handle_pair;
+ {
+ base::AutoLock locker(handle_table_lock_);
+ handle_pair = handle_table_.AddDispatcherPair(dispatcher0, dispatcher1);
+ }
+ if (handle_pair.first == MOJO_HANDLE_INVALID) {
+ DCHECK_EQ(handle_pair.second, MOJO_HANDLE_INVALID);
+ LOG(ERROR) << "Handle table full";
+ dispatcher0->Close();
+ dispatcher1->Close();
+ return MOJO_RESULT_RESOURCE_EXHAUSTED;
+ }
+
+ scoped_refptr<MessagePipe> message_pipe(new MessagePipe());
+ dispatcher0->Init(message_pipe, 0);
+ dispatcher1->Init(message_pipe, 1);
+
+ *message_pipe_handle0 = handle_pair.first;
+ *message_pipe_handle1 = handle_pair.second;
+ return MOJO_RESULT_OK;
+}
+
+// Implementation note: To properly cancel waiters and avoid other races, this
+// does not transfer dispatchers from one handle to another, even when sending a
+// message in-process. Instead, it must transfer the "contents" of the
+// dispatcher to a new dispatcher, and then close the old dispatcher. If this
+// isn't done, in the in-process case, calls on the old handle may complete
+// after the the message has been received and a new handle created (and
+// possibly even after calls have been made on the new handle).
+MojoResult Core::WriteMessage(MojoHandle message_pipe_handle,
+ const void* bytes,
+ uint32_t num_bytes,
+ const MojoHandle* handles,
+ uint32_t num_handles,
+ MojoWriteMessageFlags flags) {
+ scoped_refptr<Dispatcher> dispatcher(GetDispatcher(message_pipe_handle));
+ if (!dispatcher)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ // Easy case: not sending any handles.
+ if (num_handles == 0)
+ return dispatcher->WriteMessage(bytes, num_bytes, NULL, flags);
+
+ // We have to handle |handles| here, since we have to mark them busy in the
+ // global handle table. We can't delegate this to the dispatcher, since the
+ // handle table lock must be acquired before the dispatcher lock.
+ //
+ // (This leads to an oddity: |handles|/|num_handles| are always verified for
+ // validity, even for dispatchers that don't support |WriteMessage()| and will
+ // simply return failure unconditionally. It also breaks the usual
+ // left-to-right verification order of arguments.)
+ if (!VerifyUserPointerWithCount<MojoHandle>(handles, num_handles))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (num_handles > kMaxMessageNumHandles)
+ return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+ // We'll need to hold on to the dispatchers so that we can pass them on to
+ // |WriteMessage()| and also so that we can unlock their locks afterwards
+ // without accessing the handle table. These can be dumb pointers, since their
+ // entries in the handle table won't get removed (since they'll be marked as
+ // busy).
+ std::vector<DispatcherTransport> transports(num_handles);
+
+ // When we pass handles, we have to try to take all their dispatchers' locks
+ // and mark the handles as busy. If the call succeeds, we then remove the
+ // handles from the handle table.
+ {
+ base::AutoLock locker(handle_table_lock_);
+ MojoResult result = handle_table_.MarkBusyAndStartTransport(
+ message_pipe_handle, handles, num_handles, &transports);
+ if (result != MOJO_RESULT_OK)
+ return result;
+ }
+
+ MojoResult rv = dispatcher->WriteMessage(bytes, num_bytes, &transports,
+ flags);
+
+ // We need to release the dispatcher locks before we take the handle table
+ // lock.
+ for (uint32_t i = 0; i < num_handles; i++)
+ transports[i].End();
+
+ {
+ base::AutoLock locker(handle_table_lock_);
+ if (rv == MOJO_RESULT_OK)
+ handle_table_.RemoveBusyHandles(handles, num_handles);
+ else
+ handle_table_.RestoreBusyHandles(handles, num_handles);
+ }
+
+ return rv;
+}
+
+MojoResult Core::ReadMessage(MojoHandle message_pipe_handle,
+ void* bytes,
+ uint32_t* num_bytes,
+ MojoHandle* handles,
+ uint32_t* num_handles,
+ MojoReadMessageFlags flags) {
+ scoped_refptr<Dispatcher> dispatcher(GetDispatcher(message_pipe_handle));
+ if (!dispatcher)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ if (num_handles) {
+ if (!VerifyUserPointer<uint32_t>(num_handles))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (!VerifyUserPointerWithCount<MojoHandle>(handles, *num_handles))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ }
+
+ // Easy case: won't receive any handles.
+ if (!num_handles || *num_handles == 0)
+ return dispatcher->ReadMessage(bytes, num_bytes, NULL, num_handles, flags);
+
+ DispatcherVector dispatchers;
+ MojoResult rv = dispatcher->ReadMessage(bytes, num_bytes,
+ &dispatchers, num_handles,
+ flags);
+ if (!dispatchers.empty()) {
+ DCHECK_EQ(rv, MOJO_RESULT_OK);
+ DCHECK(num_handles);
+ DCHECK_LE(dispatchers.size(), static_cast<size_t>(*num_handles));
+
+ bool success;
+ {
+ base::AutoLock locker(handle_table_lock_);
+ success = handle_table_.AddDispatcherVector(dispatchers, handles);
+ }
+ if (!success) {
+ LOG(ERROR) << "Received message with " << dispatchers.size()
+ << " handles, but handle table full";
+ // Close dispatchers (outside the lock).
+ for (size_t i = 0; i < dispatchers.size(); i++) {
+ if (dispatchers[i])
+ dispatchers[i]->Close();
+ }
+ }
+ }
+
+ return rv;
+}
+
+MojoResult Core::CreateDataPipe(const MojoCreateDataPipeOptions* options,
+ MojoHandle* data_pipe_producer_handle,
+ MojoHandle* data_pipe_consumer_handle) {
+ MojoCreateDataPipeOptions validated_options = {};
+ // This will verify the |options| pointer.
+ MojoResult result = DataPipe::ValidateCreateOptions(options,
+ &validated_options);
+ if (result != MOJO_RESULT_OK)
+ return result;
+ if (!VerifyUserPointer<MojoHandle>(data_pipe_producer_handle))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (!VerifyUserPointer<MojoHandle>(data_pipe_consumer_handle))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ scoped_refptr<DataPipeProducerDispatcher> producer_dispatcher(
+ new DataPipeProducerDispatcher());
+ scoped_refptr<DataPipeConsumerDispatcher> consumer_dispatcher(
+ new DataPipeConsumerDispatcher());
+
+ std::pair<MojoHandle, MojoHandle> handle_pair;
+ {
+ base::AutoLock locker(handle_table_lock_);
+ handle_pair = handle_table_.AddDispatcherPair(producer_dispatcher,
+ consumer_dispatcher);
+ }
+ if (handle_pair.first == MOJO_HANDLE_INVALID) {
+ DCHECK_EQ(handle_pair.second, MOJO_HANDLE_INVALID);
+ LOG(ERROR) << "Handle table full";
+ producer_dispatcher->Close();
+ consumer_dispatcher->Close();
+ return MOJO_RESULT_RESOURCE_EXHAUSTED;
+ }
+ DCHECK_NE(handle_pair.second, MOJO_HANDLE_INVALID);
+
+ scoped_refptr<DataPipe> data_pipe(new LocalDataPipe(validated_options));
+ producer_dispatcher->Init(data_pipe);
+ consumer_dispatcher->Init(data_pipe);
+
+ *data_pipe_producer_handle = handle_pair.first;
+ *data_pipe_consumer_handle = handle_pair.second;
+ return MOJO_RESULT_OK;
+}
+
+MojoResult Core::WriteData(MojoHandle data_pipe_producer_handle,
+ const void* elements,
+ uint32_t* num_bytes,
+ MojoWriteDataFlags flags) {
+ scoped_refptr<Dispatcher> dispatcher(
+ GetDispatcher(data_pipe_producer_handle));
+ if (!dispatcher)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return dispatcher->WriteData(elements, num_bytes, flags);
+}
+
+MojoResult Core::BeginWriteData(MojoHandle data_pipe_producer_handle,
+ void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoWriteDataFlags flags) {
+ scoped_refptr<Dispatcher> dispatcher(
+ GetDispatcher(data_pipe_producer_handle));
+ if (!dispatcher)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return dispatcher->BeginWriteData(buffer, buffer_num_bytes, flags);
+}
+
+MojoResult Core::EndWriteData(MojoHandle data_pipe_producer_handle,
+ uint32_t num_bytes_written) {
+ scoped_refptr<Dispatcher> dispatcher(
+ GetDispatcher(data_pipe_producer_handle));
+ if (!dispatcher)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return dispatcher->EndWriteData(num_bytes_written);
+}
+
+MojoResult Core::ReadData(MojoHandle data_pipe_consumer_handle,
+ void* elements,
+ uint32_t* num_bytes,
+ MojoReadDataFlags flags) {
+ scoped_refptr<Dispatcher> dispatcher(
+ GetDispatcher(data_pipe_consumer_handle));
+ if (!dispatcher)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return dispatcher->ReadData(elements, num_bytes, flags);
+}
+
+MojoResult Core::BeginReadData(MojoHandle data_pipe_consumer_handle,
+ const void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoReadDataFlags flags) {
+ scoped_refptr<Dispatcher> dispatcher(
+ GetDispatcher(data_pipe_consumer_handle));
+ if (!dispatcher)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return dispatcher->BeginReadData(buffer, buffer_num_bytes, flags);
+}
+
+MojoResult Core::EndReadData(MojoHandle data_pipe_consumer_handle,
+ uint32_t num_bytes_read) {
+ scoped_refptr<Dispatcher> dispatcher(
+ GetDispatcher(data_pipe_consumer_handle));
+ if (!dispatcher)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return dispatcher->EndReadData(num_bytes_read);
+}
+
+MojoResult Core::CreateSharedBuffer(
+ const MojoCreateSharedBufferOptions* options,
+ uint64_t num_bytes,
+ MojoHandle* shared_buffer_handle) {
+ MojoCreateSharedBufferOptions validated_options = {};
+ // This will verify the |options| pointer.
+ MojoResult result =
+ SharedBufferDispatcher::ValidateCreateOptions(options,
+ &validated_options);
+ if (result != MOJO_RESULT_OK)
+ return result;
+ if (!VerifyUserPointer<MojoHandle>(shared_buffer_handle))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ scoped_refptr<SharedBufferDispatcher> dispatcher;
+ result = SharedBufferDispatcher::Create(validated_options, num_bytes,
+ &dispatcher);
+ if (result != MOJO_RESULT_OK) {
+ DCHECK(!dispatcher);
+ return result;
+ }
+
+ MojoHandle h = AddDispatcher(dispatcher);
+ if (h == MOJO_HANDLE_INVALID) {
+ LOG(ERROR) << "Handle table full";
+ dispatcher->Close();
+ return MOJO_RESULT_RESOURCE_EXHAUSTED;
+ }
+
+ *shared_buffer_handle = h;
+ return MOJO_RESULT_OK;
+}
+
+MojoResult Core::DuplicateBufferHandle(
+ MojoHandle buffer_handle,
+ const MojoDuplicateBufferHandleOptions* options,
+ MojoHandle* new_buffer_handle) {
+ scoped_refptr<Dispatcher> dispatcher(GetDispatcher(buffer_handle));
+ if (!dispatcher)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ // Don't verify |options| here; that's the dispatcher's job.
+ if (!VerifyUserPointer<MojoHandle>(new_buffer_handle))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ scoped_refptr<Dispatcher> new_dispatcher;
+ MojoResult result = dispatcher->DuplicateBufferHandle(options,
+ &new_dispatcher);
+ if (result != MOJO_RESULT_OK)
+ return result;
+
+ MojoHandle new_handle = AddDispatcher(new_dispatcher);
+ if (new_handle == MOJO_HANDLE_INVALID) {
+ LOG(ERROR) << "Handle table full";
+ dispatcher->Close();
+ return MOJO_RESULT_RESOURCE_EXHAUSTED;
+ }
+
+ *new_buffer_handle = new_handle;
+ return MOJO_RESULT_OK;
+}
+
+MojoResult Core::MapBuffer(MojoHandle buffer_handle,
+ uint64_t offset,
+ uint64_t num_bytes,
+ void** buffer,
+ MojoMapBufferFlags flags) {
+ scoped_refptr<Dispatcher> dispatcher(GetDispatcher(buffer_handle));
+ if (!dispatcher)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ if (!VerifyUserPointerWithCount<void*>(buffer, 1))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ scoped_ptr<RawSharedBufferMapping> mapping;
+ MojoResult result = dispatcher->MapBuffer(offset, num_bytes, flags, &mapping);
+ if (result != MOJO_RESULT_OK)
+ return result;
+
+ DCHECK(mapping);
+ void* address = mapping->base();
+ {
+ base::AutoLock locker(mapping_table_lock_);
+ result = mapping_table_.AddMapping(mapping.Pass());
+ }
+ if (result != MOJO_RESULT_OK)
+ return result;
+
+ *buffer = address;
+ return MOJO_RESULT_OK;
+}
+
+MojoResult Core::UnmapBuffer(void* buffer) {
+ base::AutoLock locker(mapping_table_lock_);
+ return mapping_table_.RemoveMapping(buffer);
+}
+
+// Note: We allow |handles| to repeat the same handle multiple times, since
+// different flags may be specified.
+// TODO(vtl): This incurs a performance cost in |RemoveWaiter()|. Analyze this
+// more carefully and address it if necessary.
+MojoResult Core::WaitManyInternal(const MojoHandle* handles,
+ const MojoHandleSignals* signals,
+ uint32_t num_handles,
+ MojoDeadline deadline) {
+ DCHECK_GT(num_handles, 0u);
+
+ DispatcherVector dispatchers;
+ dispatchers.reserve(num_handles);
+ for (uint32_t i = 0; i < num_handles; i++) {
+ scoped_refptr<Dispatcher> dispatcher = GetDispatcher(handles[i]);
+ if (!dispatcher)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ dispatchers.push_back(dispatcher);
+ }
+
+ // TODO(vtl): Should make the waiter live (permanently) in TLS.
+ Waiter waiter;
+ waiter.Init();
+
+ uint32_t i;
+ MojoResult rv = MOJO_RESULT_OK;
+ for (i = 0; i < num_handles; i++) {
+ rv = dispatchers[i]->AddWaiter(&waiter, signals[i], i);
+ if (rv != MOJO_RESULT_OK)
+ break;
+ }
+ uint32_t num_added = i;
+
+ if (rv == MOJO_RESULT_ALREADY_EXISTS) {
+ rv = static_cast<MojoResult>(i); // The i-th one is already "triggered".
+ } else if (rv == MOJO_RESULT_OK) {
+ uint32_t context = static_cast<uint32_t>(-1);
+ rv = waiter.Wait(deadline, &context);
+ if (rv == MOJO_RESULT_OK)
+ rv = static_cast<MojoResult>(context);
+ }
+
+ // Make sure no other dispatchers try to wake |waiter| for the current
+ // |Wait()|/|WaitMany()| call. (Only after doing this can |waiter| be
+ // destroyed, but this would still be required if the waiter were in TLS.)
+ for (i = 0; i < num_added; i++)
+ dispatchers[i]->RemoveWaiter(&waiter);
+
+ return rv;
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/core.h b/chromium/mojo/system/core.h
new file mode 100644
index 00000000000..a0bc293e203
--- /dev/null
+++ b/chromium/mojo/system/core.h
@@ -0,0 +1,131 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_CORE_H_
+#define MOJO_SYSTEM_CORE_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "mojo/public/c/system/buffer.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/system/handle_table.h"
+#include "mojo/system/mapping_table.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class Dispatcher;
+
+// |Core| is an object that implements the Mojo system calls. All public methods
+// are thread-safe.
+class MOJO_SYSTEM_IMPL_EXPORT Core {
+ public:
+ // These methods are only to be used by via the embedder API (and internally).
+ Core();
+ virtual ~Core();
+
+ // Adds |dispatcher| to the handle table, returning the handle for it. Returns
+ // |MOJO_HANDLE_INVALID| on failure, namely if the handle table is full.
+ MojoHandle AddDispatcher(const scoped_refptr<Dispatcher>& dispatcher);
+
+ // Looks up the dispatcher for the given handle. Returns null if the handle is
+ // invalid.
+ scoped_refptr<Dispatcher> GetDispatcher(MojoHandle handle);
+
+ // System calls implementation.
+ MojoTimeTicks GetTimeTicksNow();
+ MojoResult Close(MojoHandle handle);
+ MojoResult Wait(MojoHandle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline);
+ MojoResult WaitMany(const MojoHandle* handles,
+ const MojoHandleSignals* signals,
+ uint32_t num_handles,
+ MojoDeadline deadline);
+ MojoResult CreateMessagePipe(const MojoCreateMessagePipeOptions* options,
+ MojoHandle* message_pipe_handle0,
+ MojoHandle* message_pipe_handle1);
+ MojoResult WriteMessage(MojoHandle message_pipe_handle,
+ const void* bytes,
+ uint32_t num_bytes,
+ const MojoHandle* handles,
+ uint32_t num_handles,
+ MojoWriteMessageFlags flags);
+ MojoResult ReadMessage(MojoHandle message_pipe_handle,
+ void* bytes,
+ uint32_t* num_bytes,
+ MojoHandle* handles,
+ uint32_t* num_handles,
+ MojoReadMessageFlags flags);
+ MojoResult CreateDataPipe(const MojoCreateDataPipeOptions* options,
+ MojoHandle* data_pipe_producer_handle,
+ MojoHandle* data_pipe_consumer_handle);
+ MojoResult WriteData(MojoHandle data_pipe_producer_handle,
+ const void* elements,
+ uint32_t* num_bytes,
+ MojoWriteDataFlags flags);
+ MojoResult BeginWriteData(MojoHandle data_pipe_producer_handle,
+ void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoWriteDataFlags flags);
+ MojoResult EndWriteData(MojoHandle data_pipe_producer_handle,
+ uint32_t num_bytes_written);
+ MojoResult ReadData(MojoHandle data_pipe_consumer_handle,
+ void* elements,
+ uint32_t* num_bytes,
+ MojoReadDataFlags flags);
+ MojoResult BeginReadData(MojoHandle data_pipe_consumer_handle,
+ const void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoReadDataFlags flags);
+ MojoResult EndReadData(MojoHandle data_pipe_consumer_handle,
+ uint32_t num_bytes_read);
+ MojoResult CreateSharedBuffer(const MojoCreateSharedBufferOptions* options,
+ uint64_t num_bytes,
+ MojoHandle* shared_buffer_handle);
+ MojoResult DuplicateBufferHandle(
+ MojoHandle buffer_handle,
+ const MojoDuplicateBufferHandleOptions* options,
+ MojoHandle* new_buffer_handle);
+ MojoResult MapBuffer(MojoHandle buffer_handle,
+ uint64_t offset,
+ uint64_t num_bytes,
+ void** buffer,
+ MojoMapBufferFlags flags);
+ MojoResult UnmapBuffer(void* buffer);
+
+ private:
+ friend bool internal::ShutdownCheckNoLeaks(Core*);
+
+ // Internal implementation of |Wait()| and |WaitMany()|; doesn't do basic
+ // validation of arguments.
+ MojoResult WaitManyInternal(const MojoHandle* handles,
+ const MojoHandleSignals* signals,
+ uint32_t num_handles,
+ MojoDeadline deadline);
+
+ // ---------------------------------------------------------------------------
+
+ // TODO(vtl): |handle_table_lock_| should be a reader-writer lock (if only we
+ // had them).
+ base::Lock handle_table_lock_; // Protects |handle_table_|.
+ HandleTable handle_table_;
+
+ base::Lock mapping_table_lock_; // Protects |mapping_table_|.
+ MappingTable mapping_table_;
+
+ // ---------------------------------------------------------------------------
+
+ DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_CORE_H_
diff --git a/chromium/mojo/system/core_test_base.cc b/chromium/mojo/system/core_test_base.cc
new file mode 100644
index 00000000000..c91437c42c6
--- /dev/null
+++ b/chromium/mojo/system/core_test_base.cc
@@ -0,0 +1,349 @@
+// Copyright 2013 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 "mojo/system/core_test_base.h"
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/system/constants.h"
+#include "mojo/system/core.h"
+#include "mojo/system/dispatcher.h"
+#include "mojo/system/memory.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+namespace {
+
+// MockDispatcher --------------------------------------------------------------
+
+class MockDispatcher : public Dispatcher {
+ public:
+ explicit MockDispatcher(CoreTestBase::MockHandleInfo* info)
+ : info_(info) {
+ CHECK(info_);
+ info_->IncrementCtorCallCount();
+ }
+
+ // |Dispatcher| private methods:
+ virtual Type GetType() const OVERRIDE {
+ return kTypeUnknown;
+ }
+
+ private:
+ virtual ~MockDispatcher() {
+ info_->IncrementDtorCallCount();
+ }
+
+ // |Dispatcher| protected methods:
+ virtual void CloseImplNoLock() OVERRIDE {
+ info_->IncrementCloseCallCount();
+ lock().AssertAcquired();
+ }
+
+ virtual MojoResult WriteMessageImplNoLock(
+ const void* bytes,
+ uint32_t num_bytes,
+ std::vector<DispatcherTransport>* transports,
+ MojoWriteMessageFlags /*flags*/) OVERRIDE {
+ info_->IncrementWriteMessageCallCount();
+ lock().AssertAcquired();
+
+ if (!VerifyUserPointerWithSize<1>(bytes, num_bytes))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (num_bytes > kMaxMessageNumBytes)
+ return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+ if (transports)
+ return MOJO_RESULT_UNIMPLEMENTED;
+
+ return MOJO_RESULT_OK;
+ }
+
+ virtual MojoResult ReadMessageImplNoLock(
+ void* bytes,
+ uint32_t* num_bytes,
+ DispatcherVector* /*dispatchers*/,
+ uint32_t* /*num_dispatchers*/,
+ MojoReadMessageFlags /*flags*/) OVERRIDE {
+ info_->IncrementReadMessageCallCount();
+ lock().AssertAcquired();
+
+ if (num_bytes && !VerifyUserPointerWithSize<1>(bytes, *num_bytes))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return MOJO_RESULT_OK;
+ }
+
+ virtual MojoResult WriteDataImplNoLock(
+ const void* /*elements*/,
+ uint32_t* /*num_bytes*/,
+ MojoWriteDataFlags /*flags*/) OVERRIDE {
+ info_->IncrementWriteDataCallCount();
+ lock().AssertAcquired();
+ return MOJO_RESULT_UNIMPLEMENTED;
+ }
+
+ virtual MojoResult BeginWriteDataImplNoLock(
+ void** /*buffer*/,
+ uint32_t* /*buffer_num_bytes*/,
+ MojoWriteDataFlags /*flags*/) OVERRIDE {
+ info_->IncrementBeginWriteDataCallCount();
+ lock().AssertAcquired();
+ return MOJO_RESULT_UNIMPLEMENTED;
+ }
+
+ virtual MojoResult EndWriteDataImplNoLock(
+ uint32_t /*num_bytes_written*/) OVERRIDE {
+ info_->IncrementEndWriteDataCallCount();
+ lock().AssertAcquired();
+ return MOJO_RESULT_UNIMPLEMENTED;
+ }
+
+ virtual MojoResult ReadDataImplNoLock(void* /*elements*/,
+ uint32_t* /*num_bytes*/,
+ MojoReadDataFlags /*flags*/) OVERRIDE {
+ info_->IncrementReadDataCallCount();
+ lock().AssertAcquired();
+ return MOJO_RESULT_UNIMPLEMENTED;
+ }
+
+ virtual MojoResult BeginReadDataImplNoLock(
+ const void** /*buffer*/,
+ uint32_t* /*buffer_num_bytes*/,
+ MojoReadDataFlags /*flags*/) OVERRIDE {
+ info_->IncrementBeginReadDataCallCount();
+ lock().AssertAcquired();
+ return MOJO_RESULT_UNIMPLEMENTED;
+ }
+
+ virtual MojoResult EndReadDataImplNoLock(
+ uint32_t /*num_bytes_read*/) OVERRIDE {
+ info_->IncrementEndReadDataCallCount();
+ lock().AssertAcquired();
+ return MOJO_RESULT_UNIMPLEMENTED;
+ }
+
+ virtual MojoResult AddWaiterImplNoLock(Waiter* /*waiter*/,
+ MojoHandleSignals /*signals*/,
+ uint32_t /*context*/) OVERRIDE {
+ info_->IncrementAddWaiterCallCount();
+ lock().AssertAcquired();
+ return MOJO_RESULT_FAILED_PRECONDITION;
+ }
+
+ virtual void RemoveWaiterImplNoLock(Waiter* /*waiter*/) OVERRIDE {
+ info_->IncrementRemoveWaiterCallCount();
+ lock().AssertAcquired();
+ }
+
+ virtual void CancelAllWaitersNoLock() OVERRIDE {
+ info_->IncrementCancelAllWaitersCallCount();
+ lock().AssertAcquired();
+ }
+
+ virtual scoped_refptr<Dispatcher>
+ CreateEquivalentDispatcherAndCloseImplNoLock() OVERRIDE {
+ return scoped_refptr<Dispatcher>(new MockDispatcher(info_));
+ }
+
+ CoreTestBase::MockHandleInfo* const info_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockDispatcher);
+};
+
+} // namespace
+
+// CoreTestBase ----------------------------------------------------------------
+
+CoreTestBase::CoreTestBase() {
+}
+
+CoreTestBase::~CoreTestBase() {
+}
+
+void CoreTestBase::SetUp() {
+ core_ = new Core();
+}
+
+void CoreTestBase::TearDown() {
+ delete core_;
+ core_ = NULL;
+}
+
+MojoHandle CoreTestBase::CreateMockHandle(CoreTestBase::MockHandleInfo* info) {
+ CHECK(core_);
+ scoped_refptr<MockDispatcher> dispatcher(new MockDispatcher(info));
+ return core_->AddDispatcher(dispatcher);
+}
+
+// CoreTestBase_MockHandleInfo -------------------------------------------------
+
+CoreTestBase_MockHandleInfo::CoreTestBase_MockHandleInfo()
+ : ctor_call_count_(0),
+ dtor_call_count_(0),
+ close_call_count_(0),
+ write_message_call_count_(0),
+ read_message_call_count_(0),
+ write_data_call_count_(0),
+ begin_write_data_call_count_(0),
+ end_write_data_call_count_(0),
+ read_data_call_count_(0),
+ begin_read_data_call_count_(0),
+ end_read_data_call_count_(0),
+ add_waiter_call_count_(0),
+ remove_waiter_call_count_(0),
+ cancel_all_waiters_call_count_(0) {
+}
+
+CoreTestBase_MockHandleInfo::~CoreTestBase_MockHandleInfo() {
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetCtorCallCount() const {
+ base::AutoLock locker(lock_);
+ return ctor_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetDtorCallCount() const {
+ base::AutoLock locker(lock_);
+ return dtor_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetCloseCallCount() const {
+ base::AutoLock locker(lock_);
+ return close_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetWriteMessageCallCount() const {
+ base::AutoLock locker(lock_);
+ return write_message_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetReadMessageCallCount() const {
+ base::AutoLock locker(lock_);
+ return read_message_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetWriteDataCallCount() const {
+ base::AutoLock locker(lock_);
+ return write_data_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetBeginWriteDataCallCount() const {
+ base::AutoLock locker(lock_);
+ return begin_write_data_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetEndWriteDataCallCount() const {
+ base::AutoLock locker(lock_);
+ return end_write_data_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetReadDataCallCount() const {
+ base::AutoLock locker(lock_);
+ return read_data_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetBeginReadDataCallCount() const {
+ base::AutoLock locker(lock_);
+ return begin_read_data_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetEndReadDataCallCount() const {
+ base::AutoLock locker(lock_);
+ return end_read_data_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetAddWaiterCallCount() const {
+ base::AutoLock locker(lock_);
+ return add_waiter_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetRemoveWaiterCallCount() const {
+ base::AutoLock locker(lock_);
+ return remove_waiter_call_count_;
+}
+
+unsigned CoreTestBase_MockHandleInfo::GetCancelAllWaitersCallCount() const {
+ base::AutoLock locker(lock_);
+ return cancel_all_waiters_call_count_;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementCtorCallCount() {
+ base::AutoLock locker(lock_);
+ ctor_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementDtorCallCount() {
+ base::AutoLock locker(lock_);
+ dtor_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementCloseCallCount() {
+ base::AutoLock locker(lock_);
+ close_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementWriteMessageCallCount() {
+ base::AutoLock locker(lock_);
+ write_message_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementReadMessageCallCount() {
+ base::AutoLock locker(lock_);
+ read_message_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementWriteDataCallCount() {
+ base::AutoLock locker(lock_);
+ write_data_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementBeginWriteDataCallCount() {
+ base::AutoLock locker(lock_);
+ begin_write_data_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementEndWriteDataCallCount() {
+ base::AutoLock locker(lock_);
+ end_write_data_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementReadDataCallCount() {
+ base::AutoLock locker(lock_);
+ read_data_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementBeginReadDataCallCount() {
+ base::AutoLock locker(lock_);
+ begin_read_data_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementEndReadDataCallCount() {
+ base::AutoLock locker(lock_);
+ end_read_data_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementAddWaiterCallCount() {
+ base::AutoLock locker(lock_);
+ add_waiter_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementRemoveWaiterCallCount() {
+ base::AutoLock locker(lock_);
+ remove_waiter_call_count_++;
+}
+
+void CoreTestBase_MockHandleInfo::IncrementCancelAllWaitersCallCount() {
+ base::AutoLock locker(lock_);
+ cancel_all_waiters_call_count_++;
+}
+
+} // namespace test
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/core_test_base.h b/chromium/mojo/system/core_test_base.h
new file mode 100644
index 00000000000..9f5bae76846
--- /dev/null
+++ b/chromium/mojo/system/core_test_base.h
@@ -0,0 +1,105 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_CORE_TEST_BASE_H_
+#define MOJO_SYSTEM_CORE_TEST_BASE_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/synchronization/lock.h"
+#include "mojo/public/c/system/types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+
+class Core;
+
+namespace test {
+
+class CoreTestBase_MockHandleInfo;
+
+class CoreTestBase : public testing::Test {
+ public:
+ typedef CoreTestBase_MockHandleInfo MockHandleInfo;
+
+ CoreTestBase();
+ virtual ~CoreTestBase();
+
+ virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
+
+ protected:
+ // |info| must remain alive until the returned handle is closed.
+ MojoHandle CreateMockHandle(MockHandleInfo* info);
+
+ Core* core() { return core_; }
+
+ private:
+ Core* core_;
+
+ DISALLOW_COPY_AND_ASSIGN(CoreTestBase);
+};
+
+class CoreTestBase_MockHandleInfo {
+ public:
+ CoreTestBase_MockHandleInfo();
+ ~CoreTestBase_MockHandleInfo();
+
+ unsigned GetCtorCallCount() const;
+ unsigned GetDtorCallCount() const;
+ unsigned GetCloseCallCount() const;
+ unsigned GetWriteMessageCallCount() const;
+ unsigned GetReadMessageCallCount() const;
+ unsigned GetWriteDataCallCount() const;
+ unsigned GetBeginWriteDataCallCount() const;
+ unsigned GetEndWriteDataCallCount() const;
+ unsigned GetReadDataCallCount() const;
+ unsigned GetBeginReadDataCallCount() const;
+ unsigned GetEndReadDataCallCount() const;
+ unsigned GetAddWaiterCallCount() const;
+ unsigned GetRemoveWaiterCallCount() const;
+ unsigned GetCancelAllWaitersCallCount() const;
+
+ // For use by |MockDispatcher|:
+ void IncrementCtorCallCount();
+ void IncrementDtorCallCount();
+ void IncrementCloseCallCount();
+ void IncrementWriteMessageCallCount();
+ void IncrementReadMessageCallCount();
+ void IncrementWriteDataCallCount();
+ void IncrementBeginWriteDataCallCount();
+ void IncrementEndWriteDataCallCount();
+ void IncrementReadDataCallCount();
+ void IncrementBeginReadDataCallCount();
+ void IncrementEndReadDataCallCount();
+ void IncrementAddWaiterCallCount();
+ void IncrementRemoveWaiterCallCount();
+ void IncrementCancelAllWaitersCallCount();
+
+ private:
+ mutable base::Lock lock_; // Protects the following members.
+ unsigned ctor_call_count_;
+ unsigned dtor_call_count_;
+ unsigned close_call_count_;
+ unsigned write_message_call_count_;
+ unsigned read_message_call_count_;
+ unsigned write_data_call_count_;
+ unsigned begin_write_data_call_count_;
+ unsigned end_write_data_call_count_;
+ unsigned read_data_call_count_;
+ unsigned begin_read_data_call_count_;
+ unsigned end_read_data_call_count_;
+ unsigned add_waiter_call_count_;
+ unsigned remove_waiter_call_count_;
+ unsigned cancel_all_waiters_call_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(CoreTestBase_MockHandleInfo);
+};
+
+} // namespace test
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_CORE_TEST_BASE_H_
diff --git a/chromium/mojo/system/core_unittest.cc b/chromium/mojo/system/core_unittest.cc
new file mode 100644
index 00000000000..af3d0c13451
--- /dev/null
+++ b/chromium/mojo/system/core_unittest.cc
@@ -0,0 +1,888 @@
+// Copyright 2013 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 "mojo/system/core.h"
+
+#include <limits>
+
+#include "base/basictypes.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "mojo/system/core_test_base.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+typedef test::CoreTestBase CoreTest;
+
+TEST_F(CoreTest, GetTimeTicksNow) {
+ const MojoTimeTicks start = core()->GetTimeTicksNow();
+ EXPECT_NE(static_cast<MojoTimeTicks>(0), start)
+ << "GetTimeTicksNow should return nonzero value";
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(15));
+ const MojoTimeTicks finish = core()->GetTimeTicksNow();
+ // Allow for some fuzz in sleep.
+ EXPECT_GE((finish - start), static_cast<MojoTimeTicks>(8000))
+ << "Sleeping should result in increasing time ticks";
+}
+
+TEST_F(CoreTest, Basic) {
+ MockHandleInfo info;
+
+ EXPECT_EQ(0u, info.GetCtorCallCount());
+ MojoHandle h = CreateMockHandle(&info);
+ EXPECT_EQ(1u, info.GetCtorCallCount());
+ EXPECT_NE(h, MOJO_HANDLE_INVALID);
+
+ EXPECT_EQ(0u, info.GetWriteMessageCallCount());
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->WriteMessage(h, NULL, 0, NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(1u, info.GetWriteMessageCallCount());
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->WriteMessage(h, NULL, 1, NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(2u, info.GetWriteMessageCallCount());
+
+ EXPECT_EQ(0u, info.GetReadMessageCallCount());
+ uint32_t num_bytes = 0;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->ReadMessage(h, NULL, &num_bytes, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(1u, info.GetReadMessageCallCount());
+ num_bytes = 1;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->ReadMessage(h, NULL, &num_bytes, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(2u, info.GetReadMessageCallCount());
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->ReadMessage(h, NULL, NULL, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(3u, info.GetReadMessageCallCount());
+
+ EXPECT_EQ(0u, info.GetWriteDataCallCount());
+ EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+ core()->WriteData(h, NULL, NULL, MOJO_WRITE_DATA_FLAG_NONE));
+ EXPECT_EQ(1u, info.GetWriteDataCallCount());
+
+ EXPECT_EQ(0u, info.GetBeginWriteDataCallCount());
+ EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+ core()->BeginWriteData(h, NULL, NULL, MOJO_WRITE_DATA_FLAG_NONE));
+ EXPECT_EQ(1u, info.GetBeginWriteDataCallCount());
+
+ EXPECT_EQ(0u, info.GetEndWriteDataCallCount());
+ EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+ core()->EndWriteData(h, 0));
+ EXPECT_EQ(1u, info.GetEndWriteDataCallCount());
+
+ EXPECT_EQ(0u, info.GetReadDataCallCount());
+ EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+ core()->ReadData(h, NULL, NULL, MOJO_READ_DATA_FLAG_NONE));
+ EXPECT_EQ(1u, info.GetReadDataCallCount());
+
+ EXPECT_EQ(0u, info.GetBeginReadDataCallCount());
+ EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+ core()->BeginReadData(h, NULL, NULL, MOJO_READ_DATA_FLAG_NONE));
+ EXPECT_EQ(1u, info.GetBeginReadDataCallCount());
+
+ EXPECT_EQ(0u, info.GetEndReadDataCallCount());
+ EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+ core()->EndReadData(h, 0));
+ EXPECT_EQ(1u, info.GetEndReadDataCallCount());
+
+ EXPECT_EQ(0u, info.GetAddWaiterCallCount());
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE,
+ MOJO_DEADLINE_INDEFINITE));
+ EXPECT_EQ(1u, info.GetAddWaiterCallCount());
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, 0));
+ EXPECT_EQ(2u, info.GetAddWaiterCallCount());
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, 10 * 1000));
+ EXPECT_EQ(3u, info.GetAddWaiterCallCount());
+ MojoHandleSignals handle_signals = ~MOJO_HANDLE_SIGNAL_NONE;
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ core()->WaitMany(&h, &handle_signals, 1, MOJO_DEADLINE_INDEFINITE));
+ EXPECT_EQ(4u, info.GetAddWaiterCallCount());
+
+ EXPECT_EQ(0u, info.GetDtorCallCount());
+ EXPECT_EQ(0u, info.GetCloseCallCount());
+ EXPECT_EQ(0u, info.GetCancelAllWaitersCallCount());
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h));
+ EXPECT_EQ(1u, info.GetCancelAllWaitersCallCount());
+ EXPECT_EQ(1u, info.GetCloseCallCount());
+ EXPECT_EQ(1u, info.GetDtorCallCount());
+
+ // No waiters should ever have ever been added.
+ EXPECT_EQ(0u, info.GetRemoveWaiterCallCount());
+}
+
+TEST_F(CoreTest, InvalidArguments) {
+ // |Close()|:
+ {
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(MOJO_HANDLE_INVALID));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(10));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(1000000000));
+
+ // Test a double-close.
+ MockHandleInfo info;
+ MojoHandle h = CreateMockHandle(&info);
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h));
+ EXPECT_EQ(1u, info.GetCloseCallCount());
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(h));
+ EXPECT_EQ(1u, info.GetCloseCallCount());
+ }
+
+ // |Wait()|:
+ {
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->Wait(MOJO_HANDLE_INVALID, ~MOJO_HANDLE_SIGNAL_NONE,
+ MOJO_DEADLINE_INDEFINITE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->Wait(10, ~MOJO_HANDLE_SIGNAL_NONE,
+ MOJO_DEADLINE_INDEFINITE));
+ }
+
+ // |WaitMany()|:
+ {
+ MojoHandle handles[2] = {MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID};
+ MojoHandleSignals signals[2] = {~MOJO_HANDLE_SIGNAL_NONE,
+ ~MOJO_HANDLE_SIGNAL_NONE};
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->WaitMany(handles, signals, 0, MOJO_DEADLINE_INDEFINITE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->WaitMany(NULL, signals, 0, MOJO_DEADLINE_INDEFINITE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->WaitMany(handles, NULL, 0, MOJO_DEADLINE_INDEFINITE));
+
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->WaitMany(NULL, signals, 1, MOJO_DEADLINE_INDEFINITE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->WaitMany(handles, NULL, 1, MOJO_DEADLINE_INDEFINITE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->WaitMany(handles, signals, 1, MOJO_DEADLINE_INDEFINITE));
+
+ MockHandleInfo info[2];
+ handles[0] = CreateMockHandle(&info[0]);
+
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ core()->WaitMany(handles, signals, 1, MOJO_DEADLINE_INDEFINITE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->WaitMany(handles, signals, 2, MOJO_DEADLINE_INDEFINITE));
+ handles[1] = handles[0] + 1; // Invalid handle.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->WaitMany(handles, signals, 2, MOJO_DEADLINE_INDEFINITE));
+ handles[1] = CreateMockHandle(&info[1]);
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ core()->WaitMany(handles, signals, 2, MOJO_DEADLINE_INDEFINITE));
+
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(handles[0]));
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(handles[1]));
+ }
+
+ // |CreateMessagePipe()|:
+ {
+ MojoHandle h;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->CreateMessagePipe(NULL, NULL, NULL));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->CreateMessagePipe(NULL, &h, NULL));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->CreateMessagePipe(NULL, NULL, &h));
+ }
+
+ // |WriteMessage()|:
+ // Only check arguments checked by |Core|, namely |handle|, |handles|, and
+ // |num_handles|.
+ {
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->WriteMessage(MOJO_HANDLE_INVALID, NULL, 0, NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ MockHandleInfo info;
+ MojoHandle h = CreateMockHandle(&info);
+ MojoHandle handles[2] = {MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID};
+
+ // Null |handles| with nonzero |num_handles|.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->WriteMessage(h, NULL, 0, NULL, 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ // Checked by |Core|, shouldn't go through to the dispatcher.
+ EXPECT_EQ(0u, info.GetWriteMessageCallCount());
+
+ // Huge handle count (implausibly big on some systems -- more than can be
+ // stored in a 32-bit address space).
+ // Note: This may return either |MOJO_RESULT_INVALID_ARGUMENT| or
+ // |MOJO_RESULT_RESOURCE_EXHAUSTED|, depending on whether it's plausible or
+ // not.
+ EXPECT_NE(MOJO_RESULT_OK,
+ core()->WriteMessage(h, NULL, 0, handles,
+ std::numeric_limits<uint32_t>::max(),
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(0u, info.GetWriteMessageCallCount());
+
+ // Huge handle count (plausibly big).
+ EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+ core()->WriteMessage(h, NULL, 0, handles,
+ std::numeric_limits<uint32_t>::max() /
+ sizeof(handles[0]),
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(0u, info.GetWriteMessageCallCount());
+
+ // Invalid handle in |handles|.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->WriteMessage(h, NULL, 0, handles, 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(0u, info.GetWriteMessageCallCount());
+
+ // Two invalid handles in |handles|.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->WriteMessage(h, NULL, 0, handles, 2,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(0u, info.GetWriteMessageCallCount());
+
+ // Can't send a handle over itself.
+ handles[0] = h;
+ EXPECT_EQ(MOJO_RESULT_BUSY,
+ core()->WriteMessage(h, NULL, 0, handles, 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(0u, info.GetWriteMessageCallCount());
+
+ MockHandleInfo info2;
+ MojoHandle h2 = CreateMockHandle(&info2);
+
+ // This is "okay", but |MockDispatcher| doesn't implement it.
+ handles[0] = h2;
+ EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+ core()->WriteMessage(h, NULL, 0, handles, 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(1u, info.GetWriteMessageCallCount());
+
+ // One of the |handles| is still invalid.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->WriteMessage(h, NULL, 0, handles, 2,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(1u, info.GetWriteMessageCallCount());
+
+ // One of the |handles| is the same as |handle|.
+ handles[1] = h;
+ EXPECT_EQ(MOJO_RESULT_BUSY,
+ core()->WriteMessage(h, NULL, 0, handles, 2,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(1u, info.GetWriteMessageCallCount());
+
+ // Can't send a handle twice in the same message.
+ handles[1] = h2;
+ EXPECT_EQ(MOJO_RESULT_BUSY,
+ core()->WriteMessage(h, NULL, 0, handles, 2,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(1u, info.GetWriteMessageCallCount());
+
+ // Note: Since we never successfully sent anything with it, |h2| should
+ // still be valid.
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h2));
+
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h));
+ }
+
+ // |ReadMessage()|:
+ // Only check arguments checked by |Core|, namely |handle|, |handles|, and
+ // |num_handles|.
+ {
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->ReadMessage(MOJO_HANDLE_INVALID, NULL, NULL, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+
+ MockHandleInfo info;
+ MojoHandle h = CreateMockHandle(&info);
+
+ uint32_t handle_count = 1;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->ReadMessage(h, NULL, NULL, NULL, &handle_count,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ // Checked by |Core|, shouldn't go through to the dispatcher.
+ EXPECT_EQ(0u, info.GetReadMessageCallCount());
+
+ // Okay.
+ handle_count = 0;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->ReadMessage(h, NULL, NULL, NULL, &handle_count,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ // Checked by |Core|, shouldn't go through to the dispatcher.
+ EXPECT_EQ(1u, info.GetReadMessageCallCount());
+
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h));
+ }
+}
+
+// TODO(vtl): test |Wait()| and |WaitMany()| properly
+// - including |WaitMany()| with the same handle more than once (with
+// same/different signals)
+
+TEST_F(CoreTest, MessagePipe) {
+ MojoHandle h[2];
+
+ EXPECT_EQ(MOJO_RESULT_OK, core()->CreateMessagePipe(NULL, &h[0], &h[1]));
+ // Should get two distinct, valid handles.
+ EXPECT_NE(h[0], MOJO_HANDLE_INVALID);
+ EXPECT_NE(h[1], MOJO_HANDLE_INVALID);
+ EXPECT_NE(h[0], h[1]);
+
+ // Neither should be readable.
+ MojoHandleSignals signals[2] = {MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_HANDLE_SIGNAL_READABLE};
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+ core()->WaitMany(h, signals, 2, 0));
+
+ // Try to read anyway.
+ char buffer[1] = {'a'};
+ uint32_t buffer_size = 1;
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ core()->ReadMessage(h[0], buffer, &buffer_size, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ // Check that it left its inputs alone.
+ EXPECT_EQ('a', buffer[0]);
+ EXPECT_EQ(1u, buffer_size);
+
+ // Both should be writable.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->Wait(h[0], MOJO_HANDLE_SIGNAL_WRITABLE, 1000000000));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->Wait(h[1], MOJO_HANDLE_SIGNAL_WRITABLE, 1000000000));
+
+ // Also check that |h[1]| is writable using |WaitMany()|.
+ signals[0] = MOJO_HANDLE_SIGNAL_READABLE;
+ signals[1] = MOJO_HANDLE_SIGNAL_WRITABLE;
+ EXPECT_EQ(1, core()->WaitMany(h, signals, 2, MOJO_DEADLINE_INDEFINITE));
+
+ // Write to |h[1]|.
+ buffer[0] = 'b';
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->WriteMessage(h[1], buffer, 1, NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Check that |h[0]| is now readable.
+ signals[0] = MOJO_HANDLE_SIGNAL_READABLE;
+ signals[1] = MOJO_HANDLE_SIGNAL_READABLE;
+ EXPECT_EQ(0, core()->WaitMany(h, signals, 2, MOJO_DEADLINE_INDEFINITE));
+
+ // Read from |h[0]|.
+ // First, get only the size.
+ buffer_size = 0;
+ EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+ core()->ReadMessage(h[0], NULL, &buffer_size, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(1u, buffer_size);
+ // Then actually read it.
+ buffer[0] = 'c';
+ buffer_size = 1;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->ReadMessage(h[0], buffer, &buffer_size, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ('b', buffer[0]);
+ EXPECT_EQ(1u, buffer_size);
+
+ // |h[0]| should no longer be readable.
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+ core()->Wait(h[0], MOJO_HANDLE_SIGNAL_READABLE, 0));
+
+ // Write to |h[0]|.
+ buffer[0] = 'd';
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->WriteMessage(h[0], buffer, 1, NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Close |h[0]|.
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h[0]));
+
+ // Check that |h[1]| is no longer writable (and will never be).
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ core()->Wait(h[1], MOJO_HANDLE_SIGNAL_WRITABLE, 1000000000));
+
+ // Check that |h[1]| is still readable (for the moment).
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->Wait(h[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000));
+
+ // Discard a message from |h[1]|.
+ EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+ core()->ReadMessage(h[1], NULL, NULL, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+
+ // |h[1]| is no longer readable (and will never be).
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ core()->Wait(h[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000));
+
+ // Try writing to |h[1]|.
+ buffer[0] = 'e';
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ core()->WriteMessage(h[1], buffer, 1, NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h[1]));
+}
+
+// Tests passing a message pipe handle.
+TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing1) {
+ const char kHello[] = "hello";
+ const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
+ const char kWorld[] = "world!!!";
+ const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld));
+ char buffer[100];
+ const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+ uint32_t num_bytes;
+ MojoHandle handles[10];
+ uint32_t num_handles;
+ MojoHandle h_received;
+
+ MojoHandle h_passing[2];
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->CreateMessagePipe(NULL, &h_passing[0], &h_passing[1]));
+
+ // Make sure that |h_passing[]| work properly.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->WriteMessage(h_passing[0],
+ kHello, kHelloSize,
+ NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE,
+ 1000000000));
+ num_bytes = kBufferSize;
+ num_handles = arraysize(handles);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->ReadMessage(h_passing[1],
+ buffer, &num_bytes,
+ handles, &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kHelloSize, num_bytes);
+ EXPECT_STREQ(kHello, buffer);
+ EXPECT_EQ(0u, num_handles);
+
+ // Make sure that you can't pass either of the message pipe's handles over
+ // itself.
+ EXPECT_EQ(MOJO_RESULT_BUSY,
+ core()->WriteMessage(h_passing[0],
+ kHello, kHelloSize,
+ &h_passing[0], 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ core()->WriteMessage(h_passing[0],
+ kHello, kHelloSize,
+ &h_passing[1], 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ MojoHandle h_passed[2];
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->CreateMessagePipe(NULL, &h_passed[0], &h_passed[1]));
+
+ // Make sure that |h_passed[]| work properly.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->WriteMessage(h_passed[0],
+ kHello, kHelloSize,
+ NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->Wait(h_passed[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000));
+ num_bytes = kBufferSize;
+ num_handles = arraysize(handles);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->ReadMessage(h_passed[1],
+ buffer, &num_bytes,
+ handles, &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kHelloSize, num_bytes);
+ EXPECT_STREQ(kHello, buffer);
+ EXPECT_EQ(0u, num_handles);
+
+ // Send |h_passed[1]| from |h_passing[0]| to |h_passing[1]|.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->WriteMessage(h_passing[0],
+ kWorld, kWorldSize,
+ &h_passed[1], 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE,
+ 1000000000));
+ num_bytes = kBufferSize;
+ num_handles = arraysize(handles);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->ReadMessage(h_passing[1],
+ buffer, &num_bytes,
+ handles, &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kWorldSize, num_bytes);
+ EXPECT_STREQ(kWorld, buffer);
+ EXPECT_EQ(1u, num_handles);
+ h_received = handles[0];
+ EXPECT_NE(h_received, MOJO_HANDLE_INVALID);
+ EXPECT_NE(h_received, h_passing[0]);
+ EXPECT_NE(h_received, h_passing[1]);
+ EXPECT_NE(h_received, h_passed[0]);
+
+ // Note: We rely on the Mojo system not re-using handle values very often.
+ EXPECT_NE(h_received, h_passed[1]);
+
+ // |h_passed[1]| should no longer be valid; check that trying to close it
+ // fails. See above note.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(h_passed[1]));
+
+ // Write to |h_passed[0]|. Should receive on |h_received|.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->WriteMessage(h_passed[0],
+ kHello, kHelloSize,
+ NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->Wait(h_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000));
+ num_bytes = kBufferSize;
+ num_handles = arraysize(handles);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->ReadMessage(h_received,
+ buffer, &num_bytes,
+ handles, &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kHelloSize, num_bytes);
+ EXPECT_STREQ(kHello, buffer);
+ EXPECT_EQ(0u, num_handles);
+
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[0]));
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[1]));
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passed[0]));
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_received));
+}
+
+TEST_F(CoreTest, DataPipe) {
+ MojoHandle ph, ch; // p is for producer and c is for consumer.
+
+ EXPECT_EQ(MOJO_RESULT_OK, core()->CreateDataPipe(NULL, &ph, &ch));
+ // Should get two distinct, valid handles.
+ EXPECT_NE(ph, MOJO_HANDLE_INVALID);
+ EXPECT_NE(ch, MOJO_HANDLE_INVALID);
+ EXPECT_NE(ph, ch);
+
+ // Producer should be never-readable, but already writable.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ core()->Wait(ph, MOJO_HANDLE_SIGNAL_READABLE, 0));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->Wait(ph, MOJO_HANDLE_SIGNAL_WRITABLE, 0));
+
+ // Consumer should be never-writable, and not yet readable.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ core()->Wait(ch, MOJO_HANDLE_SIGNAL_WRITABLE, 0));
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+ core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0));
+
+ // Write.
+ char elements[2] = {'A', 'B'};
+ uint32_t num_bytes = 2u;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->WriteData(ph, elements, &num_bytes,
+ MOJO_WRITE_DATA_FLAG_NONE));
+ EXPECT_EQ(2u, num_bytes);
+
+ // Consumer should now be readable.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0));
+
+ // Read one character.
+ elements[0] = -1;
+ elements[1] = -1;
+ num_bytes = 1u;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->ReadData(ch, elements, &num_bytes,
+ MOJO_READ_DATA_FLAG_NONE));
+ EXPECT_EQ('A', elements[0]);
+ EXPECT_EQ(-1, elements[1]);
+
+ // Two-phase write.
+ void* write_ptr = NULL;
+ num_bytes = 0u;
+ ASSERT_EQ(MOJO_RESULT_OK,
+ core()->BeginWriteData(ph, &write_ptr, &num_bytes,
+ MOJO_WRITE_DATA_FLAG_NONE));
+ // We count on the default options providing a decent buffer size.
+ ASSERT_GE(num_bytes, 3u);
+
+ // Trying to do a normal write during a two-phase write should fail.
+ elements[0] = 'X';
+ num_bytes = 1u;
+ EXPECT_EQ(MOJO_RESULT_BUSY,
+ core()->WriteData(ph, elements, &num_bytes,
+ MOJO_WRITE_DATA_FLAG_NONE));
+
+ // Actually write the data, and complete it now.
+ static_cast<char*>(write_ptr)[0] = 'C';
+ static_cast<char*>(write_ptr)[1] = 'D';
+ static_cast<char*>(write_ptr)[2] = 'E';
+ EXPECT_EQ(MOJO_RESULT_OK, core()->EndWriteData(ph, 3u));
+
+ // Query how much data we have.
+ num_bytes = 0;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->ReadData(ch, NULL, &num_bytes, MOJO_READ_DATA_FLAG_QUERY));
+ EXPECT_EQ(4u, num_bytes);
+
+ // Try to discard ten characters, in all-or-none mode. Should fail.
+ num_bytes = 10;
+ EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+ core()->ReadData(ch, NULL, &num_bytes,
+ MOJO_READ_DATA_FLAG_DISCARD |
+ MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+
+ // Discard two characters.
+ num_bytes = 2;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->ReadData(ch, NULL, &num_bytes,
+ MOJO_READ_DATA_FLAG_DISCARD |
+ MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+
+ // Read the remaining two characters, in two-phase mode (all-or-none).
+ const void* read_ptr = NULL;
+ num_bytes = 2;
+ ASSERT_EQ(MOJO_RESULT_OK,
+ core()->BeginReadData(ch, &read_ptr, &num_bytes,
+ MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+ // Note: Count on still being able to do the contiguous read here.
+ ASSERT_EQ(2u, num_bytes);
+
+ // Discarding right now should fail.
+ num_bytes = 1;
+ EXPECT_EQ(MOJO_RESULT_BUSY,
+ core()->ReadData(ch, NULL, &num_bytes,
+ MOJO_READ_DATA_FLAG_DISCARD));
+
+ // Actually check our data and end the two-phase read.
+ EXPECT_EQ('D', static_cast<const char*>(read_ptr)[0]);
+ EXPECT_EQ('E', static_cast<const char*>(read_ptr)[1]);
+ EXPECT_EQ(MOJO_RESULT_OK, core()->EndReadData(ch, 2u));
+
+ // Consumer should now be no longer readable.
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+ core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0));
+
+ // TODO(vtl): More.
+
+ // Close the producer.
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ph));
+
+ // The consumer should now be never-readable.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0));
+
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ch));
+}
+
+// Tests passing data pipe producer and consumer handles.
+TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing2) {
+ const char kHello[] = "hello";
+ const uint32_t kHelloSize = static_cast<uint32_t>(sizeof(kHello));
+ const char kWorld[] = "world!!!";
+ const uint32_t kWorldSize = static_cast<uint32_t>(sizeof(kWorld));
+ char buffer[100];
+ const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+ uint32_t num_bytes;
+ MojoHandle handles[10];
+ uint32_t num_handles;
+
+ MojoHandle h_passing[2];
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->CreateMessagePipe(NULL, &h_passing[0], &h_passing[1]));
+
+ MojoHandle ph, ch;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->CreateDataPipe(NULL, &ph, &ch));
+
+ // Send |ch| from |h_passing[0]| to |h_passing[1]|.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->WriteMessage(h_passing[0],
+ kHello, kHelloSize,
+ &ch, 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE,
+ 1000000000));
+ num_bytes = kBufferSize;
+ num_handles = arraysize(handles);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->ReadMessage(h_passing[1],
+ buffer, &num_bytes,
+ handles, &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kHelloSize, num_bytes);
+ EXPECT_STREQ(kHello, buffer);
+ EXPECT_EQ(1u, num_handles);
+ MojoHandle ch_received = handles[0];
+ EXPECT_NE(ch_received, MOJO_HANDLE_INVALID);
+ EXPECT_NE(ch_received, h_passing[0]);
+ EXPECT_NE(ch_received, h_passing[1]);
+ EXPECT_NE(ch_received, ph);
+
+ // Note: We rely on the Mojo system not re-using handle values very often.
+ EXPECT_NE(ch_received, ch);
+
+ // |ch| should no longer be valid; check that trying to close it fails. See
+ // above note.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(ch));
+
+ // Write to |ph|. Should receive on |ch_received|.
+ num_bytes = kWorldSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->WriteData(ph, kWorld, &num_bytes,
+ MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->Wait(ch_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000));
+ num_bytes = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->ReadData(ch_received, buffer, &num_bytes,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kWorldSize, num_bytes);
+ EXPECT_STREQ(kWorld, buffer);
+
+ // Now pass |ph| in the same direction.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->WriteMessage(h_passing[0],
+ kWorld, kWorldSize,
+ &ph, 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE,
+ 1000000000));
+ num_bytes = kBufferSize;
+ num_handles = arraysize(handles);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->ReadMessage(h_passing[1],
+ buffer, &num_bytes,
+ handles, &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kWorldSize, num_bytes);
+ EXPECT_STREQ(kWorld, buffer);
+ EXPECT_EQ(1u, num_handles);
+ MojoHandle ph_received = handles[0];
+ EXPECT_NE(ph_received, MOJO_HANDLE_INVALID);
+ EXPECT_NE(ph_received, h_passing[0]);
+ EXPECT_NE(ph_received, h_passing[1]);
+ EXPECT_NE(ph_received, ch_received);
+
+ // Again, rely on the Mojo system not re-using handle values very often.
+ EXPECT_NE(ph_received, ph);
+
+ // |ph| should no longer be valid; check that trying to close it fails. See
+ // above note.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Close(ph));
+
+ // Write to |ph_received|. Should receive on |ch_received|.
+ num_bytes = kHelloSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->WriteData(ph_received, kHello, &num_bytes,
+ MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->Wait(ch_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000));
+ num_bytes = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->ReadData(ch_received, buffer, &num_bytes,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kHelloSize, num_bytes);
+ EXPECT_STREQ(kHello, buffer);
+
+ ph = ph_received;
+ ph_received = MOJO_HANDLE_INVALID;
+ ch = ch_received;
+ ch_received = MOJO_HANDLE_INVALID;
+
+ // Make sure that |ph| can't be sent if it's in a two-phase write.
+ void* write_ptr = NULL;
+ num_bytes = 0;
+ ASSERT_EQ(MOJO_RESULT_OK,
+ core()->BeginWriteData(ph, &write_ptr, &num_bytes,
+ MOJO_WRITE_DATA_FLAG_NONE));
+ ASSERT_GE(num_bytes, 1u);
+ EXPECT_EQ(MOJO_RESULT_BUSY,
+ core()->WriteMessage(h_passing[0],
+ kHello, kHelloSize,
+ &ph, 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // But |ch| can, even if |ph| is in a two-phase write.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->WriteMessage(h_passing[0],
+ kHello, kHelloSize,
+ &ch, 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ ch = MOJO_HANDLE_INVALID;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE,
+ 1000000000));
+ num_bytes = kBufferSize;
+ num_handles = arraysize(handles);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->ReadMessage(h_passing[1],
+ buffer, &num_bytes,
+ handles, &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kHelloSize, num_bytes);
+ EXPECT_STREQ(kHello, buffer);
+ EXPECT_EQ(1u, num_handles);
+ ch = handles[0];
+ EXPECT_NE(ch, MOJO_HANDLE_INVALID);
+
+ // Complete the two-phase write.
+ static_cast<char*>(write_ptr)[0] = 'x';
+ EXPECT_EQ(MOJO_RESULT_OK, core()->EndWriteData(ph, 1));
+
+ // Wait for |ch| to be readable.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 1000000000));
+
+ // Make sure that |ch| can't be sent if it's in a two-phase read.
+ const void* read_ptr = NULL;
+ num_bytes = 1;
+ ASSERT_EQ(MOJO_RESULT_OK,
+ core()->BeginReadData(ch, &read_ptr, &num_bytes,
+ MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+ EXPECT_EQ(MOJO_RESULT_BUSY,
+ core()->WriteMessage(h_passing[0],
+ kHello, kHelloSize,
+ &ch, 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // But |ph| can, even if |ch| is in a two-phase read.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->WriteMessage(h_passing[0],
+ kWorld, kWorldSize,
+ &ph, 1,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ ph = MOJO_HANDLE_INVALID;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE,
+ 1000000000));
+ num_bytes = kBufferSize;
+ num_handles = arraysize(handles);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ core()->ReadMessage(h_passing[1],
+ buffer, &num_bytes,
+ handles, &num_handles,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kWorldSize, num_bytes);
+ EXPECT_STREQ(kWorld, buffer);
+ EXPECT_EQ(1u, num_handles);
+ ph = handles[0];
+ EXPECT_NE(ph, MOJO_HANDLE_INVALID);
+
+ // Complete the two-phase read.
+ EXPECT_EQ('x', static_cast<const char*>(read_ptr)[0]);
+ EXPECT_EQ(MOJO_RESULT_OK, core()->EndReadData(ch, 1));
+
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[0]));
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h_passing[1]));
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ph));
+ EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ch));
+}
+
+// TODO(vtl): Test |DuplicateBufferHandle()| and |MapBuffer()|.
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/data_pipe.cc b/chromium/mojo/system/data_pipe.cc
new file mode 100644
index 00000000000..6337648d01c
--- /dev/null
+++ b/chromium/mojo/system/data_pipe.cc
@@ -0,0 +1,403 @@
+// Copyright 2013 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 "mojo/system/data_pipe.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "mojo/system/constants.h"
+#include "mojo/system/memory.h"
+#include "mojo/system/options_validation.h"
+#include "mojo/system/waiter_list.h"
+
+namespace mojo {
+namespace system {
+
+// static
+const MojoCreateDataPipeOptions DataPipe::kDefaultCreateOptions = {
+ static_cast<uint32_t>(sizeof(MojoCreateDataPipeOptions)),
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
+ 1u,
+ static_cast<uint32_t>(kDefaultDataPipeCapacityBytes)
+};
+
+// static
+MojoResult DataPipe::ValidateCreateOptions(
+ const MojoCreateDataPipeOptions* in_options,
+ MojoCreateDataPipeOptions* out_options) {
+ const MojoCreateDataPipeOptionsFlags kKnownFlags =
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD;
+
+ *out_options = kDefaultCreateOptions;
+ if (!in_options)
+ return MOJO_RESULT_OK;
+
+ MojoResult result =
+ ValidateOptionsStructPointerSizeAndFlags<MojoCreateDataPipeOptions>(
+ in_options, kKnownFlags, out_options);
+ if (result != MOJO_RESULT_OK)
+ return result;
+
+ // Checks for fields beyond |flags|:
+
+ if (!HAS_OPTIONS_STRUCT_MEMBER(MojoCreateDataPipeOptions, element_num_bytes,
+ in_options))
+ return MOJO_RESULT_OK;
+ if (in_options->element_num_bytes == 0)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ out_options->element_num_bytes = in_options->element_num_bytes;
+
+ if (!HAS_OPTIONS_STRUCT_MEMBER(MojoCreateDataPipeOptions, capacity_num_bytes,
+ in_options) ||
+ in_options->capacity_num_bytes == 0) {
+ // Round the default capacity down to a multiple of the element size (but at
+ // least one element).
+ out_options->capacity_num_bytes = std::max(
+ static_cast<uint32_t>(kDefaultDataPipeCapacityBytes -
+ (kDefaultDataPipeCapacityBytes % out_options->element_num_bytes)),
+ out_options->element_num_bytes);
+ return MOJO_RESULT_OK;
+ }
+ if (in_options->capacity_num_bytes % out_options->element_num_bytes != 0)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (in_options->capacity_num_bytes > kMaxDataPipeCapacityBytes)
+ return MOJO_RESULT_RESOURCE_EXHAUSTED;
+ out_options->capacity_num_bytes = in_options->capacity_num_bytes;
+
+ return MOJO_RESULT_OK;
+}
+
+void DataPipe::ProducerCancelAllWaiters() {
+ base::AutoLock locker(lock_);
+ DCHECK(has_local_producer_no_lock());
+ producer_waiter_list_->CancelAllWaiters();
+}
+
+void DataPipe::ProducerClose() {
+ base::AutoLock locker(lock_);
+ DCHECK(producer_open_);
+ producer_open_ = false;
+ DCHECK(has_local_producer_no_lock());
+ producer_waiter_list_.reset();
+ // Not a bug, except possibly in "user" code.
+ DVLOG_IF(2, producer_in_two_phase_write_no_lock())
+ << "Producer closed with active two-phase write";
+ producer_two_phase_max_num_bytes_written_ = 0;
+ ProducerCloseImplNoLock();
+ AwakeConsumerWaitersForStateChangeNoLock(
+ ConsumerGetHandleSignalsStateNoLock());
+}
+
+MojoResult DataPipe::ProducerWriteData(const void* elements,
+ uint32_t* num_bytes,
+ bool all_or_none) {
+ base::AutoLock locker(lock_);
+ DCHECK(has_local_producer_no_lock());
+
+ if (producer_in_two_phase_write_no_lock())
+ return MOJO_RESULT_BUSY;
+ if (!consumer_open_no_lock())
+ return MOJO_RESULT_FAILED_PRECONDITION;
+
+ // Returning "busy" takes priority over "invalid argument".
+ if (*num_bytes % element_num_bytes_ != 0)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ if (*num_bytes == 0)
+ return MOJO_RESULT_OK; // Nothing to do.
+
+ HandleSignalsState old_consumer_state = ConsumerGetHandleSignalsStateNoLock();
+ MojoResult rv = ProducerWriteDataImplNoLock(elements, num_bytes, all_or_none);
+ HandleSignalsState new_consumer_state = ConsumerGetHandleSignalsStateNoLock();
+ if (!new_consumer_state.equals(old_consumer_state))
+ AwakeConsumerWaitersForStateChangeNoLock(new_consumer_state);
+ return rv;
+}
+
+MojoResult DataPipe::ProducerBeginWriteData(void** buffer,
+ uint32_t* buffer_num_bytes,
+ bool all_or_none) {
+ base::AutoLock locker(lock_);
+ DCHECK(has_local_producer_no_lock());
+
+ if (producer_in_two_phase_write_no_lock())
+ return MOJO_RESULT_BUSY;
+ if (!consumer_open_no_lock())
+ return MOJO_RESULT_FAILED_PRECONDITION;
+
+ if (all_or_none && *buffer_num_bytes % element_num_bytes_ != 0)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ MojoResult rv = ProducerBeginWriteDataImplNoLock(buffer, buffer_num_bytes,
+ all_or_none);
+ if (rv != MOJO_RESULT_OK)
+ return rv;
+ // Note: No need to awake producer waiters, even though we're going from
+ // writable to non-writable (since you can't wait on non-writability).
+ // Similarly, though this may have discarded data (in "may discard" mode),
+ // making it non-readable, there's still no need to awake consumer waiters.
+ DCHECK(producer_in_two_phase_write_no_lock());
+ return MOJO_RESULT_OK;
+}
+
+MojoResult DataPipe::ProducerEndWriteData(uint32_t num_bytes_written) {
+ base::AutoLock locker(lock_);
+ DCHECK(has_local_producer_no_lock());
+
+ if (!producer_in_two_phase_write_no_lock())
+ return MOJO_RESULT_FAILED_PRECONDITION;
+ // Note: Allow successful completion of the two-phase write even if the
+ // consumer has been closed.
+
+ HandleSignalsState old_consumer_state = ConsumerGetHandleSignalsStateNoLock();
+ MojoResult rv;
+ if (num_bytes_written > producer_two_phase_max_num_bytes_written_ ||
+ num_bytes_written % element_num_bytes_ != 0) {
+ rv = MOJO_RESULT_INVALID_ARGUMENT;
+ producer_two_phase_max_num_bytes_written_ = 0;
+ } else {
+ rv = ProducerEndWriteDataImplNoLock(num_bytes_written);
+ }
+ // Two-phase write ended even on failure.
+ DCHECK(!producer_in_two_phase_write_no_lock());
+ // If we're now writable, we *became* writable (since we weren't writable
+ // during the two-phase write), so awake producer waiters.
+ HandleSignalsState new_producer_state = ProducerGetHandleSignalsStateNoLock();
+ if (new_producer_state.satisfies(MOJO_HANDLE_SIGNAL_WRITABLE))
+ AwakeProducerWaitersForStateChangeNoLock(new_producer_state);
+ HandleSignalsState new_consumer_state = ConsumerGetHandleSignalsStateNoLock();
+ if (!new_consumer_state.equals(old_consumer_state))
+ AwakeConsumerWaitersForStateChangeNoLock(new_consumer_state);
+ return rv;
+}
+
+MojoResult DataPipe::ProducerAddWaiter(Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context) {
+ base::AutoLock locker(lock_);
+ DCHECK(has_local_producer_no_lock());
+
+ HandleSignalsState producer_state = ProducerGetHandleSignalsStateNoLock();
+ if (producer_state.satisfies(signals))
+ return MOJO_RESULT_ALREADY_EXISTS;
+ if (!producer_state.can_satisfy(signals))
+ return MOJO_RESULT_FAILED_PRECONDITION;
+
+ producer_waiter_list_->AddWaiter(waiter, signals, context);
+ return MOJO_RESULT_OK;
+}
+
+void DataPipe::ProducerRemoveWaiter(Waiter* waiter) {
+ base::AutoLock locker(lock_);
+ DCHECK(has_local_producer_no_lock());
+ producer_waiter_list_->RemoveWaiter(waiter);
+}
+
+bool DataPipe::ProducerIsBusy() const {
+ base::AutoLock locker(lock_);
+ return producer_in_two_phase_write_no_lock();
+}
+
+void DataPipe::ConsumerCancelAllWaiters() {
+ base::AutoLock locker(lock_);
+ DCHECK(has_local_consumer_no_lock());
+ consumer_waiter_list_->CancelAllWaiters();
+}
+
+void DataPipe::ConsumerClose() {
+ base::AutoLock locker(lock_);
+ DCHECK(consumer_open_);
+ consumer_open_ = false;
+ DCHECK(has_local_consumer_no_lock());
+ consumer_waiter_list_.reset();
+ // Not a bug, except possibly in "user" code.
+ DVLOG_IF(2, consumer_in_two_phase_read_no_lock())
+ << "Consumer closed with active two-phase read";
+ consumer_two_phase_max_num_bytes_read_ = 0;
+ ConsumerCloseImplNoLock();
+ AwakeProducerWaitersForStateChangeNoLock(
+ ProducerGetHandleSignalsStateNoLock());
+}
+
+MojoResult DataPipe::ConsumerReadData(void* elements,
+ uint32_t* num_bytes,
+ bool all_or_none) {
+ base::AutoLock locker(lock_);
+ DCHECK(has_local_consumer_no_lock());
+
+ if (consumer_in_two_phase_read_no_lock())
+ return MOJO_RESULT_BUSY;
+
+ if (*num_bytes % element_num_bytes_ != 0)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ if (*num_bytes == 0)
+ return MOJO_RESULT_OK; // Nothing to do.
+
+ HandleSignalsState old_producer_state = ProducerGetHandleSignalsStateNoLock();
+ MojoResult rv = ConsumerReadDataImplNoLock(elements, num_bytes, all_or_none);
+ HandleSignalsState new_producer_state = ProducerGetHandleSignalsStateNoLock();
+ if (!new_producer_state.equals(old_producer_state))
+ AwakeProducerWaitersForStateChangeNoLock(new_producer_state);
+ return rv;
+}
+
+MojoResult DataPipe::ConsumerDiscardData(uint32_t* num_bytes,
+ bool all_or_none) {
+ base::AutoLock locker(lock_);
+ DCHECK(has_local_consumer_no_lock());
+
+ if (consumer_in_two_phase_read_no_lock())
+ return MOJO_RESULT_BUSY;
+
+ if (*num_bytes % element_num_bytes_ != 0)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ if (*num_bytes == 0)
+ return MOJO_RESULT_OK; // Nothing to do.
+
+ HandleSignalsState old_producer_state = ProducerGetHandleSignalsStateNoLock();
+ MojoResult rv = ConsumerDiscardDataImplNoLock(num_bytes, all_or_none);
+ HandleSignalsState new_producer_state = ProducerGetHandleSignalsStateNoLock();
+ if (!new_producer_state.equals(old_producer_state))
+ AwakeProducerWaitersForStateChangeNoLock(new_producer_state);
+ return rv;
+}
+
+MojoResult DataPipe::ConsumerQueryData(uint32_t* num_bytes) {
+ base::AutoLock locker(lock_);
+ DCHECK(has_local_consumer_no_lock());
+
+ if (consumer_in_two_phase_read_no_lock())
+ return MOJO_RESULT_BUSY;
+
+ // Note: Don't need to validate |*num_bytes| for query.
+ return ConsumerQueryDataImplNoLock(num_bytes);
+}
+
+MojoResult DataPipe::ConsumerBeginReadData(const void** buffer,
+ uint32_t* buffer_num_bytes,
+ bool all_or_none) {
+ base::AutoLock locker(lock_);
+ DCHECK(has_local_consumer_no_lock());
+
+ if (consumer_in_two_phase_read_no_lock())
+ return MOJO_RESULT_BUSY;
+
+ if (all_or_none && *buffer_num_bytes % element_num_bytes_ != 0)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ MojoResult rv = ConsumerBeginReadDataImplNoLock(buffer, buffer_num_bytes,
+ all_or_none);
+ if (rv != MOJO_RESULT_OK)
+ return rv;
+ DCHECK(consumer_in_two_phase_read_no_lock());
+ return MOJO_RESULT_OK;
+}
+
+MojoResult DataPipe::ConsumerEndReadData(uint32_t num_bytes_read) {
+ base::AutoLock locker(lock_);
+ DCHECK(has_local_consumer_no_lock());
+
+ if (!consumer_in_two_phase_read_no_lock())
+ return MOJO_RESULT_FAILED_PRECONDITION;
+
+ HandleSignalsState old_producer_state = ProducerGetHandleSignalsStateNoLock();
+ MojoResult rv;
+ if (num_bytes_read > consumer_two_phase_max_num_bytes_read_ ||
+ num_bytes_read % element_num_bytes_ != 0) {
+ rv = MOJO_RESULT_INVALID_ARGUMENT;
+ consumer_two_phase_max_num_bytes_read_ = 0;
+ } else {
+ rv = ConsumerEndReadDataImplNoLock(num_bytes_read);
+ }
+ // Two-phase read ended even on failure.
+ DCHECK(!consumer_in_two_phase_read_no_lock());
+ // If we're now readable, we *became* readable (since we weren't readable
+ // during the two-phase read), so awake consumer waiters.
+ HandleSignalsState new_consumer_state = ConsumerGetHandleSignalsStateNoLock();
+ if (new_consumer_state.satisfies(MOJO_HANDLE_SIGNAL_READABLE))
+ AwakeConsumerWaitersForStateChangeNoLock(new_consumer_state);
+ HandleSignalsState new_producer_state = ProducerGetHandleSignalsStateNoLock();
+ if (!new_producer_state.equals(old_producer_state))
+ AwakeProducerWaitersForStateChangeNoLock(new_producer_state);
+ return rv;
+}
+
+MojoResult DataPipe::ConsumerAddWaiter(Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context) {
+ base::AutoLock locker(lock_);
+ DCHECK(has_local_consumer_no_lock());
+
+ HandleSignalsState consumer_state = ConsumerGetHandleSignalsStateNoLock();
+ if (consumer_state.satisfies(signals))
+ return MOJO_RESULT_ALREADY_EXISTS;
+ if (!consumer_state.can_satisfy(signals))
+ return MOJO_RESULT_FAILED_PRECONDITION;
+
+ consumer_waiter_list_->AddWaiter(waiter, signals, context);
+ return MOJO_RESULT_OK;
+}
+
+void DataPipe::ConsumerRemoveWaiter(Waiter* waiter) {
+ base::AutoLock locker(lock_);
+ DCHECK(has_local_consumer_no_lock());
+ consumer_waiter_list_->RemoveWaiter(waiter);
+}
+
+bool DataPipe::ConsumerIsBusy() const {
+ base::AutoLock locker(lock_);
+ return consumer_in_two_phase_read_no_lock();
+}
+
+
+DataPipe::DataPipe(bool has_local_producer,
+ bool has_local_consumer,
+ const MojoCreateDataPipeOptions& validated_options)
+ : may_discard_((validated_options.flags &
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD)),
+ element_num_bytes_(validated_options.element_num_bytes),
+ capacity_num_bytes_(validated_options.capacity_num_bytes),
+ producer_open_(true),
+ consumer_open_(true),
+ producer_waiter_list_(has_local_producer ? new WaiterList() : NULL),
+ consumer_waiter_list_(has_local_consumer ? new WaiterList() : NULL),
+ producer_two_phase_max_num_bytes_written_(0),
+ consumer_two_phase_max_num_bytes_read_(0) {
+ // Check that the passed in options actually are validated.
+ MojoCreateDataPipeOptions unused ALLOW_UNUSED = { 0 };
+ DCHECK_EQ(ValidateCreateOptions(&validated_options, &unused), MOJO_RESULT_OK);
+}
+
+DataPipe::~DataPipe() {
+ DCHECK(!producer_open_);
+ DCHECK(!consumer_open_);
+ DCHECK(!producer_waiter_list_);
+ DCHECK(!consumer_waiter_list_);
+}
+
+void DataPipe::AwakeProducerWaitersForStateChangeNoLock(
+ const HandleSignalsState& new_producer_state) {
+ lock_.AssertAcquired();
+ if (!has_local_producer_no_lock())
+ return;
+ producer_waiter_list_->AwakeWaitersForStateChange(new_producer_state);
+}
+
+void DataPipe::AwakeConsumerWaitersForStateChangeNoLock(
+ const HandleSignalsState& new_consumer_state) {
+ lock_.AssertAcquired();
+ if (!has_local_consumer_no_lock())
+ return;
+ consumer_waiter_list_->AwakeWaitersForStateChange(new_consumer_state);
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/data_pipe.h b/chromium/mojo/system/data_pipe.h
new file mode 100644
index 00000000000..2cab507e462
--- /dev/null
+++ b/chromium/mojo/system/data_pipe.h
@@ -0,0 +1,205 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_DATA_PIPE_H_
+#define MOJO_SYSTEM_DATA_PIPE_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/system/handle_signals_state.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class Waiter;
+class WaiterList;
+
+// |DataPipe| is a base class for secondary objects implementing data pipes,
+// similar to |MessagePipe| (see the explanatory comment in core.cc). It is
+// typically owned by the dispatcher(s) corresponding to the local endpoints.
+// Its subclasses implement the three cases: local producer and consumer, local
+// producer and remote consumer, and remote producer and local consumer. This
+// class is thread-safe.
+class MOJO_SYSTEM_IMPL_EXPORT DataPipe :
+ public base::RefCountedThreadSafe<DataPipe> {
+ public:
+ // The default options for |MojoCreateDataPipe()|. (Real uses should obtain
+ // this via |ValidateCreateOptions()| with a null |in_options|; this is
+ // exposed directly for testing convenience.)
+ static const MojoCreateDataPipeOptions kDefaultCreateOptions;
+
+ // Validates and/or sets default options for |MojoCreateDataPipeOptions|. If
+ // non-null, |in_options| must point to a struct of at least
+ // |in_options->struct_size| bytes. |out_options| must point to a (current)
+ // |MojoCreateDataPipeOptions| and will be entirely overwritten on success (it
+ // may be partly overwritten on failure).
+ static MojoResult ValidateCreateOptions(
+ const MojoCreateDataPipeOptions* in_options,
+ MojoCreateDataPipeOptions* out_options);
+
+ // These are called by the producer dispatcher to implement its methods of
+ // corresponding names.
+ void ProducerCancelAllWaiters();
+ void ProducerClose();
+ // This does not validate its arguments, except to check that |*num_bytes| is
+ // a multiple of |element_num_bytes_|.
+ MojoResult ProducerWriteData(const void* elements,
+ uint32_t* num_bytes,
+ bool all_or_none);
+ // This does not validate its arguments.
+ MojoResult ProducerBeginWriteData(void** buffer,
+ uint32_t* buffer_num_bytes,
+ bool all_or_none);
+ MojoResult ProducerEndWriteData(uint32_t num_bytes_written);
+ MojoResult ProducerAddWaiter(Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context);
+ void ProducerRemoveWaiter(Waiter* waiter);
+ bool ProducerIsBusy() const;
+
+ // These are called by the consumer dispatcher to implement its methods of
+ // corresponding names.
+ void ConsumerCancelAllWaiters();
+ void ConsumerClose();
+ // This does not validate its arguments, except to check that |*num_bytes| is
+ // a multiple of |element_num_bytes_|.
+ MojoResult ConsumerReadData(void* elements,
+ uint32_t* num_bytes,
+ bool all_or_none);
+ MojoResult ConsumerDiscardData(uint32_t* num_bytes,
+ bool all_or_none);
+ MojoResult ConsumerQueryData(uint32_t* num_bytes);
+ // This does not validate its arguments.
+ MojoResult ConsumerBeginReadData(const void** buffer,
+ uint32_t* buffer_num_bytes,
+ bool all_or_none);
+ MojoResult ConsumerEndReadData(uint32_t num_bytes_read);
+ MojoResult ConsumerAddWaiter(Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context);
+ void ConsumerRemoveWaiter(Waiter* waiter);
+ bool ConsumerIsBusy() const;
+
+ protected:
+ DataPipe(bool has_local_producer,
+ bool has_local_consumer,
+ const MojoCreateDataPipeOptions& validated_options);
+
+ friend class base::RefCountedThreadSafe<DataPipe>;
+ virtual ~DataPipe();
+
+ virtual void ProducerCloseImplNoLock() = 0;
+ // |*num_bytes| will be a nonzero multiple of |element_num_bytes_|.
+ virtual MojoResult ProducerWriteDataImplNoLock(const void* elements,
+ uint32_t* num_bytes,
+ bool all_or_none) = 0;
+ virtual MojoResult ProducerBeginWriteDataImplNoLock(
+ void** buffer,
+ uint32_t* buffer_num_bytes,
+ bool all_or_none) = 0;
+ virtual MojoResult ProducerEndWriteDataImplNoLock(
+ uint32_t num_bytes_written) = 0;
+ // Note: A producer should not be writable during a two-phase write.
+ virtual HandleSignalsState ProducerGetHandleSignalsStateNoLock() const = 0;
+
+ virtual void ConsumerCloseImplNoLock() = 0;
+ // |*num_bytes| will be a nonzero multiple of |element_num_bytes_|.
+ virtual MojoResult ConsumerReadDataImplNoLock(void* elements,
+ uint32_t* num_bytes,
+ bool all_or_none) = 0;
+ virtual MojoResult ConsumerDiscardDataImplNoLock(uint32_t* num_bytes,
+ bool all_or_none) = 0;
+ // |*num_bytes| will be a nonzero multiple of |element_num_bytes_|.
+ virtual MojoResult ConsumerQueryDataImplNoLock(uint32_t* num_bytes) = 0;
+ virtual MojoResult ConsumerBeginReadDataImplNoLock(const void** buffer,
+ uint32_t* buffer_num_bytes,
+ bool all_or_none) = 0;
+ virtual MojoResult ConsumerEndReadDataImplNoLock(uint32_t num_bytes_read) = 0;
+ // Note: A consumer should not be writable during a two-phase read.
+ virtual HandleSignalsState ConsumerGetHandleSignalsStateNoLock() const = 0;
+
+ // Thread-safe and fast (they don't take the lock):
+ bool may_discard() const { return may_discard_; }
+ size_t element_num_bytes() const { return element_num_bytes_; }
+ size_t capacity_num_bytes() const { return capacity_num_bytes_; }
+
+ // Must be called under lock.
+ bool producer_open_no_lock() const {
+ lock_.AssertAcquired();
+ return producer_open_;
+ }
+ bool consumer_open_no_lock() const {
+ lock_.AssertAcquired();
+ return consumer_open_;
+ }
+ uint32_t producer_two_phase_max_num_bytes_written_no_lock() const {
+ lock_.AssertAcquired();
+ return producer_two_phase_max_num_bytes_written_;
+ }
+ uint32_t consumer_two_phase_max_num_bytes_read_no_lock() const {
+ lock_.AssertAcquired();
+ return consumer_two_phase_max_num_bytes_read_;
+ }
+ void set_producer_two_phase_max_num_bytes_written_no_lock(
+ uint32_t num_bytes) {
+ lock_.AssertAcquired();
+ producer_two_phase_max_num_bytes_written_ = num_bytes;
+ }
+ void set_consumer_two_phase_max_num_bytes_read_no_lock(uint32_t num_bytes) {
+ lock_.AssertAcquired();
+ consumer_two_phase_max_num_bytes_read_ = num_bytes;
+ }
+ bool producer_in_two_phase_write_no_lock() const {
+ lock_.AssertAcquired();
+ return producer_two_phase_max_num_bytes_written_ > 0;
+ }
+ bool consumer_in_two_phase_read_no_lock() const {
+ lock_.AssertAcquired();
+ return consumer_two_phase_max_num_bytes_read_ > 0;
+ }
+
+ private:
+ void AwakeProducerWaitersForStateChangeNoLock(
+ const HandleSignalsState& new_producer_state);
+ void AwakeConsumerWaitersForStateChangeNoLock(
+ const HandleSignalsState& new_consumer_state);
+
+ bool has_local_producer_no_lock() const {
+ lock_.AssertAcquired();
+ return !!producer_waiter_list_;
+ }
+ bool has_local_consumer_no_lock() const {
+ lock_.AssertAcquired();
+ return !!consumer_waiter_list_;
+ }
+
+ const bool may_discard_;
+ const size_t element_num_bytes_;
+ const size_t capacity_num_bytes_;
+
+ mutable base::Lock lock_; // Protects the following members.
+ // *Known* state of producer or consumer.
+ bool producer_open_;
+ bool consumer_open_;
+ // Non-null only if the producer or consumer, respectively, is local.
+ scoped_ptr<WaiterList> producer_waiter_list_;
+ scoped_ptr<WaiterList> consumer_waiter_list_;
+ // These are nonzero if and only if a two-phase write/read is in progress.
+ uint32_t producer_two_phase_max_num_bytes_written_;
+ uint32_t consumer_two_phase_max_num_bytes_read_;
+
+ DISALLOW_COPY_AND_ASSIGN(DataPipe);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_DATA_PIPE_H_
diff --git a/chromium/mojo/system/data_pipe_consumer_dispatcher.cc b/chromium/mojo/system/data_pipe_consumer_dispatcher.cc
new file mode 100644
index 00000000000..99818e2a120
--- /dev/null
+++ b/chromium/mojo/system/data_pipe_consumer_dispatcher.cc
@@ -0,0 +1,130 @@
+// Copyright 2013 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 "mojo/system/data_pipe_consumer_dispatcher.h"
+
+#include "base/logging.h"
+#include "mojo/system/data_pipe.h"
+#include "mojo/system/memory.h"
+
+namespace mojo {
+namespace system {
+
+DataPipeConsumerDispatcher::DataPipeConsumerDispatcher() {
+}
+
+void DataPipeConsumerDispatcher::Init(scoped_refptr<DataPipe> data_pipe) {
+ DCHECK(data_pipe);
+ data_pipe_ = data_pipe;
+}
+
+Dispatcher::Type DataPipeConsumerDispatcher::GetType() const {
+ return kTypeDataPipeConsumer;
+}
+
+DataPipeConsumerDispatcher::~DataPipeConsumerDispatcher() {
+ // |Close()|/|CloseImplNoLock()| should have taken care of the pipe.
+ DCHECK(!data_pipe_);
+}
+
+void DataPipeConsumerDispatcher::CancelAllWaitersNoLock() {
+ lock().AssertAcquired();
+ data_pipe_->ConsumerCancelAllWaiters();
+}
+
+void DataPipeConsumerDispatcher::CloseImplNoLock() {
+ lock().AssertAcquired();
+ data_pipe_->ConsumerClose();
+ data_pipe_ = NULL;
+}
+
+scoped_refptr<Dispatcher>
+DataPipeConsumerDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() {
+ lock().AssertAcquired();
+
+ scoped_refptr<DataPipeConsumerDispatcher> rv =
+ new DataPipeConsumerDispatcher();
+ rv->Init(data_pipe_);
+ data_pipe_ = NULL;
+ return scoped_refptr<Dispatcher>(rv.get());
+}
+
+MojoResult DataPipeConsumerDispatcher::ReadDataImplNoLock(
+ void* elements,
+ uint32_t* num_bytes,
+ MojoReadDataFlags flags) {
+ lock().AssertAcquired();
+
+ if (!VerifyUserPointer<uint32_t>(num_bytes))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ if ((flags & MOJO_READ_DATA_FLAG_DISCARD)) {
+ // These flags are mutally exclusive.
+ if ((flags & MOJO_READ_DATA_FLAG_QUERY))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ DVLOG_IF(2, elements) << "Discard mode: ignoring non-null |elements|";
+ return data_pipe_->ConsumerDiscardData(
+ num_bytes, (flags & MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+ }
+
+ if ((flags & MOJO_READ_DATA_FLAG_QUERY)) {
+ DCHECK(!(flags & MOJO_READ_DATA_FLAG_DISCARD)); // Handled above.
+ DVLOG_IF(2, elements) << "Query mode: ignoring non-null |elements|";
+ return data_pipe_->ConsumerQueryData(num_bytes);
+ }
+
+ // Only verify |elements| if we're neither discarding nor querying.
+ if (!VerifyUserPointerWithSize<1>(elements, *num_bytes))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return data_pipe_->ConsumerReadData(
+ elements, num_bytes, (flags & MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+}
+
+MojoResult DataPipeConsumerDispatcher::BeginReadDataImplNoLock(
+ const void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoReadDataFlags flags) {
+ lock().AssertAcquired();
+
+ if (!VerifyUserPointerWithCount<const void*>(buffer, 1))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (!VerifyUserPointer<uint32_t>(buffer_num_bytes))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ // These flags may not be used in two-phase mode.
+ if ((flags & MOJO_READ_DATA_FLAG_DISCARD) ||
+ (flags & MOJO_READ_DATA_FLAG_QUERY))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return data_pipe_->ConsumerBeginReadData(
+ buffer, buffer_num_bytes, (flags & MOJO_READ_DATA_FLAG_ALL_OR_NONE));
+}
+
+MojoResult DataPipeConsumerDispatcher::EndReadDataImplNoLock(
+ uint32_t num_bytes_read) {
+ lock().AssertAcquired();
+
+ return data_pipe_->ConsumerEndReadData(num_bytes_read);
+}
+
+MojoResult DataPipeConsumerDispatcher::AddWaiterImplNoLock(
+ Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context) {
+ lock().AssertAcquired();
+ return data_pipe_->ConsumerAddWaiter(waiter, signals, context);
+}
+
+void DataPipeConsumerDispatcher::RemoveWaiterImplNoLock(Waiter* waiter) {
+ lock().AssertAcquired();
+ data_pipe_->ConsumerRemoveWaiter(waiter);
+}
+
+bool DataPipeConsumerDispatcher::IsBusyNoLock() const {
+ lock().AssertAcquired();
+ return data_pipe_->ConsumerIsBusy();
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/data_pipe_consumer_dispatcher.h b/chromium/mojo/system/data_pipe_consumer_dispatcher.h
new file mode 100644
index 00000000000..390d0bfe917
--- /dev/null
+++ b/chromium/mojo/system/data_pipe_consumer_dispatcher.h
@@ -0,0 +1,62 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_DATA_PIPE_CONSUMER_DISPATCHER_H_
+#define MOJO_SYSTEM_DATA_PIPE_CONSUMER_DISPATCHER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/system/dispatcher.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class DataPipe;
+
+// This is the |Dispatcher| implementation for the consumer handle for data
+// pipes (created by the Mojo primitive |MojoCreateDataPipe()|). This class is
+// thread-safe.
+class MOJO_SYSTEM_IMPL_EXPORT DataPipeConsumerDispatcher : public Dispatcher {
+ public:
+ DataPipeConsumerDispatcher();
+
+ // Must be called before any other methods.
+ void Init(scoped_refptr<DataPipe> data_pipe);
+
+ // |Dispatcher| public methods:
+ virtual Type GetType() const OVERRIDE;
+
+ private:
+ virtual ~DataPipeConsumerDispatcher();
+
+ // |Dispatcher| protected methods:
+ virtual void CancelAllWaitersNoLock() OVERRIDE;
+ virtual void CloseImplNoLock() OVERRIDE;
+ virtual scoped_refptr<Dispatcher>
+ CreateEquivalentDispatcherAndCloseImplNoLock() OVERRIDE;
+ virtual MojoResult ReadDataImplNoLock(void* elements,
+ uint32_t* num_bytes,
+ MojoReadDataFlags flags) OVERRIDE;
+ virtual MojoResult BeginReadDataImplNoLock(const void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoReadDataFlags flags) OVERRIDE;
+ virtual MojoResult EndReadDataImplNoLock(uint32_t num_bytes_read) OVERRIDE;
+ virtual MojoResult AddWaiterImplNoLock(Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context) OVERRIDE;
+ virtual void RemoveWaiterImplNoLock(Waiter* waiter) OVERRIDE;
+ virtual bool IsBusyNoLock() const OVERRIDE;
+
+ // Protected by |lock()|:
+ scoped_refptr<DataPipe> data_pipe_; // This will be null if closed.
+
+ DISALLOW_COPY_AND_ASSIGN(DataPipeConsumerDispatcher);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_DATA_PIPE_CONSUMER_DISPATCHER_H_
diff --git a/chromium/mojo/system/data_pipe_producer_dispatcher.cc b/chromium/mojo/system/data_pipe_producer_dispatcher.cc
new file mode 100644
index 00000000000..ec99549b543
--- /dev/null
+++ b/chromium/mojo/system/data_pipe_producer_dispatcher.cc
@@ -0,0 +1,109 @@
+// Copyright 2013 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 "mojo/system/data_pipe_producer_dispatcher.h"
+
+#include "base/logging.h"
+#include "mojo/system/data_pipe.h"
+#include "mojo/system/memory.h"
+
+namespace mojo {
+namespace system {
+
+DataPipeProducerDispatcher::DataPipeProducerDispatcher() {
+}
+
+void DataPipeProducerDispatcher::Init(scoped_refptr<DataPipe> data_pipe) {
+ DCHECK(data_pipe);
+ data_pipe_ = data_pipe;
+}
+
+Dispatcher::Type DataPipeProducerDispatcher::GetType() const {
+ return kTypeDataPipeProducer;
+}
+
+DataPipeProducerDispatcher::~DataPipeProducerDispatcher() {
+ // |Close()|/|CloseImplNoLock()| should have taken care of the pipe.
+ DCHECK(!data_pipe_);
+}
+
+void DataPipeProducerDispatcher::CancelAllWaitersNoLock() {
+ lock().AssertAcquired();
+ data_pipe_->ProducerCancelAllWaiters();
+}
+
+void DataPipeProducerDispatcher::CloseImplNoLock() {
+ lock().AssertAcquired();
+ data_pipe_->ProducerClose();
+ data_pipe_ = NULL;
+}
+
+scoped_refptr<Dispatcher>
+DataPipeProducerDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() {
+ lock().AssertAcquired();
+
+ scoped_refptr<DataPipeProducerDispatcher> rv =
+ new DataPipeProducerDispatcher();
+ rv->Init(data_pipe_);
+ data_pipe_ = NULL;
+ return scoped_refptr<Dispatcher>(rv.get());
+}
+
+MojoResult DataPipeProducerDispatcher::WriteDataImplNoLock(
+ const void* elements,
+ uint32_t* num_bytes,
+ MojoWriteDataFlags flags) {
+ lock().AssertAcquired();
+
+ if (!VerifyUserPointer<uint32_t>(num_bytes))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (!VerifyUserPointerWithSize<1>(elements, *num_bytes))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return data_pipe_->ProducerWriteData(
+ elements, num_bytes, (flags & MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+}
+
+MojoResult DataPipeProducerDispatcher::BeginWriteDataImplNoLock(
+ void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoWriteDataFlags flags) {
+ lock().AssertAcquired();
+
+ if (!VerifyUserPointerWithCount<void*>(buffer, 1))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (!VerifyUserPointer<uint32_t>(buffer_num_bytes))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return data_pipe_->ProducerBeginWriteData(
+ buffer, buffer_num_bytes, (flags & MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
+}
+
+MojoResult DataPipeProducerDispatcher::EndWriteDataImplNoLock(
+ uint32_t num_bytes_written) {
+ lock().AssertAcquired();
+
+ return data_pipe_->ProducerEndWriteData(num_bytes_written);
+}
+
+MojoResult DataPipeProducerDispatcher::AddWaiterImplNoLock(
+ Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context) {
+ lock().AssertAcquired();
+ return data_pipe_->ProducerAddWaiter(waiter, signals, context);
+}
+
+void DataPipeProducerDispatcher::RemoveWaiterImplNoLock(Waiter* waiter) {
+ lock().AssertAcquired();
+ data_pipe_->ProducerRemoveWaiter(waiter);
+}
+
+bool DataPipeProducerDispatcher::IsBusyNoLock() const {
+ lock().AssertAcquired();
+ return data_pipe_->ProducerIsBusy();
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/data_pipe_producer_dispatcher.h b/chromium/mojo/system/data_pipe_producer_dispatcher.h
new file mode 100644
index 00000000000..63ad3184c50
--- /dev/null
+++ b/chromium/mojo/system/data_pipe_producer_dispatcher.h
@@ -0,0 +1,64 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_DATA_PIPE_PRODUCER_DISPATCHER_H_
+#define MOJO_SYSTEM_DATA_PIPE_PRODUCER_DISPATCHER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/system/dispatcher.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class DataPipe;
+
+// This is the |Dispatcher| implementation for the producer handle for data
+// pipes (created by the Mojo primitive |MojoCreateDataPipe()|). This class is
+// thread-safe.
+class MOJO_SYSTEM_IMPL_EXPORT DataPipeProducerDispatcher : public Dispatcher {
+ public:
+ DataPipeProducerDispatcher();
+
+ // Must be called before any other methods.
+ void Init(scoped_refptr<DataPipe> data_pipe);
+
+ // |Dispatcher| public methods:
+ virtual Type GetType() const OVERRIDE;
+
+ private:
+ virtual ~DataPipeProducerDispatcher();
+
+ // |Dispatcher| protected methods:
+ virtual void CancelAllWaitersNoLock() OVERRIDE;
+ virtual void CloseImplNoLock() OVERRIDE;
+ virtual scoped_refptr<Dispatcher>
+ CreateEquivalentDispatcherAndCloseImplNoLock() OVERRIDE;
+ virtual MojoResult WriteDataImplNoLock(const void* elements,
+ uint32_t* num_bytes,
+ MojoWriteDataFlags flags) OVERRIDE;
+ virtual MojoResult BeginWriteDataImplNoLock(
+ void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoWriteDataFlags flags) OVERRIDE;
+ virtual MojoResult EndWriteDataImplNoLock(
+ uint32_t num_bytes_written) OVERRIDE;
+ virtual MojoResult AddWaiterImplNoLock(Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context) OVERRIDE;
+ virtual void RemoveWaiterImplNoLock(Waiter* waiter) OVERRIDE;
+ virtual bool IsBusyNoLock() const OVERRIDE;
+
+ // Protected by |lock()|:
+ scoped_refptr<DataPipe> data_pipe_; // This will be null if closed.
+
+ DISALLOW_COPY_AND_ASSIGN(DataPipeProducerDispatcher);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_DATA_PIPE_PRODUCER_DISPATCHER_H_
diff --git a/chromium/mojo/system/data_pipe_unittest.cc b/chromium/mojo/system/data_pipe_unittest.cc
new file mode 100644
index 00000000000..6aa2ce786d0
--- /dev/null
+++ b/chromium/mojo/system/data_pipe_unittest.cc
@@ -0,0 +1,360 @@
+// Copyright 2013 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 "mojo/system/data_pipe.h"
+
+#include <stddef.h>
+
+#include <limits>
+
+#include "base/basictypes.h"
+#include "mojo/system/constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+const uint32_t kSizeOfCreateOptions =
+ static_cast<uint32_t>(sizeof(MojoCreateDataPipeOptions));
+
+// Does a cursory sanity check of |validated_options|. Calls
+// |ValidateCreateOptions()| on already-validated options. The validated options
+// should be valid, and the revalidated copy should be the same.
+void RevalidateCreateOptions(
+ const MojoCreateDataPipeOptions& validated_options) {
+ EXPECT_EQ(kSizeOfCreateOptions, validated_options.struct_size);
+ // Nothing to check for flags.
+ EXPECT_GT(validated_options.element_num_bytes, 0u);
+ EXPECT_GT(validated_options.capacity_num_bytes, 0u);
+ EXPECT_EQ(0u,
+ validated_options.capacity_num_bytes %
+ validated_options.element_num_bytes);
+
+ MojoCreateDataPipeOptions revalidated_options = {};
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&validated_options,
+ &revalidated_options));
+ EXPECT_EQ(validated_options.struct_size, revalidated_options.struct_size);
+ EXPECT_EQ(validated_options.element_num_bytes,
+ revalidated_options.element_num_bytes);
+ EXPECT_EQ(validated_options.capacity_num_bytes,
+ revalidated_options.capacity_num_bytes);
+ EXPECT_EQ(validated_options.flags, revalidated_options.flags);
+}
+
+// Checks that a default-computed capacity is correct. (Does not duplicate the
+// checks done by |RevalidateCreateOptions()|.)
+void CheckDefaultCapacity(const MojoCreateDataPipeOptions& validated_options) {
+ EXPECT_LE(validated_options.capacity_num_bytes,
+ kDefaultDataPipeCapacityBytes);
+ EXPECT_GT(validated_options.capacity_num_bytes +
+ validated_options.element_num_bytes,
+ kDefaultDataPipeCapacityBytes);
+}
+
+// Tests valid inputs to |ValidateCreateOptions()|.
+TEST(DataPipeTest, ValidateCreateOptionsValid) {
+ // Default options.
+ {
+ MojoCreateDataPipeOptions validated_options = {};
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(NULL, &validated_options));
+ RevalidateCreateOptions(validated_options);
+ CheckDefaultCapacity(validated_options);
+ }
+
+ // Size member, but nothing beyond.
+ {
+ MojoCreateDataPipeOptions options = {
+ offsetof(MojoCreateDataPipeOptions, flags) // |struct_size|.
+ };
+ MojoCreateDataPipeOptions validated_options = {};
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+ RevalidateCreateOptions(validated_options);
+ CheckDefaultCapacity(validated_options);
+ }
+
+ // Different flags.
+ MojoCreateDataPipeOptionsFlags flags_values[] = {
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD
+ };
+ for (size_t i = 0; i < arraysize(flags_values); i++) {
+ const MojoCreateDataPipeOptionsFlags flags = flags_values[i];
+
+ // Flags member, but nothing beyond.
+ {
+ MojoCreateDataPipeOptions options = {
+ // |struct_size|.
+ offsetof(MojoCreateDataPipeOptions, element_num_bytes),
+ flags // |flags|.
+ };
+ MojoCreateDataPipeOptions validated_options = {};
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+ RevalidateCreateOptions(validated_options);
+ EXPECT_EQ(options.flags, validated_options.flags);
+ CheckDefaultCapacity(validated_options);
+ }
+
+ // Different capacities (size 1).
+ for (uint32_t capacity = 1; capacity <= 100 * 1000 * 1000; capacity *= 10) {
+ MojoCreateDataPipeOptions options = {
+ kSizeOfCreateOptions, // |struct_size|.
+ flags, // |flags|.
+ 1, // |element_num_bytes|.
+ capacity // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = {};
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options))
+ << capacity;
+ RevalidateCreateOptions(validated_options);
+ EXPECT_EQ(options.flags, validated_options.flags);
+ EXPECT_EQ(options.element_num_bytes,
+ validated_options.element_num_bytes);
+ EXPECT_EQ(options.capacity_num_bytes,
+ validated_options.capacity_num_bytes);
+ }
+
+ // Small sizes.
+ for (uint32_t size = 1; size < 100; size++) {
+ // Different capacities.
+ for (uint32_t elements = 1; elements <= 1000 * 1000; elements *= 10) {
+ MojoCreateDataPipeOptions options = {
+ kSizeOfCreateOptions, // |struct_size|.
+ flags, // |flags|.
+ size, // |element_num_bytes|.
+ size * elements // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = {};
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options))
+ << size << ", " << elements;
+ RevalidateCreateOptions(validated_options);
+ EXPECT_EQ(options.flags, validated_options.flags);
+ EXPECT_EQ(options.element_num_bytes,
+ validated_options.element_num_bytes);
+ EXPECT_EQ(options.capacity_num_bytes,
+ validated_options.capacity_num_bytes);
+ }
+
+ // Default capacity.
+ {
+ MojoCreateDataPipeOptions options = {
+ kSizeOfCreateOptions, // |struct_size|.
+ flags, // |flags|.
+ size, // |element_num_bytes|.
+ 0 // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = {};
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options))
+ << size;
+ RevalidateCreateOptions(validated_options);
+ EXPECT_EQ(options.flags, validated_options.flags);
+ EXPECT_EQ(options.element_num_bytes,
+ validated_options.element_num_bytes);
+ CheckDefaultCapacity(validated_options);
+ }
+
+ // No capacity field.
+ {
+ MojoCreateDataPipeOptions options = {
+ // |struct_size|.
+ offsetof(MojoCreateDataPipeOptions, capacity_num_bytes),
+ flags, // |flags|.
+ size // |element_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = {};
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options))
+ << size;
+ RevalidateCreateOptions(validated_options);
+ EXPECT_EQ(options.flags, validated_options.flags);
+ EXPECT_EQ(options.element_num_bytes,
+ validated_options.element_num_bytes);
+ CheckDefaultCapacity(validated_options);
+ }
+ }
+
+ // Larger sizes.
+ for (uint32_t size = 100; size <= 100 * 1000; size *= 10) {
+ // Capacity of 1000 elements.
+ {
+ MojoCreateDataPipeOptions options = {
+ kSizeOfCreateOptions, // |struct_size|.
+ flags, // |flags|.
+ size, // |element_num_bytes|.
+ 1000 * size // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = {};
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options))
+ << size;
+ RevalidateCreateOptions(validated_options);
+ EXPECT_EQ(options.flags, validated_options.flags);
+ EXPECT_EQ(options.element_num_bytes,
+ validated_options.element_num_bytes);
+ EXPECT_EQ(options.capacity_num_bytes,
+ validated_options.capacity_num_bytes);
+ }
+
+ // Default capacity.
+ {
+ MojoCreateDataPipeOptions options = {
+ kSizeOfCreateOptions, // |struct_size|.
+ flags, // |flags|.
+ size, // |element_num_bytes|.
+ 0 // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = {};
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options))
+ << size;
+ RevalidateCreateOptions(validated_options);
+ EXPECT_EQ(options.flags, validated_options.flags);
+ EXPECT_EQ(options.element_num_bytes,
+ validated_options.element_num_bytes);
+ CheckDefaultCapacity(validated_options);
+ }
+
+ // No capacity field.
+ {
+ MojoCreateDataPipeOptions options = {
+ // |struct_size|.
+ offsetof(MojoCreateDataPipeOptions, capacity_num_bytes),
+ flags, // |flags|.
+ size // |element_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = {};
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options))
+ << size;
+ RevalidateCreateOptions(validated_options);
+ EXPECT_EQ(options.flags, validated_options.flags);
+ EXPECT_EQ(options.element_num_bytes,
+ validated_options.element_num_bytes);
+ CheckDefaultCapacity(validated_options);
+ }
+ }
+ }
+}
+
+TEST(DataPipeTest, ValidateCreateOptionsInvalid) {
+ // Invalid |struct_size|.
+ {
+ MojoCreateDataPipeOptions options = {
+ 1, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ 1, // |element_num_bytes|.
+ 0 // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions unused;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ DataPipe::ValidateCreateOptions(&options, &unused));
+ }
+
+ // Unknown |flags|.
+ {
+ MojoCreateDataPipeOptions options = {
+ kSizeOfCreateOptions, // |struct_size|.
+ ~0u, // |flags|.
+ 1, // |element_num_bytes|.
+ 0 // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions unused;
+ EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+ DataPipe::ValidateCreateOptions(&options, &unused));
+ }
+
+ // Invalid |element_num_bytes|.
+ {
+ MojoCreateDataPipeOptions options = {
+ kSizeOfCreateOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ 0, // |element_num_bytes|.
+ 1000 // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions unused;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ DataPipe::ValidateCreateOptions(&options, &unused));
+ }
+ // |element_num_bytes| too big.
+ {
+ MojoCreateDataPipeOptions options = {
+ kSizeOfCreateOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ std::numeric_limits<uint32_t>::max(), // |element_num_bytes|.
+ std::numeric_limits<uint32_t>::max() // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions unused;
+ EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+ DataPipe::ValidateCreateOptions(&options, &unused));
+ }
+ {
+ MojoCreateDataPipeOptions options = {
+ kSizeOfCreateOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ std::numeric_limits<uint32_t>::max() - 1000, // |element_num_bytes|.
+ std::numeric_limits<uint32_t>::max() - 1000 // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions unused;
+ EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+ DataPipe::ValidateCreateOptions(&options, &unused));
+ }
+
+ // Invalid |capacity_num_bytes|.
+ {
+ MojoCreateDataPipeOptions options = {
+ kSizeOfCreateOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ 2, // |element_num_bytes|.
+ 1 // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions unused;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ DataPipe::ValidateCreateOptions(&options, &unused));
+ }
+ {
+ MojoCreateDataPipeOptions options = {
+ kSizeOfCreateOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ 2, // |element_num_bytes|.
+ 111 // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions unused;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ DataPipe::ValidateCreateOptions(&options, &unused));
+ }
+ {
+ MojoCreateDataPipeOptions options = {
+ kSizeOfCreateOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ 5, // |element_num_bytes|.
+ 104 // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions unused;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ DataPipe::ValidateCreateOptions(&options, &unused));
+ }
+ // |capacity_num_bytes| too big.
+ {
+ MojoCreateDataPipeOptions options = {
+ kSizeOfCreateOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ 8, // |element_num_bytes|.
+ 0xffff0000 // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions unused;
+ EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+ DataPipe::ValidateCreateOptions(&options, &unused));
+ }
+}
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/dispatcher.cc b/chromium/mojo/system/dispatcher.cc
new file mode 100644
index 00000000000..d03fca71ab1
--- /dev/null
+++ b/chromium/mojo/system/dispatcher.cc
@@ -0,0 +1,461 @@
+// Copyright 2013 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 "mojo/system/dispatcher.h"
+
+#include "base/logging.h"
+#include "mojo/system/constants.h"
+#include "mojo/system/message_pipe_dispatcher.h"
+#include "mojo/system/platform_handle_dispatcher.h"
+#include "mojo/system/shared_buffer_dispatcher.h"
+
+namespace mojo {
+namespace system {
+
+namespace test {
+
+// TODO(vtl): Maybe this should be defined in a test-only file instead.
+DispatcherTransport DispatcherTryStartTransport(
+ Dispatcher* dispatcher) {
+ return Dispatcher::HandleTableAccess::TryStartTransport(dispatcher);
+}
+
+} // namespace test
+
+// Dispatcher ------------------------------------------------------------------
+
+// static
+DispatcherTransport Dispatcher::HandleTableAccess::TryStartTransport(
+ Dispatcher* dispatcher) {
+ DCHECK(dispatcher);
+
+ if (!dispatcher->lock_.Try())
+ return DispatcherTransport();
+
+ // We shouldn't race with things that close dispatchers, since closing can
+ // only take place either under |handle_table_lock_| or when the handle is
+ // marked as busy.
+ DCHECK(!dispatcher->is_closed_);
+
+ return DispatcherTransport(dispatcher);
+}
+
+// static
+void Dispatcher::TransportDataAccess::StartSerialize(
+ Dispatcher* dispatcher,
+ Channel* channel,
+ size_t* max_size,
+ size_t* max_platform_handles) {
+ DCHECK(dispatcher);
+ dispatcher->StartSerialize(channel, max_size, max_platform_handles);
+}
+
+// static
+bool Dispatcher::TransportDataAccess::EndSerializeAndClose(
+ Dispatcher* dispatcher,
+ Channel* channel,
+ void* destination,
+ size_t* actual_size,
+ embedder::PlatformHandleVector* platform_handles) {
+ DCHECK(dispatcher);
+ return dispatcher->EndSerializeAndClose(channel, destination, actual_size,
+ platform_handles);
+}
+
+// static
+scoped_refptr<Dispatcher> Dispatcher::TransportDataAccess::Deserialize(
+ Channel* channel,
+ int32_t type,
+ const void* source,
+ size_t size,
+ embedder::PlatformHandleVector* platform_handles) {
+ switch (static_cast<int32_t>(type)) {
+ case kTypeUnknown:
+ DVLOG(2) << "Deserializing invalid handle";
+ return scoped_refptr<Dispatcher>();
+ case kTypeMessagePipe:
+ return scoped_refptr<Dispatcher>(
+ MessagePipeDispatcher::Deserialize(channel, source, size));
+ case kTypeDataPipeProducer:
+ case kTypeDataPipeConsumer:
+ // TODO(vtl): Implement.
+ LOG(WARNING) << "Deserialization of dispatcher type " << type
+ << " not supported";
+ return scoped_refptr<Dispatcher>();
+ case kTypeSharedBuffer:
+ return scoped_refptr<Dispatcher>(
+ SharedBufferDispatcher::Deserialize(channel, source, size,
+ platform_handles));
+ case kTypePlatformHandle:
+ return scoped_refptr<Dispatcher>(
+ PlatformHandleDispatcher::Deserialize(channel, source, size,
+ platform_handles));
+ }
+ LOG(WARNING) << "Unknown dispatcher type " << type;
+ return scoped_refptr<Dispatcher>();
+}
+
+MojoResult Dispatcher::Close() {
+ base::AutoLock locker(lock_);
+ if (is_closed_)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ CloseNoLock();
+ return MOJO_RESULT_OK;
+}
+
+MojoResult Dispatcher::WriteMessage(
+ const void* bytes,
+ uint32_t num_bytes,
+ std::vector<DispatcherTransport>* transports,
+ MojoWriteMessageFlags flags) {
+ DCHECK(!transports || (transports->size() > 0 &&
+ transports->size() < kMaxMessageNumHandles));
+
+ base::AutoLock locker(lock_);
+ if (is_closed_)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return WriteMessageImplNoLock(bytes, num_bytes, transports, flags);
+}
+
+MojoResult Dispatcher::ReadMessage(void* bytes,
+ uint32_t* num_bytes,
+ DispatcherVector* dispatchers,
+ uint32_t* num_dispatchers,
+ MojoReadMessageFlags flags) {
+ DCHECK(!num_dispatchers || *num_dispatchers == 0 ||
+ (dispatchers && dispatchers->empty()));
+
+ base::AutoLock locker(lock_);
+ if (is_closed_)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return ReadMessageImplNoLock(bytes, num_bytes, dispatchers, num_dispatchers,
+ flags);
+}
+
+MojoResult Dispatcher::WriteData(const void* elements,
+ uint32_t* num_bytes,
+ MojoWriteDataFlags flags) {
+ base::AutoLock locker(lock_);
+ if (is_closed_)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return WriteDataImplNoLock(elements, num_bytes, flags);
+}
+
+MojoResult Dispatcher::BeginWriteData(void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoWriteDataFlags flags) {
+ base::AutoLock locker(lock_);
+ if (is_closed_)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return BeginWriteDataImplNoLock(buffer, buffer_num_bytes, flags);
+}
+
+MojoResult Dispatcher::EndWriteData(uint32_t num_bytes_written) {
+ base::AutoLock locker(lock_);
+ if (is_closed_)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return EndWriteDataImplNoLock(num_bytes_written);
+}
+
+MojoResult Dispatcher::ReadData(void* elements,
+ uint32_t* num_bytes,
+ MojoReadDataFlags flags) {
+ base::AutoLock locker(lock_);
+ if (is_closed_)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return ReadDataImplNoLock(elements, num_bytes, flags);
+}
+
+MojoResult Dispatcher::BeginReadData(const void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoReadDataFlags flags) {
+ base::AutoLock locker(lock_);
+ if (is_closed_)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return BeginReadDataImplNoLock(buffer, buffer_num_bytes, flags);
+}
+
+MojoResult Dispatcher::EndReadData(uint32_t num_bytes_read) {
+ base::AutoLock locker(lock_);
+ if (is_closed_)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return EndReadDataImplNoLock(num_bytes_read);
+}
+
+MojoResult Dispatcher::DuplicateBufferHandle(
+ const MojoDuplicateBufferHandleOptions* options,
+ scoped_refptr<Dispatcher>* new_dispatcher) {
+ base::AutoLock locker(lock_);
+ if (is_closed_)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return DuplicateBufferHandleImplNoLock(options, new_dispatcher);
+}
+
+MojoResult Dispatcher::MapBuffer(
+ uint64_t offset,
+ uint64_t num_bytes,
+ MojoMapBufferFlags flags,
+ scoped_ptr<RawSharedBufferMapping>* mapping) {
+ base::AutoLock locker(lock_);
+ if (is_closed_)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return MapBufferImplNoLock(offset, num_bytes, flags, mapping);
+}
+
+MojoResult Dispatcher::AddWaiter(Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context) {
+ base::AutoLock locker(lock_);
+ if (is_closed_)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return AddWaiterImplNoLock(waiter, signals, context);
+}
+
+void Dispatcher::RemoveWaiter(Waiter* waiter) {
+ base::AutoLock locker(lock_);
+ if (is_closed_)
+ return;
+ RemoveWaiterImplNoLock(waiter);
+}
+
+Dispatcher::Dispatcher()
+ : is_closed_(false) {
+}
+
+Dispatcher::~Dispatcher() {
+ // Make sure that |Close()| was called.
+ DCHECK(is_closed_);
+}
+
+void Dispatcher::CancelAllWaitersNoLock() {
+ lock_.AssertAcquired();
+ DCHECK(is_closed_);
+ // By default, waiting isn't supported. Only dispatchers that can be waited on
+ // will do something nontrivial.
+}
+
+void Dispatcher::CloseImplNoLock() {
+ lock_.AssertAcquired();
+ DCHECK(is_closed_);
+ // This may not need to do anything. Dispatchers should override this to do
+ // any actual close-time cleanup necessary.
+}
+
+MojoResult Dispatcher::WriteMessageImplNoLock(
+ const void* /*bytes*/,
+ uint32_t /*num_bytes*/,
+ std::vector<DispatcherTransport>* /*transports*/,
+ MojoWriteMessageFlags /*flags*/) {
+ lock_.AssertAcquired();
+ DCHECK(!is_closed_);
+ // By default, not supported. Only needed for message pipe dispatchers.
+ return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::ReadMessageImplNoLock(void* /*bytes*/,
+ uint32_t* /*num_bytes*/,
+ DispatcherVector* /*dispatchers*/,
+ uint32_t* /*num_dispatchers*/,
+ MojoReadMessageFlags /*flags*/) {
+ lock_.AssertAcquired();
+ DCHECK(!is_closed_);
+ // By default, not supported. Only needed for message pipe dispatchers.
+ return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::WriteDataImplNoLock(const void* /*elements*/,
+ uint32_t* /*num_bytes*/,
+ MojoWriteDataFlags /*flags*/) {
+ lock_.AssertAcquired();
+ DCHECK(!is_closed_);
+ // By default, not supported. Only needed for data pipe dispatchers.
+ return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::BeginWriteDataImplNoLock(void** /*buffer*/,
+ uint32_t* /*buffer_num_bytes*/,
+ MojoWriteDataFlags /*flags*/) {
+ lock_.AssertAcquired();
+ DCHECK(!is_closed_);
+ // By default, not supported. Only needed for data pipe dispatchers.
+ return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::EndWriteDataImplNoLock(uint32_t /*num_bytes_written*/) {
+ lock_.AssertAcquired();
+ DCHECK(!is_closed_);
+ // By default, not supported. Only needed for data pipe dispatchers.
+ return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::ReadDataImplNoLock(void* /*elements*/,
+ uint32_t* /*num_bytes*/,
+ MojoReadDataFlags /*flags*/) {
+ lock_.AssertAcquired();
+ DCHECK(!is_closed_);
+ // By default, not supported. Only needed for data pipe dispatchers.
+ return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::BeginReadDataImplNoLock(const void** /*buffer*/,
+ uint32_t* /*buffer_num_bytes*/,
+ MojoReadDataFlags /*flags*/) {
+ lock_.AssertAcquired();
+ DCHECK(!is_closed_);
+ // By default, not supported. Only needed for data pipe dispatchers.
+ return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::EndReadDataImplNoLock(uint32_t /*num_bytes_read*/) {
+ lock_.AssertAcquired();
+ DCHECK(!is_closed_);
+ // By default, not supported. Only needed for data pipe dispatchers.
+ return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::DuplicateBufferHandleImplNoLock(
+ const MojoDuplicateBufferHandleOptions* /*options*/,
+ scoped_refptr<Dispatcher>* /*new_dispatcher*/) {
+ lock_.AssertAcquired();
+ DCHECK(!is_closed_);
+ // By default, not supported. Only needed for buffer dispatchers.
+ return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::MapBufferImplNoLock(
+ uint64_t /*offset*/,
+ uint64_t /*num_bytes*/,
+ MojoMapBufferFlags /*flags*/,
+ scoped_ptr<RawSharedBufferMapping>* /*mapping*/) {
+ lock_.AssertAcquired();
+ DCHECK(!is_closed_);
+ // By default, not supported. Only needed for buffer dispatchers.
+ return MOJO_RESULT_INVALID_ARGUMENT;
+}
+
+MojoResult Dispatcher::AddWaiterImplNoLock(Waiter* /*waiter*/,
+ MojoHandleSignals /*signals*/,
+ uint32_t /*context*/) {
+ lock_.AssertAcquired();
+ DCHECK(!is_closed_);
+ // By default, waiting isn't supported. Only dispatchers that can be waited on
+ // will do something nontrivial.
+ return MOJO_RESULT_FAILED_PRECONDITION;
+}
+
+void Dispatcher::RemoveWaiterImplNoLock(Waiter* /*waiter*/) {
+ lock_.AssertAcquired();
+ DCHECK(!is_closed_);
+ // By default, waiting isn't supported. Only dispatchers that can be waited on
+ // will do something nontrivial.
+}
+
+void Dispatcher::StartSerializeImplNoLock(Channel* /*channel*/,
+ size_t* max_size,
+ size_t* max_platform_handles) {
+ DCHECK(HasOneRef()); // Only one ref => no need to take the lock.
+ DCHECK(!is_closed_);
+ *max_size = 0;
+ *max_platform_handles = 0;
+}
+
+bool Dispatcher::EndSerializeAndCloseImplNoLock(
+ Channel* /*channel*/,
+ void* /*destination*/,
+ size_t* /*actual_size*/,
+ embedder::PlatformHandleVector* /*platform_handles*/) {
+ DCHECK(HasOneRef()); // Only one ref => no need to take the lock.
+ DCHECK(is_closed_);
+ // By default, serializing isn't supported, so just close.
+ CloseImplNoLock();
+ return false;
+}
+
+bool Dispatcher::IsBusyNoLock() const {
+ lock_.AssertAcquired();
+ DCHECK(!is_closed_);
+ // Most dispatchers support only "atomic" operations, so they are never busy
+ // (in this sense).
+ return false;
+}
+
+void Dispatcher::CloseNoLock() {
+ lock_.AssertAcquired();
+ DCHECK(!is_closed_);
+
+ is_closed_ = true;
+ CancelAllWaitersNoLock();
+ CloseImplNoLock();
+}
+
+scoped_refptr<Dispatcher>
+Dispatcher::CreateEquivalentDispatcherAndCloseNoLock() {
+ lock_.AssertAcquired();
+ DCHECK(!is_closed_);
+
+ is_closed_ = true;
+ CancelAllWaitersNoLock();
+ return CreateEquivalentDispatcherAndCloseImplNoLock();
+}
+
+void Dispatcher::StartSerialize(Channel* channel,
+ size_t* max_size,
+ size_t* max_platform_handles) {
+ DCHECK(channel);
+ DCHECK(max_size);
+ DCHECK(max_platform_handles);
+ DCHECK(HasOneRef()); // Only one ref => no need to take the lock.
+ DCHECK(!is_closed_);
+ StartSerializeImplNoLock(channel, max_size, max_platform_handles);
+}
+
+bool Dispatcher::EndSerializeAndClose(
+ Channel* channel,
+ void* destination,
+ size_t* actual_size,
+ embedder::PlatformHandleVector* platform_handles) {
+ DCHECK(channel);
+ DCHECK(actual_size);
+ DCHECK(HasOneRef()); // Only one ref => no need to take the lock.
+ DCHECK(!is_closed_);
+
+ // Like other |...Close()| methods, we mark ourselves as closed before calling
+ // the impl.
+ is_closed_ = true;
+ // No need to cancel waiters: we shouldn't have any (and shouldn't be in
+ // |Core|'s handle table.
+
+#if !defined(NDEBUG)
+ // See the comment above |EndSerializeAndCloseImplNoLock()|. In brief: Locking
+ // isn't actually needed, but we need to satisfy assertions (which we don't
+ // want to remove or weaken).
+ base::AutoLock locker(lock_);
+#endif
+
+ return EndSerializeAndCloseImplNoLock(channel, destination, actual_size,
+ platform_handles);
+}
+
+// DispatcherTransport ---------------------------------------------------------
+
+void DispatcherTransport::End() {
+ DCHECK(dispatcher_);
+ dispatcher_->lock_.Release();
+ dispatcher_ = NULL;
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/dispatcher.h b/chromium/mojo/system/dispatcher.h
new file mode 100644
index 00000000000..051e2bc2e9b
--- /dev/null
+++ b/chromium/mojo/system/dispatcher.h
@@ -0,0 +1,379 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_DISPATCHER_H_
+#define MOJO_SYSTEM_DISPATCHER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "mojo/embedder/platform_handle.h"
+#include "mojo/embedder/platform_handle_vector.h"
+#include "mojo/public/c/system/buffer.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class Channel;
+class Core;
+class Dispatcher;
+class DispatcherTransport;
+class HandleTable;
+class LocalMessagePipeEndpoint;
+class ProxyMessagePipeEndpoint;
+class RawSharedBufferMapping;
+class TransportData;
+class Waiter;
+
+typedef std::vector<scoped_refptr<Dispatcher> > DispatcherVector;
+
+namespace test {
+
+// Test helper. We need to declare it here so we can friend it.
+MOJO_SYSTEM_IMPL_EXPORT DispatcherTransport DispatcherTryStartTransport(
+ Dispatcher* dispatcher);
+
+} // namespace test
+
+// A |Dispatcher| implements Mojo primitives that are "attached" to a particular
+// handle. This includes most (all?) primitives except for |MojoWait...()|. This
+// object is thread-safe, with its state being protected by a single lock
+// |lock_|, which is also made available to implementation subclasses (via the
+// |lock()| method).
+class MOJO_SYSTEM_IMPL_EXPORT Dispatcher :
+ public base::RefCountedThreadSafe<Dispatcher> {
+ public:
+ enum Type {
+ kTypeUnknown = 0,
+ kTypeMessagePipe,
+ kTypeDataPipeProducer,
+ kTypeDataPipeConsumer,
+ kTypeSharedBuffer,
+
+ // "Private" types (not exposed via the public interface):
+ kTypePlatformHandle = -1
+ };
+ virtual Type GetType() const = 0;
+
+ // These methods implement the various primitives named |Mojo...()|. These
+ // take |lock_| and handle races with |Close()|. Then they call out to
+ // subclasses' |...ImplNoLock()| methods (still under |lock_|), which actually
+ // implement the primitives.
+ // NOTE(vtl): This puts a big lock around each dispatcher (i.e., handle), and
+ // prevents the various |...ImplNoLock()|s from releasing the lock as soon as
+ // possible. If this becomes an issue, we can rethink this.
+ MojoResult Close();
+
+ // |transports| may be non-null if and only if there are handles to be
+ // written; not that |this| must not be in |transports|. On success, all the
+ // dispatchers in |transports| must have been moved to a closed state; on
+ // failure, they should remain in their original state.
+ MojoResult WriteMessage(const void* bytes,
+ uint32_t num_bytes,
+ std::vector<DispatcherTransport>* transports,
+ MojoWriteMessageFlags flags);
+ // |dispatchers| must be non-null but empty, if |num_dispatchers| is non-null
+ // and nonzero. On success, it will be set to the dispatchers to be received
+ // (and assigned handles) as part of the message.
+ MojoResult ReadMessage(void* bytes,
+ uint32_t* num_bytes,
+ DispatcherVector* dispatchers,
+ uint32_t* num_dispatchers,
+ MojoReadMessageFlags flags);
+ MojoResult WriteData(const void* elements,
+ uint32_t* elements_num_bytes,
+ MojoWriteDataFlags flags);
+ MojoResult BeginWriteData(void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoWriteDataFlags flags);
+ MojoResult EndWriteData(uint32_t num_bytes_written);
+ MojoResult ReadData(void* elements,
+ uint32_t* num_bytes,
+ MojoReadDataFlags flags);
+ MojoResult BeginReadData(const void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoReadDataFlags flags);
+ MojoResult EndReadData(uint32_t num_bytes_read);
+ // |options| may be null. |new_dispatcher| must not be null, but
+ // |*new_dispatcher| should be null (and will contain the dispatcher for the
+ // new handle on success).
+ MojoResult DuplicateBufferHandle(
+ const MojoDuplicateBufferHandleOptions* options,
+ scoped_refptr<Dispatcher>* new_dispatcher);
+ MojoResult MapBuffer(uint64_t offset,
+ uint64_t num_bytes,
+ MojoMapBufferFlags flags,
+ scoped_ptr<RawSharedBufferMapping>* mapping);
+
+ // Adds a waiter to this dispatcher. The waiter will be woken up when this
+ // object changes state to satisfy |signals| with context |context|. It will
+ // also be woken up when it becomes impossible for the object to ever satisfy
+ // |signals| with a suitable error status.
+ //
+ // Returns:
+ // - |MOJO_RESULT_OK| if the waiter was added;
+ // - |MOJO_RESULT_ALREADY_EXISTS| if |signals| is already satisfied;
+ // - |MOJO_RESULT_INVALID_ARGUMENT| if the dispatcher has been closed; and
+ // - |MOJO_RESULT_FAILED_PRECONDITION| if it is not (or no longer) possible
+ // that |signals| will ever be satisfied.
+ MojoResult AddWaiter(Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context);
+ void RemoveWaiter(Waiter* waiter);
+
+ // A dispatcher must be put into a special state in order to be sent across a
+ // message pipe. Outside of tests, only |HandleTableAccess| is allowed to do
+ // this, since there are requirements on the handle table (see below).
+ //
+ // In this special state, only a restricted set of operations is allowed.
+ // These are the ones available as |DispatcherTransport| methods. Other
+ // |Dispatcher| methods must not be called until |DispatcherTransport::End()|
+ // has been called.
+ class HandleTableAccess {
+ private:
+ friend class Core;
+ friend class HandleTable;
+ // Tests also need this, to avoid needing |Core|.
+ friend DispatcherTransport test::DispatcherTryStartTransport(Dispatcher*);
+
+ // This must be called under the handle table lock and only if the handle
+ // table entry is not marked busy. The caller must maintain a reference to
+ // |dispatcher| until |DispatcherTransport::End()| is called.
+ static DispatcherTransport TryStartTransport(Dispatcher* dispatcher);
+ };
+
+ // A |TransportData| may serialize dispatchers that are given to it (and which
+ // were previously attached to the |MessageInTransit| that is creating it) to
+ // a given |Channel| and then (probably in a different process) deserialize.
+ // Note that the |MessageInTransit| "owns" (i.e., has the only ref to) these
+ // dispatchers, so there are no locking issues. (There's no lock ordering
+ // issue, and in fact no need to take dispatcher locks at all.)
+ // TODO(vtl): Consider making another wrapper similar to |DispatcherTransport|
+ // (but with an owning, unique reference), and having
+ // |CreateEquivalentDispatcherAndCloseImplNoLock()| return that wrapper (and
+ // |MessageInTransit|, etc. only holding on to such wrappers).
+ class TransportDataAccess {
+ private:
+ friend class TransportData;
+
+ // Serialization API. These functions may only be called on such
+ // dispatchers. (|channel| is the |Channel| to which the dispatcher is to be
+ // serialized.) See the |Dispatcher| methods of the same names for more
+ // details.
+ static void StartSerialize(Dispatcher* dispatcher,
+ Channel* channel,
+ size_t* max_size,
+ size_t* max_platform_handles);
+ static bool EndSerializeAndClose(
+ Dispatcher* dispatcher,
+ Channel* channel,
+ void* destination,
+ size_t* actual_size,
+ embedder::PlatformHandleVector* platform_handles);
+
+ // Deserialization API.
+ // Note: This "clears" (i.e., reset to the invalid handle) any platform
+ // handles that it takes ownership of.
+ static scoped_refptr<Dispatcher> Deserialize(
+ Channel* channel,
+ int32_t type,
+ const void* source,
+ size_t size,
+ embedder::PlatformHandleVector* platform_handles);
+ };
+
+ protected:
+ friend class base::RefCountedThreadSafe<Dispatcher>;
+
+ Dispatcher();
+ virtual ~Dispatcher();
+
+ // These are to be overridden by subclasses (if necessary). They are called
+ // exactly once -- first |CancelAllWaitersNoLock()|, then |CloseImplNoLock()|,
+ // when the dispatcher is being closed. They are called under |lock_|.
+ virtual void CancelAllWaitersNoLock();
+ virtual void CloseImplNoLock();
+ virtual scoped_refptr<Dispatcher>
+ CreateEquivalentDispatcherAndCloseImplNoLock() = 0;
+
+ // These are to be overridden by subclasses (if necessary). They are never
+ // called after the dispatcher has been closed. They are called under |lock_|.
+ // See the descriptions of the methods without the "ImplNoLock" for more
+ // information.
+ virtual MojoResult WriteMessageImplNoLock(
+ const void* bytes,
+ uint32_t num_bytes,
+ std::vector<DispatcherTransport>* transports,
+ MojoWriteMessageFlags flags);
+ virtual MojoResult ReadMessageImplNoLock(void* bytes,
+ uint32_t* num_bytes,
+ DispatcherVector* dispatchers,
+ uint32_t* num_dispatchers,
+ MojoReadMessageFlags flags);
+ virtual MojoResult WriteDataImplNoLock(const void* elements,
+ uint32_t* num_bytes,
+ MojoWriteDataFlags flags);
+ virtual MojoResult BeginWriteDataImplNoLock(void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoWriteDataFlags flags);
+ virtual MojoResult EndWriteDataImplNoLock(uint32_t num_bytes_written);
+ virtual MojoResult ReadDataImplNoLock(void* elements,
+ uint32_t* num_bytes,
+ MojoReadDataFlags flags);
+ virtual MojoResult BeginReadDataImplNoLock(const void** buffer,
+ uint32_t* buffer_num_bytes,
+ MojoReadDataFlags flags);
+ virtual MojoResult EndReadDataImplNoLock(uint32_t num_bytes_read);
+ virtual MojoResult DuplicateBufferHandleImplNoLock(
+ const MojoDuplicateBufferHandleOptions* options,
+ scoped_refptr<Dispatcher>* new_dispatcher);
+ virtual MojoResult MapBufferImplNoLock(
+ uint64_t offset,
+ uint64_t num_bytes,
+ MojoMapBufferFlags flags,
+ scoped_ptr<RawSharedBufferMapping>* mapping);
+ virtual MojoResult AddWaiterImplNoLock(Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context);
+ virtual void RemoveWaiterImplNoLock(Waiter* waiter);
+
+ // These implement the API used to serialize dispatchers to a |Channel|
+ // (described below). They will only be called on a dispatcher that's attached
+ // to and "owned" by a |MessageInTransit|. See the non-"impl" versions for
+ // more information.
+ //
+ // Note: |StartSerializeImplNoLock()| is actually called with |lock_| NOT
+ // held, since the dispatcher should only be accessible to the calling thread.
+ // On Debug builds, |EndSerializeAndCloseImplNoLock()| is called with |lock_|
+ // held, to satisfy any |lock_.AssertAcquired()| (e.g., in |CloseImplNoLock()|
+ // -- and anything it calls); disentangling those assertions is
+ // difficult/fragile, and would weaken our general checking of invariants.
+ //
+ // TODO(vtl): Consider making these pure virtual once most things support
+ // being passed over a message pipe.
+ virtual void StartSerializeImplNoLock(Channel* channel,
+ size_t* max_size,
+ size_t* max_platform_handles);
+ virtual bool EndSerializeAndCloseImplNoLock(
+ Channel* channel,
+ void* destination,
+ size_t* actual_size,
+ embedder::PlatformHandleVector* platform_handles);
+
+ // Available to subclasses. (Note: Returns a non-const reference, just like
+ // |base::AutoLock|'s constructor takes a non-const reference.)
+ base::Lock& lock() const { return lock_; }
+
+ private:
+ friend class DispatcherTransport;
+
+ // This should be overridden to return true if/when there's an ongoing
+ // operation (e.g., two-phase read/writes on data pipes) that should prevent a
+ // handle from being sent over a message pipe (with status "busy").
+ virtual bool IsBusyNoLock() const;
+
+ // Closes the dispatcher. This must be done under lock, and unlike |Close()|,
+ // the dispatcher must not be closed already. (This is the "equivalent" of
+ // |CreateEquivalentDispatcherAndCloseNoLock()|, for situations where the
+ // dispatcher must be disposed of instead of "transferred".)
+ void CloseNoLock();
+
+ // Creates an equivalent dispatcher -- representing the same resource as this
+ // dispatcher -- and close (i.e., disable) this dispatcher. I.e., this
+ // dispatcher will look as though it was closed, but the resource it
+ // represents will be assigned to the new dispatcher. This must be called
+ // under the dispatcher's lock.
+ scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndCloseNoLock();
+
+ // API to serialize dispatchers to a |Channel|, exposed to only
+ // |TransportData| (via |TransportData|). They may only be called on a
+ // dispatcher attached to a |MessageInTransit| (and in particular not in
+ // |CoreImpl|'s handle table).
+ //
+ // Starts the serialization. Returns (via the two "out" parameters) the
+ // maximum amount of space that may be needed to serialize this dispatcher to
+ // the given |Channel| (no more than
+ // |TransportData::kMaxSerializedDispatcherSize|) and the maximum number of
+ // |PlatformHandle|s that may need to be attached (no more than
+ // |TransportData::kMaxSerializedDispatcherPlatformHandles|). If this
+ // dispatcher cannot be serialized to the given |Channel|, |*max_size| and
+ // |*max_platform_handles| should be set to zero. A call to this method will
+ // ALWAYS be followed by a call to |EndSerializeAndClose()| (even if this
+ // dispatcher cannot be serialized to the given |Channel|).
+ void StartSerialize(Channel* channel,
+ size_t* max_size,
+ size_t* max_platform_handles);
+ // Completes the serialization of this dispatcher to the given |Channel| and
+ // closes it. (This call will always follow an earlier call to
+ // |StartSerialize()|, with the same |Channel|.) This does so by writing to
+ // |destination| and appending any |PlatformHandle|s needed to
+ // |platform_handles| (which may be null if no platform handles were indicated
+ // to be required to |StartSerialize()|). This may write no more than the
+ // amount indicated by |StartSerialize()|. (WARNING: Beware of races, e.g., if
+ // something can be mutated between the two calls!) Returns true on success,
+ // in which case |*actual_size| is set to the amount it actually wrote to
+ // |destination|. On failure, |*actual_size| should not be modified; however,
+ // the dispatcher will still be closed.
+ bool EndSerializeAndClose(Channel* channel,
+ void* destination,
+ size_t* actual_size,
+ embedder::PlatformHandleVector* platform_handles);
+
+ // This protects the following members as well as any state added by
+ // subclasses.
+ mutable base::Lock lock_;
+ bool is_closed_;
+
+ DISALLOW_COPY_AND_ASSIGN(Dispatcher);
+};
+
+// Wrapper around a |Dispatcher| pointer, while it's being processed to be
+// passed in a message pipe. See the comment about
+// |Dispatcher::HandleTableAccess| for more details.
+//
+// Note: This class is deliberately "thin" -- no more expensive than a
+// |Dispatcher*|.
+class MOJO_SYSTEM_IMPL_EXPORT DispatcherTransport {
+ public:
+ DispatcherTransport() : dispatcher_(NULL) {}
+
+ void End();
+
+ Dispatcher::Type GetType() const { return dispatcher_->GetType(); }
+ bool IsBusy() const { return dispatcher_->IsBusyNoLock(); }
+ void Close() { dispatcher_->CloseNoLock(); }
+ scoped_refptr<Dispatcher> CreateEquivalentDispatcherAndClose() {
+ return dispatcher_->CreateEquivalentDispatcherAndCloseNoLock();
+ }
+
+ bool is_valid() const { return !!dispatcher_; }
+
+ protected:
+ Dispatcher* dispatcher() { return dispatcher_; }
+
+ private:
+ friend class Dispatcher::HandleTableAccess;
+
+ explicit DispatcherTransport(Dispatcher* dispatcher)
+ : dispatcher_(dispatcher) {}
+
+ Dispatcher* dispatcher_;
+
+ // Copy and assign allowed.
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_DISPATCHER_H_
diff --git a/chromium/mojo/system/dispatcher_unittest.cc b/chromium/mojo/system/dispatcher_unittest.cc
new file mode 100644
index 00000000000..917808225d8
--- /dev/null
+++ b/chromium/mojo/system/dispatcher_unittest.cc
@@ -0,0 +1,274 @@
+// Copyright 2013 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 "mojo/system/dispatcher.h"
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_vector.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/simple_thread.h"
+#include "mojo/system/raw_shared_buffer.h"
+#include "mojo/system/waiter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+// Trivial subclass that makes the constructor public.
+class TrivialDispatcher : public Dispatcher {
+ public:
+ TrivialDispatcher() {}
+
+ virtual Type GetType() const OVERRIDE {
+ return kTypeUnknown;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<TrivialDispatcher>;
+ virtual ~TrivialDispatcher() {}
+
+ virtual scoped_refptr<Dispatcher>
+ CreateEquivalentDispatcherAndCloseImplNoLock() OVERRIDE {
+ lock().AssertAcquired();
+ return scoped_refptr<Dispatcher>(new TrivialDispatcher());
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(TrivialDispatcher);
+};
+
+TEST(DispatcherTest, Basic) {
+ scoped_refptr<Dispatcher> d(new TrivialDispatcher());
+
+ EXPECT_EQ(Dispatcher::kTypeUnknown, d->GetType());
+
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->WriteMessage(NULL, 0, NULL, MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->ReadMessage(NULL, NULL, NULL, NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->WriteData(NULL, NULL, MOJO_WRITE_DATA_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->BeginWriteData(NULL, NULL, MOJO_WRITE_DATA_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->EndWriteData(0));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->ReadData(NULL, NULL, MOJO_READ_DATA_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->BeginReadData(NULL, NULL, MOJO_READ_DATA_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->EndReadData(0));
+ Waiter w;
+ w.Init();
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ d->AddWaiter(&w, ~MOJO_HANDLE_SIGNAL_NONE, 0));
+ // Okay to remove even if it wasn't added (or was already removed).
+ d->RemoveWaiter(&w);
+ d->RemoveWaiter(&w);
+
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->WriteMessage(NULL, 0, NULL, MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->ReadMessage(NULL, NULL, NULL, NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->WriteData(NULL, NULL, MOJO_WRITE_DATA_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->BeginWriteData(NULL, NULL, MOJO_WRITE_DATA_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->EndWriteData(0));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->ReadData(NULL, NULL, MOJO_READ_DATA_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->BeginReadData(NULL, NULL, MOJO_READ_DATA_FLAG_NONE));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->EndReadData(0));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->AddWaiter(&w, ~MOJO_HANDLE_SIGNAL_NONE, 0));
+ d->RemoveWaiter(&w);
+}
+
+class ThreadSafetyStressThread : public base::SimpleThread {
+ public:
+ enum DispatcherOp {
+ CLOSE = 0,
+ WRITE_MESSAGE,
+ READ_MESSAGE,
+ WRITE_DATA,
+ BEGIN_WRITE_DATA,
+ END_WRITE_DATA,
+ READ_DATA,
+ BEGIN_READ_DATA,
+ END_READ_DATA,
+ DUPLICATE_BUFFER_HANDLE,
+ MAP_BUFFER,
+ ADD_WAITER,
+ REMOVE_WAITER,
+
+ DISPATCHER_OP_COUNT
+ };
+
+ ThreadSafetyStressThread(base::WaitableEvent* event,
+ scoped_refptr<Dispatcher> dispatcher,
+ DispatcherOp op)
+ : base::SimpleThread("thread_safety_stress_thread"),
+ event_(event),
+ dispatcher_(dispatcher),
+ op_(op) {
+ CHECK_LE(0, op_);
+ CHECK_LT(op_, DISPATCHER_OP_COUNT);
+ }
+
+ virtual ~ThreadSafetyStressThread() {
+ Join();
+ }
+
+ private:
+ virtual void Run() OVERRIDE {
+ event_->Wait();
+
+ waiter_.Init();
+ switch(op_) {
+ case CLOSE: {
+ MojoResult r = dispatcher_->Close();
+ EXPECT_TRUE(r == MOJO_RESULT_OK || r == MOJO_RESULT_INVALID_ARGUMENT)
+ << "Result: " << r;
+ break;
+ }
+ case WRITE_MESSAGE:
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dispatcher_->WriteMessage(NULL, 0, NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ break;
+ case READ_MESSAGE:
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dispatcher_->ReadMessage(NULL, NULL, NULL, NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ break;
+ case WRITE_DATA:
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dispatcher_->WriteData(NULL, NULL,
+ MOJO_WRITE_DATA_FLAG_NONE));
+ break;
+ case BEGIN_WRITE_DATA:
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dispatcher_->BeginWriteData(NULL, NULL,
+ MOJO_WRITE_DATA_FLAG_NONE));
+ break;
+ case END_WRITE_DATA:
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dispatcher_->EndWriteData(0));
+ break;
+ case READ_DATA:
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dispatcher_->ReadData(NULL, NULL, MOJO_READ_DATA_FLAG_NONE));
+ break;
+ case BEGIN_READ_DATA:
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dispatcher_->BeginReadData(NULL, NULL,
+ MOJO_READ_DATA_FLAG_NONE));
+ break;
+ case END_READ_DATA:
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dispatcher_->EndReadData(0));
+ break;
+ case DUPLICATE_BUFFER_HANDLE: {
+ scoped_refptr<Dispatcher> unused;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dispatcher_->DuplicateBufferHandle(NULL, &unused));
+ break;
+ }
+ case MAP_BUFFER: {
+ scoped_ptr<RawSharedBufferMapping> unused;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dispatcher_->MapBuffer(0u, 0u, MOJO_MAP_BUFFER_FLAG_NONE,
+ &unused));
+ break;
+ }
+ case ADD_WAITER: {
+ MojoResult r = dispatcher_->AddWaiter(&waiter_,
+ ~MOJO_HANDLE_SIGNAL_NONE, 0);
+ EXPECT_TRUE(r == MOJO_RESULT_FAILED_PRECONDITION ||
+ r == MOJO_RESULT_INVALID_ARGUMENT);
+ break;
+ }
+ case REMOVE_WAITER:
+ dispatcher_->RemoveWaiter(&waiter_);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ // Always try to remove the waiter, in case we added it.
+ dispatcher_->RemoveWaiter(&waiter_);
+ }
+
+ base::WaitableEvent* const event_;
+ const scoped_refptr<Dispatcher> dispatcher_;
+ const DispatcherOp op_;
+
+ Waiter waiter_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadSafetyStressThread);
+};
+
+TEST(DispatcherTest, ThreadSafetyStress) {
+ static const size_t kRepeatCount = 20;
+ static const size_t kNumThreads = 100;
+
+ for (size_t i = 0; i < kRepeatCount; i++) {
+ // Manual reset, not initially signalled.
+ base::WaitableEvent event(true, false);
+ scoped_refptr<Dispatcher> d(new TrivialDispatcher());
+
+ {
+ ScopedVector<ThreadSafetyStressThread> threads;
+ for (size_t j = 0; j < kNumThreads; j++) {
+ ThreadSafetyStressThread::DispatcherOp op =
+ static_cast<ThreadSafetyStressThread::DispatcherOp>(
+ (i+j) % ThreadSafetyStressThread::DISPATCHER_OP_COUNT);
+ threads.push_back(new ThreadSafetyStressThread(&event, d, op));
+ threads.back()->Start();
+ }
+ event.Signal(); // Kicks off real work on the threads.
+ } // Joins all the threads.
+
+ // One of the threads should already have closed the dispatcher.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, d->Close());
+ }
+}
+
+TEST(DispatcherTest, ThreadSafetyStressNoClose) {
+ static const size_t kRepeatCount = 20;
+ static const size_t kNumThreads = 100;
+
+ for (size_t i = 0; i < kRepeatCount; i++) {
+ // Manual reset, not initially signalled.
+ base::WaitableEvent event(true, false);
+ scoped_refptr<Dispatcher> d(new TrivialDispatcher());
+
+ {
+ ScopedVector<ThreadSafetyStressThread> threads;
+ for (size_t j = 0; j < kNumThreads; j++) {
+ ThreadSafetyStressThread::DispatcherOp op =
+ static_cast<ThreadSafetyStressThread::DispatcherOp>(
+ (i+j) % (ThreadSafetyStressThread::DISPATCHER_OP_COUNT-1) + 1);
+ threads.push_back(new ThreadSafetyStressThread(&event, d, op));
+ threads.back()->Start();
+ }
+ event.Signal(); // Kicks off real work on the threads.
+ } // Joins all the threads.
+
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ }
+}
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/entrypoints.cc b/chromium/mojo/system/entrypoints.cc
new file mode 100644
index 00000000000..b70fcadf8b6
--- /dev/null
+++ b/chromium/mojo/system/entrypoints.cc
@@ -0,0 +1,158 @@
+// Copyright 2014 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 "mojo/system/entrypoints.h"
+
+#include "mojo/public/c/system/buffer.h"
+#include "mojo/public/c/system/data_pipe.h"
+#include "mojo/public/c/system/functions.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/system/core.h"
+
+static mojo::system::Core* g_core = NULL;
+
+namespace mojo {
+namespace system {
+namespace entrypoints {
+
+void SetCore(Core* core) {
+ g_core = core;
+}
+
+Core* GetCore() {
+ return g_core;
+}
+
+} // namespace entrypoints
+} // namepace system
+} // namespace mojo
+
+// Definitions of the system functions.
+extern "C" {
+
+MojoTimeTicks MojoGetTimeTicksNow() {
+ return g_core->GetTimeTicksNow();
+}
+
+MojoResult MojoClose(MojoHandle handle) {
+ return g_core->Close(handle);
+}
+
+MojoResult MojoWait(MojoHandle handle,
+ MojoHandleSignals signals,
+ MojoDeadline deadline) {
+ return g_core->Wait(handle, signals, deadline);
+}
+
+MojoResult MojoWaitMany(const MojoHandle* handles,
+ const MojoHandleSignals* signals,
+ uint32_t num_handles,
+ MojoDeadline deadline) {
+ return g_core->WaitMany(handles, signals, num_handles, deadline);
+}
+
+MojoResult MojoCreateMessagePipe(const MojoCreateMessagePipeOptions* options,
+ MojoHandle* message_pipe_handle0,
+ MojoHandle* message_pipe_handle1) {
+ return g_core->CreateMessagePipe(
+ options, message_pipe_handle0, message_pipe_handle1);
+}
+
+MojoResult MojoWriteMessage(MojoHandle message_pipe_handle,
+ const void* bytes,
+ uint32_t num_bytes,
+ const MojoHandle* handles,
+ uint32_t num_handles,
+ MojoWriteMessageFlags flags) {
+ return g_core->WriteMessage(
+ message_pipe_handle, bytes, num_bytes, handles, num_handles, flags);
+}
+
+MojoResult MojoReadMessage(MojoHandle message_pipe_handle,
+ void* bytes,
+ uint32_t* num_bytes,
+ MojoHandle* handles,
+ uint32_t* num_handles,
+ MojoReadMessageFlags flags) {
+ return g_core->ReadMessage(
+ message_pipe_handle, bytes, num_bytes, handles, num_handles, flags);
+}
+
+MojoResult MojoCreateDataPipe(const MojoCreateDataPipeOptions* options,
+ MojoHandle* data_pipe_producer_handle,
+ MojoHandle* data_pipe_consumer_handle) {
+ return g_core->CreateDataPipe(
+ options, data_pipe_producer_handle, data_pipe_consumer_handle);
+}
+
+MojoResult MojoWriteData(MojoHandle data_pipe_producer_handle,
+ const void* elements,
+ uint32_t* num_elements,
+ MojoWriteDataFlags flags) {
+ return g_core->WriteData(
+ data_pipe_producer_handle, elements, num_elements, flags);
+}
+
+MojoResult MojoBeginWriteData(MojoHandle data_pipe_producer_handle,
+ void** buffer,
+ uint32_t* buffer_num_elements,
+ MojoWriteDataFlags flags) {
+ return g_core->BeginWriteData(
+ data_pipe_producer_handle, buffer, buffer_num_elements, flags);
+}
+
+MojoResult MojoEndWriteData(MojoHandle data_pipe_producer_handle,
+ uint32_t num_elements_written) {
+ return g_core->EndWriteData(data_pipe_producer_handle, num_elements_written);
+}
+
+MojoResult MojoReadData(MojoHandle data_pipe_consumer_handle,
+ void* elements,
+ uint32_t* num_elements,
+ MojoReadDataFlags flags) {
+ return g_core->ReadData(
+ data_pipe_consumer_handle, elements, num_elements, flags);
+}
+
+MojoResult MojoBeginReadData(MojoHandle data_pipe_consumer_handle,
+ const void** buffer,
+ uint32_t* buffer_num_elements,
+ MojoReadDataFlags flags) {
+ return g_core->BeginReadData(
+ data_pipe_consumer_handle, buffer, buffer_num_elements, flags);
+}
+
+MojoResult MojoEndReadData(MojoHandle data_pipe_consumer_handle,
+ uint32_t num_elements_read) {
+ return g_core->EndReadData(data_pipe_consumer_handle, num_elements_read);
+}
+
+MojoResult MojoCreateSharedBuffer(
+ const struct MojoCreateSharedBufferOptions* options,
+ uint64_t num_bytes,
+ MojoHandle* shared_buffer_handle) {
+ return g_core->CreateSharedBuffer(options, num_bytes, shared_buffer_handle);
+}
+
+MojoResult MojoDuplicateBufferHandle(
+ MojoHandle buffer_handle,
+ const struct MojoDuplicateBufferHandleOptions* options,
+ MojoHandle* new_buffer_handle) {
+ return g_core->DuplicateBufferHandle(
+ buffer_handle, options, new_buffer_handle);
+}
+
+MojoResult MojoMapBuffer(MojoHandle buffer_handle,
+ uint64_t offset,
+ uint64_t num_bytes,
+ void** buffer,
+ MojoMapBufferFlags flags) {
+ return g_core->MapBuffer(buffer_handle, offset, num_bytes, buffer, flags);
+}
+
+MojoResult MojoUnmapBuffer(void* buffer) {
+ return g_core->UnmapBuffer(buffer);
+}
+
+} // extern "C"
diff --git a/chromium/mojo/system/entrypoints.h b/chromium/mojo/system/entrypoints.h
new file mode 100644
index 00000000000..65a03631e6a
--- /dev/null
+++ b/chromium/mojo/system/entrypoints.h
@@ -0,0 +1,24 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SYSTEM_ENTRYPOINTS_H
+#define MOJO_SYSTEM_ENTRYPOINTS_H
+
+namespace mojo {
+namespace system {
+
+class Core;
+
+namespace entrypoints {
+
+// Sets the instance of Core to be used by system functions.
+void SetCore(Core* core);
+// Gets the instance of Core to be used by system functions.
+Core* GetCore();
+
+} // namespace entrypoints
+} // namepace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_ENTRYPOINTS_H
diff --git a/chromium/mojo/system/handle_signals_state.h b/chromium/mojo/system/handle_signals_state.h
new file mode 100644
index 00000000000..d76fafb302d
--- /dev/null
+++ b/chromium/mojo/system/handle_signals_state.h
@@ -0,0 +1,50 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SYSTEM_HANDLE_SIGNALS_STATE_H_
+#define MOJO_SYSTEM_HANDLE_SIGNALS_STATE_H_
+
+#include "base/macros.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+// Just "add" some constructors and methods to the C struct
+// |MojoHandleSignalsState| (for convenience). This should add no overhead.
+struct MOJO_SYSTEM_IMPL_EXPORT HandleSignalsState
+ : public MojoHandleSignalsState {
+ HandleSignalsState() {
+ satisfied_signals = MOJO_HANDLE_SIGNAL_NONE;
+ satisfiable_signals = MOJO_HANDLE_SIGNAL_NONE;
+ }
+ HandleSignalsState(MojoHandleSignals satisfied,
+ MojoHandleSignals satisfiable) {
+ satisfied_signals = satisfied;
+ satisfiable_signals = satisfiable;
+ }
+
+ bool equals(const HandleSignalsState& other) const {
+ return satisfied_signals == other.satisfied_signals &&
+ satisfiable_signals == other.satisfiable_signals;
+ }
+
+ bool satisfies(MojoHandleSignals signals) const {
+ return !!(satisfied_signals & signals);
+ }
+
+ bool can_satisfy(MojoHandleSignals signals) const {
+ return !!(satisfiable_signals & signals);
+ }
+
+ // (Copy and assignment allowed.)
+};
+COMPILE_ASSERT(sizeof(HandleSignalsState) == sizeof(MojoHandleSignalsState),
+ HandleSignalsState_should_add_no_overhead);
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_HANDLE_SIGNALS_STATE_H_
diff --git a/chromium/mojo/system/handle_table.cc b/chromium/mojo/system/handle_table.cc
new file mode 100644
index 00000000000..2e4d22a9824
--- /dev/null
+++ b/chromium/mojo/system/handle_table.cc
@@ -0,0 +1,237 @@
+// Copyright 2014 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 "mojo/system/handle_table.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "mojo/system/constants.h"
+#include "mojo/system/dispatcher.h"
+
+namespace mojo {
+namespace system {
+
+HandleTable::Entry::Entry()
+ : busy(false) {
+}
+
+HandleTable::Entry::Entry(const scoped_refptr<Dispatcher>& dispatcher)
+ : dispatcher(dispatcher),
+ busy(false) {
+}
+
+HandleTable::Entry::~Entry() {
+ DCHECK(!busy);
+}
+
+HandleTable::HandleTable()
+ : next_handle_(MOJO_HANDLE_INVALID + 1) {
+}
+
+HandleTable::~HandleTable() {
+ // This should usually not be reached (the only instance should be owned by
+ // the singleton |Core|, which lives forever), except in tests.
+}
+
+Dispatcher* HandleTable::GetDispatcher(MojoHandle handle) {
+ DCHECK_NE(handle, MOJO_HANDLE_INVALID);
+
+ HandleToEntryMap::iterator it = handle_to_entry_map_.find(handle);
+ if (it == handle_to_entry_map_.end())
+ return NULL;
+ return it->second.dispatcher;
+}
+
+MojoResult HandleTable::GetAndRemoveDispatcher(
+ MojoHandle handle,
+ scoped_refptr<Dispatcher>* dispatcher) {
+ DCHECK_NE(handle, MOJO_HANDLE_INVALID);
+ DCHECK(dispatcher);
+
+ HandleToEntryMap::iterator it = handle_to_entry_map_.find(handle);
+ if (it == handle_to_entry_map_.end())
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (it->second.busy)
+ return MOJO_RESULT_BUSY;
+ *dispatcher = it->second.dispatcher;
+ handle_to_entry_map_.erase(it);
+
+ return MOJO_RESULT_OK;
+}
+
+MojoHandle HandleTable::AddDispatcher(
+ const scoped_refptr<Dispatcher>& dispatcher) {
+ if (handle_to_entry_map_.size() >= kMaxHandleTableSize)
+ return MOJO_HANDLE_INVALID;
+ return AddDispatcherNoSizeCheck(dispatcher);
+}
+
+std::pair<MojoHandle, MojoHandle> HandleTable::AddDispatcherPair(
+ const scoped_refptr<Dispatcher>& dispatcher0,
+ const scoped_refptr<Dispatcher>& dispatcher1) {
+ if (handle_to_entry_map_.size() + 1 >= kMaxHandleTableSize)
+ return std::make_pair(MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID);
+ return std::make_pair(AddDispatcherNoSizeCheck(dispatcher0),
+ AddDispatcherNoSizeCheck(dispatcher1));
+}
+
+bool HandleTable::AddDispatcherVector(const DispatcherVector& dispatchers,
+ MojoHandle* handles) {
+ DCHECK_LE(dispatchers.size(), kMaxMessageNumHandles);
+ DCHECK(handles);
+ // TODO(vtl): |std::numeric_limits<size_t>::max()| isn't a compile-time
+ // expression in C++03.
+ COMPILE_ASSERT(
+ static_cast<uint64_t>(kMaxHandleTableSize) + kMaxMessageNumHandles <
+ (sizeof(size_t) == 8 ? kuint64max :
+ static_cast<uint64_t>(kuint32max)),
+ addition_may_overflow);
+
+ if (handle_to_entry_map_.size() + dispatchers.size() > kMaxHandleTableSize)
+ return false;
+
+ for (size_t i = 0; i < dispatchers.size(); i++) {
+ if (dispatchers[i]) {
+ handles[i] = AddDispatcherNoSizeCheck(dispatchers[i]);
+ } else {
+ LOG(WARNING) << "Invalid dispatcher at index " << i;
+ handles[i] = MOJO_HANDLE_INVALID;
+ }
+ }
+ return true;
+}
+
+MojoResult HandleTable::MarkBusyAndStartTransport(
+ MojoHandle disallowed_handle,
+ const MojoHandle* handles,
+ uint32_t num_handles,
+ std::vector<DispatcherTransport>* transports) {
+ DCHECK_NE(disallowed_handle, MOJO_HANDLE_INVALID);
+ DCHECK(handles);
+ DCHECK_LE(num_handles, kMaxMessageNumHandles);
+ DCHECK(transports);
+ DCHECK_EQ(transports->size(), num_handles);
+
+ std::vector<Entry*> entries(num_handles);
+
+ // First verify all the handles and get their dispatchers.
+ uint32_t i;
+ MojoResult error_result = MOJO_RESULT_INTERNAL;
+ for (i = 0; i < num_handles; i++) {
+ // Sending your own handle is not allowed (and, for consistency, returns
+ // "busy").
+ if (handles[i] == disallowed_handle) {
+ error_result = MOJO_RESULT_BUSY;
+ break;
+ }
+
+ HandleToEntryMap::iterator it = handle_to_entry_map_.find(handles[i]);
+ if (it == handle_to_entry_map_.end()) {
+ error_result = MOJO_RESULT_INVALID_ARGUMENT;
+ break;
+ }
+
+ entries[i] = &it->second;
+ if (entries[i]->busy) {
+ error_result = MOJO_RESULT_BUSY;
+ break;
+ }
+ // Note: By marking the handle as busy here, we're also preventing the
+ // same handle from being sent multiple times in the same message.
+ entries[i]->busy = true;
+
+ // Try to start the transport.
+ DispatcherTransport transport =
+ Dispatcher::HandleTableAccess::TryStartTransport(
+ entries[i]->dispatcher.get());
+ if (!transport.is_valid()) {
+ // Unset the busy flag (since it won't be unset below).
+ entries[i]->busy = false;
+ error_result = MOJO_RESULT_BUSY;
+ break;
+ }
+
+ // Check if the dispatcher is busy (e.g., in a two-phase read/write).
+ // (Note that this must be done after the dispatcher's lock is acquired.)
+ if (transport.IsBusy()) {
+ // Unset the busy flag and end the transport (since it won't be done
+ // below).
+ entries[i]->busy = false;
+ transport.End();
+ error_result = MOJO_RESULT_BUSY;
+ break;
+ }
+
+ // Hang on to the transport (which we'll need to end the transport).
+ (*transports)[i] = transport;
+ }
+ if (i < num_handles) {
+ DCHECK_NE(error_result, MOJO_RESULT_INTERNAL);
+
+ // Unset the busy flags and release the locks.
+ for (uint32_t j = 0; j < i; j++) {
+ DCHECK(entries[j]->busy);
+ entries[j]->busy = false;
+ (*transports)[j].End();
+ }
+ return error_result;
+ }
+
+ return MOJO_RESULT_OK;
+}
+
+MojoHandle HandleTable::AddDispatcherNoSizeCheck(
+ const scoped_refptr<Dispatcher>& dispatcher) {
+ DCHECK(dispatcher);
+ DCHECK_LT(handle_to_entry_map_.size(), kMaxHandleTableSize);
+ DCHECK_NE(next_handle_, MOJO_HANDLE_INVALID);
+
+ // TODO(vtl): Maybe we want to do something different/smarter. (Or maybe try
+ // assigning randomly?)
+ while (handle_to_entry_map_.find(next_handle_) !=
+ handle_to_entry_map_.end()) {
+ next_handle_++;
+ if (next_handle_ == MOJO_HANDLE_INVALID)
+ next_handle_++;
+ }
+
+ MojoHandle new_handle = next_handle_;
+ handle_to_entry_map_[new_handle] = Entry(dispatcher);
+
+ next_handle_++;
+ if (next_handle_ == MOJO_HANDLE_INVALID)
+ next_handle_++;
+
+ return new_handle;
+}
+
+void HandleTable::RemoveBusyHandles(const MojoHandle* handles,
+ uint32_t num_handles) {
+ DCHECK(handles);
+ DCHECK_LE(num_handles, kMaxMessageNumHandles);
+
+ for (uint32_t i = 0; i < num_handles; i++) {
+ HandleToEntryMap::iterator it = handle_to_entry_map_.find(handles[i]);
+ DCHECK(it != handle_to_entry_map_.end());
+ DCHECK(it->second.busy);
+ it->second.busy = false; // For the sake of a |DCHECK()|.
+ handle_to_entry_map_.erase(it);
+ }
+}
+
+void HandleTable::RestoreBusyHandles(const MojoHandle* handles,
+ uint32_t num_handles) {
+ DCHECK(handles);
+ DCHECK_LE(num_handles, kMaxMessageNumHandles);
+
+ for (uint32_t i = 0; i < num_handles; i++) {
+ HandleToEntryMap::iterator it = handle_to_entry_map_.find(handles[i]);
+ DCHECK(it != handle_to_entry_map_.end());
+ DCHECK(it->second.busy);
+ it->second.busy = false;
+ }
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/handle_table.h b/chromium/mojo/system/handle_table.h
new file mode 100644
index 00000000000..4a25939175d
--- /dev/null
+++ b/chromium/mojo/system/handle_table.h
@@ -0,0 +1,144 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SYSTEM_HANDLE_TABLE_H_
+#define MOJO_SYSTEM_HANDLE_TABLE_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class Core;
+class Dispatcher;
+class DispatcherTransport;
+
+typedef std::vector<scoped_refptr<Dispatcher> > DispatcherVector;
+
+// Test-only function (defined/used in embedder/test_embedder.cc). Declared here
+// so it can be friended.
+namespace internal {
+bool ShutdownCheckNoLeaks(Core*);
+}
+
+// This class provides the (global) handle table (owned by |Core|), which maps
+// (valid) |MojoHandle|s to |Dispatcher|s. This is abstracted so that, e.g.,
+// caching may be added.
+//
+// This class is NOT thread-safe; locking is left to |Core| (since it may need
+// to make several changes -- "atomically" or in rapid successsion, in which
+// case the extra locking/unlocking would be unnecessary overhead).
+
+class MOJO_SYSTEM_IMPL_EXPORT HandleTable {
+ public:
+ HandleTable();
+ ~HandleTable();
+
+ // Gets the dispatcher for a given handle (which should not be
+ // |MOJO_HANDLE_INVALID|). Returns null if there's no dispatcher for the given
+ // handle.
+ // WARNING: For efficiency, this returns a dumb pointer. If you're going to
+ // use the result outside |Core|'s lock, you MUST take a reference (e.g., by
+ // storing the result inside a |scoped_refptr|).
+ Dispatcher* GetDispatcher(MojoHandle handle);
+
+ // On success, gets the dispatcher for a given handle (which should not be
+ // |MOJO_HANDLE_INVALID|) and removes it. (On failure, returns an appropriate
+ // result (and leaves |dispatcher| alone), namely
+ // |MOJO_RESULT_INVALID_ARGUMENT| if there's no dispatcher for the given
+ // handle or |MOJO_RESULT_BUSY| if the handle is marked as busy.)
+ MojoResult GetAndRemoveDispatcher(MojoHandle handle,
+ scoped_refptr<Dispatcher>* dispatcher);
+
+ // Adds a dispatcher (which must be valid), returning the handle for it.
+ // Returns |MOJO_HANDLE_INVALID| on failure (if the handle table is full).
+ MojoHandle AddDispatcher(const scoped_refptr<Dispatcher>& dispatcher);
+
+ // Adds a pair of dispatchers (which must be valid), return a pair of handles
+ // for them. On failure (if the handle table is full), the first (and second)
+ // handles will be |MOJO_HANDLE_INVALID|, and neither dispatcher will be
+ // added.
+ std::pair<MojoHandle, MojoHandle> AddDispatcherPair(
+ const scoped_refptr<Dispatcher>& dispatcher0,
+ const scoped_refptr<Dispatcher>& dispatcher1);
+
+ // Adds the given vector of dispatchers (of size at most
+ // |kMaxMessageNumHandles|). |handles| must point to an array of size at least
+ // |dispatchers.size()|. Unlike the other |AddDispatcher...()| functions, some
+ // of the dispatchers may be invalid (null). Returns true on success and false
+ // on failure (if the handle table is full), in which case it leaves
+ // |handles[...]| untouched (and all dispatchers unadded).
+ bool AddDispatcherVector(const DispatcherVector& dispatchers,
+ MojoHandle* handles);
+
+ // Tries to mark the given handles as busy and start transport on them (i.e.,
+ // take their dispatcher locks); |transports| must be sized to contain
+ // |num_handles| elements. On failure, returns them to their original
+ // (non-busy, unlocked state).
+ MojoResult MarkBusyAndStartTransport(
+ MojoHandle disallowed_handle,
+ const MojoHandle* handles,
+ uint32_t num_handles,
+ std::vector<DispatcherTransport>* transports);
+
+ // Remove the given handles, which must all be present and which should have
+ // previously been marked busy by |MarkBusyAndStartTransport()|.
+ void RemoveBusyHandles(const MojoHandle* handles, uint32_t num_handles);
+
+ // Restores the given handles, which must all be present and which should have
+ // previously been marked busy by |MarkBusyAndStartTransport()|, to a non-busy
+ // state.
+ void RestoreBusyHandles(const MojoHandle* handles, uint32_t num_handles);
+
+ private:
+ friend bool internal::ShutdownCheckNoLeaks(Core*);
+
+ // The |busy| member is used only to deal with functions (in particular
+ // |Core::WriteMessage()|) that want to hold on to a dispatcher and later
+ // remove it from the handle table, without holding on to the handle table
+ // lock.
+ //
+ // For example, if |Core::WriteMessage()| is called with a handle to be sent,
+ // (under the handle table lock) it must first check that that handle is not
+ // busy (if it is busy, then it fails with |MOJO_RESULT_BUSY|) and then marks
+ // it as busy. To avoid deadlock, it should also try to acquire the locks for
+ // all the dispatchers for the handles that it is sending (and fail with
+ // |MOJO_RESULT_BUSY| if the attempt fails). At this point, it can release the
+ // handle table lock.
+ //
+ // If |Core::Close()| is simultaneously called on that handle, it too checks
+ // if the handle is marked busy. If it is, it fails (with |MOJO_RESULT_BUSY|).
+ // This prevents |Core::WriteMessage()| from sending a handle that has been
+ // closed (or learning about this too late).
+ struct Entry {
+ Entry();
+ explicit Entry(const scoped_refptr<Dispatcher>& dispatcher);
+ ~Entry();
+
+ scoped_refptr<Dispatcher> dispatcher;
+ bool busy;
+ };
+ typedef base::hash_map<MojoHandle, Entry> HandleToEntryMap;
+
+ // Adds the given dispatcher to the handle table, not doing any size checks.
+ MojoHandle AddDispatcherNoSizeCheck(
+ const scoped_refptr<Dispatcher>& dispatcher);
+
+ HandleToEntryMap handle_to_entry_map_;
+ MojoHandle next_handle_; // Invariant: never |MOJO_HANDLE_INVALID|.
+
+ DISALLOW_COPY_AND_ASSIGN(HandleTable);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_HANDLE_TABLE_H_
diff --git a/chromium/mojo/system/local_data_pipe.cc b/chromium/mojo/system/local_data_pipe.cc
new file mode 100644
index 00000000000..8f4bbd6a47e
--- /dev/null
+++ b/chromium/mojo/system/local_data_pipe.cc
@@ -0,0 +1,332 @@
+// Copyright 2013 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.
+
+// TODO(vtl): I currently potentially overflow in doing index calculations.
+// E.g., |start_index_| and |current_num_bytes_| fit into a |uint32_t|, but
+// their sum may not. This is bad and poses a security risk. (We're currently
+// saved by the limit on capacity -- the maximum size of the buffer, checked in
+// |DataPipe::ValidateOptions()|, is currently sufficiently small.
+
+#include "mojo/system/local_data_pipe.h"
+
+#include <string.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "mojo/system/constants.h"
+
+namespace mojo {
+namespace system {
+
+LocalDataPipe::LocalDataPipe(const MojoCreateDataPipeOptions& options)
+ : DataPipe(true, true, options),
+ start_index_(0),
+ current_num_bytes_(0) {
+ // Note: |buffer_| is lazily allocated, since a common case will be that one
+ // of the handles is immediately passed off to another process.
+}
+
+LocalDataPipe::~LocalDataPipe() {
+}
+
+void LocalDataPipe::ProducerCloseImplNoLock() {
+ // If the consumer is still open and we still have data, we have to keep the
+ // buffer around. Currently, we won't free it even if it empties later. (We
+ // could do this -- requiring a check on every read -- but that seems to be
+ // optimizing for the uncommon case.)
+ if (!consumer_open_no_lock() || !current_num_bytes_) {
+ // Note: There can only be a two-phase *read* (by the consumer) if we still
+ // have data.
+ DCHECK(!consumer_in_two_phase_read_no_lock());
+ DestroyBufferNoLock();
+ }
+}
+
+MojoResult LocalDataPipe::ProducerWriteDataImplNoLock(const void* elements,
+ uint32_t* num_bytes,
+ bool all_or_none) {
+ DCHECK_EQ(*num_bytes % element_num_bytes(), 0u);
+ DCHECK_GT(*num_bytes, 0u);
+ DCHECK(consumer_open_no_lock());
+
+ size_t num_bytes_to_write = 0;
+ if (may_discard()) {
+ if (all_or_none && *num_bytes > capacity_num_bytes())
+ return MOJO_RESULT_OUT_OF_RANGE;
+
+ num_bytes_to_write = std::min(static_cast<size_t>(*num_bytes),
+ capacity_num_bytes());
+ if (num_bytes_to_write > capacity_num_bytes() - current_num_bytes_) {
+ // Discard as much as needed (discard oldest first).
+ MarkDataAsConsumedNoLock(
+ num_bytes_to_write - (capacity_num_bytes() - current_num_bytes_));
+ // No need to wake up write waiters, since we're definitely going to leave
+ // the buffer full.
+ }
+ } else {
+ if (all_or_none && *num_bytes > capacity_num_bytes() - current_num_bytes_) {
+ // Don't return "should wait" since you can't wait for a specified amount
+ // of data.
+ return MOJO_RESULT_OUT_OF_RANGE;
+ }
+
+ num_bytes_to_write = std::min(static_cast<size_t>(*num_bytes),
+ capacity_num_bytes() - current_num_bytes_);
+ }
+ if (num_bytes_to_write == 0)
+ return MOJO_RESULT_SHOULD_WAIT;
+
+ // The amount we can write in our first |memcpy()|.
+ size_t num_bytes_to_write_first =
+ std::min(num_bytes_to_write, GetMaxNumBytesToWriteNoLock());
+ // Do the first (and possibly only) |memcpy()|.
+ size_t first_write_index =
+ (start_index_ + current_num_bytes_) % capacity_num_bytes();
+ EnsureBufferNoLock();
+ memcpy(buffer_.get() + first_write_index, elements, num_bytes_to_write_first);
+
+ if (num_bytes_to_write_first < num_bytes_to_write) {
+ // The "second write index" is zero.
+ memcpy(buffer_.get(),
+ static_cast<const char*>(elements) + num_bytes_to_write_first,
+ num_bytes_to_write - num_bytes_to_write_first);
+ }
+
+ current_num_bytes_ += num_bytes_to_write;
+ DCHECK_LE(current_num_bytes_, capacity_num_bytes());
+ *num_bytes = static_cast<uint32_t>(num_bytes_to_write);
+ return MOJO_RESULT_OK;
+}
+
+MojoResult LocalDataPipe::ProducerBeginWriteDataImplNoLock(
+ void** buffer,
+ uint32_t* buffer_num_bytes,
+ bool all_or_none) {
+ DCHECK(consumer_open_no_lock());
+
+ // The index we need to start writing at.
+ size_t write_index =
+ (start_index_ + current_num_bytes_) % capacity_num_bytes();
+
+ size_t max_num_bytes_to_write = GetMaxNumBytesToWriteNoLock();
+ if (all_or_none && *buffer_num_bytes > max_num_bytes_to_write) {
+ // In "may discard" mode, we can always write from the write index to the
+ // end of the buffer.
+ if (may_discard() &&
+ *buffer_num_bytes <= capacity_num_bytes() - write_index) {
+ // To do so, we need to discard an appropriate amount of data.
+ // We should only reach here if the start index is after the write index!
+ DCHECK_GE(start_index_, write_index);
+ DCHECK_GT(*buffer_num_bytes - max_num_bytes_to_write, 0u);
+ MarkDataAsConsumedNoLock(*buffer_num_bytes - max_num_bytes_to_write);
+ max_num_bytes_to_write = *buffer_num_bytes;
+ } else {
+ // Don't return "should wait" since you can't wait for a specified amount
+ // of data.
+ return MOJO_RESULT_OUT_OF_RANGE;
+ }
+ }
+
+ // Don't go into a two-phase write if there's no room.
+ if (max_num_bytes_to_write == 0)
+ return MOJO_RESULT_SHOULD_WAIT;
+
+ EnsureBufferNoLock();
+ *buffer = buffer_.get() + write_index;
+ *buffer_num_bytes = static_cast<uint32_t>(max_num_bytes_to_write);
+ set_producer_two_phase_max_num_bytes_written_no_lock(
+ static_cast<uint32_t>(max_num_bytes_to_write));
+ return MOJO_RESULT_OK;
+}
+
+MojoResult LocalDataPipe::ProducerEndWriteDataImplNoLock(
+ uint32_t num_bytes_written) {
+ DCHECK_LE(num_bytes_written,
+ producer_two_phase_max_num_bytes_written_no_lock());
+ current_num_bytes_ += num_bytes_written;
+ DCHECK_LE(current_num_bytes_, capacity_num_bytes());
+ set_producer_two_phase_max_num_bytes_written_no_lock(0);
+ return MOJO_RESULT_OK;
+}
+
+HandleSignalsState LocalDataPipe::ProducerGetHandleSignalsStateNoLock() const {
+ HandleSignalsState rv;
+ if (consumer_open_no_lock()) {
+ if ((may_discard() || current_num_bytes_ < capacity_num_bytes()) &&
+ !producer_in_two_phase_write_no_lock())
+ rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_WRITABLE;
+ rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_WRITABLE;
+ }
+ return rv;
+}
+
+void LocalDataPipe::ConsumerCloseImplNoLock() {
+ // If the producer is around and in a two-phase write, we have to keep the
+ // buffer around. (We then don't free it until the producer is closed. This
+ // could be rectified, but again seems like optimizing for the uncommon case.)
+ if (!producer_open_no_lock() || !producer_in_two_phase_write_no_lock())
+ DestroyBufferNoLock();
+ current_num_bytes_ = 0;
+}
+
+MojoResult LocalDataPipe::ConsumerReadDataImplNoLock(void* elements,
+ uint32_t* num_bytes,
+ bool all_or_none) {
+ DCHECK_EQ(*num_bytes % element_num_bytes(), 0u);
+ DCHECK_GT(*num_bytes, 0u);
+
+ if (all_or_none && *num_bytes > current_num_bytes_) {
+ // Don't return "should wait" since you can't wait for a specified amount of
+ // data.
+ return producer_open_no_lock() ? MOJO_RESULT_OUT_OF_RANGE :
+ MOJO_RESULT_FAILED_PRECONDITION;
+ }
+
+ size_t num_bytes_to_read =
+ std::min(static_cast<size_t>(*num_bytes), current_num_bytes_);
+ if (num_bytes_to_read == 0) {
+ return producer_open_no_lock() ? MOJO_RESULT_SHOULD_WAIT :
+ MOJO_RESULT_FAILED_PRECONDITION;
+ }
+
+ // The amount we can read in our first |memcpy()|.
+ size_t num_bytes_to_read_first =
+ std::min(num_bytes_to_read, GetMaxNumBytesToReadNoLock());
+ memcpy(elements, buffer_.get() + start_index_, num_bytes_to_read_first);
+
+ if (num_bytes_to_read_first < num_bytes_to_read) {
+ // The "second read index" is zero.
+ memcpy(static_cast<char*>(elements) + num_bytes_to_read_first,
+ buffer_.get(),
+ num_bytes_to_read - num_bytes_to_read_first);
+ }
+
+ MarkDataAsConsumedNoLock(num_bytes_to_read);
+ *num_bytes = static_cast<uint32_t>(num_bytes_to_read);
+ return MOJO_RESULT_OK;
+}
+
+MojoResult LocalDataPipe::ConsumerDiscardDataImplNoLock(uint32_t* num_bytes,
+ bool all_or_none) {
+ DCHECK_EQ(*num_bytes % element_num_bytes(), 0u);
+ DCHECK_GT(*num_bytes, 0u);
+
+ if (all_or_none && *num_bytes > current_num_bytes_) {
+ // Don't return "should wait" since you can't wait for a specified amount of
+ // data.
+ return producer_open_no_lock() ? MOJO_RESULT_OUT_OF_RANGE :
+ MOJO_RESULT_FAILED_PRECONDITION;
+ }
+
+ // Be consistent with other operations; error if no data available.
+ if (current_num_bytes_ == 0) {
+ return producer_open_no_lock() ? MOJO_RESULT_SHOULD_WAIT :
+ MOJO_RESULT_FAILED_PRECONDITION;
+ }
+
+ size_t num_bytes_to_discard =
+ std::min(static_cast<size_t>(*num_bytes), current_num_bytes_);
+ MarkDataAsConsumedNoLock(num_bytes_to_discard);
+ *num_bytes = static_cast<uint32_t>(num_bytes_to_discard);
+ return MOJO_RESULT_OK;
+}
+
+MojoResult LocalDataPipe::ConsumerQueryDataImplNoLock(uint32_t* num_bytes) {
+ // Note: This cast is safe, since the capacity fits into a |uint32_t|.
+ *num_bytes = static_cast<uint32_t>(current_num_bytes_);
+ return MOJO_RESULT_OK;
+}
+
+MojoResult LocalDataPipe::ConsumerBeginReadDataImplNoLock(
+ const void** buffer,
+ uint32_t* buffer_num_bytes,
+ bool all_or_none) {
+ size_t max_num_bytes_to_read = GetMaxNumBytesToReadNoLock();
+ if (all_or_none && *buffer_num_bytes > max_num_bytes_to_read) {
+ // Don't return "should wait" since you can't wait for a specified amount of
+ // data.
+ return producer_open_no_lock() ? MOJO_RESULT_OUT_OF_RANGE :
+ MOJO_RESULT_FAILED_PRECONDITION;
+ }
+
+ // Don't go into a two-phase read if there's no data.
+ if (max_num_bytes_to_read == 0) {
+ return producer_open_no_lock() ? MOJO_RESULT_SHOULD_WAIT :
+ MOJO_RESULT_FAILED_PRECONDITION;
+ }
+
+ *buffer = buffer_.get() + start_index_;
+ *buffer_num_bytes = static_cast<uint32_t>(max_num_bytes_to_read);
+ set_consumer_two_phase_max_num_bytes_read_no_lock(
+ static_cast<uint32_t>(max_num_bytes_to_read));
+ return MOJO_RESULT_OK;
+}
+
+MojoResult LocalDataPipe::ConsumerEndReadDataImplNoLock(
+ uint32_t num_bytes_read) {
+ DCHECK_LE(num_bytes_read, consumer_two_phase_max_num_bytes_read_no_lock());
+ DCHECK_LE(start_index_ + num_bytes_read, capacity_num_bytes());
+ MarkDataAsConsumedNoLock(num_bytes_read);
+ set_consumer_two_phase_max_num_bytes_read_no_lock(0);
+ return MOJO_RESULT_OK;
+}
+
+HandleSignalsState LocalDataPipe::ConsumerGetHandleSignalsStateNoLock() const {
+ HandleSignalsState rv;
+ if (current_num_bytes_ > 0) {
+ if (!consumer_in_two_phase_read_no_lock())
+ rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_READABLE;
+ rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE;
+ } else if (producer_open_no_lock()) {
+ rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE;
+ }
+ return rv;
+}
+
+void LocalDataPipe::EnsureBufferNoLock() {
+ DCHECK(producer_open_no_lock());
+ if (buffer_.get())
+ return;
+ buffer_.reset(static_cast<char*>(
+ base::AlignedAlloc(capacity_num_bytes(), kDataPipeBufferAlignmentBytes)));
+}
+
+void LocalDataPipe::DestroyBufferNoLock() {
+#ifndef NDEBUG
+ // Scribble on the buffer to help detect use-after-frees. (This also helps the
+ // unit test detect certain bugs without needing ASAN or similar.)
+ if (buffer_.get())
+ memset(buffer_.get(), 0xcd, capacity_num_bytes());
+#endif
+ buffer_.reset();
+}
+
+size_t LocalDataPipe::GetMaxNumBytesToWriteNoLock() {
+ size_t next_index = start_index_ + current_num_bytes_;
+ if (next_index >= capacity_num_bytes()) {
+ next_index %= capacity_num_bytes();
+ DCHECK_GE(start_index_, next_index);
+ DCHECK_EQ(start_index_ - next_index,
+ capacity_num_bytes() - current_num_bytes_);
+ return start_index_ - next_index;
+ }
+ return capacity_num_bytes() - next_index;
+}
+
+size_t LocalDataPipe::GetMaxNumBytesToReadNoLock() {
+ if (start_index_ + current_num_bytes_ > capacity_num_bytes())
+ return capacity_num_bytes() - start_index_;
+ return current_num_bytes_;
+}
+
+void LocalDataPipe::MarkDataAsConsumedNoLock(size_t num_bytes) {
+ DCHECK_LE(num_bytes, current_num_bytes_);
+ start_index_ += num_bytes;
+ start_index_ %= capacity_num_bytes();
+ current_num_bytes_ -= num_bytes;
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/local_data_pipe.h b/chromium/mojo/system/local_data_pipe.h
new file mode 100644
index 00000000000..74ed15604cb
--- /dev/null
+++ b/chromium/mojo/system/local_data_pipe.h
@@ -0,0 +1,84 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_LOCAL_DATA_PIPE_H_
+#define MOJO_SYSTEM_LOCAL_DATA_PIPE_H_
+
+#include "base/basictypes.h"
+#include "base/memory/aligned_memory.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/system/data_pipe.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+// |LocalDataPipe| is a subclass that "implements" |DataPipe| for data pipes
+// whose producer and consumer are both local. This class is thread-safe (with
+// protection provided by |DataPipe|'s |lock_|.
+class MOJO_SYSTEM_IMPL_EXPORT LocalDataPipe : public DataPipe {
+ public:
+ // |validated_options| should be the output of |DataPipe::ValidateOptions()|.
+ // In particular: |struct_size| is ignored (so |validated_options| must be the
+ // current version of the struct) and |capacity_num_bytes| must be nonzero.
+ explicit LocalDataPipe(const MojoCreateDataPipeOptions& validated_options);
+
+ private:
+ friend class base::RefCountedThreadSafe<LocalDataPipe>;
+ virtual ~LocalDataPipe();
+
+ // |DataPipe| implementation:
+ virtual void ProducerCloseImplNoLock() OVERRIDE;
+ virtual MojoResult ProducerWriteDataImplNoLock(const void* elements,
+ uint32_t* num_bytes,
+ bool all_or_none) OVERRIDE;
+ virtual MojoResult ProducerBeginWriteDataImplNoLock(
+ void** buffer,
+ uint32_t* buffer_num_bytes,
+ bool all_or_none) OVERRIDE;
+ virtual MojoResult ProducerEndWriteDataImplNoLock(
+ uint32_t num_bytes_written) OVERRIDE;
+ virtual HandleSignalsState
+ ProducerGetHandleSignalsStateNoLock() const OVERRIDE;
+ virtual void ConsumerCloseImplNoLock() OVERRIDE;
+ virtual MojoResult ConsumerReadDataImplNoLock(void* elements,
+ uint32_t* num_bytes,
+ bool all_or_none) OVERRIDE;
+ virtual MojoResult ConsumerDiscardDataImplNoLock(uint32_t* num_bytes,
+ bool all_or_none) OVERRIDE;
+ virtual MojoResult ConsumerQueryDataImplNoLock(uint32_t* num_bytes) OVERRIDE;
+ virtual MojoResult ConsumerBeginReadDataImplNoLock(const void** buffer,
+ uint32_t* buffer_num_bytes,
+ bool all_or_none) OVERRIDE;
+ virtual MojoResult ConsumerEndReadDataImplNoLock(
+ uint32_t num_bytes_read) OVERRIDE;
+ virtual HandleSignalsState
+ ConsumerGetHandleSignalsStateNoLock() const OVERRIDE;
+
+ void EnsureBufferNoLock();
+ void DestroyBufferNoLock();
+
+ // Get the maximum (single) write/read size right now (in number of elements);
+ // result fits in a |uint32_t|.
+ size_t GetMaxNumBytesToWriteNoLock();
+ size_t GetMaxNumBytesToReadNoLock();
+
+ // Marks the given number of bytes as consumed/discarded. |num_bytes| must be
+ // greater than |current_num_bytes_|.
+ void MarkDataAsConsumedNoLock(size_t num_bytes);
+
+ // The members below are protected by |DataPipe|'s |lock_|:
+ scoped_ptr<char, base::AlignedFreeDeleter> buffer_;
+ // Circular buffer.
+ size_t start_index_;
+ size_t current_num_bytes_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocalDataPipe);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_LOCAL_DATA_PIPE_H_
diff --git a/chromium/mojo/system/local_data_pipe_unittest.cc b/chromium/mojo/system/local_data_pipe_unittest.cc
new file mode 100644
index 00000000000..6d747bdf707
--- /dev/null
+++ b/chromium/mojo/system/local_data_pipe_unittest.cc
@@ -0,0 +1,1644 @@
+// Copyright 2013 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 "mojo/system/local_data_pipe.h"
+
+#include <string.h>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/system/data_pipe.h"
+#include "mojo/system/waiter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+const uint32_t kSizeOfOptions =
+ static_cast<uint32_t>(sizeof(MojoCreateDataPipeOptions));
+
+// Validate options.
+TEST(LocalDataPipeTest, Creation) {
+ // Create using default options.
+ {
+ // Get default options.
+ MojoCreateDataPipeOptions default_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(NULL, &default_options));
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(default_options));
+ dp->ProducerClose();
+ dp->ConsumerClose();
+ }
+
+ // Create using non-default options.
+ {
+ const MojoCreateDataPipeOptions options = {
+ kSizeOfOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ 1, // |element_num_bytes|.
+ 1000 // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+ dp->ProducerClose();
+ dp->ConsumerClose();
+ }
+ {
+ const MojoCreateDataPipeOptions options = {
+ kSizeOfOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ 4, // |element_num_bytes|.
+ 4000 // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+ dp->ProducerClose();
+ dp->ConsumerClose();
+ }
+ {
+ const MojoCreateDataPipeOptions options = {
+ kSizeOfOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD, // |flags|.
+ 7, // |element_num_bytes|.
+ 7000000 // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+ dp->ProducerClose();
+ dp->ConsumerClose();
+ }
+ // Default capacity.
+ {
+ const MojoCreateDataPipeOptions options = {
+ kSizeOfOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD, // |flags|.
+ 100, // |element_num_bytes|.
+ 0 // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+ dp->ProducerClose();
+ dp->ConsumerClose();
+ }
+}
+
+TEST(LocalDataPipeTest, SimpleReadWrite) {
+ const MojoCreateDataPipeOptions options = {
+ kSizeOfOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|.
+ 1000 * sizeof(int32_t) // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+ int32_t elements[10] = { 0 };
+ uint32_t num_bytes = 0;
+
+ // Try reading; nothing there yet.
+ num_bytes = static_cast<uint32_t>(arraysize(elements) * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ dp->ConsumerReadData(elements, &num_bytes, false));
+
+ // Query; nothing there yet.
+ num_bytes = 0;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(0u, num_bytes);
+
+ // Discard; nothing there yet.
+ num_bytes = static_cast<uint32_t>(5u * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ dp->ConsumerDiscardData(&num_bytes, false));
+
+ // Read with invalid |num_bytes|.
+ num_bytes = sizeof(elements[0]) + 1;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dp->ConsumerReadData(elements, &num_bytes, false));
+
+ // Write two elements.
+ elements[0] = 123;
+ elements[1] = 456;
+ num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerWriteData(elements, &num_bytes, false));
+ // It should have written everything (even without "all or none").
+ EXPECT_EQ(2u * sizeof(elements[0]), num_bytes);
+
+ // Query.
+ num_bytes = 0;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(2 * sizeof(elements[0]), num_bytes);
+
+ // Read one element.
+ elements[0] = -1;
+ elements[1] = -1;
+ num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerReadData(elements, &num_bytes, false));
+ EXPECT_EQ(1u * sizeof(elements[0]), num_bytes);
+ EXPECT_EQ(123, elements[0]);
+ EXPECT_EQ(-1, elements[1]);
+
+ // Query.
+ num_bytes = 0;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(1 * sizeof(elements[0]), num_bytes);
+
+ // Try to read two elements, with "all or none".
+ elements[0] = -1;
+ elements[1] = -1;
+ num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+ dp->ConsumerReadData(elements, &num_bytes, true));
+ EXPECT_EQ(-1, elements[0]);
+ EXPECT_EQ(-1, elements[1]);
+
+ // Try to read two elements, without "all or none".
+ elements[0] = -1;
+ elements[1] = -1;
+ num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerReadData(elements, &num_bytes, false));
+ EXPECT_EQ(456, elements[0]);
+ EXPECT_EQ(-1, elements[1]);
+
+ // Query.
+ num_bytes = 0;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(0u, num_bytes);
+
+ dp->ProducerClose();
+ dp->ConsumerClose();
+}
+
+// Note: The "basic" waiting tests test that the "wait states" are correct in
+// various situations; they don't test that waiters are properly awoken on state
+// changes. (For that, we need to use multiple threads.)
+TEST(LocalDataPipeTest, BasicProducerWaiting) {
+ // Note: We take advantage of the fact that for |LocalDataPipe|, capacities
+ // are strict maximums. This is not guaranteed by the API.
+
+ const MojoCreateDataPipeOptions options = {
+ kSizeOfOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|.
+ 2 * sizeof(int32_t) // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+ Waiter waiter;
+ uint32_t context = 0;
+
+ // Never readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 12));
+
+ // Already writable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 34));
+
+ // Write two elements.
+ int32_t elements[2] = { 123, 456 };
+ uint32_t num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerWriteData(elements, &num_bytes, true));
+ EXPECT_EQ(static_cast<uint32_t>(2u * sizeof(elements[0])), num_bytes);
+
+ // Adding a waiter should now succeed.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 56));
+ // And it shouldn't be writable yet.
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, NULL));
+ dp->ProducerRemoveWaiter(&waiter);
+
+ // Do it again.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 78));
+
+ // Read one element.
+ elements[0] = -1;
+ elements[1] = -1;
+ num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerReadData(elements, &num_bytes, true));
+ EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+ EXPECT_EQ(123, elements[0]);
+ EXPECT_EQ(-1, elements[1]);
+
+ // Waiting should now succeed.
+ EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(1000, &context));
+ EXPECT_EQ(78u, context);
+ dp->ProducerRemoveWaiter(&waiter);
+
+ // Try writing, using a two-phase write.
+ void* buffer = NULL;
+ num_bytes = static_cast<uint32_t>(3u * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerBeginWriteData(&buffer, &num_bytes, false));
+ EXPECT_TRUE(buffer != NULL);
+ EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+
+ static_cast<int32_t*>(buffer)[0] = 789;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerEndWriteData(
+ static_cast<uint32_t>(1u * sizeof(elements[0]))));
+
+ // Add a waiter.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 90));
+
+ // Read one element, using a two-phase read.
+ const void* read_buffer = NULL;
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerBeginReadData(&read_buffer, &num_bytes, false));
+ EXPECT_TRUE(read_buffer != NULL);
+ // Since we only read one element (after having written three in all), the
+ // two-phase read should only allow us to read one. This checks an
+ // implementation detail!
+ EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+ EXPECT_EQ(456, static_cast<const int32_t*>(read_buffer)[0]);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerEndReadData(
+ static_cast<uint32_t>(1u * sizeof(elements[0]))));
+
+ // Waiting should succeed.
+ EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(1000, &context));
+ EXPECT_EQ(90u, context);
+ dp->ProducerRemoveWaiter(&waiter);
+
+ // Write one element.
+ elements[0] = 123;
+ num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerWriteData(elements, &num_bytes, false));
+ EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+
+ // Add a waiter.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 12));
+
+ // Close the consumer.
+ dp->ConsumerClose();
+
+ // It should now be never-writable.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, waiter.Wait(1000, &context));
+ EXPECT_EQ(12u, context);
+ dp->ProducerRemoveWaiter(&waiter);
+
+ dp->ProducerClose();
+}
+
+TEST(LocalDataPipeTest, BasicConsumerWaiting) {
+ const MojoCreateDataPipeOptions options = {
+ kSizeOfOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|.
+ 1000 * sizeof(int32_t) // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+
+ {
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+ Waiter waiter;
+ uint32_t context = 0;
+
+ // Never writable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 12));
+
+ // Not yet readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 34));
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, NULL));
+ dp->ConsumerRemoveWaiter(&waiter);
+
+ // Write two elements.
+ int32_t elements[2] = { 123, 456 };
+ uint32_t num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerWriteData(elements, &num_bytes, true));
+
+ // Should already be readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 56));
+
+ // Discard one element.
+ num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerDiscardData(&num_bytes, true));
+ EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+
+ // Should still be readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 78));
+
+ // Read one element.
+ elements[0] = -1;
+ elements[1] = -1;
+ num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerReadData(elements, &num_bytes, true));
+ EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+ EXPECT_EQ(456, elements[0]);
+ EXPECT_EQ(-1, elements[1]);
+
+ // Adding a waiter should now succeed.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 90));
+
+ // Write one element.
+ elements[0] = 789;
+ elements[1] = -1;
+ num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerWriteData(elements, &num_bytes, true));
+
+ // Waiting should now succeed.
+ EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(1000, &context));
+ EXPECT_EQ(90u, context);
+ dp->ConsumerRemoveWaiter(&waiter);
+
+ // Close the producer.
+ dp->ProducerClose();
+
+ // Should still be readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 12));
+
+ // Read one element.
+ elements[0] = -1;
+ elements[1] = -1;
+ num_bytes = static_cast<uint32_t>(1u * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerReadData(elements, &num_bytes, true));
+ EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+ EXPECT_EQ(789, elements[0]);
+ EXPECT_EQ(-1, elements[1]);
+
+ // Should be never-readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 34));
+
+ dp->ConsumerClose();
+ }
+
+ // Test with two-phase APIs and closing the producer with an active consumer
+ // waiter.
+ {
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+ Waiter waiter;
+ uint32_t context = 0;
+
+ // Write two elements.
+ int32_t* elements = NULL;
+ void* buffer = NULL;
+ // Request room for three (but we'll only write two).
+ uint32_t num_bytes = static_cast<uint32_t>(3u * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerBeginWriteData(&buffer, &num_bytes, true));
+ EXPECT_TRUE(buffer != NULL);
+ EXPECT_GE(num_bytes, static_cast<uint32_t>(3u * sizeof(elements[0])));
+ elements = static_cast<int32_t*>(buffer);
+ elements[0] = 123;
+ elements[1] = 456;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerEndWriteData(
+ static_cast<uint32_t>(2u * sizeof(elements[0]))));
+
+ // Should already be readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 12));
+
+ // Read one element.
+ // Request two in all-or-none mode, but only read one.
+ const void* read_buffer = NULL;
+ num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerBeginReadData(&read_buffer, &num_bytes, true));
+ EXPECT_TRUE(read_buffer != NULL);
+ EXPECT_EQ(static_cast<uint32_t>(2u * sizeof(elements[0])), num_bytes);
+ const int32_t* read_elements = static_cast<const int32_t*>(read_buffer);
+ EXPECT_EQ(123, read_elements[0]);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerEndReadData(
+ static_cast<uint32_t>(1u * sizeof(elements[0]))));
+
+ // Should still be readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 34));
+
+ // Read one element.
+ // Request three, but not in all-or-none mode.
+ read_buffer = NULL;
+ num_bytes = static_cast<uint32_t>(3u * sizeof(elements[0]));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerBeginReadData(&read_buffer, &num_bytes, false));
+ EXPECT_TRUE(read_buffer != NULL);
+ EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
+ read_elements = static_cast<const int32_t*>(read_buffer);
+ EXPECT_EQ(456, read_elements[0]);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerEndReadData(
+ static_cast<uint32_t>(1u * sizeof(elements[0]))));
+
+ // Adding a waiter should now succeed.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 56));
+
+ // Close the producer.
+ dp->ProducerClose();
+
+ // Should be never-readable.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, waiter.Wait(1000, &context));
+ EXPECT_EQ(56u, context);
+ dp->ConsumerRemoveWaiter(&waiter);
+
+ dp->ConsumerClose();
+ }
+}
+
+// Tests that data pipes aren't writable/readable during two-phase writes/reads.
+TEST(LocalDataPipeTest, BasicTwoPhaseWaiting) {
+ const MojoCreateDataPipeOptions options = {
+ kSizeOfOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|.
+ 1000 * sizeof(int32_t) // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+ Waiter waiter;
+
+ // It should be writable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 0));
+
+ uint32_t num_bytes = static_cast<uint32_t>(1u * sizeof(int32_t));
+ void* write_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerBeginWriteData(&write_ptr, &num_bytes, false));
+ EXPECT_TRUE(write_ptr != NULL);
+ EXPECT_GE(num_bytes, static_cast<uint32_t>(1u * sizeof(int32_t)));
+
+ // At this point, it shouldn't be writable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 1));
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, NULL));
+ dp->ProducerRemoveWaiter(&waiter);
+
+ // It shouldn't be readable yet either.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 2));
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, NULL));
+ dp->ConsumerRemoveWaiter(&waiter);
+
+ static_cast<int32_t*>(write_ptr)[0] = 123;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerEndWriteData(
+ static_cast<uint32_t>(1u * sizeof(int32_t))));
+
+ // It should be writable again.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 3));
+
+ // And readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 4));
+
+ // Start another two-phase write and check that it's readable even in the
+ // middle of it.
+ num_bytes = static_cast<uint32_t>(1u * sizeof(int32_t));
+ write_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerBeginWriteData(&write_ptr, &num_bytes, false));
+ EXPECT_TRUE(write_ptr != NULL);
+ EXPECT_GE(num_bytes, static_cast<uint32_t>(1u * sizeof(int32_t)));
+
+ // It should be readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 5));
+
+ // End the two-phase write without writing anything.
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(0u));
+
+ // Start a two-phase read.
+ num_bytes = static_cast<uint32_t>(1u * sizeof(int32_t));
+ const void* read_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerBeginReadData(&read_ptr, &num_bytes, false));
+ EXPECT_TRUE(read_ptr != NULL);
+ EXPECT_EQ(static_cast<uint32_t>(1u * sizeof(int32_t)), num_bytes);
+
+ // At this point, it should still be writable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 6));
+
+ // But not readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 7));
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, NULL));
+ dp->ConsumerRemoveWaiter(&waiter);
+
+ // End the two-phase read without reading anything.
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(0u));
+
+ // It should be readable again.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 8));
+
+ dp->ProducerClose();
+ dp->ConsumerClose();
+}
+
+// Test that a "may discard" data pipe is writable even when it's full.
+TEST(LocalDataPipeTest, BasicMayDiscardWaiting) {
+ const MojoCreateDataPipeOptions options = {
+ kSizeOfOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD, // |flags|.
+ static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|.
+ 1 * sizeof(int32_t) // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+ Waiter waiter;
+
+ // Writable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 0));
+
+ // Not readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 1));
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, NULL));
+ dp->ConsumerRemoveWaiter(&waiter);
+
+ uint32_t num_bytes = static_cast<uint32_t>(sizeof(int32_t));
+ int32_t element = 123;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerWriteData(&element, &num_bytes, false));
+ EXPECT_EQ(static_cast<uint32_t>(sizeof(int32_t)), num_bytes);
+
+ // Still writable (even though it's full).
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 2));
+
+ // Now readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 3));
+
+ // Overwrite that element.
+ num_bytes = static_cast<uint32_t>(sizeof(int32_t));
+ element = 456;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerWriteData(&element, &num_bytes, false));
+ EXPECT_EQ(static_cast<uint32_t>(sizeof(int32_t)), num_bytes);
+
+ // Still writable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 4));
+
+ // And still readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 5));
+
+ // Read that element.
+ num_bytes = static_cast<uint32_t>(sizeof(int32_t));
+ element = 0;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerReadData(&element, &num_bytes, false));
+ EXPECT_EQ(static_cast<uint32_t>(sizeof(int32_t)), num_bytes);
+ EXPECT_EQ(456, element);
+
+ // Still writable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ dp->ProducerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 6));
+
+ // No longer readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerAddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 7));
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, NULL));
+ dp->ConsumerRemoveWaiter(&waiter);
+
+ dp->ProducerClose();
+ dp->ConsumerClose();
+}
+
+void Seq(int32_t start, size_t count, int32_t* out) {
+ for (size_t i = 0; i < count; i++)
+ out[i] = start + static_cast<int32_t>(i);
+}
+
+TEST(LocalDataPipeTest, MayDiscard) {
+ const MojoCreateDataPipeOptions options = {
+ kSizeOfOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD, // |flags|.
+ static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|.
+ 10 * sizeof(int32_t) // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+ int32_t buffer[100] = { 0 };
+ uint32_t num_bytes = 0;
+
+ num_bytes = 20u * sizeof(int32_t);
+ Seq(0, arraysize(buffer), buffer);
+ // Try writing more than capacity. (This test relies on the implementation
+ // enforcing the capacity strictly.)
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerWriteData(buffer, &num_bytes, false));
+ EXPECT_EQ(10u * sizeof(int32_t), num_bytes);
+
+ // Read half of what we wrote.
+ num_bytes = 5u * sizeof(int32_t);
+ memset(buffer, 0xab, sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerReadData(buffer, &num_bytes, false));
+ EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+ int32_t expected_buffer[100];
+ memset(expected_buffer, 0xab, sizeof(expected_buffer));
+ Seq(0, 5u, expected_buffer);
+ EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+ // Internally, a circular buffer would now look like:
+ // -, -, -, -, -, 5, 6, 7, 8, 9
+
+ // Write a bit more than the space that's available.
+ num_bytes = 8u * sizeof(int32_t);
+ Seq(100, arraysize(buffer), buffer);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerWriteData(buffer, &num_bytes, false));
+ EXPECT_EQ(8u * sizeof(int32_t), num_bytes);
+ // Internally, a circular buffer would now look like:
+ // 100, 101, 102, 103, 104, 105, 106, 107, 8, 9
+
+ // Read half of what's available.
+ num_bytes = 5u * sizeof(int32_t);
+ memset(buffer, 0xab, sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerReadData(buffer, &num_bytes, false));
+ EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+ memset(expected_buffer, 0xab, sizeof(expected_buffer));
+ expected_buffer[0] = 8;
+ expected_buffer[1] = 9;
+ expected_buffer[2] = 100;
+ expected_buffer[3] = 101;
+ expected_buffer[4] = 102;
+ EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+ // Internally, a circular buffer would now look like:
+ // -, -, -, 103, 104, 105, 106, 107, -, -
+
+ // Write one integer.
+ num_bytes = 1u * sizeof(int32_t);
+ Seq(200, arraysize(buffer), buffer);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerWriteData(buffer, &num_bytes, false));
+ EXPECT_EQ(1u * sizeof(int32_t), num_bytes);
+ // Internally, a circular buffer would now look like:
+ // -, -, -, 103, 104, 105, 106, 107, 200, -
+
+ // Write five more.
+ num_bytes = 5u * sizeof(int32_t);
+ Seq(300, arraysize(buffer), buffer);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerWriteData(buffer, &num_bytes, false));
+ EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+ // Internally, a circular buffer would now look like:
+ // 301, 302, 303, 304, 104, 105, 106, 107, 200, 300
+
+ // Read it all.
+ num_bytes = sizeof(buffer);
+ memset(buffer, 0xab, sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerReadData(buffer, &num_bytes, false));
+ EXPECT_EQ(10u * sizeof(int32_t), num_bytes);
+ memset(expected_buffer, 0xab, sizeof(expected_buffer));
+ expected_buffer[0] = 104;
+ expected_buffer[1] = 105;
+ expected_buffer[2] = 106;
+ expected_buffer[3] = 107;
+ expected_buffer[4] = 200;
+ expected_buffer[5] = 300;
+ expected_buffer[6] = 301;
+ expected_buffer[7] = 302;
+ expected_buffer[8] = 303;
+ expected_buffer[9] = 304;
+ EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+ // Test two-phase writes, including in all-or-none mode.
+ // Note: Again, the following depends on an implementation detail -- namely
+ // that the write pointer will point at the 5th element of the buffer (and the
+ // buffer has exactly the capacity requested).
+
+ num_bytes = 0u;
+ void* write_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerBeginWriteData(&write_ptr, &num_bytes, false));
+ EXPECT_TRUE(write_ptr != NULL);
+ EXPECT_EQ(6u * sizeof(int32_t), num_bytes);
+ Seq(400, 6, static_cast<int32_t*>(write_ptr));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(6u * sizeof(int32_t)));
+ // Internally, a circular buffer would now look like:
+ // -, -, -, -, 400, 401, 402, 403, 404, 405
+
+ // |ProducerBeginWriteData()| ignores |*num_bytes| except in "all-or-none"
+ // mode.
+ num_bytes = 6u * sizeof(int32_t);
+ write_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerBeginWriteData(&write_ptr, &num_bytes, false));
+ EXPECT_EQ(4u * sizeof(int32_t), num_bytes);
+ static_cast<int32_t*>(write_ptr)[0] = 500;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(1u * sizeof(int32_t)));
+ // Internally, a circular buffer would now look like:
+ // 500, -, -, -, 400, 401, 402, 403, 404, 405
+
+ // Requesting a 10-element buffer in all-or-none mode fails at this point.
+ num_bytes = 10u * sizeof(int32_t);
+ write_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+ dp->ProducerBeginWriteData(&write_ptr, &num_bytes, true));
+
+ // But requesting, say, a 5-element (up to 9, really) buffer should be okay.
+ // It will discard two elements.
+ num_bytes = 5u * sizeof(int32_t);
+ write_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerBeginWriteData(&write_ptr, &num_bytes, true));
+ EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+ // Only write 4 elements though.
+ Seq(600, 4, static_cast<int32_t*>(write_ptr));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(4u * sizeof(int32_t)));
+ // Internally, a circular buffer would now look like:
+ // 500, 600, 601, 602, 603, -, 402, 403, 404, 405
+
+ // Do this again. Make sure we can get a buffer all the way out to the end of
+ // the internal buffer.
+ num_bytes = 5u * sizeof(int32_t);
+ write_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerBeginWriteData(&write_ptr, &num_bytes, true));
+ EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+ // Only write 3 elements though.
+ Seq(700, 3, static_cast<int32_t*>(write_ptr));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(3u * sizeof(int32_t)));
+ // Internally, a circular buffer would now look like:
+ // 500, 600, 601, 602, 603, 700, 701, 702, -, -
+
+ // Read everything.
+ num_bytes = sizeof(buffer);
+ memset(buffer, 0xab, sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerReadData(buffer, &num_bytes, false));
+ EXPECT_EQ(8u * sizeof(int32_t), num_bytes);
+ memset(expected_buffer, 0xab, sizeof(expected_buffer));
+ expected_buffer[0] = 500;
+ expected_buffer[1] = 600;
+ expected_buffer[2] = 601;
+ expected_buffer[3] = 602;
+ expected_buffer[4] = 603;
+ expected_buffer[5] = 700;
+ expected_buffer[6] = 701;
+ expected_buffer[7] = 702;
+ EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+ dp->ProducerClose();
+ dp->ConsumerClose();
+}
+
+TEST(LocalDataPipeTest, AllOrNone) {
+ const MojoCreateDataPipeOptions options = {
+ kSizeOfOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|.
+ 10 * sizeof(int32_t) // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+ // Try writing way too much.
+ uint32_t num_bytes = 20u * sizeof(int32_t);
+ int32_t buffer[100];
+ Seq(0, arraysize(buffer), buffer);
+ EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+ dp->ProducerWriteData(buffer, &num_bytes, true));
+
+ // Should still be empty.
+ num_bytes = ~0u;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(0u, num_bytes);
+
+ // Write some data.
+ num_bytes = 5u * sizeof(int32_t);
+ Seq(100, arraysize(buffer), buffer);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerWriteData(buffer, &num_bytes, true));
+ EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+
+ // Half full.
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+
+ // Too much.
+ num_bytes = 6u * sizeof(int32_t);
+ Seq(200, arraysize(buffer), buffer);
+ EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+ dp->ProducerWriteData(buffer, &num_bytes, true));
+
+ // Try reading too much.
+ num_bytes = 11u * sizeof(int32_t);
+ memset(buffer, 0xab, sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+ dp->ConsumerReadData(buffer, &num_bytes, true));
+ int32_t expected_buffer[100];
+ memset(expected_buffer, 0xab, sizeof(expected_buffer));
+ EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+ // Try discarding too much.
+ num_bytes = 11u * sizeof(int32_t);
+ EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+ dp->ConsumerDiscardData(&num_bytes, true));
+
+ // Just a little.
+ num_bytes = 2u * sizeof(int32_t);
+ Seq(300, arraysize(buffer), buffer);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerWriteData(buffer, &num_bytes, true));
+ EXPECT_EQ(2u * sizeof(int32_t), num_bytes);
+
+ // Just right.
+ num_bytes = 3u * sizeof(int32_t);
+ Seq(400, arraysize(buffer), buffer);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerWriteData(buffer, &num_bytes, true));
+ EXPECT_EQ(3u * sizeof(int32_t), num_bytes);
+
+ // Exactly full.
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(10u * sizeof(int32_t), num_bytes);
+
+ // Read half.
+ num_bytes = 5u * sizeof(int32_t);
+ memset(buffer, 0xab, sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerReadData(buffer, &num_bytes, true));
+ EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+ memset(expected_buffer, 0xab, sizeof(expected_buffer));
+ Seq(100, 5, expected_buffer);
+ EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+ // Try reading too much again.
+ num_bytes = 6u * sizeof(int32_t);
+ memset(buffer, 0xab, sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+ dp->ConsumerReadData(buffer, &num_bytes, true));
+ memset(expected_buffer, 0xab, sizeof(expected_buffer));
+ EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+ // Try discarding too much again.
+ num_bytes = 6u * sizeof(int32_t);
+ EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+ dp->ConsumerDiscardData(&num_bytes, true));
+
+ // Discard a little.
+ num_bytes = 2u * sizeof(int32_t);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerDiscardData(&num_bytes, true));
+ EXPECT_EQ(2u * sizeof(int32_t), num_bytes);
+
+ // Three left.
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(3u * sizeof(int32_t), num_bytes);
+
+ // Close the producer, then test producer-closed cases.
+ dp->ProducerClose();
+
+ // Try reading too much; "failed precondition" since the producer is closed.
+ num_bytes = 4u * sizeof(int32_t);
+ memset(buffer, 0xab, sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ dp->ConsumerReadData(buffer, &num_bytes, true));
+ memset(expected_buffer, 0xab, sizeof(expected_buffer));
+ EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+ // Try discarding too much; "failed precondition" again.
+ num_bytes = 4u * sizeof(int32_t);
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ dp->ConsumerDiscardData(&num_bytes, true));
+
+ // Read a little.
+ num_bytes = 2u * sizeof(int32_t);
+ memset(buffer, 0xab, sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerReadData(buffer, &num_bytes, true));
+ EXPECT_EQ(2u * sizeof(int32_t), num_bytes);
+ memset(expected_buffer, 0xab, sizeof(expected_buffer));
+ Seq(400, 2, expected_buffer);
+ EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+ // Discard the remaining element.
+ num_bytes = 1u * sizeof(int32_t);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerDiscardData(&num_bytes, true));
+ EXPECT_EQ(1u * sizeof(int32_t), num_bytes);
+
+ // Empty again.
+ num_bytes = ~0u;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(0u, num_bytes);
+
+ dp->ConsumerClose();
+}
+
+TEST(LocalDataPipeTest, AllOrNoneMayDiscard) {
+ const MojoCreateDataPipeOptions options = {
+ kSizeOfOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD, // |flags|.
+ static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|.
+ 10 * sizeof(int32_t) // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+ // Try writing way too much.
+ uint32_t num_bytes = 20u * sizeof(int32_t);
+ int32_t buffer[100];
+ Seq(0, arraysize(buffer), buffer);
+ EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+ dp->ProducerWriteData(buffer, &num_bytes, true));
+
+ // Write some stuff.
+ num_bytes = 5u * sizeof(int32_t);
+ Seq(100, arraysize(buffer), buffer);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerWriteData(buffer, &num_bytes, true));
+ EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+
+ // Write lots of stuff (discarding all but "104").
+ num_bytes = 9u * sizeof(int32_t);
+ Seq(200, arraysize(buffer), buffer);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerWriteData(buffer, &num_bytes, true));
+ EXPECT_EQ(9u * sizeof(int32_t), num_bytes);
+
+ // Read one.
+ num_bytes = 1u * sizeof(int32_t);
+ memset(buffer, 0xab, sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerReadData(buffer, &num_bytes, true));
+ EXPECT_EQ(1u * sizeof(int32_t), num_bytes);
+ int32_t expected_buffer[100];
+ memset(expected_buffer, 0xab, sizeof(expected_buffer));
+ expected_buffer[0] = 104;
+ EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+ // Try reading too many.
+ num_bytes = 10u * sizeof(int32_t);
+ memset(buffer, 0xab, sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+ dp->ConsumerReadData(buffer, &num_bytes, true));
+ memset(expected_buffer, 0xab, sizeof(expected_buffer));
+ EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+ // Try discarding too many.
+ num_bytes = 10u * sizeof(int32_t);
+ EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+ dp->ConsumerDiscardData(&num_bytes, true));
+
+ // Discard a bunch.
+ num_bytes = 4u * sizeof(int32_t);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerDiscardData(&num_bytes, true));
+
+ // Half full.
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(5u * sizeof(int32_t), num_bytes);
+
+ // Write as much as possible.
+ num_bytes = 10u * sizeof(int32_t);
+ Seq(300, arraysize(buffer), buffer);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerWriteData(buffer, &num_bytes, true));
+ EXPECT_EQ(10u * sizeof(int32_t), num_bytes);
+
+ // Read everything.
+ num_bytes = 10u * sizeof(int32_t);
+ memset(buffer, 0xab, sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerReadData(buffer, &num_bytes, true));
+ memset(expected_buffer, 0xab, sizeof(expected_buffer));
+ EXPECT_EQ(10u * sizeof(int32_t), num_bytes);
+ Seq(300, 10, expected_buffer);
+ EXPECT_EQ(0, memcmp(buffer, expected_buffer, sizeof(buffer)));
+
+ // Note: All-or-none two-phase writes on a "may discard" data pipe are tested
+ // in LocalDataPipeTest.MayDiscard.
+
+ dp->ProducerClose();
+ dp->ConsumerClose();
+}
+
+TEST(LocalDataPipeTest, TwoPhaseAllOrNone) {
+ const MojoCreateDataPipeOptions options = {
+ kSizeOfOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|.
+ 10 * sizeof(int32_t) // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+ // Try writing way too much (two-phase).
+ uint32_t num_bytes = 20u * sizeof(int32_t);
+ void* write_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+ dp->ProducerBeginWriteData(&write_ptr, &num_bytes, true));
+
+ // Try writing an amount which isn't a multiple of the element size
+ // (two-phase).
+ COMPILE_ASSERT(sizeof(int32_t) > 1u, wow_int32_ts_have_size_1);
+ num_bytes = 1u;
+ write_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dp->ProducerBeginWriteData(&write_ptr, &num_bytes, true));
+
+ // Try reading way too much (two-phase).
+ num_bytes = 20u * sizeof(int32_t);
+ const void* read_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+ dp->ConsumerBeginReadData(&read_ptr, &num_bytes, true));
+
+ // Write half (two-phase).
+ num_bytes = 5u * sizeof(int32_t);
+ write_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerBeginWriteData(&write_ptr, &num_bytes, true));
+ // May provide more space than requested.
+ EXPECT_GE(num_bytes, 5u * sizeof(int32_t));
+ EXPECT_TRUE(write_ptr != NULL);
+ Seq(0, 5, static_cast<int32_t*>(write_ptr));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(5u * sizeof(int32_t)));
+
+ // Try reading an amount which isn't a multiple of the element size
+ // (two-phase).
+ num_bytes = 1u;
+ read_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dp->ConsumerBeginReadData(&read_ptr, &num_bytes, true));
+
+ // Read one (two-phase).
+ num_bytes = 1u * sizeof(int32_t);
+ read_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerBeginReadData(&read_ptr, &num_bytes, true));
+ EXPECT_GE(num_bytes, 1u * sizeof(int32_t));
+ EXPECT_EQ(0, static_cast<const int32_t*>(read_ptr)[0]);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(1u * sizeof(int32_t)));
+
+ // We should have four left, leaving room for six.
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(4u * sizeof(int32_t), num_bytes);
+
+ // Assuming a tight circular buffer of the specified capacity, we can't do a
+ // two-phase write of six now.
+ num_bytes = 6u * sizeof(int32_t);
+ write_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+ dp->ProducerBeginWriteData(&write_ptr, &num_bytes, true));
+
+ // Write six elements (simple), filling the buffer.
+ num_bytes = 6u * sizeof(int32_t);
+ int32_t buffer[100];
+ Seq(100, 6, buffer);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerWriteData(buffer, &num_bytes, true));
+ EXPECT_EQ(6u * sizeof(int32_t), num_bytes);
+
+ // We have ten.
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(10u * sizeof(int32_t), num_bytes);
+
+ // But a two-phase read of ten should fail.
+ num_bytes = 10u * sizeof(int32_t);
+ read_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
+ dp->ConsumerBeginReadData(&read_ptr, &num_bytes, true));
+
+ // Close the producer.
+ dp->ProducerClose();
+
+ // A two-phase read of nine should work.
+ num_bytes = 9u * sizeof(int32_t);
+ read_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerBeginReadData(&read_ptr, &num_bytes, true));
+ EXPECT_GE(num_bytes, 9u * sizeof(int32_t));
+ EXPECT_EQ(1, static_cast<const int32_t*>(read_ptr)[0]);
+ EXPECT_EQ(2, static_cast<const int32_t*>(read_ptr)[1]);
+ EXPECT_EQ(3, static_cast<const int32_t*>(read_ptr)[2]);
+ EXPECT_EQ(4, static_cast<const int32_t*>(read_ptr)[3]);
+ EXPECT_EQ(100, static_cast<const int32_t*>(read_ptr)[4]);
+ EXPECT_EQ(101, static_cast<const int32_t*>(read_ptr)[5]);
+ EXPECT_EQ(102, static_cast<const int32_t*>(read_ptr)[6]);
+ EXPECT_EQ(103, static_cast<const int32_t*>(read_ptr)[7]);
+ EXPECT_EQ(104, static_cast<const int32_t*>(read_ptr)[8]);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(9u * sizeof(int32_t)));
+
+ // A two-phase read of two should fail, with "failed precondition".
+ num_bytes = 2u * sizeof(int32_t);
+ read_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ dp->ConsumerBeginReadData(&read_ptr, &num_bytes, true));
+
+ dp->ConsumerClose();
+}
+
+// Tests that |ProducerWriteData()| and |ConsumerReadData()| writes and reads,
+// respectively, as much as possible, even if it has to "wrap around" the
+// internal circular buffer. (Note that the two-phase write and read do not do
+// this.)
+TEST(LocalDataPipeTest, WrapAround) {
+ unsigned char test_data[1000];
+ for (size_t i = 0; i < arraysize(test_data); i++)
+ test_data[i] = static_cast<unsigned char>(i);
+
+ const MojoCreateDataPipeOptions options = {
+ kSizeOfOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ 1u, // |element_num_bytes|.
+ 100u // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+ // This test won't be valid if |ValidateCreateOptions()| decides to give the
+ // pipe more space.
+ ASSERT_EQ(100u, validated_options.capacity_num_bytes);
+
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+ // Write 20 bytes.
+ uint32_t num_bytes = 20u;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerWriteData(&test_data[0], &num_bytes, false));
+ EXPECT_EQ(20u, num_bytes);
+
+ // Read 10 bytes.
+ unsigned char read_buffer[1000] = { 0 };
+ num_bytes = 10u;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerReadData(read_buffer, &num_bytes, false));
+ EXPECT_EQ(10u, num_bytes);
+ EXPECT_EQ(0, memcmp(read_buffer, &test_data[0], 10u));
+
+ // Check that a two-phase write can now only write (at most) 80 bytes. (This
+ // checks an implementation detail; this behavior is not guaranteed, but we
+ // need it for this test.)
+ void* write_buffer_ptr = NULL;
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerBeginWriteData(&write_buffer_ptr, &num_bytes, false));
+ EXPECT_TRUE(write_buffer_ptr != NULL);
+ EXPECT_EQ(80u, num_bytes);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(0u));
+
+ // Write as much data as we can (using |ProducerWriteData()|). We should write
+ // 90 bytes.
+ num_bytes = 200u;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerWriteData(&test_data[20], &num_bytes, false));
+ EXPECT_EQ(90u, num_bytes);
+
+ // Check that a two-phase read can now only read (at most) 90 bytes. (This
+ // checks an implementation detail; this behavior is not guaranteed, but we
+ // need it for this test.)
+ const void* read_buffer_ptr = NULL;
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerBeginReadData(&read_buffer_ptr, &num_bytes, false));
+ EXPECT_TRUE(read_buffer_ptr != NULL);
+ EXPECT_EQ(90u, num_bytes);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(0u));
+
+ // Read as much as possible (using |ConsumerReadData()|). We should read 100
+ // bytes.
+ num_bytes =
+ static_cast<uint32_t>(arraysize(read_buffer) * sizeof(read_buffer[0]));
+ memset(read_buffer, 0, num_bytes);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerReadData(read_buffer, &num_bytes, false));
+ EXPECT_EQ(100u, num_bytes);
+ EXPECT_EQ(0, memcmp(read_buffer, &test_data[10], 100u));
+
+ dp->ProducerClose();
+ dp->ConsumerClose();
+}
+
+// Tests the behavior of closing the producer or consumer with respect to
+// writes and reads (simple and two-phase).
+TEST(LocalDataPipeTest, CloseWriteRead) {
+ const char kTestData[] = "hello world";
+ const uint32_t kTestDataSize = static_cast<uint32_t>(sizeof(kTestData));
+
+ const MojoCreateDataPipeOptions options = {
+ kSizeOfOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ 1u, // |element_num_bytes|.
+ 1000u // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+
+ // Close producer first, then consumer.
+ {
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+ // Write some data, so we'll have something to read.
+ uint32_t num_bytes = kTestDataSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerWriteData(kTestData, &num_bytes, false));
+ EXPECT_EQ(kTestDataSize, num_bytes);
+
+ // Write it again, so we'll have something left over.
+ num_bytes = kTestDataSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerWriteData(kTestData, &num_bytes, false));
+ EXPECT_EQ(kTestDataSize, num_bytes);
+
+ // Start two-phase write.
+ void* write_buffer_ptr = NULL;
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerBeginWriteData(&write_buffer_ptr, &num_bytes, false));
+ EXPECT_TRUE(write_buffer_ptr != NULL);
+ EXPECT_GT(num_bytes, 0u);
+
+ // Start two-phase read.
+ const void* read_buffer_ptr = NULL;
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerBeginReadData(&read_buffer_ptr, &num_bytes, false));
+ EXPECT_TRUE(read_buffer_ptr != NULL);
+ EXPECT_EQ(2u * kTestDataSize, num_bytes);
+
+ // Close the producer.
+ dp->ProducerClose();
+
+ // The consumer can finish its two-phase read.
+ EXPECT_EQ(0, memcmp(read_buffer_ptr, kTestData, kTestDataSize));
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(kTestDataSize));
+
+ // And start another.
+ read_buffer_ptr = NULL;
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerBeginReadData(&read_buffer_ptr, &num_bytes, false));
+ EXPECT_TRUE(read_buffer_ptr != NULL);
+ EXPECT_EQ(kTestDataSize, num_bytes);
+
+ // Close the consumer, which cancels the two-phase read.
+ dp->ConsumerClose();
+ }
+
+ // Close consumer first, then producer.
+ {
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+ // Write some data, so we'll have something to read.
+ uint32_t num_bytes = kTestDataSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerWriteData(kTestData, &num_bytes, false));
+ EXPECT_EQ(kTestDataSize, num_bytes);
+
+ // Start two-phase write.
+ void* write_buffer_ptr = NULL;
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerBeginWriteData(&write_buffer_ptr, &num_bytes, false));
+ EXPECT_TRUE(write_buffer_ptr != NULL);
+ ASSERT_GT(num_bytes, kTestDataSize);
+
+ // Start two-phase read.
+ const void* read_buffer_ptr = NULL;
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerBeginReadData(&read_buffer_ptr, &num_bytes, false));
+ EXPECT_TRUE(read_buffer_ptr != NULL);
+ EXPECT_EQ(kTestDataSize, num_bytes);
+
+ // Close the consumer.
+ dp->ConsumerClose();
+
+ // Actually write some data. (Note: Premature freeing of the buffer would
+ // probably only be detected under ASAN or similar.)
+ memcpy(write_buffer_ptr, kTestData, kTestDataSize);
+ // Note: Even though the consumer has been closed, ending the two-phase
+ // write will report success.
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerEndWriteData(kTestDataSize));
+
+ // But trying to write should result in failure.
+ num_bytes = kTestDataSize;
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ dp->ProducerWriteData(kTestData, &num_bytes, false));
+
+ // As will trying to start another two-phase write.
+ write_buffer_ptr = NULL;
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ dp->ProducerBeginWriteData(&write_buffer_ptr, &num_bytes, false));
+
+ dp->ProducerClose();
+ }
+
+ // Test closing the consumer first, then the producer, with an active
+ // two-phase write.
+ {
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+ // Start two-phase write.
+ void* write_buffer_ptr = NULL;
+ uint32_t num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerBeginWriteData(&write_buffer_ptr, &num_bytes, false));
+ EXPECT_TRUE(write_buffer_ptr != NULL);
+ ASSERT_GT(num_bytes, kTestDataSize);
+
+ dp->ConsumerClose();
+ dp->ProducerClose();
+ }
+
+ // Test closing the producer and then trying to read (with no data).
+ {
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+ // Write some data, so we'll have something to read.
+ uint32_t num_bytes = kTestDataSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerWriteData(kTestData, &num_bytes, false));
+ EXPECT_EQ(kTestDataSize, num_bytes);
+
+ // Close the producer.
+ dp->ProducerClose();
+
+ // Read that data.
+ char buffer[1000];
+ num_bytes = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerReadData(buffer, &num_bytes, false));
+ EXPECT_EQ(kTestDataSize, num_bytes);
+ EXPECT_EQ(0, memcmp(buffer, kTestData, kTestDataSize));
+
+ // A second read should fail.
+ num_bytes = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ dp->ConsumerReadData(buffer, &num_bytes, false));
+
+ // A two-phase read should also fail.
+ const void* read_buffer_ptr = NULL;
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ dp->ConsumerBeginReadData(&read_buffer_ptr, &num_bytes, false));
+
+ // Ditto for discard.
+ num_bytes = 10u;
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ dp->ConsumerDiscardData(&num_bytes, false));
+
+ dp->ConsumerClose();
+ }
+}
+
+TEST(LocalDataPipeTest, TwoPhaseMoreInvalidArguments) {
+ const MojoCreateDataPipeOptions options = {
+ kSizeOfOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
+ static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|.
+ 10 * sizeof(int32_t) // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+ // No data.
+ uint32_t num_bytes = 1000u;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(0u, num_bytes);
+
+ // Try "ending" a two-phase write when one isn't active.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ dp->ProducerEndWriteData(1u * sizeof(int32_t)));
+
+ // Still no data.
+ num_bytes = 1000u;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(0u, num_bytes);
+
+ // Try ending a two-phase write with an invalid amount (too much).
+ num_bytes = 0u;
+ void* write_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerBeginWriteData(&write_ptr, &num_bytes, false));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dp->ProducerEndWriteData(
+ num_bytes + static_cast<uint32_t>(sizeof(int32_t))));
+
+ // But the two-phase write still ended.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, dp->ProducerEndWriteData(0u));
+
+ // Still no data.
+ num_bytes = 1000u;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(0u, num_bytes);
+
+ // Try ending a two-phase write with an invalid amount (not a multiple of the
+ // element size).
+ num_bytes = 0u;
+ write_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ProducerBeginWriteData(&write_ptr, &num_bytes, false));
+ EXPECT_GE(num_bytes, 1u);
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, dp->ProducerEndWriteData(1u));
+
+ // But the two-phase write still ended.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, dp->ProducerEndWriteData(0u));
+
+ // Still no data.
+ num_bytes = 1000u;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(0u, num_bytes);
+
+ // Now write some data, so we'll be able to try reading.
+ int32_t element = 123;
+ num_bytes = 1u * sizeof(int32_t);
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerWriteData(&element, &num_bytes, false));
+
+ // One element available.
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(1u * sizeof(int32_t), num_bytes);
+
+ // Try "ending" a two-phase read when one isn't active.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ dp->ConsumerEndReadData(1u * sizeof(int32_t)));
+
+ // Still one element available.
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(1u * sizeof(int32_t), num_bytes);
+
+ // Try ending a two-phase read with an invalid amount (too much).
+ num_bytes = 0u;
+ const void* read_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerBeginReadData(&read_ptr, &num_bytes, false));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dp->ConsumerEndReadData(
+ num_bytes + static_cast<uint32_t>(sizeof(int32_t))));
+
+ // Still one element available.
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(1u * sizeof(int32_t), num_bytes);
+
+ // Try ending a two-phase read with an invalid amount (not a multiple of the
+ // element size).
+ num_bytes = 0u;
+ read_ptr = NULL;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerBeginReadData(&read_ptr, &num_bytes, false));
+ EXPECT_EQ(1u * sizeof(int32_t), num_bytes);
+ EXPECT_EQ(123, static_cast<const int32_t*>(read_ptr)[0]);
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, dp->ConsumerEndReadData(1u));
+
+ // Still one element available.
+ num_bytes = 0u;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerQueryData(&num_bytes));
+ EXPECT_EQ(1u * sizeof(int32_t), num_bytes);
+
+ dp->ProducerClose();
+ dp->ConsumerClose();
+}
+
+// Tests that even with "may discard", the data won't change under a two-phase
+// read.
+// TODO(vtl): crbug.com/348644: We currently don't pass this. (There are two
+// related issues: First, we don't recognize that the data given to
+// |ConsumerBeginReadData()| isn't discardable until |ConsumerEndReadData()|,
+// and thus we erroneously allow |ProducerWriteData()| to succeed. Second, the
+// |ProducerWriteData()| then changes the data underneath the two-phase read.)
+TEST(LocalDataPipeTest, DISABLED_MayDiscardTwoPhaseConsistent) {
+ const MojoCreateDataPipeOptions options = {
+ kSizeOfOptions, // |struct_size|.
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD, // |flags|.
+ 1, // |element_num_bytes|.
+ 2 // |capacity_num_bytes|.
+ };
+ MojoCreateDataPipeOptions validated_options = { 0 };
+ EXPECT_EQ(MOJO_RESULT_OK,
+ DataPipe::ValidateCreateOptions(&options, &validated_options));
+
+ scoped_refptr<LocalDataPipe> dp(new LocalDataPipe(validated_options));
+
+ // Write some elements.
+ char elements[2] = { 'a', 'b' };
+ uint32_t num_bytes = 2u;
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerWriteData(elements, &num_bytes, false));
+ EXPECT_EQ(2u, num_bytes);
+
+ // Begin reading.
+ const void* read_ptr = NULL;
+ num_bytes = 2u;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerBeginReadData(&read_ptr, &num_bytes, false));
+ EXPECT_EQ(2u, num_bytes);
+ EXPECT_EQ('a', static_cast<const char*>(read_ptr)[0]);
+ EXPECT_EQ('b', static_cast<const char*>(read_ptr)[1]);
+
+ // Try to write some more. But nothing should be discardable right now.
+ elements[0] = 'x';
+ elements[1] = 'y';
+ num_bytes = 2u;
+ // TODO(vtl): This should be:
+ // EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ // dp->ProducerWriteData(elements, &num_bytes, false));
+ // but we incorrectly think that the bytes being read are discardable. Letting
+ // this through reveals the significant consequence.
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerWriteData(elements, &num_bytes, false));
+
+ // Check that our read buffer hasn't changed underneath us.
+ EXPECT_EQ('a', static_cast<const char*>(read_ptr)[0]);
+ EXPECT_EQ('b', static_cast<const char*>(read_ptr)[1]);
+
+ // End reading.
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(2u));
+
+ // Now writing should succeed.
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ProducerWriteData(elements, &num_bytes, false));
+
+ // And if we read, we should get the new values.
+ read_ptr = NULL;
+ num_bytes = 2u;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dp->ConsumerBeginReadData(&read_ptr, &num_bytes, false));
+ EXPECT_EQ(2u, num_bytes);
+ EXPECT_EQ('x', static_cast<const char*>(read_ptr)[0]);
+ EXPECT_EQ('y', static_cast<const char*>(read_ptr)[1]);
+
+ // End reading.
+ EXPECT_EQ(MOJO_RESULT_OK, dp->ConsumerEndReadData(2u));
+
+ dp->ProducerClose();
+ dp->ConsumerClose();
+}
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/local_message_pipe_endpoint.cc b/chromium/mojo/system/local_message_pipe_endpoint.cc
new file mode 100644
index 00000000000..09decd3fabb
--- /dev/null
+++ b/chromium/mojo/system/local_message_pipe_endpoint.cc
@@ -0,0 +1,165 @@
+// Copyright 2013 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 "mojo/system/local_message_pipe_endpoint.h"
+
+#include <string.h>
+
+#include "base/logging.h"
+#include "mojo/system/dispatcher.h"
+#include "mojo/system/message_in_transit.h"
+
+namespace mojo {
+namespace system {
+
+LocalMessagePipeEndpoint::LocalMessagePipeEndpoint()
+ : is_open_(true),
+ is_peer_open_(true) {
+}
+
+LocalMessagePipeEndpoint::~LocalMessagePipeEndpoint() {
+ DCHECK(!is_open_);
+ DCHECK(message_queue_.IsEmpty()); // Should be implied by not being open.
+}
+
+MessagePipeEndpoint::Type LocalMessagePipeEndpoint::GetType() const {
+ return kTypeLocal;
+}
+
+bool LocalMessagePipeEndpoint::OnPeerClose() {
+ DCHECK(is_open_);
+ DCHECK(is_peer_open_);
+
+ HandleSignalsState old_state = GetHandleSignalsState();
+ is_peer_open_ = false;
+ HandleSignalsState new_state = GetHandleSignalsState();
+
+ if (!new_state.equals(old_state))
+ waiter_list_.AwakeWaitersForStateChange(new_state);
+
+ return true;
+}
+
+void LocalMessagePipeEndpoint::EnqueueMessage(
+ scoped_ptr<MessageInTransit> message) {
+ DCHECK(is_open_);
+ DCHECK(is_peer_open_);
+
+ bool was_empty = message_queue_.IsEmpty();
+ message_queue_.AddMessage(message.Pass());
+ if (was_empty)
+ waiter_list_.AwakeWaitersForStateChange(GetHandleSignalsState());
+}
+
+void LocalMessagePipeEndpoint::Close() {
+ DCHECK(is_open_);
+ is_open_ = false;
+ message_queue_.Clear();
+}
+
+void LocalMessagePipeEndpoint::CancelAllWaiters() {
+ DCHECK(is_open_);
+ waiter_list_.CancelAllWaiters();
+}
+
+MojoResult LocalMessagePipeEndpoint::ReadMessage(void* bytes,
+ uint32_t* num_bytes,
+ DispatcherVector* dispatchers,
+ uint32_t* num_dispatchers,
+ MojoReadMessageFlags flags) {
+ DCHECK(is_open_);
+ DCHECK(!dispatchers || dispatchers->empty());
+
+ const uint32_t max_bytes = num_bytes ? *num_bytes : 0;
+ const uint32_t max_num_dispatchers = num_dispatchers ? *num_dispatchers : 0;
+
+ if (message_queue_.IsEmpty()) {
+ return is_peer_open_ ? MOJO_RESULT_SHOULD_WAIT :
+ MOJO_RESULT_FAILED_PRECONDITION;
+ }
+
+ // TODO(vtl): If |flags & MOJO_READ_MESSAGE_FLAG_MAY_DISCARD|, we could pop
+ // and release the lock immediately.
+ bool enough_space = true;
+ MessageInTransit* message = message_queue_.PeekMessage();
+ if (num_bytes)
+ *num_bytes = message->num_bytes();
+ if (message->num_bytes() <= max_bytes)
+ memcpy(bytes, message->bytes(), message->num_bytes());
+ else
+ enough_space = false;
+
+ if (DispatcherVector* queued_dispatchers = message->dispatchers()) {
+ if (num_dispatchers)
+ *num_dispatchers = static_cast<uint32_t>(queued_dispatchers->size());
+ if (enough_space) {
+ if (queued_dispatchers->empty()) {
+ // Nothing to do.
+ } else if (queued_dispatchers->size() <= max_num_dispatchers) {
+ DCHECK(dispatchers);
+ dispatchers->swap(*queued_dispatchers);
+ } else {
+ enough_space = false;
+ }
+ }
+ } else {
+ if (num_dispatchers)
+ *num_dispatchers = 0;
+ }
+
+ message = NULL;
+
+ if (enough_space || (flags & MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)) {
+ message_queue_.DiscardMessage();
+
+ // Now it's empty, thus no longer readable.
+ if (message_queue_.IsEmpty()) {
+ // It's currently not possible to wait for non-readability, but we should
+ // do the state change anyway.
+ waiter_list_.AwakeWaitersForStateChange(GetHandleSignalsState());
+ }
+ }
+
+ if (!enough_space)
+ return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+ return MOJO_RESULT_OK;
+}
+
+MojoResult LocalMessagePipeEndpoint::AddWaiter(Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context) {
+ DCHECK(is_open_);
+
+ HandleSignalsState state = GetHandleSignalsState();
+ if (state.satisfies(signals))
+ return MOJO_RESULT_ALREADY_EXISTS;
+ if (!state.can_satisfy(signals))
+ return MOJO_RESULT_FAILED_PRECONDITION;
+
+ waiter_list_.AddWaiter(waiter, signals, context);
+ return MOJO_RESULT_OK;
+}
+
+void LocalMessagePipeEndpoint::RemoveWaiter(Waiter* waiter) {
+ DCHECK(is_open_);
+ waiter_list_.RemoveWaiter(waiter);
+}
+
+HandleSignalsState LocalMessagePipeEndpoint::GetHandleSignalsState() {
+ HandleSignalsState rv;
+ if (!message_queue_.IsEmpty()) {
+ rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_READABLE;
+ rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE;
+ }
+ if (is_peer_open_) {
+ rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_WRITABLE;
+ rv.satisfiable_signals |=
+ MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE;
+ }
+ return rv;
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/local_message_pipe_endpoint.h b/chromium/mojo/system/local_message_pipe_endpoint.h
new file mode 100644
index 00000000000..4e59cfbc421
--- /dev/null
+++ b/chromium/mojo/system/local_message_pipe_endpoint.h
@@ -0,0 +1,63 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_LOCAL_MESSAGE_PIPE_ENDPOINT_H_
+#define MOJO_SYSTEM_LOCAL_MESSAGE_PIPE_ENDPOINT_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "mojo/system/handle_signals_state.h"
+#include "mojo/system/message_in_transit_queue.h"
+#include "mojo/system/message_pipe_endpoint.h"
+#include "mojo/system/system_impl_export.h"
+#include "mojo/system/waiter_list.h"
+
+namespace mojo {
+namespace system {
+
+class MOJO_SYSTEM_IMPL_EXPORT LocalMessagePipeEndpoint
+ : public MessagePipeEndpoint {
+ public:
+ LocalMessagePipeEndpoint();
+ virtual ~LocalMessagePipeEndpoint();
+
+ // |MessagePipeEndpoint| implementation:
+ virtual Type GetType() const OVERRIDE;
+ virtual bool OnPeerClose() OVERRIDE;
+ virtual void EnqueueMessage(scoped_ptr<MessageInTransit> message) OVERRIDE;
+
+ // There's a dispatcher for |LocalMessagePipeEndpoint|s, so we have to
+ // implement/override these:
+ virtual void Close() OVERRIDE;
+ virtual void CancelAllWaiters() OVERRIDE;
+ virtual MojoResult ReadMessage(void* bytes,
+ uint32_t* num_bytes,
+ DispatcherVector* dispatchers,
+ uint32_t* num_dispatchers,
+ MojoReadMessageFlags flags) OVERRIDE;
+ virtual MojoResult AddWaiter(Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context) OVERRIDE;
+ virtual void RemoveWaiter(Waiter* waiter) OVERRIDE;
+
+ // This is only to be used by |ProxyMessagePipeEndpoint|:
+ MessageInTransitQueue* message_queue() { return &message_queue_; }
+
+ private:
+ HandleSignalsState GetHandleSignalsState();
+
+ bool is_open_;
+ bool is_peer_open_;
+
+ // Queue of incoming messages.
+ MessageInTransitQueue message_queue_;
+ WaiterList waiter_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocalMessagePipeEndpoint);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_LOCAL_MESSAGE_PIPE_ENDPOINT_H_
diff --git a/chromium/mojo/system/mapping_table.cc b/chromium/mojo/system/mapping_table.cc
new file mode 100644
index 00000000000..a6e5bb39cb8
--- /dev/null
+++ b/chromium/mojo/system/mapping_table.cc
@@ -0,0 +1,48 @@
+// Copyright 2014 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 "mojo/system/mapping_table.h"
+
+#include "base/logging.h"
+#include "mojo/system/constants.h"
+#include "mojo/system/raw_shared_buffer.h"
+
+namespace mojo {
+namespace system {
+
+MappingTable::MappingTable() {
+}
+
+MappingTable::~MappingTable() {
+ // This should usually not be reached (the only instance should be owned by
+ // the singleton |Core|, which lives forever), except in tests.
+}
+
+MojoResult MappingTable::AddMapping(
+ scoped_ptr<RawSharedBufferMapping> mapping) {
+ DCHECK(mapping);
+
+ if (address_to_mapping_map_.size() >= kMaxMappingTableSize)
+ return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+ uintptr_t address = reinterpret_cast<uintptr_t>(mapping->base());
+ DCHECK(address_to_mapping_map_.find(address) ==
+ address_to_mapping_map_.end());
+ address_to_mapping_map_[address] = mapping.release();
+ return MOJO_RESULT_OK;
+}
+
+MojoResult MappingTable::RemoveMapping(void* address) {
+ AddressToMappingMap::iterator it =
+ address_to_mapping_map_.find(reinterpret_cast<uintptr_t>(address));
+ if (it == address_to_mapping_map_.end())
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ RawSharedBufferMapping* mapping_to_delete = it->second;
+ address_to_mapping_map_.erase(it);
+ delete mapping_to_delete;
+ return MOJO_RESULT_OK;
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/mapping_table.h b/chromium/mojo/system/mapping_table.h
new file mode 100644
index 00000000000..5268234b1fe
--- /dev/null
+++ b/chromium/mojo/system/mapping_table.h
@@ -0,0 +1,57 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SYSTEM_MAPPING_TABLE_H_
+#define MOJO_SYSTEM_MAPPING_TABLE_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class Core;
+class RawSharedBufferMapping;
+
+// Test-only function (defined/used in embedder/test_embedder.cc). Declared here
+// so it can be friended.
+namespace internal {
+bool ShutdownCheckNoLeaks(Core*);
+}
+
+// This class provides the (global) table of memory mappings (owned by |Core|),
+// which maps mapping base addresses to |RawSharedBuffer::Mapping|s.
+//
+// This class is NOT thread-safe; locking is left to |Core|.
+class MOJO_SYSTEM_IMPL_EXPORT MappingTable {
+ public:
+ MappingTable();
+ ~MappingTable();
+
+ // Tries to add a mapping. (Takes ownership of the mapping in all cases; on
+ // failure, it will be destroyed.)
+ MojoResult AddMapping(scoped_ptr<RawSharedBufferMapping> mapping);
+ MojoResult RemoveMapping(void* address);
+
+ private:
+ friend bool internal::ShutdownCheckNoLeaks(Core*);
+
+ typedef base::hash_map<uintptr_t, RawSharedBufferMapping*>
+ AddressToMappingMap;
+ AddressToMappingMap address_to_mapping_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(MappingTable);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_MAPPING_TABLE_H_
diff --git a/chromium/mojo/system/memory.cc b/chromium/mojo/system/memory.cc
new file mode 100644
index 00000000000..cee7e052145
--- /dev/null
+++ b/chromium/mojo/system/memory.cc
@@ -0,0 +1,99 @@
+// Copyright 2013 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 "mojo/system/memory.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "build/build_config.h"
+
+namespace mojo {
+namespace system {
+
+namespace internal {
+
+template <size_t alignment>
+bool IsAligned(const void* pointer) {
+ return reinterpret_cast<uintptr_t>(pointer) % alignment == 0;
+}
+
+// MSVS (2010, 2013) sometimes (on the stack) aligns, e.g., |int64_t|s (for
+// which |__alignof(int64_t)| is 8) to 4-byte boundaries. http://goo.gl/Y2n56T
+#if defined(COMPILER_MSVC) && defined(ARCH_CPU_32_BITS)
+template <>
+bool IsAligned<8>(const void* pointer) {
+ return reinterpret_cast<uintptr_t>(pointer) % 4 == 0;
+}
+#endif
+
+template <size_t size, size_t alignment>
+bool MOJO_SYSTEM_IMPL_EXPORT VerifyUserPointerHelper(const void* pointer) {
+ // TODO(vtl): If running in kernel mode, do a full verification. For now, just
+ // check that it's non-null and aligned. (A faster user mode implementation is
+ // also possible if this check is skipped.)
+ return !!pointer && IsAligned<alignment>(pointer);
+}
+
+// Explicitly instantiate the sizes we need. Add instantiations as needed.
+template bool MOJO_SYSTEM_IMPL_EXPORT VerifyUserPointerHelper<1, 1>(
+ const void*);
+template bool MOJO_SYSTEM_IMPL_EXPORT VerifyUserPointerHelper<4, 4>(
+ const void*);
+template bool MOJO_SYSTEM_IMPL_EXPORT VerifyUserPointerHelper<8, 8>(
+ const void*);
+// Notwithstanding the comments above about MSVS, whenever we expect an
+// alignment of 8 for something of size 4, it's due to an explicit (e.g.,
+// #pragma align) alignment specification, which MSVS *does* respect. We want
+// this in particular to check that various Options structs are aligned.
+#if defined(COMPILER_MSVC) && defined(ARCH_CPU_32_BITS)
+template <>
+bool MOJO_SYSTEM_IMPL_EXPORT VerifyUserPointerHelper<4, 8>(
+ const void* pointer) {
+ return !!pointer && reinterpret_cast<uintptr_t>(pointer) % 8 == 0;
+}
+#else
+template MOJO_SYSTEM_IMPL_EXPORT bool VerifyUserPointerHelper<4, 8>(
+ const void*);
+#endif
+
+template <size_t size, size_t alignment>
+bool VerifyUserPointerWithCountHelper(const void* pointer, size_t count) {
+ if (count > std::numeric_limits<size_t>::max() / size)
+ return false;
+
+ // TODO(vtl): If running in kernel mode, do a full verification. For now, just
+ // check that it's non-null and aligned if |count| is nonzero. (A faster user
+ // mode implementation is also possible if this check is skipped.)
+ return count == 0 || (!!pointer && IsAligned<alignment>(pointer));
+}
+
+// Explicitly instantiate the sizes we need. Add instantiations as needed.
+template bool MOJO_SYSTEM_IMPL_EXPORT VerifyUserPointerWithCountHelper<1, 1>(
+ const void*, size_t);
+template bool MOJO_SYSTEM_IMPL_EXPORT VerifyUserPointerWithCountHelper<4, 4>(
+ const void*, size_t);
+template bool MOJO_SYSTEM_IMPL_EXPORT VerifyUserPointerWithCountHelper<8, 8>(
+ const void*, size_t);
+
+} // nameespace internal
+
+template <size_t alignment>
+bool VerifyUserPointerWithSize(const void* pointer, size_t size) {
+ // TODO(vtl): If running in kernel mode, do a full verification. For now, just
+ // check that it's non-null and aligned. (A faster user mode implementation is
+ // also possible if this check is skipped.)
+ return size == 0 || (!!pointer && internal::IsAligned<alignment>(pointer));
+}
+
+// Explicitly instantiate the alignments we need. Add instantiations as needed.
+template bool MOJO_SYSTEM_IMPL_EXPORT VerifyUserPointerWithSize<1>(const void*,
+ size_t);
+template bool MOJO_SYSTEM_IMPL_EXPORT VerifyUserPointerWithSize<4>(const void*,
+ size_t);
+template bool MOJO_SYSTEM_IMPL_EXPORT VerifyUserPointerWithSize<8>(const void*,
+ size_t);
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/memory.h b/chromium/mojo/system/memory.h
new file mode 100644
index 00000000000..96f800ee15d
--- /dev/null
+++ b/chromium/mojo/system/memory.h
@@ -0,0 +1,56 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_MEMORY_H_
+#define MOJO_SYSTEM_MEMORY_H_
+
+#include <stddef.h>
+
+#include "mojo/public/c/system/macros.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+namespace internal {
+
+template <size_t size, size_t alignment>
+bool MOJO_SYSTEM_IMPL_EXPORT VerifyUserPointerHelper(const void* pointer);
+
+// Note: This is also used by options_validation.h.
+template <size_t size, size_t alignment>
+bool MOJO_SYSTEM_IMPL_EXPORT VerifyUserPointerWithCountHelper(
+ const void* pointer,
+ size_t count);
+
+} // namespace internal
+
+// Verify (insofar as possible/necessary) that a |T| can be read from the user
+// |pointer|.
+template <typename T>
+bool VerifyUserPointer(const T* pointer) {
+ return internal::VerifyUserPointerHelper<sizeof(T), MOJO_ALIGNOF(T)>(pointer);
+}
+
+// Verify (insofar as possible/necessary) that |count| |T|s can be read from the
+// user |pointer|; |count| may be zero. (This is done carefully since |count *
+// sizeof(T)| may overflow a |size_t|.)
+template <typename T>
+bool VerifyUserPointerWithCount(const T* pointer, size_t count) {
+ return internal::VerifyUserPointerWithCountHelper<sizeof(T),
+ MOJO_ALIGNOF(T)>(pointer,
+ count);
+}
+
+// Verify that |size| bytes (which may be zero) can be read from the user
+// |pointer|, and that |pointer| has the specified |alignment| (if |size| is
+// nonzero).
+template <size_t alignment>
+bool MOJO_SYSTEM_IMPL_EXPORT VerifyUserPointerWithSize(const void* pointer,
+ size_t size);
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_MEMORY_H_
diff --git a/chromium/mojo/system/memory_unittest.cc b/chromium/mojo/system/memory_unittest.cc
new file mode 100644
index 00000000000..b23d50ae7be
--- /dev/null
+++ b/chromium/mojo/system/memory_unittest.cc
@@ -0,0 +1,136 @@
+// Copyright 2014 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 "mojo/system/memory.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+
+#include "mojo/public/c/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+TEST(MemoryTest, Valid) {
+ char my_char;
+ int32_t my_int32;
+ int64_t my_int64;
+ char my_char_array[5];
+ int32_t my_int32_array[5];
+ int64_t my_int64_array[5];
+
+ // |VerifyUserPointer|:
+
+ EXPECT_TRUE(VerifyUserPointer<char>(&my_char));
+ EXPECT_TRUE(VerifyUserPointer<int32_t>(&my_int32));
+ EXPECT_TRUE(VerifyUserPointer<int64_t>(&my_int64));
+
+ // |VerifyUserPointerWithCount|:
+
+ EXPECT_TRUE(VerifyUserPointerWithCount<char>(my_char_array, 5));
+ EXPECT_TRUE(VerifyUserPointerWithCount<int32_t>(my_int32_array, 5));
+ EXPECT_TRUE(VerifyUserPointerWithCount<int64_t>(my_int64_array, 5));
+
+ // It shouldn't care what the pointer is if the count is zero.
+ EXPECT_TRUE(VerifyUserPointerWithCount<char>(NULL, 0));
+ EXPECT_TRUE(VerifyUserPointerWithCount<int32_t>(NULL, 0));
+ EXPECT_TRUE(VerifyUserPointerWithCount<int32_t>(
+ reinterpret_cast<const int32_t*>(1), 0));
+ EXPECT_TRUE(VerifyUserPointerWithCount<int64_t>(NULL, 0));
+ EXPECT_TRUE(VerifyUserPointerWithCount<int64_t>(
+ reinterpret_cast<const int64_t*>(1), 0));
+
+ // |VerifyUserPointerWithSize|:
+
+ EXPECT_TRUE(VerifyUserPointerWithSize<1>(&my_char, sizeof(my_char)));
+ EXPECT_TRUE(VerifyUserPointerWithSize<1>(&my_int32, sizeof(my_int32)));
+ EXPECT_TRUE(VerifyUserPointerWithSize<MOJO_ALIGNOF(int32_t)>(
+ &my_int32, sizeof(my_int32)));
+ EXPECT_TRUE(VerifyUserPointerWithSize<1>(&my_int64, sizeof(my_int64)));
+ EXPECT_TRUE(VerifyUserPointerWithSize<MOJO_ALIGNOF(int64_t)>(
+ &my_int64, sizeof(my_int64)));
+
+ EXPECT_TRUE(VerifyUserPointerWithSize<1>(my_char_array,
+ sizeof(my_char_array)));
+ EXPECT_TRUE(VerifyUserPointerWithSize<1>(my_int32_array,
+ sizeof(my_int32_array)));
+ EXPECT_TRUE(VerifyUserPointerWithSize<MOJO_ALIGNOF(int32_t)>(
+ my_int32_array, sizeof(my_int32_array)));
+ EXPECT_TRUE(VerifyUserPointerWithSize<1>(my_int64_array,
+ sizeof(my_int64_array)));
+ EXPECT_TRUE(VerifyUserPointerWithSize<MOJO_ALIGNOF(int64_t)>(
+ my_int64_array, sizeof(my_int64_array)));
+}
+
+TEST(MemoryTest, Invalid) {
+ // Note: |VerifyUserPointer...()| are defined to be "best effort" checks (and
+ // may always return true). Thus these tests of invalid cases only reflect the
+ // current implementation.
+
+ // These tests depend on |int32_t| and |int64_t| having nontrivial alignment.
+ MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int32_t) != 1,
+ int32_t_does_not_have_to_be_aligned);
+ MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int64_t) != 1,
+ int64_t_does_not_have_to_be_aligned);
+
+ int32_t my_int32;
+ int64_t my_int64;
+
+ // |VerifyUserPointer|:
+
+ EXPECT_FALSE(VerifyUserPointer<char>(NULL));
+ EXPECT_FALSE(VerifyUserPointer<int32_t>(NULL));
+ EXPECT_FALSE(VerifyUserPointer<int64_t>(NULL));
+
+ // Unaligned:
+ EXPECT_FALSE(VerifyUserPointer<int32_t>(reinterpret_cast<const int32_t*>(1)));
+ EXPECT_FALSE(VerifyUserPointer<int64_t>(reinterpret_cast<const int64_t*>(1)));
+
+ // |VerifyUserPointerWithCount|:
+
+ EXPECT_FALSE(VerifyUserPointerWithCount<char>(NULL, 1));
+ EXPECT_FALSE(VerifyUserPointerWithCount<int32_t>(NULL, 1));
+ EXPECT_FALSE(VerifyUserPointerWithCount<int64_t>(NULL, 1));
+
+ // Unaligned:
+ EXPECT_FALSE(VerifyUserPointerWithCount<int32_t>(
+ reinterpret_cast<const int32_t*>(1), 1));
+ EXPECT_FALSE(VerifyUserPointerWithCount<int64_t>(
+ reinterpret_cast<const int64_t*>(1), 1));
+
+ // Count too big:
+ EXPECT_FALSE(VerifyUserPointerWithCount<int32_t>(
+ &my_int32, std::numeric_limits<size_t>::max()));
+ EXPECT_FALSE(VerifyUserPointerWithCount<int64_t>(
+ &my_int64, std::numeric_limits<size_t>::max()));
+
+ // |VerifyUserPointerWithSize|:
+
+ EXPECT_FALSE(VerifyUserPointerWithSize<1>(NULL, 1));
+ EXPECT_FALSE(VerifyUserPointerWithSize<4>(NULL, 1));
+ EXPECT_FALSE(VerifyUserPointerWithSize<4>(NULL, 4));
+ EXPECT_FALSE(VerifyUserPointerWithSize<8>(NULL, 1));
+ EXPECT_FALSE(VerifyUserPointerWithSize<8>(NULL, 4));
+ EXPECT_FALSE(VerifyUserPointerWithSize<8>(NULL, 8));
+
+ // Unaligned:
+ EXPECT_FALSE(VerifyUserPointerWithSize<4>(reinterpret_cast<const int32_t*>(1),
+ 1));
+ EXPECT_FALSE(VerifyUserPointerWithSize<4>(reinterpret_cast<const int32_t*>(1),
+ 4));
+ EXPECT_FALSE(VerifyUserPointerWithSize<8>(reinterpret_cast<const int32_t*>(1),
+ 1));
+ EXPECT_FALSE(VerifyUserPointerWithSize<8>(reinterpret_cast<const int32_t*>(1),
+ 4));
+ EXPECT_FALSE(VerifyUserPointerWithSize<8>(reinterpret_cast<const int32_t*>(1),
+ 8));
+}
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/message_in_transit.cc b/chromium/mojo/system/message_in_transit.cc
new file mode 100644
index 00000000000..944e12b19ae
--- /dev/null
+++ b/chromium/mojo/system/message_in_transit.cc
@@ -0,0 +1,206 @@
+// Copyright 2013 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 "mojo/system/message_in_transit.h"
+
+#include <string.h>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "mojo/system/constants.h"
+#include "mojo/system/transport_data.h"
+
+namespace mojo {
+namespace system {
+
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type
+ MessageInTransit::kTypeMessagePipeEndpoint;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type
+ MessageInTransit::kTypeMessagePipe;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type
+ MessageInTransit::kTypeChannel;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type
+ MessageInTransit::kTypeRawChannel;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype
+ MessageInTransit::kSubtypeMessagePipeEndpointData;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype
+ MessageInTransit::kSubtypeChannelRunMessagePipeEndpoint;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype
+ MessageInTransit::kSubtypeChannelRemoveMessagePipeEndpoint;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype
+ MessageInTransit::kSubtypeChannelRemoveMessagePipeEndpointAck;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype
+ MessageInTransit::kSubtypeRawChannelPosixExtraPlatformHandles;
+STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::EndpointId
+ MessageInTransit::kInvalidEndpointId;
+STATIC_CONST_MEMBER_DEFINITION const size_t MessageInTransit::kMessageAlignment;
+
+struct MessageInTransit::PrivateStructForCompileAsserts {
+ // The size of |Header| must be a multiple of the alignment.
+ COMPILE_ASSERT(sizeof(Header) % kMessageAlignment == 0,
+ sizeof_MessageInTransit_Header_invalid);
+ // Avoid dangerous situations, but making sure that the size of the "header" +
+ // the size of the data fits into a 31-bit number.
+ COMPILE_ASSERT(static_cast<uint64_t>(sizeof(Header)) + kMaxMessageNumBytes <=
+ 0x7fffffffULL,
+ kMaxMessageNumBytes_too_big);
+
+ // We assume (to avoid extra rounding code) that the maximum message (data)
+ // size is a multiple of the alignment.
+ COMPILE_ASSERT(kMaxMessageNumBytes % kMessageAlignment == 0,
+ kMessageAlignment_not_a_multiple_of_alignment);
+};
+
+MessageInTransit::View::View(size_t message_size, const void* buffer)
+ : buffer_(buffer) {
+ size_t next_message_size = 0;
+ DCHECK(MessageInTransit::GetNextMessageSize(buffer_, message_size,
+ &next_message_size));
+ DCHECK_EQ(message_size, next_message_size);
+ // This should be equivalent.
+ DCHECK_EQ(message_size, total_size());
+}
+
+bool MessageInTransit::View::IsValid(size_t serialized_platform_handle_size,
+ const char** error_message) const {
+ // Note: This also implies a check on the |main_buffer_size()|, which is just
+ // |RoundUpMessageAlignment(sizeof(Header) + num_bytes())|.
+ if (num_bytes() > kMaxMessageNumBytes) {
+ *error_message = "Message data payload too large";
+ return false;
+ }
+
+ if (transport_data_buffer_size() > 0) {
+ const char* e =
+ TransportData::ValidateBuffer(serialized_platform_handle_size,
+ transport_data_buffer(),
+ transport_data_buffer_size());
+ if (e) {
+ *error_message = e;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+MessageInTransit::MessageInTransit(Type type,
+ Subtype subtype,
+ uint32_t num_bytes,
+ const void* bytes)
+ : main_buffer_size_(RoundUpMessageAlignment(sizeof(Header) + num_bytes)),
+ main_buffer_(static_cast<char*>(base::AlignedAlloc(main_buffer_size_,
+ kMessageAlignment))) {
+ DCHECK_LE(num_bytes, kMaxMessageNumBytes);
+
+ // |total_size| is updated below, from the other values.
+ header()->type = type;
+ header()->subtype = subtype;
+ header()->source_id = kInvalidEndpointId;
+ header()->destination_id = kInvalidEndpointId;
+ header()->num_bytes = num_bytes;
+ header()->unused = 0;
+ // Note: If dispatchers are subsequently attached, then |total_size| will have
+ // to be adjusted.
+ UpdateTotalSize();
+
+ if (bytes) {
+ memcpy(MessageInTransit::bytes(), bytes, num_bytes);
+ memset(static_cast<char*>(MessageInTransit::bytes()) + num_bytes, 0,
+ main_buffer_size_ - sizeof(Header) - num_bytes);
+ } else {
+ memset(MessageInTransit::bytes(), 0, main_buffer_size_ - sizeof(Header));
+ }
+}
+
+MessageInTransit::MessageInTransit(const View& message_view)
+ : main_buffer_size_(message_view.main_buffer_size()),
+ main_buffer_(static_cast<char*>(base::AlignedAlloc(main_buffer_size_,
+ kMessageAlignment))) {
+ DCHECK_GE(main_buffer_size_, sizeof(Header));
+ DCHECK_EQ(main_buffer_size_ % kMessageAlignment, 0u);
+
+ memcpy(main_buffer_.get(), message_view.main_buffer(), main_buffer_size_);
+ DCHECK_EQ(main_buffer_size_,
+ RoundUpMessageAlignment(sizeof(Header) + num_bytes()));
+}
+
+MessageInTransit::~MessageInTransit() {
+ if (dispatchers_) {
+ for (size_t i = 0; i < dispatchers_->size(); i++) {
+ if (!(*dispatchers_)[i])
+ continue;
+
+ DCHECK((*dispatchers_)[i]->HasOneRef());
+ (*dispatchers_)[i]->Close();
+ }
+ }
+}
+
+// static
+bool MessageInTransit::GetNextMessageSize(const void* buffer,
+ size_t buffer_size,
+ size_t* next_message_size) {
+ DCHECK(next_message_size);
+ if (!buffer_size)
+ return false;
+ DCHECK(buffer);
+ DCHECK_EQ(reinterpret_cast<uintptr_t>(buffer) %
+ MessageInTransit::kMessageAlignment, 0u);
+
+ if (buffer_size < sizeof(Header))
+ return false;
+
+ const Header* header = static_cast<const Header*>(buffer);
+ *next_message_size = header->total_size;
+ DCHECK_EQ(*next_message_size % kMessageAlignment, 0u);
+ return true;
+}
+
+void MessageInTransit::SetDispatchers(
+ scoped_ptr<DispatcherVector> dispatchers) {
+ DCHECK(dispatchers);
+ DCHECK(!dispatchers_);
+ DCHECK(!transport_data_);
+
+ dispatchers_ = dispatchers.Pass();
+#ifndef NDEBUG
+ for (size_t i = 0; i < dispatchers_->size(); i++)
+ DCHECK(!(*dispatchers_)[i] || (*dispatchers_)[i]->HasOneRef());
+#endif
+}
+
+void MessageInTransit::SetTransportData(
+ scoped_ptr<TransportData> transport_data) {
+ DCHECK(transport_data);
+ DCHECK(!transport_data_);
+ DCHECK(!dispatchers_);
+
+ transport_data_ = transport_data.Pass();
+}
+
+void MessageInTransit::SerializeAndCloseDispatchers(Channel* channel) {
+ DCHECK(channel);
+ DCHECK(!transport_data_);
+
+ if (!dispatchers_ || !dispatchers_->size())
+ return;
+
+ transport_data_.reset(new TransportData(dispatchers_.Pass(), channel));
+
+ // Update the sizes in the message header.
+ UpdateTotalSize();
+}
+
+void MessageInTransit::UpdateTotalSize() {
+ DCHECK_EQ(main_buffer_size_ % kMessageAlignment, 0u);
+ header()->total_size = static_cast<uint32_t>(main_buffer_size_);
+ if (transport_data_) {
+ header()->total_size +=
+ static_cast<uint32_t>(transport_data_->buffer_size());
+ }
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/message_in_transit.h b/chromium/mojo/system/message_in_transit.h
new file mode 100644
index 00000000000..5a4a614c119
--- /dev/null
+++ b/chromium/mojo/system/message_in_transit.h
@@ -0,0 +1,258 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_MESSAGE_IN_TRANSIT_H_
+#define MOJO_SYSTEM_MESSAGE_IN_TRANSIT_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/aligned_memory.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/system/dispatcher.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class Channel;
+class TransportData;
+
+// This class is used to represent data in transit. It is thread-unsafe.
+//
+// |MessageInTransit| buffers:
+//
+// A |MessageInTransit| can be serialized by writing the main buffer and then,
+// if it has one, the transport data buffer. Both buffers are
+// |kMessageAlignment|-byte aligned and a multiple of |kMessageAlignment| bytes
+// in size.
+//
+// The main buffer consists of the header (of type |Header|, which is an
+// internal detail of this class) followed immediately by the message data
+// (accessed by |bytes()| and of size |num_bytes()|, and also
+// |kMessageAlignment|-byte aligned), and then any padding needed to make the
+// main buffer a multiple of |kMessageAlignment| bytes in size.
+//
+// See |TransportData| for a description of the (serialized) transport data
+// buffer.
+class MOJO_SYSTEM_IMPL_EXPORT MessageInTransit {
+ public:
+ typedef uint16_t Type;
+ // Messages that are forwarded to |MessagePipeEndpoint|s.
+ static const Type kTypeMessagePipeEndpoint = 0;
+ // Messages that are forwarded to |MessagePipe|s.
+ static const Type kTypeMessagePipe = 1;
+ // Messages that are consumed by the |Channel|.
+ static const Type kTypeChannel = 2;
+ // Messages that are consumed by the |RawChannel| (implementation).
+ static const Type kTypeRawChannel = 3;
+
+ typedef uint16_t Subtype;
+ // Subtypes for type |kTypeMessagePipeEndpoint|:
+ static const Subtype kSubtypeMessagePipeEndpointData = 0;
+ // Subtypes for type |kTypeMessagePipe|:
+ // Nothing currently.
+ // Subtypes for type |kTypeChannel|:
+ static const Subtype kSubtypeChannelRunMessagePipeEndpoint = 0;
+ static const Subtype kSubtypeChannelRemoveMessagePipeEndpoint = 1;
+ static const Subtype kSubtypeChannelRemoveMessagePipeEndpointAck = 2;
+ // Subtypes for type |kTypeRawChannel|:
+ static const Subtype kSubtypeRawChannelPosixExtraPlatformHandles = 0;
+
+ typedef uint32_t EndpointId;
+ // Never a valid endpoint ID.
+ static const EndpointId kInvalidEndpointId = 0;
+
+ // Messages (the header and data) must always be aligned to a multiple of this
+ // quantity (which must be a power of 2).
+ static const size_t kMessageAlignment = 8;
+
+ // Forward-declare |Header| so that |View| can use it:
+ private:
+ struct Header;
+ public:
+ // This represents a view of serialized message data in a raw buffer.
+ class MOJO_SYSTEM_IMPL_EXPORT View {
+ public:
+ // Constructs a view from the given buffer of the given size. (The size must
+ // be as provided by |MessageInTransit::GetNextMessageSize()|.) The buffer
+ // must remain alive/unmodified through the lifetime of this object.
+ // |buffer| should be |kMessageAlignment|-byte aligned.
+ View(size_t message_size, const void* buffer);
+
+ // Checks that the given |View| appears to be for a valid message, within
+ // predetermined limits (e.g., |num_bytes()| and |main_buffer_size()|, that
+ // |transport_data_buffer()|/|transport_data_buffer_size()| is for valid
+ // transport data -- see |TransportData::ValidateBuffer()|).
+ //
+ // It returns true (and leaves |error_message| alone) if this object appears
+ // to be a valid message (according to the above) and false, pointing
+ // |*error_message| to a suitable error message, if not.
+ bool IsValid(size_t serialized_platform_handle_size,
+ const char** error_message) const;
+
+ // API parallel to that for |MessageInTransit| itself (mostly getters for
+ // header data).
+ const void* main_buffer() const { return buffer_; }
+ size_t main_buffer_size() const {
+ return RoundUpMessageAlignment(sizeof(Header) + header()->num_bytes);
+ }
+ const void* transport_data_buffer() const {
+ return (total_size() > main_buffer_size()) ?
+ static_cast<const char*>(buffer_) + main_buffer_size() : NULL;
+ }
+ size_t transport_data_buffer_size() const {
+ return total_size() - main_buffer_size();
+ }
+ size_t total_size() const { return header()->total_size; }
+ uint32_t num_bytes() const { return header()->num_bytes; }
+ const void* bytes() const {
+ return static_cast<const char*>(buffer_) + sizeof(Header);
+ }
+ Type type() const { return header()->type; }
+ Subtype subtype() const { return header()->subtype; }
+ EndpointId source_id() const { return header()->source_id; }
+ EndpointId destination_id() const { return header()->destination_id; }
+
+ private:
+ const Header* header() const { return static_cast<const Header*>(buffer_); }
+
+ const void* const buffer_;
+
+ // Though this struct is trivial, disallow copy and assign, since it doesn't
+ // own its data. (If you're copying/assigning this, you're probably doing
+ // something wrong.)
+ DISALLOW_COPY_AND_ASSIGN(View);
+ };
+
+ // |bytes| is optional; if null, the message data will be zero-initialized.
+ MessageInTransit(Type type,
+ Subtype subtype,
+ uint32_t num_bytes,
+ const void* bytes);
+ // Constructs a |MessageInTransit| from a |View|.
+ explicit MessageInTransit(const View& message_view);
+
+ ~MessageInTransit();
+
+ // Gets the size of the next message from |buffer|, which has |buffer_size|
+ // bytes currently available, returning true and setting |*next_message_size|
+ // on success. |buffer| should be aligned on a |kMessageAlignment| boundary
+ // (and on success, |*next_message_size| will be a multiple of
+ // |kMessageAlignment|).
+ // TODO(vtl): In |RawChannelPosix|, the alignment requirements are currently
+ // satisified on a faith-based basis.
+ static bool GetNextMessageSize(const void* buffer,
+ size_t buffer_size,
+ size_t* next_message_size);
+
+ // Makes this message "own" the given set of dispatchers. The dispatchers must
+ // not be referenced from anywhere else (in particular, not from the handle
+ // table), i.e., each dispatcher must have a reference count of 1. This
+ // message must not already have dispatchers.
+ void SetDispatchers(scoped_ptr<DispatcherVector> dispatchers);
+
+ // Sets the |TransportData| for this message. This should only be done when
+ // there are no dispatchers and no existing |TransportData|.
+ void SetTransportData(scoped_ptr<TransportData> transport_data);
+
+ // Serializes any dispatchers to the secondary buffer. This message must not
+ // already have a secondary buffer (so this must only be called once). The
+ // caller must ensure (e.g., by holding on to a reference) that |channel|
+ // stays alive through the call.
+ void SerializeAndCloseDispatchers(Channel* channel);
+
+ // Gets the main buffer and its size (in number of bytes), respectively.
+ const void* main_buffer() const { return main_buffer_.get(); }
+ size_t main_buffer_size() const { return main_buffer_size_; }
+
+ // Gets the transport data buffer (if any).
+ const TransportData* transport_data() const { return transport_data_.get(); }
+ TransportData* transport_data() { return transport_data_.get(); }
+
+ // Gets the total size of the message (see comment in |Header|, below).
+ size_t total_size() const { return header()->total_size; }
+
+ // Gets the size of the message data.
+ uint32_t num_bytes() const { return header()->num_bytes; }
+
+ // Gets the message data (of size |num_bytes()| bytes).
+ const void* bytes() const { return main_buffer_.get() + sizeof(Header); }
+ void* bytes() { return main_buffer_.get() + sizeof(Header); }
+
+ Type type() const { return header()->type; }
+ Subtype subtype() const { return header()->subtype; }
+ EndpointId source_id() const { return header()->source_id; }
+ EndpointId destination_id() const { return header()->destination_id; }
+
+ void set_source_id(EndpointId source_id) { header()->source_id = source_id; }
+ void set_destination_id(EndpointId destination_id) {
+ header()->destination_id = destination_id;
+ }
+
+ // Gets the dispatchers attached to this message; this may return null if
+ // there are none. Note that the caller may mutate the set of dispatchers
+ // (e.g., take ownership of all the dispatchers, leaving the vector empty).
+ DispatcherVector* dispatchers() { return dispatchers_.get(); }
+
+ // Returns true if this message has dispatchers attached.
+ bool has_dispatchers() const {
+ return dispatchers_ && !dispatchers_->empty();
+ }
+
+ // Rounds |n| up to a multiple of |kMessageAlignment|.
+ static inline size_t RoundUpMessageAlignment(size_t n) {
+ return (n + kMessageAlignment - 1) & ~(kMessageAlignment - 1);
+ }
+
+ private:
+ // To allow us to make compile-assertions about |Header| in the .cc file.
+ struct PrivateStructForCompileAsserts;
+
+ // Header for the data (main buffer). Must be a multiple of
+ // |kMessageAlignment| bytes in size. Must be POD.
+ struct Header {
+ // Total size of the message, including the header, the message data
+ // ("bytes") including padding (to make it a multiple of |kMessageAlignment|
+ // bytes), and serialized handle information. Note that this may not be the
+ // correct value if dispatchers are attached but
+ // |SerializeAndCloseDispatchers()| has not been called.
+ uint32_t total_size;
+ Type type; // 2 bytes.
+ Subtype subtype; // 2 bytes.
+ EndpointId source_id; // 4 bytes.
+ EndpointId destination_id; // 4 bytes.
+ // Size of actual message data.
+ uint32_t num_bytes;
+ uint32_t unused;
+ };
+
+ const Header* header() const {
+ return reinterpret_cast<const Header*>(main_buffer_.get());
+ }
+ Header* header() { return reinterpret_cast<Header*>(main_buffer_.get()); }
+
+ void UpdateTotalSize();
+
+ const size_t main_buffer_size_;
+ const scoped_ptr<char, base::AlignedFreeDeleter> main_buffer_; // Never null.
+
+ scoped_ptr<TransportData> transport_data_; // May be null.
+
+ // Any dispatchers that may be attached to this message. These dispatchers
+ // should be "owned" by this message, i.e., have a ref count of exactly 1. (We
+ // allow a dispatcher entry to be null, in case it couldn't be duplicated for
+ // some reason.)
+ scoped_ptr<DispatcherVector> dispatchers_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageInTransit);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_MESSAGE_IN_TRANSIT_H_
diff --git a/chromium/mojo/system/message_in_transit_queue.cc b/chromium/mojo/system/message_in_transit_queue.cc
new file mode 100644
index 00000000000..48f77799e07
--- /dev/null
+++ b/chromium/mojo/system/message_in_transit_queue.cc
@@ -0,0 +1,33 @@
+// Copyright 2014 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 "mojo/system/message_in_transit_queue.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+
+namespace mojo {
+namespace system {
+
+MessageInTransitQueue::MessageInTransitQueue() {
+}
+
+MessageInTransitQueue::MessageInTransitQueue(PassContents,
+ MessageInTransitQueue* other) {
+ queue_.swap(other->queue_);
+}
+
+MessageInTransitQueue::~MessageInTransitQueue() {
+ if (!IsEmpty()) {
+ LOG(WARNING) << "Destroying nonempty message queue";
+ Clear();
+ }
+}
+
+void MessageInTransitQueue::Clear() {
+ STLDeleteElements(&queue_);
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/message_in_transit_queue.h b/chromium/mojo/system/message_in_transit_queue.h
new file mode 100644
index 00000000000..a70a7ef742b
--- /dev/null
+++ b/chromium/mojo/system/message_in_transit_queue.h
@@ -0,0 +1,69 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SYSTEM_MESSAGE_QUEUE_H_
+#define MOJO_SYSTEM_MESSAGE_QUEUE_H_
+
+#include <deque>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/system/message_in_transit.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+// A simple queue for |MessageInTransit|s (that owns its messages).
+// This class is not thread-safe.
+// TODO(vtl): Write tests.
+class MOJO_SYSTEM_IMPL_EXPORT MessageInTransitQueue {
+ public:
+ MessageInTransitQueue();
+
+ struct PassContents {};
+ // Constructor that takes over the contents of another
+ // |MessageInTransitQueue|, leaving it empty.
+ MessageInTransitQueue(PassContents, MessageInTransitQueue* other);
+
+ ~MessageInTransitQueue();
+
+ bool IsEmpty() const {
+ return queue_.empty();
+ }
+
+ void AddMessage(scoped_ptr<MessageInTransit> message) {
+ queue_.push_back(message.release());
+ }
+
+ scoped_ptr<MessageInTransit> GetMessage() {
+ MessageInTransit* rv = queue_.front();
+ queue_.pop_front();
+ return make_scoped_ptr(rv);
+ }
+
+ MessageInTransit* PeekMessage() {
+ return queue_.front();
+ }
+
+ void DiscardMessage() {
+ delete queue_.front();
+ queue_.pop_front();
+ }
+
+ void Clear();
+
+ private:
+ // TODO(vtl): When C++11 is available, switch this to a deque of
+ // |scoped_ptr|/|unique_ptr|s.
+ std::deque<MessageInTransit*> queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageInTransitQueue);
+};
+
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_MESSAGE_QUEUE_H_
diff --git a/chromium/mojo/system/message_pipe.cc b/chromium/mojo/system/message_pipe.cc
new file mode 100644
index 00000000000..c297f385f6f
--- /dev/null
+++ b/chromium/mojo/system/message_pipe.cc
@@ -0,0 +1,285 @@
+// Copyright 2013 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 "mojo/system/message_pipe.h"
+
+#include "base/logging.h"
+#include "mojo/system/channel.h"
+#include "mojo/system/local_message_pipe_endpoint.h"
+#include "mojo/system/message_in_transit.h"
+#include "mojo/system/message_pipe_dispatcher.h"
+#include "mojo/system/message_pipe_endpoint.h"
+#include "mojo/system/proxy_message_pipe_endpoint.h"
+
+namespace mojo {
+namespace system {
+
+MessagePipe::MessagePipe(scoped_ptr<MessagePipeEndpoint> endpoint0,
+ scoped_ptr<MessagePipeEndpoint> endpoint1) {
+ endpoints_[0].reset(endpoint0.release());
+ endpoints_[1].reset(endpoint1.release());
+}
+
+MessagePipe::MessagePipe() {
+ endpoints_[0].reset(new LocalMessagePipeEndpoint());
+ endpoints_[1].reset(new LocalMessagePipeEndpoint());
+}
+
+// static
+unsigned MessagePipe::GetPeerPort(unsigned port) {
+ DCHECK(port == 0 || port == 1);
+ return port ^ 1;
+}
+
+MessagePipeEndpoint::Type MessagePipe::GetType(unsigned port) {
+ DCHECK(port == 0 || port == 1);
+ base::AutoLock locker(lock_);
+ DCHECK(endpoints_[port]);
+
+ return endpoints_[port]->GetType();
+}
+
+void MessagePipe::CancelAllWaiters(unsigned port) {
+ DCHECK(port == 0 || port == 1);
+
+ base::AutoLock locker(lock_);
+ DCHECK(endpoints_[port]);
+ endpoints_[port]->CancelAllWaiters();
+}
+
+void MessagePipe::Close(unsigned port) {
+ DCHECK(port == 0 || port == 1);
+
+ unsigned destination_port = GetPeerPort(port);
+
+ base::AutoLock locker(lock_);
+ DCHECK(endpoints_[port]);
+
+ endpoints_[port]->Close();
+ if (endpoints_[destination_port]) {
+ if (!endpoints_[destination_port]->OnPeerClose())
+ endpoints_[destination_port].reset();
+ }
+ endpoints_[port].reset();
+}
+
+// TODO(vtl): Handle flags.
+MojoResult MessagePipe::WriteMessage(
+ unsigned port,
+ const void* bytes,
+ uint32_t num_bytes,
+ std::vector<DispatcherTransport>* transports,
+ MojoWriteMessageFlags flags) {
+ DCHECK(port == 0 || port == 1);
+ return EnqueueMessageInternal(
+ GetPeerPort(port),
+ make_scoped_ptr(new MessageInTransit(
+ MessageInTransit::kTypeMessagePipeEndpoint,
+ MessageInTransit::kSubtypeMessagePipeEndpointData,
+ num_bytes,
+ bytes)),
+ transports);
+}
+
+MojoResult MessagePipe::ReadMessage(unsigned port,
+ void* bytes,
+ uint32_t* num_bytes,
+ DispatcherVector* dispatchers,
+ uint32_t* num_dispatchers,
+ MojoReadMessageFlags flags) {
+ DCHECK(port == 0 || port == 1);
+
+ base::AutoLock locker(lock_);
+ DCHECK(endpoints_[port]);
+
+ return endpoints_[port]->ReadMessage(bytes, num_bytes, dispatchers,
+ num_dispatchers, flags);
+}
+
+MojoResult MessagePipe::AddWaiter(unsigned port,
+ Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context) {
+ DCHECK(port == 0 || port == 1);
+
+ base::AutoLock locker(lock_);
+ DCHECK(endpoints_[port]);
+
+ return endpoints_[port]->AddWaiter(waiter, signals, context);
+}
+
+void MessagePipe::RemoveWaiter(unsigned port, Waiter* waiter) {
+ DCHECK(port == 0 || port == 1);
+
+ base::AutoLock locker(lock_);
+ DCHECK(endpoints_[port]);
+
+ endpoints_[port]->RemoveWaiter(waiter);
+}
+
+void MessagePipe::ConvertLocalToProxy(unsigned port) {
+ DCHECK(port == 0 || port == 1);
+
+ base::AutoLock locker(lock_);
+ DCHECK(endpoints_[port]);
+ DCHECK_EQ(endpoints_[port]->GetType(), MessagePipeEndpoint::kTypeLocal);
+
+ bool is_peer_open = !!endpoints_[GetPeerPort(port)];
+
+ // TODO(vtl): Hopefully this will work if the peer has been closed and when
+ // the peer is local. If the peer is remote, we should do something more
+ // sophisticated.
+ DCHECK(!is_peer_open ||
+ endpoints_[GetPeerPort(port)]->GetType() ==
+ MessagePipeEndpoint::kTypeLocal);
+
+ scoped_ptr<MessagePipeEndpoint> replacement_endpoint(
+ new ProxyMessagePipeEndpoint(
+ static_cast<LocalMessagePipeEndpoint*>(endpoints_[port].get()),
+ is_peer_open));
+ endpoints_[port].swap(replacement_endpoint);
+}
+
+MojoResult MessagePipe::EnqueueMessage(
+ unsigned port,
+ scoped_ptr<MessageInTransit> message) {
+ return EnqueueMessageInternal(port, message.Pass(), NULL);
+}
+
+bool MessagePipe::Attach(unsigned port,
+ scoped_refptr<Channel> channel,
+ MessageInTransit::EndpointId local_id) {
+ DCHECK(port == 0 || port == 1);
+ DCHECK(channel);
+ DCHECK_NE(local_id, MessageInTransit::kInvalidEndpointId);
+
+ base::AutoLock locker(lock_);
+ if (!endpoints_[port])
+ return false;
+
+ DCHECK_EQ(endpoints_[port]->GetType(), MessagePipeEndpoint::kTypeProxy);
+ endpoints_[port]->Attach(channel, local_id);
+ return true;
+}
+
+void MessagePipe::Run(unsigned port, MessageInTransit::EndpointId remote_id) {
+ DCHECK(port == 0 || port == 1);
+ DCHECK_NE(remote_id, MessageInTransit::kInvalidEndpointId);
+
+ base::AutoLock locker(lock_);
+ DCHECK(endpoints_[port]);
+ if (!endpoints_[port]->Run(remote_id))
+ endpoints_[port].reset();
+}
+
+void MessagePipe::OnRemove(unsigned port) {
+ unsigned destination_port = GetPeerPort(port);
+
+ base::AutoLock locker(lock_);
+ // A |OnPeerClose()| can come in first, before |OnRemove()| gets called.
+ if (!endpoints_[port])
+ return;
+
+ endpoints_[port]->OnRemove();
+ if (endpoints_[destination_port]) {
+ if (!endpoints_[destination_port]->OnPeerClose())
+ endpoints_[destination_port].reset();
+ }
+ endpoints_[port].reset();
+}
+
+MessagePipe::~MessagePipe() {
+ // Owned by the dispatchers. The owning dispatchers should only release us via
+ // their |Close()| method, which should inform us of being closed via our
+ // |Close()|. Thus these should already be null.
+ DCHECK(!endpoints_[0]);
+ DCHECK(!endpoints_[1]);
+}
+
+MojoResult MessagePipe::EnqueueMessageInternal(
+ unsigned port,
+ scoped_ptr<MessageInTransit> message,
+ std::vector<DispatcherTransport>* transports) {
+ DCHECK(port == 0 || port == 1);
+ DCHECK(message);
+
+ if (message->type() == MessageInTransit::kTypeMessagePipe) {
+ DCHECK(!transports);
+ return HandleControlMessage(port, message.Pass());
+ }
+
+ DCHECK_EQ(message->type(), MessageInTransit::kTypeMessagePipeEndpoint);
+
+ base::AutoLock locker(lock_);
+ DCHECK(endpoints_[GetPeerPort(port)]);
+
+ // The destination port need not be open, unlike the source port.
+ if (!endpoints_[port])
+ return MOJO_RESULT_FAILED_PRECONDITION;
+
+ if (transports) {
+ MojoResult result = AttachTransportsNoLock(port, message.get(), transports);
+ if (result != MOJO_RESULT_OK)
+ return result;
+ }
+
+ // The endpoint's |EnqueueMessage()| may not report failure.
+ endpoints_[port]->EnqueueMessage(message.Pass());
+ return MOJO_RESULT_OK;
+}
+
+MojoResult MessagePipe::AttachTransportsNoLock(
+ unsigned port,
+ MessageInTransit* message,
+ std::vector<DispatcherTransport>* transports) {
+ DCHECK(!message->has_dispatchers());
+
+ // You're not allowed to send either handle to a message pipe over the message
+ // pipe, so check for this. (The case of trying to write a handle to itself is
+ // taken care of by |Core|. That case kind of makes sense, but leads to
+ // complications if, e.g., both sides try to do the same thing with their
+ // respective handles simultaneously. The other case, of trying to write the
+ // peer handle to a handle, doesn't make sense -- since no handle will be
+ // available to read the message from.)
+ for (size_t i = 0; i < transports->size(); i++) {
+ if (!(*transports)[i].is_valid())
+ continue;
+ if ((*transports)[i].GetType() == Dispatcher::kTypeMessagePipe) {
+ MessagePipeDispatcherTransport mp_transport((*transports)[i]);
+ if (mp_transport.GetMessagePipe() == this) {
+ // The other case should have been disallowed by |Core|. (Note: |port|
+ // is the peer port of the handle given to |WriteMessage()|.)
+ DCHECK_EQ(mp_transport.GetPort(), port);
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ }
+ }
+ }
+
+ // Clone the dispatchers and attach them to the message. (This must be done as
+ // a separate loop, since we want to leave the dispatchers alone on failure.)
+ scoped_ptr<DispatcherVector> dispatchers(new DispatcherVector());
+ dispatchers->reserve(transports->size());
+ for (size_t i = 0; i < transports->size(); i++) {
+ if ((*transports)[i].is_valid()) {
+ dispatchers->push_back(
+ (*transports)[i].CreateEquivalentDispatcherAndClose());
+ } else {
+ LOG(WARNING) << "Enqueueing null dispatcher";
+ dispatchers->push_back(scoped_refptr<Dispatcher>());
+ }
+ }
+ message->SetDispatchers(dispatchers.Pass());
+ return MOJO_RESULT_OK;
+}
+
+MojoResult MessagePipe::HandleControlMessage(
+ unsigned /*port*/,
+ scoped_ptr<MessageInTransit> message) {
+ LOG(WARNING) << "Unrecognized MessagePipe control message subtype "
+ << message->subtype();
+ return MOJO_RESULT_UNKNOWN;
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/message_pipe.h b/chromium/mojo/system/message_pipe.h
new file mode 100644
index 00000000000..e43dcb667a1
--- /dev/null
+++ b/chromium/mojo/system/message_pipe.h
@@ -0,0 +1,122 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_MESSAGE_PIPE_H_
+#define MOJO_SYSTEM_MESSAGE_PIPE_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/system/dispatcher.h"
+#include "mojo/system/message_in_transit.h"
+#include "mojo/system/message_pipe_endpoint.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class Channel;
+class Waiter;
+
+// |MessagePipe| is the secondary object implementing a message pipe (see the
+// explanatory comment in core.cc). It is typically owned by the dispatcher(s)
+// corresponding to the local endpoints. This class is thread-safe.
+class MOJO_SYSTEM_IMPL_EXPORT MessagePipe :
+ public base::RefCountedThreadSafe<MessagePipe> {
+ public:
+ MessagePipe(scoped_ptr<MessagePipeEndpoint> endpoint0,
+ scoped_ptr<MessagePipeEndpoint> endpoint1);
+
+ // Convenience constructor that constructs a |MessagePipe| with two new
+ // |LocalMessagePipeEndpoint|s.
+ MessagePipe();
+
+ // Gets the other port number (i.e., 0 -> 1, 1 -> 0).
+ static unsigned GetPeerPort(unsigned port);
+
+ // Gets the type of the endpoint (used for assertions, etc.).
+ MessagePipeEndpoint::Type GetType(unsigned port);
+
+ // These are called by the dispatcher to implement its methods of
+ // corresponding names. In all cases, the port |port| must be open.
+ void CancelAllWaiters(unsigned port);
+ void Close(unsigned port);
+ // Unlike |MessagePipeDispatcher::WriteMessage()|, this does not validate its
+ // arguments.
+ MojoResult WriteMessage(unsigned port,
+ const void* bytes,
+ uint32_t num_bytes,
+ std::vector<DispatcherTransport>* transports,
+ MojoWriteMessageFlags flags);
+ // Unlike |MessagePipeDispatcher::ReadMessage()|, this does not validate its
+ // arguments.
+ MojoResult ReadMessage(unsigned port,
+ void* bytes,
+ uint32_t* num_bytes,
+ DispatcherVector* dispatchers,
+ uint32_t* num_dispatchers,
+ MojoReadMessageFlags flags);
+ MojoResult AddWaiter(unsigned port,
+ Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context);
+ void RemoveWaiter(unsigned port, Waiter* waiter);
+
+ // This is called by the dispatcher to convert a local endpoint to a proxy
+ // endpoint.
+ void ConvertLocalToProxy(unsigned port);
+
+ // This is used by |Channel| to enqueue messages (typically to a
+ // |LocalMessagePipeEndpoint|). Unlike |WriteMessage()|, |port| is the
+ // *destination* port.
+ MojoResult EnqueueMessage(unsigned port,
+ scoped_ptr<MessageInTransit> message);
+
+ // These are used by |Channel|.
+ bool Attach(unsigned port,
+ scoped_refptr<Channel> channel,
+ MessageInTransit::EndpointId local_id);
+ void Run(unsigned port, MessageInTransit::EndpointId remote_id);
+ void OnRemove(unsigned port);
+
+ private:
+ friend class base::RefCountedThreadSafe<MessagePipe>;
+ virtual ~MessagePipe();
+
+ // This is used internally by |WriteMessage()| and by |EnqueueMessage()|.
+ // |transports| may be non-null only if it's nonempty and |message| has no
+ // dispatchers attached.
+ MojoResult EnqueueMessageInternal(
+ unsigned port,
+ scoped_ptr<MessageInTransit> message,
+ std::vector<DispatcherTransport>* transports);
+
+ // Helper for |EnqueueMessageInternal()|. Must be called with |lock_| held.
+ MojoResult AttachTransportsNoLock(
+ unsigned port,
+ MessageInTransit* message,
+ std::vector<DispatcherTransport>* transports);
+
+ // Used by |EnqueueMessageInternal()| to handle control messages that are
+ // actually meant for us.
+ MojoResult HandleControlMessage(unsigned port,
+ scoped_ptr<MessageInTransit> message);
+
+ base::Lock lock_; // Protects the following members.
+ scoped_ptr<MessagePipeEndpoint> endpoints_[2];
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePipe);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_MESSAGE_PIPE_H_
diff --git a/chromium/mojo/system/message_pipe_dispatcher.cc b/chromium/mojo/system/message_pipe_dispatcher.cc
new file mode 100644
index 00000000000..484613ca5d1
--- /dev/null
+++ b/chromium/mojo/system/message_pipe_dispatcher.cc
@@ -0,0 +1,281 @@
+// Copyright 2013 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 "mojo/system/message_pipe_dispatcher.h"
+
+#include "base/logging.h"
+#include "mojo/system/channel.h"
+#include "mojo/system/constants.h"
+#include "mojo/system/local_message_pipe_endpoint.h"
+#include "mojo/system/memory.h"
+#include "mojo/system/message_in_transit.h"
+#include "mojo/system/message_pipe.h"
+#include "mojo/system/options_validation.h"
+#include "mojo/system/proxy_message_pipe_endpoint.h"
+
+namespace mojo {
+namespace system {
+
+namespace {
+
+const unsigned kInvalidPort = static_cast<unsigned>(-1);
+
+struct SerializedMessagePipeDispatcher {
+ MessageInTransit::EndpointId endpoint_id;
+};
+
+} // namespace
+
+// MessagePipeDispatcher -------------------------------------------------------
+
+// static
+const MojoCreateMessagePipeOptions
+ MessagePipeDispatcher::kDefaultCreateOptions = {
+ static_cast<uint32_t>(sizeof(MojoCreateMessagePipeOptions)),
+ MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE
+};
+
+MessagePipeDispatcher::MessagePipeDispatcher(
+ const MojoCreateMessagePipeOptions& /*validated_options*/)
+ : port_(kInvalidPort) {
+}
+
+// static
+MojoResult MessagePipeDispatcher::ValidateCreateOptions(
+ const MojoCreateMessagePipeOptions* in_options,
+ MojoCreateMessagePipeOptions* out_options) {
+ const MojoCreateMessagePipeOptionsFlags kKnownFlags =
+ MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE;
+
+ *out_options = kDefaultCreateOptions;
+ if (!in_options)
+ return MOJO_RESULT_OK;
+
+ MojoResult result =
+ ValidateOptionsStructPointerSizeAndFlags<MojoCreateMessagePipeOptions>(
+ in_options, kKnownFlags, out_options);
+ if (result != MOJO_RESULT_OK)
+ return result;
+
+ // Checks for fields beyond |flags|:
+
+ // (Nothing here yet.)
+
+ return MOJO_RESULT_OK;
+}
+
+void MessagePipeDispatcher::Init(scoped_refptr<MessagePipe> message_pipe,
+ unsigned port) {
+ DCHECK(message_pipe);
+ DCHECK(port == 0 || port == 1);
+
+ message_pipe_ = message_pipe;
+ port_ = port;
+}
+
+Dispatcher::Type MessagePipeDispatcher::GetType() const {
+ return kTypeMessagePipe;
+}
+
+// static
+std::pair<scoped_refptr<MessagePipeDispatcher>, scoped_refptr<MessagePipe> >
+MessagePipeDispatcher::CreateRemoteMessagePipe() {
+ scoped_refptr<MessagePipe> message_pipe(
+ new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+ scoped_refptr<MessagePipeDispatcher> dispatcher(new MessagePipeDispatcher(
+ MessagePipeDispatcher::kDefaultCreateOptions));
+ dispatcher->Init(message_pipe, 0);
+
+ return std::make_pair(dispatcher, message_pipe);
+}
+
+// static
+scoped_refptr<MessagePipeDispatcher> MessagePipeDispatcher::Deserialize(
+ Channel* channel,
+ const void* source,
+ size_t size) {
+ if (size != sizeof(SerializedMessagePipeDispatcher)) {
+ LOG(ERROR) << "Invalid serialized message pipe dispatcher";
+ return scoped_refptr<MessagePipeDispatcher>();
+ }
+
+ std::pair<scoped_refptr<MessagePipeDispatcher>, scoped_refptr<MessagePipe> >
+ remote_message_pipe = CreateRemoteMessagePipe();
+
+ MessageInTransit::EndpointId remote_id =
+ static_cast<const SerializedMessagePipeDispatcher*>(source)->endpoint_id;
+ if (remote_id == MessageInTransit::kInvalidEndpointId) {
+ // This means that the other end was closed, and there were no messages
+ // enqueued for us.
+ // TODO(vtl): This is wrong. We should produce a "dead" message pipe
+ // dispatcher.
+ NOTIMPLEMENTED();
+ return scoped_refptr<MessagePipeDispatcher>();
+ }
+ MessageInTransit::EndpointId local_id =
+ channel->AttachMessagePipeEndpoint(remote_message_pipe.second, 1);
+ if (local_id == MessageInTransit::kInvalidEndpointId) {
+ LOG(ERROR) << "Failed to deserialize message pipe dispatcher (failed to "
+ "attach; remote ID = " << remote_id << ")";
+ return scoped_refptr<MessagePipeDispatcher>();
+ }
+ DVLOG(2) << "Deserializing message pipe dispatcher (remote ID = "
+ << remote_id << ", new local ID = " << local_id << ")";
+
+ if (!channel->RunMessagePipeEndpoint(local_id, remote_id)) {
+ // In general, this shouldn't fail, since we generated |local_id| locally.
+ NOTREACHED();
+ return scoped_refptr<MessagePipeDispatcher>();
+ }
+
+ // TODO(vtl): FIXME -- Need some error handling here.
+ channel->RunRemoteMessagePipeEndpoint(local_id, remote_id);
+ return remote_message_pipe.first;
+}
+
+MessagePipeDispatcher::~MessagePipeDispatcher() {
+ // |Close()|/|CloseImplNoLock()| should have taken care of the pipe.
+ DCHECK(!message_pipe_);
+}
+
+MessagePipe* MessagePipeDispatcher::GetMessagePipeNoLock() const {
+ lock().AssertAcquired();
+ return message_pipe_.get();
+}
+
+unsigned MessagePipeDispatcher::GetPortNoLock() const {
+ lock().AssertAcquired();
+ return port_;
+}
+
+void MessagePipeDispatcher::CancelAllWaitersNoLock() {
+ lock().AssertAcquired();
+ message_pipe_->CancelAllWaiters(port_);
+}
+
+void MessagePipeDispatcher::CloseImplNoLock() {
+ lock().AssertAcquired();
+ message_pipe_->Close(port_);
+ message_pipe_ = NULL;
+ port_ = kInvalidPort;
+}
+
+scoped_refptr<Dispatcher>
+MessagePipeDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() {
+ lock().AssertAcquired();
+
+ // TODO(vtl): Currently, there are no options, so we just use
+ // |kDefaultCreateOptions|. Eventually, we'll have to duplicate the options
+ // too.
+ scoped_refptr<MessagePipeDispatcher> rv =
+ new MessagePipeDispatcher(kDefaultCreateOptions);
+ rv->Init(message_pipe_, port_);
+ message_pipe_ = NULL;
+ port_ = kInvalidPort;
+ return scoped_refptr<Dispatcher>(rv.get());
+}
+
+MojoResult MessagePipeDispatcher::WriteMessageImplNoLock(
+ const void* bytes,
+ uint32_t num_bytes,
+ std::vector<DispatcherTransport>* transports,
+ MojoWriteMessageFlags flags) {
+ DCHECK(!transports || (transports->size() > 0 &&
+ transports->size() <= kMaxMessageNumHandles));
+
+ lock().AssertAcquired();
+
+ if (!VerifyUserPointerWithSize<1>(bytes, num_bytes))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (num_bytes > kMaxMessageNumBytes)
+ return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+ return message_pipe_->WriteMessage(port_, bytes, num_bytes, transports,
+ flags);
+}
+
+MojoResult MessagePipeDispatcher::ReadMessageImplNoLock(
+ void* bytes,
+ uint32_t* num_bytes,
+ DispatcherVector* dispatchers,
+ uint32_t* num_dispatchers,
+ MojoReadMessageFlags flags) {
+ lock().AssertAcquired();
+
+ if (num_bytes) {
+ if (!VerifyUserPointer<uint32_t>(num_bytes))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (!VerifyUserPointerWithSize<1>(bytes, *num_bytes))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ }
+
+ return message_pipe_->ReadMessage(port_, bytes, num_bytes, dispatchers,
+ num_dispatchers, flags);
+}
+
+MojoResult MessagePipeDispatcher::AddWaiterImplNoLock(Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context) {
+ lock().AssertAcquired();
+ return message_pipe_->AddWaiter(port_, waiter, signals, context);
+}
+
+void MessagePipeDispatcher::RemoveWaiterImplNoLock(Waiter* waiter) {
+ lock().AssertAcquired();
+ message_pipe_->RemoveWaiter(port_, waiter);
+}
+
+void MessagePipeDispatcher::StartSerializeImplNoLock(
+ Channel* /*channel*/,
+ size_t* max_size,
+ size_t* max_platform_handles) {
+ DCHECK(HasOneRef()); // Only one ref => no need to take the lock.
+ *max_size = sizeof(SerializedMessagePipeDispatcher);
+ *max_platform_handles = 0;
+}
+
+bool MessagePipeDispatcher::EndSerializeAndCloseImplNoLock(
+ Channel* channel,
+ void* destination,
+ size_t* actual_size,
+ embedder::PlatformHandleVector* /*platform_handles*/) {
+ DCHECK(HasOneRef()); // Only one ref => no need to take the lock.
+
+ // Convert the local endpoint to a proxy endpoint (moving the message queue).
+ message_pipe_->ConvertLocalToProxy(port_);
+
+ // Attach the new proxy endpoint to the channel.
+ MessageInTransit::EndpointId endpoint_id =
+ channel->AttachMessagePipeEndpoint(message_pipe_, port_);
+ // Note: It's okay to get an endpoint ID of |kInvalidEndpointId|. (It's
+ // possible that the other endpoint -- the one that we're not sending -- was
+ // closed in the intervening time.) In that case, we need to deserialize a
+ // "dead" message pipe dispatcher on the other end. (Note that this is
+ // different from just producing |MOJO_HANDLE_INVALID|.)
+ DVLOG(2) << "Serializing message pipe dispatcher (local ID = " << endpoint_id
+ << ")";
+
+ // We now have a local ID. Before we can run the proxy endpoint, we need to
+ // get an ack back from the other side with the remote ID.
+ static_cast<SerializedMessagePipeDispatcher*>(destination)->endpoint_id =
+ endpoint_id;
+
+ message_pipe_ = NULL;
+ port_ = kInvalidPort;
+
+ *actual_size = sizeof(SerializedMessagePipeDispatcher);
+ return true;
+}
+
+// MessagePipeDispatcherTransport ----------------------------------------------
+
+MessagePipeDispatcherTransport::MessagePipeDispatcherTransport(
+ DispatcherTransport transport) : DispatcherTransport(transport) {
+ DCHECK_EQ(message_pipe_dispatcher()->GetType(), Dispatcher::kTypeMessagePipe);
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/message_pipe_dispatcher.h b/chromium/mojo/system/message_pipe_dispatcher.h
new file mode 100644
index 00000000000..d3a3946edfa
--- /dev/null
+++ b/chromium/mojo/system/message_pipe_dispatcher.h
@@ -0,0 +1,131 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_MESSAGE_PIPE_DISPATCHER_H_
+#define MOJO_SYSTEM_MESSAGE_PIPE_DISPATCHER_H_
+
+#include <utility>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/system/dispatcher.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class MessagePipe;
+class MessagePipeDispatcherTransport;
+
+// This is the |Dispatcher| implementation for message pipes (created by the
+// Mojo primitive |MojoCreateMessagePipe()|). This class is thread-safe.
+class MOJO_SYSTEM_IMPL_EXPORT MessagePipeDispatcher : public Dispatcher {
+ public:
+ // The default options to use for |MojoCreateMessagePipe()|. (Real uses
+ // should obtain this via |ValidateCreateOptions()| with a null |in_options|;
+ // this is exposed directly for testing convenience.)
+ static const MojoCreateMessagePipeOptions kDefaultCreateOptions;
+
+ MessagePipeDispatcher(
+ const MojoCreateMessagePipeOptions& /*validated_options*/);
+
+ // Validates and/or sets default options for |MojoCreateMessagePipeOptions|.
+ // If non-null, |in_options| must point to a struct of at least
+ // |in_options->struct_size| bytes. |out_options| must point to a (current)
+ // |MojoCreateMessagePipeOptions| and will be entirely overwritten on success
+ // (it may be partly overwritten on failure).
+ static MojoResult ValidateCreateOptions(
+ const MojoCreateMessagePipeOptions* in_options,
+ MojoCreateMessagePipeOptions* out_options);
+
+ // Must be called before any other methods. (This method is not thread-safe.)
+ void Init(scoped_refptr<MessagePipe> message_pipe, unsigned port);
+
+ // |Dispatcher| public methods:
+ virtual Type GetType() const OVERRIDE;
+
+ // Creates a |MessagePipe| with a local endpoint (at port 0) and a proxy
+ // endpoint, and creates/initializes a |MessagePipeDispatcher| (attached to
+ // the message pipe, port 0).
+ // TODO(vtl): This currently uses |kDefaultCreateOptions|, which is okay since
+ // there aren't any options, but eventually options should be plumbed through.
+ static std::pair<scoped_refptr<MessagePipeDispatcher>,
+ scoped_refptr<MessagePipe> > CreateRemoteMessagePipe();
+
+ // The "opposite" of |SerializeAndClose()|. (Typically this is called by
+ // |Dispatcher::Deserialize()|.)
+ static scoped_refptr<MessagePipeDispatcher> Deserialize(Channel* channel,
+ const void* source,
+ size_t size);
+
+ private:
+ friend class MessagePipeDispatcherTransport;
+
+ virtual ~MessagePipeDispatcher();
+
+ // Gets a dumb pointer to |message_pipe_|. This must be called under the
+ // |Dispatcher| lock (that it's a dumb pointer is okay since it's under lock).
+ // This is needed when sending handles across processes, where nontrivial,
+ // invasive work needs to be done.
+ MessagePipe* GetMessagePipeNoLock() const;
+ // Similarly for the port.
+ unsigned GetPortNoLock() const;
+
+ // |Dispatcher| protected methods:
+ virtual void CancelAllWaitersNoLock() OVERRIDE;
+ virtual void CloseImplNoLock() OVERRIDE;
+ virtual scoped_refptr<Dispatcher>
+ CreateEquivalentDispatcherAndCloseImplNoLock() OVERRIDE;
+ virtual MojoResult WriteMessageImplNoLock(
+ const void* bytes,
+ uint32_t num_bytes,
+ std::vector<DispatcherTransport>* transports,
+ MojoWriteMessageFlags flags) OVERRIDE;
+ virtual MojoResult ReadMessageImplNoLock(void* bytes,
+ uint32_t* num_bytes,
+ DispatcherVector* dispatchers,
+ uint32_t* num_dispatchers,
+ MojoReadMessageFlags flags) OVERRIDE;
+ virtual MojoResult AddWaiterImplNoLock(Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context) OVERRIDE;
+ virtual void RemoveWaiterImplNoLock(Waiter* waiter) OVERRIDE;
+ virtual void StartSerializeImplNoLock(Channel* channel,
+ size_t* max_size,
+ size_t* max_platform_handles) OVERRIDE;
+ virtual bool EndSerializeAndCloseImplNoLock(
+ Channel* channel,
+ void* destination,
+ size_t* actual_size,
+ embedder::PlatformHandleVector* platform_handles) OVERRIDE;
+
+ // Protected by |lock()|:
+ scoped_refptr<MessagePipe> message_pipe_; // This will be null if closed.
+ unsigned port_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePipeDispatcher);
+};
+
+class MessagePipeDispatcherTransport : public DispatcherTransport {
+ public:
+ explicit MessagePipeDispatcherTransport(DispatcherTransport transport);
+
+ MessagePipe* GetMessagePipe() {
+ return message_pipe_dispatcher()->GetMessagePipeNoLock();
+ }
+ unsigned GetPort() { return message_pipe_dispatcher()->GetPortNoLock(); }
+
+ private:
+ MessagePipeDispatcher* message_pipe_dispatcher() {
+ return static_cast<MessagePipeDispatcher*>(dispatcher());
+ }
+
+ // Copy and assign allowed.
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_MESSAGE_PIPE_DISPATCHER_H_
diff --git a/chromium/mojo/system/message_pipe_dispatcher_unittest.cc b/chromium/mojo/system/message_pipe_dispatcher_unittest.cc
new file mode 100644
index 00000000000..291a2c152f5
--- /dev/null
+++ b/chromium/mojo/system/message_pipe_dispatcher_unittest.cc
@@ -0,0 +1,598 @@
+// Copyright 2013 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.
+
+// NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a
+// heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to
+// increase tolerance and reduce observed flakiness (though doing so reduces the
+// meaningfulness of the test).
+
+#include "mojo/system/message_pipe_dispatcher.h"
+
+#include <string.h>
+
+#include <limits>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_vector.h"
+#include "base/rand_util.h"
+#include "base/threading/platform_thread.h" // For |Sleep()|.
+#include "base/threading/simple_thread.h"
+#include "base/time/time.h"
+#include "mojo/system/message_pipe.h"
+#include "mojo/system/test_utils.h"
+#include "mojo/system/waiter.h"
+#include "mojo/system/waiter_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+TEST(MessagePipeDispatcherTest, Basic) {
+ test::Stopwatch stopwatch;
+ int32_t buffer[1];
+ const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+ uint32_t buffer_size;
+
+ // Run this test both with |d0| as port 0, |d1| as port 1 and vice versa.
+ for (unsigned i = 0; i < 2; i++) {
+ scoped_refptr<MessagePipeDispatcher> d0(new MessagePipeDispatcher(
+ MessagePipeDispatcher::kDefaultCreateOptions));
+ EXPECT_EQ(Dispatcher::kTypeMessagePipe, d0->GetType());
+ scoped_refptr<MessagePipeDispatcher> d1(new MessagePipeDispatcher(
+ MessagePipeDispatcher::kDefaultCreateOptions));
+ {
+ scoped_refptr<MessagePipe> mp(new MessagePipe());
+ d0->Init(mp, i); // 0, 1.
+ d1->Init(mp, i ^ 1); // 1, 0.
+ }
+ Waiter w;
+ uint32_t context = 0;
+
+ // Try adding a writable waiter when already writable.
+ w.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 0));
+ // Shouldn't need to remove the waiter (it was not added).
+
+ // Add a readable waiter to |d0|, then make it readable (by writing to
+ // |d1|), then wait.
+ w.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 1));
+ buffer[0] = 123456789;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ d1->WriteMessage(buffer, kBufferSize,
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_OK, w.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_EQ(1u, context);
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+ d0->RemoveWaiter(&w);
+
+ // Try adding a readable waiter when already readable (from above).
+ w.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 2));
+ // Shouldn't need to remove the waiter (it was not added).
+
+ // Make |d0| no longer readable (by reading from it).
+ buffer[0] = 0;
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ d0->ReadMessage(buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kBufferSize, buffer_size);
+ EXPECT_EQ(123456789, buffer[0]);
+
+ // Wait for zero time for readability on |d0| (will time out).
+ w.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 3));
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, w.Wait(0, NULL));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+ d0->RemoveWaiter(&w);
+
+ // Wait for non-zero, finite time for readability on |d0| (will time out).
+ w.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 3));
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+ w.Wait(2 * test::EpsilonTimeout().InMicroseconds(), NULL));
+ base::TimeDelta elapsed = stopwatch.Elapsed();
+ EXPECT_GT(elapsed, (2-1) * test::EpsilonTimeout());
+ EXPECT_LT(elapsed, (2+1) * test::EpsilonTimeout());
+ d0->RemoveWaiter(&w);
+
+ EXPECT_EQ(MOJO_RESULT_OK, d0->Close());
+ EXPECT_EQ(MOJO_RESULT_OK, d1->Close());
+ }
+}
+
+TEST(MessagePipeDispatcherTest, InvalidParams) {
+ char buffer[1];
+
+ scoped_refptr<MessagePipeDispatcher> d0(new MessagePipeDispatcher(
+ MessagePipeDispatcher::kDefaultCreateOptions));
+ scoped_refptr<MessagePipeDispatcher> d1(new MessagePipeDispatcher(
+ MessagePipeDispatcher::kDefaultCreateOptions));
+ {
+ scoped_refptr<MessagePipe> mp(new MessagePipe());
+ d0->Init(mp, 0);
+ d1->Init(mp, 1);
+ }
+
+ // |WriteMessage|:
+ // Null buffer with nonzero buffer size.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d0->WriteMessage(NULL, 1,
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ // Huge buffer size.
+ EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+ d0->WriteMessage(buffer, std::numeric_limits<uint32_t>::max(),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // |ReadMessage|:
+ // Null buffer with nonzero buffer size.
+ uint32_t buffer_size = 1;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d0->ReadMessage(NULL, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+
+ EXPECT_EQ(MOJO_RESULT_OK, d0->Close());
+ EXPECT_EQ(MOJO_RESULT_OK, d1->Close());
+}
+
+// Test what happens when one end is closed (single-threaded test).
+TEST(MessagePipeDispatcherTest, BasicClosed) {
+ int32_t buffer[1];
+ const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+ uint32_t buffer_size;
+
+ // Run this test both with |d0| as port 0, |d1| as port 1 and vice versa.
+ for (unsigned i = 0; i < 2; i++) {
+ scoped_refptr<MessagePipeDispatcher> d0(new MessagePipeDispatcher(
+ MessagePipeDispatcher::kDefaultCreateOptions));
+ scoped_refptr<MessagePipeDispatcher> d1(new MessagePipeDispatcher(
+ MessagePipeDispatcher::kDefaultCreateOptions));
+ {
+ scoped_refptr<MessagePipe> mp(new MessagePipe());
+ d0->Init(mp, i); // 0, 1.
+ d1->Init(mp, i ^ 1); // 1, 0.
+ }
+ Waiter w;
+
+ // Write (twice) to |d1|.
+ buffer[0] = 123456789;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ d1->WriteMessage(buffer, kBufferSize,
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ buffer[0] = 234567890;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ d1->WriteMessage(buffer, kBufferSize,
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Try waiting for readable on |d0|; should fail (already satisfied).
+ w.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 0));
+
+ // Try reading from |d1|; should fail (nothing to read).
+ buffer[0] = 0;
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ d1->ReadMessage(buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+
+ // Close |d1|.
+ EXPECT_EQ(MOJO_RESULT_OK, d1->Close());
+
+ // Try waiting for readable on |d0|; should fail (already satisfied).
+ w.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 1));
+
+ // Read from |d0|.
+ buffer[0] = 0;
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ d0->ReadMessage(buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kBufferSize, buffer_size);
+ EXPECT_EQ(123456789, buffer[0]);
+
+ // Try waiting for readable on |d0|; should fail (already satisfied).
+ w.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 2));
+
+ // Read again from |d0|.
+ buffer[0] = 0;
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ d0->ReadMessage(buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kBufferSize, buffer_size);
+ EXPECT_EQ(234567890, buffer[0]);
+
+ // Try waiting for readable on |d0|; should fail (unsatisfiable).
+ w.Init();
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 3));
+
+ // Try waiting for writable on |d0|; should fail (unsatisfiable).
+ w.Init();
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ d0->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 4));
+
+ // Try reading from |d0|; should fail (nothing to read and other end
+ // closed).
+ buffer[0] = 0;
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ d0->ReadMessage(buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+
+ // Try writing to |d0|; should fail (other end closed).
+ buffer[0] = 345678901;
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ d0->WriteMessage(buffer, kBufferSize,
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ EXPECT_EQ(MOJO_RESULT_OK, d0->Close());
+ }
+}
+
+TEST(MessagePipeDispatcherTest, BasicThreaded) {
+ test::Stopwatch stopwatch;
+ int32_t buffer[1];
+ const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+ uint32_t buffer_size;
+ base::TimeDelta elapsed;
+ bool did_wait;
+ MojoResult result;
+ uint32_t context;
+
+ // Run this test both with |d0| as port 0, |d1| as port 1 and vice versa.
+ for (unsigned i = 0; i < 2; i++) {
+ scoped_refptr<MessagePipeDispatcher> d0(new MessagePipeDispatcher(
+ MessagePipeDispatcher::kDefaultCreateOptions));
+ scoped_refptr<MessagePipeDispatcher> d1(new MessagePipeDispatcher(
+ MessagePipeDispatcher::kDefaultCreateOptions));
+ {
+ scoped_refptr<MessagePipe> mp(new MessagePipe());
+ d0->Init(mp, i); // 0, 1.
+ d1->Init(mp, i ^ 1); // 1, 0.
+ }
+
+ // Wait for readable on |d1|, which will become readable after some time.
+ {
+ test::WaiterThread thread(d1,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ 1,
+ &did_wait, &result, &context);
+ stopwatch.Start();
+ thread.Start();
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ // Wake it up by writing to |d0|.
+ buffer[0] = 123456789;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ d0->WriteMessage(buffer, kBufferSize,
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ } // Joins the thread.
+ elapsed = stopwatch.Elapsed();
+ EXPECT_GT(elapsed, (2-1) * test::EpsilonTimeout());
+ EXPECT_LT(elapsed, (2+1) * test::EpsilonTimeout());
+ EXPECT_TRUE(did_wait);
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(1u, context);
+
+ // Now |d1| is already readable. Try waiting for it again.
+ {
+ test::WaiterThread thread(d1,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ 2,
+ &did_wait, &result, &context);
+ stopwatch.Start();
+ thread.Start();
+ } // Joins the thread.
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+ EXPECT_FALSE(did_wait);
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, result);
+
+ // Consume what we wrote to |d0|.
+ buffer[0] = 0;
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ d1->ReadMessage(buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kBufferSize, buffer_size);
+ EXPECT_EQ(123456789, buffer[0]);
+
+ // Wait for readable on |d1| and close |d0| after some time, which should
+ // cancel that wait.
+ {
+ test::WaiterThread thread(d1,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ 3,
+ &did_wait, &result, &context);
+ stopwatch.Start();
+ thread.Start();
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ EXPECT_EQ(MOJO_RESULT_OK, d0->Close());
+ } // Joins the thread.
+ elapsed = stopwatch.Elapsed();
+ EXPECT_GT(elapsed, (2-1) * test::EpsilonTimeout());
+ EXPECT_LT(elapsed, (2+1) * test::EpsilonTimeout());
+ EXPECT_TRUE(did_wait);
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+ EXPECT_EQ(3u, context);
+
+ EXPECT_EQ(MOJO_RESULT_OK, d1->Close());
+ }
+
+ for (unsigned i = 0; i < 2; i++) {
+ scoped_refptr<MessagePipeDispatcher> d0(new MessagePipeDispatcher(
+ MessagePipeDispatcher::kDefaultCreateOptions));
+ scoped_refptr<MessagePipeDispatcher> d1(new MessagePipeDispatcher(
+ MessagePipeDispatcher::kDefaultCreateOptions));
+ {
+ scoped_refptr<MessagePipe> mp(new MessagePipe());
+ d0->Init(mp, i); // 0, 1.
+ d1->Init(mp, i ^ 1); // 1, 0.
+ }
+
+ // Wait for readable on |d1| and close |d1| after some time, which should
+ // cancel that wait.
+ {
+ test::WaiterThread thread(d1,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ 4,
+ &did_wait, &result, &context);
+ stopwatch.Start();
+ thread.Start();
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ EXPECT_EQ(MOJO_RESULT_OK, d1->Close());
+ } // Joins the thread.
+ elapsed = stopwatch.Elapsed();
+ EXPECT_GT(elapsed, (2-1) * test::EpsilonTimeout());
+ EXPECT_LT(elapsed, (2+1) * test::EpsilonTimeout());
+ EXPECT_TRUE(did_wait);
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+ EXPECT_EQ(4u, context);
+
+ EXPECT_EQ(MOJO_RESULT_OK, d0->Close());
+ }
+}
+
+// Stress test -----------------------------------------------------------------
+
+const size_t kMaxMessageSize = 2000;
+
+class WriterThread : public base::SimpleThread {
+ public:
+ // |*messages_written| and |*bytes_written| belong to the thread while it's
+ // alive.
+ WriterThread(scoped_refptr<Dispatcher> write_dispatcher,
+ size_t* messages_written, size_t* bytes_written)
+ : base::SimpleThread("writer_thread"),
+ write_dispatcher_(write_dispatcher),
+ messages_written_(messages_written),
+ bytes_written_(bytes_written) {
+ *messages_written_ = 0;
+ *bytes_written_ = 0;
+ }
+
+ virtual ~WriterThread() {
+ Join();
+ }
+
+ private:
+ virtual void Run() OVERRIDE {
+ // Make some data to write.
+ unsigned char buffer[kMaxMessageSize];
+ for (size_t i = 0; i < kMaxMessageSize; i++)
+ buffer[i] = static_cast<unsigned char>(i);
+
+ // Number of messages to write.
+ *messages_written_ = static_cast<size_t>(base::RandInt(1000, 6000));
+
+ // Write messages.
+ for (size_t i = 0; i < *messages_written_; i++) {
+ uint32_t bytes_to_write = static_cast<uint32_t>(
+ base::RandInt(1, static_cast<int>(kMaxMessageSize)));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ write_dispatcher_->WriteMessage(buffer, bytes_to_write,
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ *bytes_written_ += bytes_to_write;
+ }
+
+ // Write one last "quit" message.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ write_dispatcher_->WriteMessage("quit", 4,
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ }
+
+ const scoped_refptr<Dispatcher> write_dispatcher_;
+ size_t* const messages_written_;
+ size_t* const bytes_written_;
+
+ DISALLOW_COPY_AND_ASSIGN(WriterThread);
+};
+
+class ReaderThread : public base::SimpleThread {
+ public:
+ // |*messages_read| and |*bytes_read| belong to the thread while it's alive.
+ ReaderThread(scoped_refptr<Dispatcher> read_dispatcher,
+ size_t* messages_read, size_t* bytes_read)
+ : base::SimpleThread("reader_thread"),
+ read_dispatcher_(read_dispatcher),
+ messages_read_(messages_read),
+ bytes_read_(bytes_read) {
+ *messages_read_ = 0;
+ *bytes_read_ = 0;
+ }
+
+ virtual ~ReaderThread() {
+ Join();
+ }
+
+ private:
+ virtual void Run() OVERRIDE {
+ unsigned char buffer[kMaxMessageSize];
+ MojoResult result;
+ Waiter w;
+
+ // Read messages.
+ for (;;) {
+ // Wait for it to be readable.
+ w.Init();
+ result = read_dispatcher_->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 0);
+ EXPECT_TRUE(result == MOJO_RESULT_OK ||
+ result == MOJO_RESULT_ALREADY_EXISTS) << "result: " << result;
+ if (result == MOJO_RESULT_OK) {
+ // Actually need to wait.
+ EXPECT_EQ(MOJO_RESULT_OK, w.Wait(MOJO_DEADLINE_INDEFINITE, NULL));
+ read_dispatcher_->RemoveWaiter(&w);
+ }
+
+ // Now, try to do the read.
+ // Clear the buffer so that we can check the result.
+ memset(buffer, 0, sizeof(buffer));
+ uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ result = read_dispatcher_->ReadMessage(buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+ EXPECT_TRUE(result == MOJO_RESULT_OK ||
+ result == MOJO_RESULT_SHOULD_WAIT) << "result: " << result;
+ // We're racing with others to read, so maybe we failed.
+ if (result == MOJO_RESULT_SHOULD_WAIT)
+ continue; // In which case, try again.
+ // Check for quit.
+ if (buffer_size == 4 && memcmp("quit", buffer, 4) == 0)
+ return;
+ EXPECT_GE(buffer_size, 1u);
+ EXPECT_LE(buffer_size, kMaxMessageSize);
+ EXPECT_TRUE(IsValidMessage(buffer, buffer_size));
+
+ (*messages_read_)++;
+ *bytes_read_ += buffer_size;
+ }
+ }
+
+ static bool IsValidMessage(const unsigned char* buffer,
+ uint32_t message_size) {
+ size_t i;
+ for (i = 0; i < message_size; i++) {
+ if (buffer[i] != static_cast<unsigned char>(i))
+ return false;
+ }
+ // Check that the remaining bytes weren't stomped on.
+ for (; i < kMaxMessageSize; i++) {
+ if (buffer[i] != 0)
+ return false;
+ }
+ return true;
+ }
+
+ const scoped_refptr<Dispatcher> read_dispatcher_;
+ size_t* const messages_read_;
+ size_t* const bytes_read_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReaderThread);
+};
+
+TEST(MessagePipeDispatcherTest, Stress) {
+ static const size_t kNumWriters = 30;
+ static const size_t kNumReaders = kNumWriters;
+
+ scoped_refptr<MessagePipeDispatcher> d_write(new MessagePipeDispatcher(
+ MessagePipeDispatcher::kDefaultCreateOptions));
+ scoped_refptr<MessagePipeDispatcher> d_read(new MessagePipeDispatcher(
+ MessagePipeDispatcher::kDefaultCreateOptions));
+ {
+ scoped_refptr<MessagePipe> mp(new MessagePipe());
+ d_write->Init(mp, 0);
+ d_read->Init(mp, 1);
+ }
+
+ size_t messages_written[kNumWriters];
+ size_t bytes_written[kNumWriters];
+ size_t messages_read[kNumReaders];
+ size_t bytes_read[kNumReaders];
+ {
+ // Make writers.
+ ScopedVector<WriterThread> writers;
+ for (size_t i = 0; i < kNumWriters; i++) {
+ writers.push_back(
+ new WriterThread(d_write, &messages_written[i], &bytes_written[i]));
+ }
+
+ // Make readers.
+ ScopedVector<ReaderThread> readers;
+ for (size_t i = 0; i < kNumReaders; i++) {
+ readers.push_back(
+ new ReaderThread(d_read, &messages_read[i], &bytes_read[i]));
+ }
+
+ // Start writers.
+ for (size_t i = 0; i < kNumWriters; i++)
+ writers[i]->Start();
+
+ // Start readers.
+ for (size_t i = 0; i < kNumReaders; i++)
+ readers[i]->Start();
+
+ // TODO(vtl): Maybe I should have an event that triggers all the threads to
+ // start doing stuff for real (so that the first ones created/started aren't
+ // advantaged).
+ } // Joins all the threads.
+
+ size_t total_messages_written = 0;
+ size_t total_bytes_written = 0;
+ for (size_t i = 0; i < kNumWriters; i++) {
+ total_messages_written += messages_written[i];
+ total_bytes_written += bytes_written[i];
+ }
+ size_t total_messages_read = 0;
+ size_t total_bytes_read = 0;
+ for (size_t i = 0; i < kNumReaders; i++) {
+ total_messages_read += messages_read[i];
+ total_bytes_read += bytes_read[i];
+ // We'd have to be really unlucky to have read no messages on a thread.
+ EXPECT_GT(messages_read[i], 0u) << "reader: " << i;
+ EXPECT_GE(bytes_read[i], messages_read[i]) << "reader: " << i;
+ }
+ EXPECT_EQ(total_messages_written, total_messages_read);
+ EXPECT_EQ(total_bytes_written, total_bytes_read);
+
+ EXPECT_EQ(MOJO_RESULT_OK, d_write->Close());
+ EXPECT_EQ(MOJO_RESULT_OK, d_read->Close());
+}
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/message_pipe_endpoint.cc b/chromium/mojo/system/message_pipe_endpoint.cc
new file mode 100644
index 00000000000..f5ccfc771b3
--- /dev/null
+++ b/chromium/mojo/system/message_pipe_endpoint.cc
@@ -0,0 +1,56 @@
+// Copyright 2013 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 "mojo/system/message_pipe_endpoint.h"
+
+#include "base/logging.h"
+#include "mojo/system/channel.h"
+
+namespace mojo {
+namespace system {
+
+void MessagePipeEndpoint::Close() {
+ NOTREACHED();
+}
+
+void MessagePipeEndpoint::CancelAllWaiters() {
+ NOTREACHED();
+}
+
+MojoResult MessagePipeEndpoint::ReadMessage(void* /*bytes*/,
+ uint32_t* /*num_bytes*/,
+ DispatcherVector* /*dispatchers*/,
+ uint32_t* /*num_dispatchers*/,
+ MojoReadMessageFlags /*flags*/) {
+ NOTREACHED();
+ return MOJO_RESULT_INTERNAL;
+}
+
+MojoResult MessagePipeEndpoint::AddWaiter(Waiter* /*waiter*/,
+ MojoHandleSignals /*signals*/,
+ uint32_t /*context*/) {
+ NOTREACHED();
+ return MOJO_RESULT_INTERNAL;
+}
+
+void MessagePipeEndpoint::RemoveWaiter(Waiter* /*waiter*/) {
+ NOTREACHED();
+}
+
+void MessagePipeEndpoint::Attach(scoped_refptr<Channel> /*channel*/,
+ MessageInTransit::EndpointId /*local_id*/) {
+ NOTREACHED();
+}
+
+bool MessagePipeEndpoint::Run(MessageInTransit::EndpointId /*remote_id*/) {
+ NOTREACHED();
+ return true;
+}
+
+void MessagePipeEndpoint::OnRemove() {
+ NOTREACHED();
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/message_pipe_endpoint.h b/chromium/mojo/system/message_pipe_endpoint.h
new file mode 100644
index 00000000000..7da65f07a26
--- /dev/null
+++ b/chromium/mojo/system/message_pipe_endpoint.h
@@ -0,0 +1,93 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_MESSAGE_PIPE_ENDPOINT_H_
+#define MOJO_SYSTEM_MESSAGE_PIPE_ENDPOINT_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/c/system/message_pipe.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/system/dispatcher.h"
+#include "mojo/system/message_in_transit.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class Channel;
+class Waiter;
+
+// This is an interface to one of the ends of a message pipe, and is used by
+// |MessagePipe|. Its most important role is to provide a sink for messages
+// (i.e., a place where messages can be sent). It has a secondary role: When the
+// endpoint is local (i.e., in the current process), there'll be a dispatcher
+// corresponding to the endpoint. In that case, the implementation of
+// |MessagePipeEndpoint| also implements the functionality required by the
+// dispatcher, e.g., to read messages and to wait. Implementations of this class
+// are not thread-safe; instances are protected by |MesssagePipe|'s lock.
+class MOJO_SYSTEM_IMPL_EXPORT MessagePipeEndpoint {
+ public:
+ virtual ~MessagePipeEndpoint() {}
+
+ enum Type {
+ kTypeLocal,
+ kTypeProxy
+ };
+ virtual Type GetType() const = 0;
+
+ // All implementations must implement these.
+ // Returns false if the endpoint should be closed and destroyed, else true.
+ virtual bool OnPeerClose() = 0;
+ // Implements |MessagePipe::EnqueueMessage()|. The major differences are that:
+ // a) Dispatchers have been vetted and cloned/attached to the message.
+ // b) At this point, we cannot report failure (if, e.g., a channel is torn
+ // down at this point, we should silently swallow the message).
+ virtual void EnqueueMessage(scoped_ptr<MessageInTransit> message) = 0;
+
+ // Implementations must override these if they represent a local endpoint,
+ // i.e., one for which there's a |MessagePipeDispatcher| (and thus a handle).
+ // An implementation for a proxy endpoint (for which there's no dispatcher)
+ // needs not override these methods, since they should never be called.
+ //
+ // These methods implement the methods of the same name in |MessagePipe|,
+ // though |MessagePipe|'s implementation may have to do a little more if the
+ // operation involves both endpoints.
+ virtual void Close();
+ virtual void CancelAllWaiters();
+ virtual MojoResult ReadMessage(void* bytes,
+ uint32_t* num_bytes,
+ DispatcherVector* dispatchers,
+ uint32_t* num_dispatchers,
+ MojoReadMessageFlags flags);
+ virtual MojoResult AddWaiter(Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context);
+ virtual void RemoveWaiter(Waiter* waiter);
+
+ // Implementations must override these if they represent a proxy endpoint. An
+ // implementation for a local endpoint needs not override these methods, since
+ // they should never be called.
+ virtual void Attach(scoped_refptr<Channel> channel,
+ MessageInTransit::EndpointId local_id);
+ // Returns false if the endpoint should be closed and destroyed, else true.
+ virtual bool Run(MessageInTransit::EndpointId remote_id);
+ virtual void OnRemove();
+
+ protected:
+ MessagePipeEndpoint() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MessagePipeEndpoint);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_MESSAGE_PIPE_ENDPOINT_H_
diff --git a/chromium/mojo/system/message_pipe_unittest.cc b/chromium/mojo/system/message_pipe_unittest.cc
new file mode 100644
index 00000000000..5405fd52441
--- /dev/null
+++ b/chromium/mojo/system/message_pipe_unittest.cc
@@ -0,0 +1,525 @@
+// Copyright 2013 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 "mojo/system/message_pipe.h"
+
+#include "base/memory/ref_counted.h"
+#include "base/threading/platform_thread.h" // For |Sleep()|.
+#include "base/time/time.h"
+#include "mojo/system/waiter.h"
+#include "mojo/system/waiter_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+// Tests:
+// - only default flags
+// - reading messages from a port
+// - when there are no/one/two messages available for that port
+// - with buffer size 0 (and null buffer) -- should get size
+// - with too-small buffer -- should get size
+// - also verify that buffers aren't modified when/where they shouldn't be
+// - writing messages to a port
+// - in the obvious scenarios (as above)
+// - to a port that's been closed
+// - writing a message to a port, closing the other (would be the source) port,
+// and reading it
+TEST(MessagePipeTest, Basic) {
+ scoped_refptr<MessagePipe> mp(new MessagePipe());
+
+ int32_t buffer[2];
+ const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+ uint32_t buffer_size;
+
+ // Nothing to read yet on port 0.
+ buffer[0] = 123;
+ buffer[1] = 456;
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ mp->ReadMessage(0,
+ buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kBufferSize, buffer_size);
+ EXPECT_EQ(123, buffer[0]);
+ EXPECT_EQ(456, buffer[1]);
+
+ // Ditto for port 1.
+ buffer[0] = 123;
+ buffer[1] = 456;
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ mp->ReadMessage(1,
+ buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+
+ // Write from port 1 (to port 0).
+ buffer[0] = 789012345;
+ buffer[1] = 0;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->WriteMessage(1,
+ buffer, static_cast<uint32_t>(sizeof(buffer[0])),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Read from port 0.
+ buffer[0] = 123;
+ buffer[1] = 456;
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->ReadMessage(0,
+ buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+ EXPECT_EQ(789012345, buffer[0]);
+ EXPECT_EQ(456, buffer[1]);
+
+ // Read again from port 0 -- it should be empty.
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ mp->ReadMessage(0,
+ buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+
+ // Write two messages from port 0 (to port 1).
+ buffer[0] = 123456789;
+ buffer[1] = 0;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->WriteMessage(0,
+ buffer, static_cast<uint32_t>(sizeof(buffer[0])),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ buffer[0] = 234567890;
+ buffer[1] = 0;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->WriteMessage(0,
+ buffer, static_cast<uint32_t>(sizeof(buffer[0])),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Read from port 1 with buffer size 0 (should get the size of next message).
+ // Also test that giving a null buffer is okay when the buffer size is 0.
+ buffer_size = 0;
+ EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+ mp->ReadMessage(1,
+ NULL, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+
+ // Read from port 1 with buffer size 1 (too small; should get the size of next
+ // message).
+ buffer[0] = 123;
+ buffer[1] = 456;
+ buffer_size = 1;
+ EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+ mp->ReadMessage(1,
+ buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+ EXPECT_EQ(123, buffer[0]);
+ EXPECT_EQ(456, buffer[1]);
+
+ // Read from port 1.
+ buffer[0] = 123;
+ buffer[1] = 456;
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->ReadMessage(1,
+ buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+ EXPECT_EQ(123456789, buffer[0]);
+ EXPECT_EQ(456, buffer[1]);
+
+ // Read again from port 1.
+ buffer[0] = 123;
+ buffer[1] = 456;
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->ReadMessage(1,
+ buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+ EXPECT_EQ(234567890, buffer[0]);
+ EXPECT_EQ(456, buffer[1]);
+
+ // Read again from port 1 -- it should be empty.
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ mp->ReadMessage(1,
+ buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+
+ // Write from port 0 (to port 1).
+ buffer[0] = 345678901;
+ buffer[1] = 0;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->WriteMessage(0,
+ buffer, static_cast<uint32_t>(sizeof(buffer[0])),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Close port 0.
+ mp->Close(0);
+
+ // Try to write from port 1 (to port 0).
+ buffer[0] = 456789012;
+ buffer[1] = 0;
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ mp->WriteMessage(1,
+ buffer, static_cast<uint32_t>(sizeof(buffer[0])),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Read from port 1; should still get message (even though port 0 was closed).
+ buffer[0] = 123;
+ buffer[1] = 456;
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->ReadMessage(1,
+ buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+ EXPECT_EQ(345678901, buffer[0]);
+ EXPECT_EQ(456, buffer[1]);
+
+ // Read again from port 1 -- it should be empty (and port 0 is closed).
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ mp->ReadMessage(1,
+ buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+
+ mp->Close(1);
+}
+
+TEST(MessagePipeTest, CloseWithQueuedIncomingMessages) {
+ scoped_refptr<MessagePipe> mp(new MessagePipe());
+
+ int32_t buffer[1];
+ const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+ uint32_t buffer_size;
+
+ // Write some messages from port 1 (to port 0).
+ for (int32_t i = 0; i < 5; i++) {
+ buffer[0] = i;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->WriteMessage(1,
+ buffer, kBufferSize,
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ }
+
+ // Port 0 shouldn't be empty.
+ buffer_size = 0;
+ EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+ mp->ReadMessage(0,
+ NULL, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(kBufferSize, buffer_size);
+
+ // Close port 0 first, which should have outstanding (incoming) messages.
+ mp->Close(0);
+ mp->Close(1);
+}
+
+TEST(MessagePipeTest, DiscardMode) {
+ scoped_refptr<MessagePipe> mp(new MessagePipe());
+
+ int32_t buffer[2];
+ const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+ uint32_t buffer_size;
+
+ // Write from port 1 (to port 0).
+ buffer[0] = 789012345;
+ buffer[1] = 0;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->WriteMessage(1,
+ buffer, static_cast<uint32_t>(sizeof(buffer[0])),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Read/discard from port 0 (no buffer); get size.
+ buffer_size = 0;
+ EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+ mp->ReadMessage(0,
+ NULL, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+ EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+
+ // Read again from port 0 -- it should be empty.
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ mp->ReadMessage(0,
+ buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+
+ // Write from port 1 (to port 0).
+ buffer[0] = 890123456;
+ buffer[1] = 0;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->WriteMessage(1,
+ buffer, static_cast<uint32_t>(sizeof(buffer[0])),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Read from port 0 (buffer big enough).
+ buffer[0] = 123;
+ buffer[1] = 456;
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->ReadMessage(0,
+ buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+ EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+ EXPECT_EQ(890123456, buffer[0]);
+ EXPECT_EQ(456, buffer[1]);
+
+ // Read again from port 0 -- it should be empty.
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ mp->ReadMessage(0,
+ buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+
+ // Write from port 1 (to port 0).
+ buffer[0] = 901234567;
+ buffer[1] = 0;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->WriteMessage(1,
+ buffer, static_cast<uint32_t>(sizeof(buffer[0])),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Read/discard from port 0 (buffer too small); get size.
+ buffer_size = 1;
+ EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+ mp->ReadMessage(0,
+ buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+ EXPECT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size);
+
+ // Read again from port 0 -- it should be empty.
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ mp->ReadMessage(0,
+ buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+
+ // Write from port 1 (to port 0).
+ buffer[0] = 123456789;
+ buffer[1] = 0;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->WriteMessage(1,
+ buffer, static_cast<uint32_t>(sizeof(buffer[0])),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Discard from port 0.
+ buffer_size = 1;
+ EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+ mp->ReadMessage(0,
+ NULL, NULL,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+
+ // Read again from port 0 -- it should be empty.
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ mp->ReadMessage(0,
+ buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_MAY_DISCARD));
+
+ mp->Close(0);
+ mp->Close(1);
+}
+
+TEST(MessagePipeTest, BasicWaiting) {
+ scoped_refptr<MessagePipe> mp(new MessagePipe());
+ Waiter waiter;
+
+ int32_t buffer[1];
+ const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+ uint32_t buffer_size;
+
+ // Always writable (until the other port is closed).
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ mp->AddWaiter(0, &waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 0));
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ mp->AddWaiter(0,
+ &waiter,
+ MOJO_HANDLE_SIGNAL_READABLE |
+ MOJO_HANDLE_SIGNAL_WRITABLE,
+ 0));
+
+ // Not yet readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->AddWaiter(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 1));
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, NULL));
+ mp->RemoveWaiter(0, &waiter);
+
+ // Write from port 0 (to port 1), to make port 1 readable.
+ buffer[0] = 123456789;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->WriteMessage(0,
+ buffer, kBufferSize,
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Port 1 should already be readable now.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ mp->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 2));
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ mp->AddWaiter(1,
+ &waiter,
+ MOJO_HANDLE_SIGNAL_READABLE |
+ MOJO_HANDLE_SIGNAL_WRITABLE,
+ 0));
+ // ... and still writable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ mp->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 3));
+
+ // Close port 0.
+ mp->Close(0);
+
+ // Now port 1 should not be writable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ mp->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_WRITABLE, 4));
+
+ // But it should still be readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ mp->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 5));
+
+ // Read from port 1.
+ buffer[0] = 0;
+ buffer_size = kBufferSize;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->ReadMessage(1,
+ buffer, &buffer_size,
+ 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(123456789, buffer[0]);
+
+ // Now port 1 should no longer be readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ mp->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 6));
+
+ mp->Close(1);
+}
+
+TEST(MessagePipeTest, ThreadedWaiting) {
+ int32_t buffer[1];
+ const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer));
+
+ MojoResult result;
+ uint32_t context;
+
+ // Write to wake up waiter waiting for read.
+ {
+ scoped_refptr<MessagePipe> mp(new MessagePipe());
+ test::SimpleWaiterThread thread(&result, &context);
+
+ thread.waiter()->Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->AddWaiter(1, thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE,
+ 1));
+ thread.Start();
+
+ buffer[0] = 123456789;
+ // Write from port 0 (to port 1), which should wake up the waiter.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->WriteMessage(0,
+ buffer, kBufferSize,
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ mp->RemoveWaiter(1, thread.waiter());
+
+ mp->Close(0);
+ mp->Close(1);
+ } // Joins |thread|.
+ // The waiter should have woken up successfully.
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(1u, context);
+
+ // Close to cancel waiter.
+ {
+ scoped_refptr<MessagePipe> mp(new MessagePipe());
+ test::SimpleWaiterThread thread(&result, &context);
+
+ thread.waiter()->Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->AddWaiter(1, thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE,
+ 2));
+ thread.Start();
+
+ // Close port 1 first -- this should result in the waiter being cancelled.
+ mp->CancelAllWaiters(1);
+ mp->Close(1);
+
+ // Port 1 is closed, so |Dispatcher::RemoveWaiter()| wouldn't call into the
+ // |MessagePipe| to remove any waiter.
+
+ mp->Close(0);
+ } // Joins |thread|.
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+ EXPECT_EQ(2u, context);
+
+ // Close to make waiter un-wake-up-able.
+ {
+ scoped_refptr<MessagePipe> mp(new MessagePipe());
+ test::SimpleWaiterThread thread(&result, &context);
+
+ thread.waiter()->Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->AddWaiter(1, thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE,
+ 3));
+ thread.Start();
+
+ // Close port 0 first -- this should wake the waiter up, since port 1 will
+ // never be readable.
+ mp->CancelAllWaiters(0);
+ mp->Close(0);
+
+ mp->RemoveWaiter(1, thread.waiter());
+
+ mp->CancelAllWaiters(1);
+ mp->Close(1);
+ } // Joins |thread|.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+ EXPECT_EQ(3u, context);
+}
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/multiprocess_message_pipe_unittest.cc b/chromium/mojo/system/multiprocess_message_pipe_unittest.cc
new file mode 100644
index 00000000000..55527788c13
--- /dev/null
+++ b/chromium/mojo/system/multiprocess_message_pipe_unittest.cc
@@ -0,0 +1,555 @@
+// Copyright 2013 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 <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/threading/platform_thread.h" // For |Sleep()|.
+#include "build/build_config.h" // TODO(vtl): Remove this.
+#include "mojo/common/test/multiprocess_test_helper.h"
+#include "mojo/common/test/test_utils.h"
+#include "mojo/embedder/scoped_platform_handle.h"
+#include "mojo/system/channel.h"
+#include "mojo/system/dispatcher.h"
+#include "mojo/system/local_message_pipe_endpoint.h"
+#include "mojo/system/message_pipe.h"
+#include "mojo/system/platform_handle_dispatcher.h"
+#include "mojo/system/proxy_message_pipe_endpoint.h"
+#include "mojo/system/raw_channel.h"
+#include "mojo/system/raw_shared_buffer.h"
+#include "mojo/system/shared_buffer_dispatcher.h"
+#include "mojo/system/test_utils.h"
+#include "mojo/system/waiter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+class ChannelThread {
+ public:
+ ChannelThread() : test_io_thread_(test::TestIOThread::kManualStart) {}
+ ~ChannelThread() {
+ Stop();
+ }
+
+ void Start(embedder::ScopedPlatformHandle platform_handle,
+ scoped_refptr<MessagePipe> message_pipe) {
+ test_io_thread_.Start();
+ test_io_thread_.PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&ChannelThread::InitChannelOnIOThread,
+ base::Unretained(this), base::Passed(&platform_handle),
+ message_pipe));
+ }
+
+ void Stop() {
+ if (channel_) {
+ // Hack to flush write buffers before quitting.
+ // TODO(vtl): Remove this once |Channel| has a
+ // |FlushWriteBufferAndShutdown()| (or whatever).
+ while (!channel_->IsWriteBufferEmpty())
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
+
+ test_io_thread_.PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&ChannelThread::ShutdownChannelOnIOThread,
+ base::Unretained(this)));
+ }
+ test_io_thread_.Stop();
+ }
+
+ private:
+ void InitChannelOnIOThread(embedder::ScopedPlatformHandle platform_handle,
+ scoped_refptr<MessagePipe> message_pipe) {
+ CHECK_EQ(base::MessageLoop::current(), test_io_thread_.message_loop());
+ CHECK(platform_handle.is_valid());
+
+ // Create and initialize |Channel|.
+ channel_ = new Channel();
+ CHECK(channel_->Init(RawChannel::Create(platform_handle.Pass())));
+
+ // Attach the message pipe endpoint.
+ // Note: On the "server" (parent process) side, we need not attach the
+ // message pipe endpoint immediately. However, on the "client" (child
+ // process) side, this *must* be done here -- otherwise, the |Channel| may
+ // receive/process messages (which it can do as soon as it's hooked up to
+ // the IO thread message loop, and that message loop runs) before the
+ // message pipe endpoint is attached.
+ CHECK_EQ(channel_->AttachMessagePipeEndpoint(message_pipe, 1),
+ Channel::kBootstrapEndpointId);
+ CHECK(channel_->RunMessagePipeEndpoint(Channel::kBootstrapEndpointId,
+ Channel::kBootstrapEndpointId));
+ }
+
+ void ShutdownChannelOnIOThread() {
+ CHECK(channel_);
+ channel_->Shutdown();
+ channel_ = NULL;
+ }
+
+ test::TestIOThread test_io_thread_;
+ scoped_refptr<Channel> channel_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChannelThread);
+};
+
+class MultiprocessMessagePipeTest : public testing::Test {
+ public:
+ MultiprocessMessagePipeTest() {}
+ virtual ~MultiprocessMessagePipeTest() {}
+
+ protected:
+ void Init(scoped_refptr<MessagePipe> mp) {
+ channel_thread_.Start(helper_.server_platform_handle.Pass(), mp);
+ }
+
+ mojo::test::MultiprocessTestHelper* helper() { return &helper_; }
+
+ private:
+ ChannelThread channel_thread_;
+ mojo::test::MultiprocessTestHelper helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(MultiprocessMessagePipeTest);
+};
+
+MojoResult WaitIfNecessary(scoped_refptr<MessagePipe> mp,
+ MojoHandleSignals signals) {
+ Waiter waiter;
+ waiter.Init();
+
+ MojoResult add_result = mp->AddWaiter(0, &waiter, signals, 0);
+ if (add_result != MOJO_RESULT_OK) {
+ return (add_result == MOJO_RESULT_ALREADY_EXISTS) ? MOJO_RESULT_OK :
+ add_result;
+ }
+
+ MojoResult wait_result = waiter.Wait(MOJO_DEADLINE_INDEFINITE, NULL);
+ mp->RemoveWaiter(0, &waiter);
+ return wait_result;
+}
+
+// For each message received, sends a reply message with the same contents
+// repeated twice, until the other end is closed or it receives "quitquitquit"
+// (which it doesn't reply to). It'll return the number of messages received,
+// not including any "quitquitquit" message, modulo 100.
+MOJO_MULTIPROCESS_TEST_CHILD_MAIN(EchoEcho) {
+ ChannelThread channel_thread;
+ embedder::ScopedPlatformHandle client_platform_handle =
+ mojo::test::MultiprocessTestHelper::client_platform_handle.Pass();
+ CHECK(client_platform_handle.is_valid());
+ scoped_refptr<MessagePipe> mp(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+ channel_thread.Start(client_platform_handle.Pass(), mp);
+
+ const std::string quitquitquit("quitquitquit");
+ int rv = 0;
+ for (;; rv = (rv + 1) % 100) {
+ // Wait for our end of the message pipe to be readable.
+ MojoResult result = WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE);
+ if (result != MOJO_RESULT_OK) {
+ // It was closed, probably.
+ CHECK_EQ(result, MOJO_RESULT_FAILED_PRECONDITION);
+ break;
+ }
+
+ std::string read_buffer(1000, '\0');
+ uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size());
+ CHECK_EQ(mp->ReadMessage(0,
+ &read_buffer[0], &read_buffer_size,
+ NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE),
+ MOJO_RESULT_OK);
+ read_buffer.resize(read_buffer_size);
+ VLOG(2) << "Child got: " << read_buffer;
+
+ if (read_buffer == quitquitquit) {
+ VLOG(2) << "Child quitting.";
+ break;
+ }
+
+ std::string write_buffer = read_buffer + read_buffer;
+ CHECK_EQ(mp->WriteMessage(0,
+ write_buffer.data(),
+ static_cast<uint32_t>(write_buffer.size()),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE),
+ MOJO_RESULT_OK);
+ }
+
+ mp->Close(0);
+ return rv;
+}
+
+// Sends "hello" to child, and expects "hellohello" back.
+TEST_F(MultiprocessMessagePipeTest, Basic) {
+ helper()->StartChild("EchoEcho");
+
+ scoped_refptr<MessagePipe> mp(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+ Init(mp);
+
+ std::string hello("hello");
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->WriteMessage(0,
+ hello.data(), static_cast<uint32_t>(hello.size()),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ EXPECT_EQ(MOJO_RESULT_OK, WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE));
+
+ std::string read_buffer(1000, '\0');
+ uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size());
+ CHECK_EQ(mp->ReadMessage(0,
+ &read_buffer[0], &read_buffer_size,
+ NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE),
+ MOJO_RESULT_OK);
+ read_buffer.resize(read_buffer_size);
+ VLOG(2) << "Parent got: " << read_buffer;
+ EXPECT_EQ(hello + hello, read_buffer);
+
+ mp->Close(0);
+
+ // We sent one message.
+ EXPECT_EQ(1 % 100, helper()->WaitForChildShutdown());
+}
+
+// Sends a bunch of messages to the child. Expects them "repeated" back. Waits
+// for the child to close its end before quitting.
+TEST_F(MultiprocessMessagePipeTest, QueueMessages) {
+ helper()->StartChild("EchoEcho");
+
+ scoped_refptr<MessagePipe> mp(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+ Init(mp);
+
+ static const size_t kNumMessages = 1001;
+ for (size_t i = 0; i < kNumMessages; i++) {
+ std::string write_buffer(i, 'A' + (i % 26));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->WriteMessage(0,
+ write_buffer.data(),
+ static_cast<uint32_t>(write_buffer.size()),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ }
+
+ const std::string quitquitquit("quitquitquit");
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->WriteMessage(0,
+ quitquitquit.data(),
+ static_cast<uint32_t>(quitquitquit.size()),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ for (size_t i = 0; i < kNumMessages; i++) {
+ EXPECT_EQ(MOJO_RESULT_OK, WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE));
+
+ std::string read_buffer(kNumMessages * 2, '\0');
+ uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size());
+ CHECK_EQ(mp->ReadMessage(0,
+ &read_buffer[0], &read_buffer_size,
+ NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE),
+ MOJO_RESULT_OK);
+ read_buffer.resize(read_buffer_size);
+
+ EXPECT_EQ(std::string(i * 2, 'A' + (i % 26)), read_buffer);
+ }
+
+ // Wait for it to become readable, which should fail (since we sent
+ // "quitquitquit").
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE));
+
+ mp->Close(0);
+
+ EXPECT_EQ(static_cast<int>(kNumMessages % 100),
+ helper()->WaitForChildShutdown());
+}
+
+MOJO_MULTIPROCESS_TEST_CHILD_MAIN(CheckSharedBuffer) {
+ ChannelThread channel_thread;
+ embedder::ScopedPlatformHandle client_platform_handle =
+ mojo::test::MultiprocessTestHelper::client_platform_handle.Pass();
+ CHECK(client_platform_handle.is_valid());
+ scoped_refptr<MessagePipe> mp(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+ channel_thread.Start(client_platform_handle.Pass(), mp);
+
+ // Wait for the first message from our parent.
+ CHECK_EQ(WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE), MOJO_RESULT_OK);
+
+ // It should have a shared buffer.
+ std::string read_buffer(100, '\0');
+ uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size());
+ DispatcherVector dispatchers;
+ uint32_t num_dispatchers = 10; // Maximum number to receive.
+ CHECK_EQ(mp->ReadMessage(0,
+ &read_buffer[0], &num_bytes,
+ &dispatchers, &num_dispatchers,
+ MOJO_READ_MESSAGE_FLAG_NONE),
+ MOJO_RESULT_OK);
+ read_buffer.resize(num_bytes);
+ CHECK_EQ(read_buffer, std::string("go 1"));
+ CHECK_EQ(num_dispatchers, 1u);
+
+ CHECK_EQ(dispatchers[0]->GetType(), Dispatcher::kTypeSharedBuffer);
+
+ scoped_refptr<SharedBufferDispatcher> dispatcher(
+ static_cast<SharedBufferDispatcher*>(dispatchers[0].get()));
+
+ // Make a mapping.
+ scoped_ptr<RawSharedBufferMapping> mapping;
+ CHECK_EQ(dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping),
+ MOJO_RESULT_OK);
+ CHECK(mapping);
+ CHECK(mapping->base());
+ CHECK_EQ(mapping->length(), 100u);
+
+ // Write some stuff to the shared buffer.
+ static const char kHello[] = "hello";
+ memcpy(mapping->base(), kHello, sizeof(kHello));
+
+ // We should be able to close the dispatcher now.
+ dispatcher->Close();
+
+ // And send a message to signal that we've written stuff.
+ const std::string go2("go 2");
+ CHECK_EQ(mp->WriteMessage(0,
+ &go2[0],
+ static_cast<uint32_t>(go2.size()),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE),
+ MOJO_RESULT_OK);
+
+ // Now wait for our parent to send us a message.
+ CHECK_EQ(WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE), MOJO_RESULT_OK);
+
+ read_buffer = std::string(100, '\0');
+ num_bytes = static_cast<uint32_t>(read_buffer.size());
+ CHECK_EQ(mp->ReadMessage(0,
+ &read_buffer[0], &num_bytes,
+ NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE),
+ MOJO_RESULT_OK);
+ read_buffer.resize(num_bytes);
+ CHECK_EQ(read_buffer, std::string("go 3"));
+
+ // It should have written something to the shared buffer.
+ static const char kWorld[] = "world!!!";
+ CHECK_EQ(memcmp(mapping->base(), kWorld, sizeof(kWorld)), 0);
+
+ // And we're done.
+ mp->Close(0);
+
+ return 0;
+}
+
+#if defined(OS_POSIX)
+#define MAYBE_SharedBufferPassing SharedBufferPassing
+#else
+// Not yet implemented (on Windows).
+#define MAYBE_SharedBufferPassing DISABLED_SharedBufferPassing
+#endif
+TEST_F(MultiprocessMessagePipeTest, MAYBE_SharedBufferPassing) {
+ helper()->StartChild("CheckSharedBuffer");
+
+ scoped_refptr<MessagePipe> mp(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+ Init(mp);
+
+ // Make a shared buffer.
+ scoped_refptr<SharedBufferDispatcher> dispatcher;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ SharedBufferDispatcher::Create(
+ SharedBufferDispatcher::kDefaultCreateOptions, 100,
+ &dispatcher));
+ ASSERT_TRUE(dispatcher);
+
+ // Make a mapping.
+ scoped_ptr<RawSharedBufferMapping> mapping;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping));
+ ASSERT_TRUE(mapping);
+ ASSERT_TRUE(mapping->base());
+ ASSERT_EQ(100u, mapping->length());
+
+ // Send the shared buffer.
+ const std::string go1("go 1");
+ DispatcherTransport transport(
+ test::DispatcherTryStartTransport(dispatcher.get()));
+ ASSERT_TRUE(transport.is_valid());
+
+ std::vector<DispatcherTransport> transports;
+ transports.push_back(transport);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->WriteMessage(0,
+ &go1[0],
+ static_cast<uint32_t>(go1.size()),
+ &transports,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ transport.End();
+
+ EXPECT_TRUE(dispatcher->HasOneRef());
+ dispatcher = NULL;
+
+ // Wait for a message from the child.
+ EXPECT_EQ(MOJO_RESULT_OK, WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE));
+
+ std::string read_buffer(100, '\0');
+ uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size());
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->ReadMessage(0,
+ &read_buffer[0], &num_bytes,
+ NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ read_buffer.resize(num_bytes);
+ EXPECT_EQ(std::string("go 2"), read_buffer);
+
+ // After we get it, the child should have written something to the shared
+ // buffer.
+ static const char kHello[] = "hello";
+ EXPECT_EQ(0, memcmp(mapping->base(), kHello, sizeof(kHello)));
+
+ // Now we'll write some stuff to the shared buffer.
+ static const char kWorld[] = "world!!!";
+ memcpy(mapping->base(), kWorld, sizeof(kWorld));
+
+ // And send a message to signal that we've written stuff.
+ const std::string go3("go 3");
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->WriteMessage(0,
+ &go3[0],
+ static_cast<uint32_t>(go3.size()),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Wait for |mp| to become readable, which should fail.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE));
+
+ mp->Close(0);
+
+ EXPECT_EQ(0, helper()->WaitForChildShutdown());
+}
+
+MOJO_MULTIPROCESS_TEST_CHILD_MAIN(CheckPlatformHandleFile) {
+ ChannelThread channel_thread;
+ embedder::ScopedPlatformHandle client_platform_handle =
+ mojo::test::MultiprocessTestHelper::client_platform_handle.Pass();
+ CHECK(client_platform_handle.is_valid());
+ scoped_refptr<MessagePipe> mp(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+ channel_thread.Start(client_platform_handle.Pass(), mp);
+
+ CHECK_EQ(WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE), MOJO_RESULT_OK);
+
+ std::string read_buffer(100, '\0');
+ uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size());
+ DispatcherVector dispatchers;
+ uint32_t num_dispatchers = 10; // Maximum number to receive.
+ CHECK_EQ(mp->ReadMessage(0,
+ &read_buffer[0], &num_bytes,
+ &dispatchers, &num_dispatchers,
+ MOJO_READ_MESSAGE_FLAG_NONE),
+ MOJO_RESULT_OK);
+ mp->Close(0);
+
+ read_buffer.resize(num_bytes);
+ CHECK_EQ(read_buffer, std::string("hello"));
+ CHECK_EQ(num_dispatchers, 1u);
+
+ CHECK_EQ(dispatchers[0]->GetType(), Dispatcher::kTypePlatformHandle);
+
+ scoped_refptr<PlatformHandleDispatcher> dispatcher(
+ static_cast<PlatformHandleDispatcher*>(dispatchers[0].get()));
+ embedder::ScopedPlatformHandle h = dispatcher->PassPlatformHandle().Pass();
+ CHECK(h.is_valid());
+ dispatcher->Close();
+
+ base::ScopedFILE fp(mojo::test::FILEFromPlatformHandle(h.Pass(), "r"));
+ CHECK(fp);
+ std::string fread_buffer(100, '\0');
+ size_t bytes_read = fread(&fread_buffer[0], 1, fread_buffer.size(), fp.get());
+ fread_buffer.resize(bytes_read);
+ CHECK_EQ(fread_buffer, "world");
+
+ return 0;
+}
+
+#if defined(OS_POSIX)
+#define MAYBE_PlatformHandlePassing PlatformHandlePassing
+#else
+// Not yet implemented (on Windows).
+#define MAYBE_PlatformHandlePassing DISABLED_PlatformHandlePassing
+#endif
+TEST_F(MultiprocessMessagePipeTest, MAYBE_PlatformHandlePassing) {
+ helper()->StartChild("CheckPlatformHandleFile");
+
+ scoped_refptr<MessagePipe> mp(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+ Init(mp);
+
+ base::FilePath unused;
+ base::ScopedFILE fp(CreateAndOpenTemporaryFile(&unused));
+ const std::string world("world");
+ ASSERT_EQ(fwrite(&world[0], 1, world.size(), fp.get()), world.size());
+ fflush(fp.get());
+ rewind(fp.get());
+
+ embedder::ScopedPlatformHandle h(
+ mojo::test::PlatformHandleFromFILE(fp.Pass()));
+ scoped_refptr<PlatformHandleDispatcher> dispatcher(
+ new PlatformHandleDispatcher(h.Pass()));
+
+ const std::string hello("hello");
+ DispatcherTransport transport(
+ test::DispatcherTryStartTransport(dispatcher.get()));
+ ASSERT_TRUE(transport.is_valid());
+
+ std::vector<DispatcherTransport> transports;
+ transports.push_back(transport);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp->WriteMessage(0,
+ &hello[0],
+ static_cast<uint32_t>(hello.size()),
+ &transports,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ transport.End();
+
+ EXPECT_TRUE(dispatcher->HasOneRef());
+ dispatcher = NULL;
+
+ // Wait for it to become readable, which should fail.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE));
+
+ mp->Close(0);
+
+ EXPECT_EQ(0, helper()->WaitForChildShutdown());
+}
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/options_validation.h b/chromium/mojo/system/options_validation.h
new file mode 100644
index 00000000000..04514adf752
--- /dev/null
+++ b/chromium/mojo/system/options_validation.h
@@ -0,0 +1,123 @@
+// Copyright 2014 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.
+
+// Functions to help with verifying various |Mojo...Options| structs from the
+// (public, C) API. These are "extensible" structs, which all have |struct_size|
+// as their first member. All fields (other than |struct_size|) are optional,
+// but any |flags| specified must be known to the system (otherwise, an error of
+// |MOJO_RESULT_UNIMPLEMENTED| should be returned).
+
+#ifndef MOJO_SYSTEM_OPTIONS_VALIDATION_H_
+#define MOJO_SYSTEM_OPTIONS_VALIDATION_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/system/memory.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+// Checks that |buffer| appears to contain a valid Options struct, namely
+// properly aligned and with a |struct_size| field (which must the first field
+// of the struct and be a |uint32_t|) containing a plausible size.
+template <class Options>
+bool IsOptionsStructPointerAndSizeValid(const void* buffer) {
+ COMPILE_ASSERT(offsetof(Options, struct_size) == 0,
+ Options_struct_size_not_first_member);
+ // TODO(vtl): With C++11, use |sizeof(Options::struct_size)| instead.
+ COMPILE_ASSERT(sizeof(static_cast<const Options*>(buffer)->struct_size) ==
+ sizeof(uint32_t),
+ Options_struct_size_not_32_bits);
+
+ // Note: Use |MOJO_ALIGNOF()| here to match the exact macro used in the
+ // declaration of Options structs.
+ if (!internal::VerifyUserPointerHelper<sizeof(uint32_t),
+ MOJO_ALIGNOF(Options)>(buffer))
+ return false;
+
+ return static_cast<const Options*>(buffer)->struct_size >= sizeof(uint32_t);
+}
+
+// Checks that the Options struct in |buffer| has a member with the given offset
+// and size. This may be called only if |IsOptionsStructPointerAndSizeValid()|
+// returned true.
+//
+// You may want to use the macro |HAS_OPTIONS_STRUCT_MEMBER()| instead.
+template <class Options, size_t offset, size_t size>
+bool HasOptionsStructMember(const void* buffer) {
+ // We assume that |offset| and |size| are reasonable, since they should come
+ // from |offsetof(Options, some_member)| and |sizeof(Options::some_member)|,
+ // respectively.
+ return static_cast<const Options*>(buffer)->struct_size >=
+ offset + size;
+}
+
+// Macro to invoke |HasOptionsStructMember()| parametrized by member name
+// instead of offset and size.
+//
+// (We can't just give |HasOptionsStructMember()| a member pointer template
+// argument instead, since there's no good/strictly-correct way to get an offset
+// from that.)
+//
+// TODO(vtl): With C++11, use |sizeof(Options::member)| instead.
+#define HAS_OPTIONS_STRUCT_MEMBER(Options, member, buffer) \
+ (HasOptionsStructMember< \
+ Options, \
+ offsetof(Options, member), \
+ sizeof(static_cast<const Options*>(buffer)->member)>(buffer))
+
+// Checks that the (standard) |flags| member consists of only known flags. This
+// should only be called if |HAS_OPTIONS_STRUCT_MEMBER()| returned true for the
+// |flags| field.
+//
+// The rationale for *not* ignoring these flags is that the caller should have a
+// way of specifying that certain options not be ignored. E.g., one may have a
+// |MOJO_..._OPTIONS_FLAG_DONT_IGNORE_FOO| flag and a |foo| member; if the flag
+// is set, it will guarantee that the version of the system knows about the
+// |foo| member (and won't ignore it).
+template <class Options>
+bool AreOptionsFlagsAllKnown(const void* buffer, uint32_t known_flags) {
+ return (static_cast<const Options*>(buffer)->flags & ~known_flags) == 0;
+}
+
+// Does basic cursory checks on |in_options| (|struct_size| and |flags|; |flags|
+// must immediately follow |struct_size|); |in_options| must be non-null. The
+// following should be done before calling this:
+// - Set |out_options| to the default options.
+// - If |in_options| is null, don't continue (success).
+// This function then:
+// - Checks if (according to |IsOptionsStructPointerAndSizeValid()|),
+// |struct_size| is valid; if not returns |MOJO_RESULT_INVALID_ARGUMENT|.
+// - If |in_options| has a |flags| field, checks that it only has
+// |known_flags| set; if so copies it to |out_options->flags|, and if not
+// returns |MOJO_RESULT_UNIMPLEMENTED|.
+// - At this point, returns |MOJO_RESULT_OK|.
+template <class Options>
+MojoResult ValidateOptionsStructPointerSizeAndFlags(
+ const Options* in_options,
+ uint32_t known_flags,
+ Options* out_options) {
+ COMPILE_ASSERT(offsetof(Options, flags) == sizeof(uint32_t),
+ Options_flags_doesnt_immediately_follow_struct_size);
+
+ if (!IsOptionsStructPointerAndSizeValid<Options>(in_options))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ if (HAS_OPTIONS_STRUCT_MEMBER(Options, flags, in_options)) {
+ if (!AreOptionsFlagsAllKnown<Options>(in_options, known_flags))
+ return MOJO_RESULT_UNIMPLEMENTED;
+ out_options->flags = in_options->flags;
+ }
+
+ return MOJO_RESULT_OK;
+}
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_OPTIONS_VALIDATION_H_
diff --git a/chromium/mojo/system/options_validation_unittest.cc b/chromium/mojo/system/options_validation_unittest.cc
new file mode 100644
index 00000000000..4a26fc675db
--- /dev/null
+++ b/chromium/mojo/system/options_validation_unittest.cc
@@ -0,0 +1,190 @@
+// Copyright 2014 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 "mojo/system/options_validation.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "mojo/public/c/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+// Declare a test options struct just as we do in actual public headers.
+
+typedef uint32_t TestOptionsFlags;
+
+MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int64_t) == 8, int64_t_has_weird_alignment);
+struct MOJO_ALIGNAS(8) TestOptions {
+ uint32_t struct_size;
+ TestOptionsFlags flags;
+ uint32_t member1;
+ uint32_t member2;
+};
+MOJO_COMPILE_ASSERT(sizeof(TestOptions) == 16, TestOptions_has_wrong_size);
+
+const uint32_t kSizeOfTestOptions = static_cast<uint32_t>(sizeof(TestOptions));
+
+TEST(OptionsValidationTest, Valid) {
+ const TestOptions kOptions1 = {
+ kSizeOfTestOptions
+ };
+
+ EXPECT_TRUE(IsOptionsStructPointerAndSizeValid<TestOptions>(&kOptions1));
+ EXPECT_TRUE(HAS_OPTIONS_STRUCT_MEMBER(TestOptions, flags, &kOptions1));
+ EXPECT_TRUE(HAS_OPTIONS_STRUCT_MEMBER(TestOptions, member1, &kOptions1));
+ EXPECT_TRUE(HAS_OPTIONS_STRUCT_MEMBER(TestOptions, member2, &kOptions1));
+
+ const TestOptions kOptions2 = {
+ static_cast<uint32_t>(offsetof(TestOptions, struct_size) + sizeof(uint32_t))
+ };
+ EXPECT_TRUE(IsOptionsStructPointerAndSizeValid<TestOptions>(&kOptions2));
+ EXPECT_FALSE(HAS_OPTIONS_STRUCT_MEMBER(TestOptions, flags, &kOptions2));
+ EXPECT_FALSE(HAS_OPTIONS_STRUCT_MEMBER(TestOptions, member1, &kOptions2));
+ EXPECT_FALSE(HAS_OPTIONS_STRUCT_MEMBER(TestOptions, member2, &kOptions2));
+
+ const TestOptions kOptions3 = {
+ static_cast<uint32_t>(offsetof(TestOptions, flags) + sizeof(uint32_t))
+ };
+ EXPECT_TRUE(IsOptionsStructPointerAndSizeValid<TestOptions>(&kOptions3));
+ EXPECT_TRUE(HAS_OPTIONS_STRUCT_MEMBER(TestOptions, flags, &kOptions3));
+ EXPECT_FALSE(HAS_OPTIONS_STRUCT_MEMBER(TestOptions, member1, &kOptions3));
+ EXPECT_FALSE(HAS_OPTIONS_STRUCT_MEMBER(TestOptions, member2, &kOptions3));
+
+ MOJO_ALIGNAS(8) char buf[sizeof(TestOptions) + 100] = {};
+ TestOptions* options = reinterpret_cast<TestOptions*>(buf);
+ options->struct_size = kSizeOfTestOptions + 1;
+ EXPECT_TRUE(IsOptionsStructPointerAndSizeValid<TestOptions>(options));
+ EXPECT_TRUE(HAS_OPTIONS_STRUCT_MEMBER(TestOptions, flags, options));
+ EXPECT_TRUE(HAS_OPTIONS_STRUCT_MEMBER(TestOptions, member1, options));
+ EXPECT_TRUE(HAS_OPTIONS_STRUCT_MEMBER(TestOptions, member2, options));
+
+ options->struct_size = kSizeOfTestOptions + 4;
+ EXPECT_TRUE(IsOptionsStructPointerAndSizeValid<TestOptions>(options));
+ EXPECT_TRUE(HAS_OPTIONS_STRUCT_MEMBER(TestOptions, flags, options));
+ EXPECT_TRUE(HAS_OPTIONS_STRUCT_MEMBER(TestOptions, member1, options));
+ EXPECT_TRUE(HAS_OPTIONS_STRUCT_MEMBER(TestOptions, member2, options));
+}
+
+TEST(OptionsValidationTest, Invalid) {
+ // Null:
+ EXPECT_FALSE(IsOptionsStructPointerAndSizeValid<TestOptions>(NULL));
+
+ // Unaligned:
+ EXPECT_FALSE(IsOptionsStructPointerAndSizeValid<TestOptions>(
+ reinterpret_cast<const void*>(1)));
+ EXPECT_FALSE(IsOptionsStructPointerAndSizeValid<TestOptions>(
+ reinterpret_cast<const void*>(4)));
+
+ // Size too small:
+ for (size_t i = 0; i < sizeof(uint32_t); i++) {
+ TestOptions options = {static_cast<uint32_t>(i)};
+ EXPECT_FALSE(IsOptionsStructPointerAndSizeValid<TestOptions>(&options))
+ << i;
+ }
+}
+
+TEST(OptionsValidationTest, CheckFlags) {
+ const TestOptions kOptions1 = {kSizeOfTestOptions, 0};
+ EXPECT_TRUE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions1, 0u));
+ EXPECT_TRUE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions1, 1u));
+ EXPECT_TRUE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions1, 3u));
+ EXPECT_TRUE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions1, 7u));
+ EXPECT_TRUE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions1, ~0u));
+
+ const TestOptions kOptions2 = {kSizeOfTestOptions, 1};
+ EXPECT_FALSE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions2, 0u));
+ EXPECT_TRUE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions2, 1u));
+ EXPECT_TRUE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions2, 3u));
+ EXPECT_TRUE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions2, 7u));
+ EXPECT_TRUE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions2, ~0u));
+
+ const TestOptions kOptions3 = {kSizeOfTestOptions, 2};
+ EXPECT_FALSE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions3, 0u));
+ EXPECT_FALSE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions3, 1u));
+ EXPECT_TRUE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions3, 3u));
+ EXPECT_TRUE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions3, 7u));
+ EXPECT_TRUE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions3, ~0u));
+
+ const TestOptions kOptions4 = {kSizeOfTestOptions, 5};
+ EXPECT_FALSE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions4, 0u));
+ EXPECT_FALSE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions4, 1u));
+ EXPECT_FALSE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions4, 3u));
+ EXPECT_TRUE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions4, 7u));
+ EXPECT_TRUE(AreOptionsFlagsAllKnown<TestOptions>(&kOptions4, ~0u));
+}
+
+TEST(OptionsValidationTest, ValidateOptionsStructPointerSizeAndFlags) {
+ const TestOptions kDefaultOptions = {kSizeOfTestOptions, 1u, 123u, 456u};
+
+ // Valid cases:
+
+ // "Normal":
+ {
+ const TestOptions kOptions = {kSizeOfTestOptions, 0u, 12u, 34u};
+ TestOptions validated_options = kDefaultOptions;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ ValidateOptionsStructPointerSizeAndFlags<TestOptions>(
+ &kOptions, 3u, &validated_options));
+ EXPECT_EQ(kDefaultOptions.struct_size, validated_options.struct_size);
+ // Copied |flags|.
+ EXPECT_EQ(kOptions.flags, validated_options.flags);
+ // Didn't touch subsequent members.
+ EXPECT_EQ(kDefaultOptions.member1, validated_options.member1);
+ EXPECT_EQ(kDefaultOptions.member2, validated_options.member2);
+ }
+
+ // Doesn't actually have |flags|:
+ {
+ const TestOptions kOptions = {
+ static_cast<uint32_t>(sizeof(uint32_t)), 0u, 12u, 34u
+ };
+ TestOptions validated_options = kDefaultOptions;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ ValidateOptionsStructPointerSizeAndFlags<TestOptions>(
+ &kOptions, 3u, &validated_options));
+ EXPECT_EQ(kDefaultOptions.struct_size, validated_options.struct_size);
+ // Didn't copy |flags|.
+ EXPECT_EQ(kDefaultOptions.flags, validated_options.flags);
+ // Didn't touch subsequent members.
+ EXPECT_EQ(kDefaultOptions.member1, validated_options.member1);
+ EXPECT_EQ(kDefaultOptions.member2, validated_options.member2);
+ }
+
+ // Invalid cases:
+
+ // Unaligned:
+ {
+ TestOptions validated_options = kDefaultOptions;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ ValidateOptionsStructPointerSizeAndFlags<TestOptions>(
+ reinterpret_cast<const TestOptions*>(1), 3u,
+ &validated_options));
+ }
+
+ // |struct_size| too small:
+ {
+ const TestOptions kOptions = {1u, 0u, 12u, 34u};
+ TestOptions validated_options = kDefaultOptions;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ ValidateOptionsStructPointerSizeAndFlags<TestOptions>(
+ &kOptions, 3u, &validated_options));
+ }
+
+ // Unknown |flag|:
+ {
+ const TestOptions kOptions = {kSizeOfTestOptions, 5u, 12u, 34u};
+ TestOptions validated_options = kDefaultOptions;
+ EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+ ValidateOptionsStructPointerSizeAndFlags<TestOptions>(
+ &kOptions, 3u, &validated_options));
+ }
+}
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/platform_handle_dispatcher.cc b/chromium/mojo/system/platform_handle_dispatcher.cc
new file mode 100644
index 00000000000..63a5e716d21
--- /dev/null
+++ b/chromium/mojo/system/platform_handle_dispatcher.cc
@@ -0,0 +1,123 @@
+// Copyright 2014 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 "mojo/system/platform_handle_dispatcher.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace system {
+
+namespace {
+
+const size_t kInvalidPlatformHandleIndex = static_cast<size_t>(-1);
+
+struct SerializedPlatformHandleDispatcher {
+ size_t platform_handle_index; // (Or |kInvalidPlatformHandleIndex|.)
+};
+
+} // namespace
+
+PlatformHandleDispatcher::PlatformHandleDispatcher(
+ embedder::ScopedPlatformHandle platform_handle)
+ : platform_handle_(platform_handle.Pass()) {
+}
+
+embedder::ScopedPlatformHandle PlatformHandleDispatcher::PassPlatformHandle() {
+ base::AutoLock locker(lock());
+ return platform_handle_.Pass();
+}
+
+Dispatcher::Type PlatformHandleDispatcher::GetType() const {
+ return kTypePlatformHandle;
+}
+
+// static
+scoped_refptr<PlatformHandleDispatcher> PlatformHandleDispatcher::Deserialize(
+ Channel* channel,
+ const void* source,
+ size_t size,
+ embedder::PlatformHandleVector* platform_handles) {
+ if (size != sizeof(SerializedPlatformHandleDispatcher)) {
+ LOG(ERROR) << "Invalid serialized platform handle dispatcher (bad size)";
+ return scoped_refptr<PlatformHandleDispatcher>();
+ }
+
+ const SerializedPlatformHandleDispatcher* serialization =
+ static_cast<const SerializedPlatformHandleDispatcher*>(source);
+ size_t platform_handle_index = serialization->platform_handle_index;
+
+ // Starts off invalid, which is what we want.
+ embedder::PlatformHandle platform_handle;
+
+ if (platform_handle_index != kInvalidPlatformHandleIndex) {
+ if (!platform_handles ||
+ platform_handle_index >= platform_handles->size()) {
+ LOG(ERROR)
+ << "Invalid serialized platform handle dispatcher (missing handles)";
+ return scoped_refptr<PlatformHandleDispatcher>();
+ }
+
+ // We take ownership of the handle, so we have to invalidate the one in
+ // |platform_handles|.
+ std::swap(platform_handle, (*platform_handles)[platform_handle_index]);
+ }
+
+ return scoped_refptr<PlatformHandleDispatcher>(new PlatformHandleDispatcher(
+ embedder::ScopedPlatformHandle(platform_handle)));
+}
+
+PlatformHandleDispatcher::~PlatformHandleDispatcher() {
+}
+
+void PlatformHandleDispatcher::CloseImplNoLock() {
+ lock().AssertAcquired();
+ platform_handle_.reset();
+}
+
+scoped_refptr<Dispatcher>
+ PlatformHandleDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() {
+ lock().AssertAcquired();
+ return scoped_refptr<Dispatcher>(
+ new PlatformHandleDispatcher(platform_handle_.Pass()));
+}
+
+void PlatformHandleDispatcher::StartSerializeImplNoLock(
+ Channel* /*channel*/,
+ size_t* max_size,
+ size_t* max_platform_handles) {
+ DCHECK(HasOneRef()); // Only one ref => no need to take the lock.
+ *max_size = sizeof(SerializedPlatformHandleDispatcher);
+ *max_platform_handles = 1;
+}
+
+bool PlatformHandleDispatcher::EndSerializeAndCloseImplNoLock(
+ Channel* /*channel*/,
+ void* destination,
+ size_t* actual_size,
+ embedder::PlatformHandleVector* platform_handles) {
+ DCHECK(HasOneRef()); // Only one ref => no need to take the lock.
+
+ SerializedPlatformHandleDispatcher* serialization =
+ static_cast<SerializedPlatformHandleDispatcher*>(destination);
+ if (platform_handle_.is_valid()) {
+ serialization->platform_handle_index = platform_handles->size();
+ platform_handles->push_back(platform_handle_.release());
+ } else {
+ serialization->platform_handle_index = kInvalidPlatformHandleIndex;
+ }
+
+ *actual_size = sizeof(SerializedPlatformHandleDispatcher);
+ return true;
+}
+
+HandleSignalsState
+ PlatformHandleDispatcher::GetHandleSignalsStateNoLock() const {
+ return HandleSignalsState();
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/platform_handle_dispatcher.h b/chromium/mojo/system/platform_handle_dispatcher.h
new file mode 100644
index 00000000000..f09684efdc0
--- /dev/null
+++ b/chromium/mojo/system/platform_handle_dispatcher.h
@@ -0,0 +1,64 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SYSTEM_PLATFORM_HANDLE_DISPATCHER_H_
+#define MOJO_SYSTEM_PLATFORM_HANDLE_DISPATCHER_H_
+
+#include "base/macros.h"
+#include "mojo/embedder/scoped_platform_handle.h"
+#include "mojo/system/simple_dispatcher.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+// A dispatcher that simply wraps/transports a |PlatformHandle| (only for use by
+// the embedder).
+class MOJO_SYSTEM_IMPL_EXPORT PlatformHandleDispatcher
+ : public SimpleDispatcher {
+ public:
+ explicit PlatformHandleDispatcher(
+ embedder::ScopedPlatformHandle platform_handle);
+
+ embedder::ScopedPlatformHandle PassPlatformHandle();
+
+ // |Dispatcher| public methods:
+ virtual Type GetType() const OVERRIDE;
+
+ // The "opposite" of |SerializeAndClose()|. (Typically this is called by
+ // |Dispatcher::Deserialize()|.)
+ static scoped_refptr<PlatformHandleDispatcher> Deserialize(
+ Channel* channel,
+ const void* source,
+ size_t size,
+ embedder::PlatformHandleVector* platform_handles);
+
+ private:
+ virtual ~PlatformHandleDispatcher();
+
+ // |Dispatcher| protected methods:
+ virtual void CloseImplNoLock() OVERRIDE;
+ virtual scoped_refptr<Dispatcher>
+ CreateEquivalentDispatcherAndCloseImplNoLock() OVERRIDE;
+ virtual void StartSerializeImplNoLock(Channel* channel,
+ size_t* max_size,
+ size_t* max_platform_handles) OVERRIDE;
+ virtual bool EndSerializeAndCloseImplNoLock(
+ Channel* channel,
+ void* destination,
+ size_t* actual_size,
+ embedder::PlatformHandleVector* platform_handles) OVERRIDE;
+
+ // |SimpleDispatcher| method:
+ virtual HandleSignalsState GetHandleSignalsStateNoLock() const OVERRIDE;
+
+ embedder::ScopedPlatformHandle platform_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformHandleDispatcher);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_PLATFORM_HANDLE_DISPATCHER_H_
diff --git a/chromium/mojo/system/platform_handle_dispatcher_unittest.cc b/chromium/mojo/system/platform_handle_dispatcher_unittest.cc
new file mode 100644
index 00000000000..5c22f55a114
--- /dev/null
+++ b/chromium/mojo/system/platform_handle_dispatcher_unittest.cc
@@ -0,0 +1,102 @@
+// Copyright 2014 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 "mojo/system/platform_handle_dispatcher.h"
+
+#include <stdio.h>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/common/test/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+TEST(PlatformHandleDispatcherTest, Basic) {
+ static const char kHelloWorld[] = "hello world";
+
+ base::FilePath unused;
+ base::ScopedFILE fp(CreateAndOpenTemporaryFile(&unused));
+ ASSERT_TRUE(fp);
+ EXPECT_EQ(sizeof(kHelloWorld),
+ fwrite(kHelloWorld, 1, sizeof(kHelloWorld), fp.get()));
+
+ embedder::ScopedPlatformHandle
+ h(mojo::test::PlatformHandleFromFILE(fp.Pass()));
+ EXPECT_FALSE(fp);
+ ASSERT_TRUE(h.is_valid());
+
+ scoped_refptr<PlatformHandleDispatcher> dispatcher(
+ new PlatformHandleDispatcher(h.Pass()));
+ EXPECT_FALSE(h.is_valid());
+ EXPECT_EQ(Dispatcher::kTypePlatformHandle, dispatcher->GetType());
+
+ h = dispatcher->PassPlatformHandle().Pass();
+ EXPECT_TRUE(h.is_valid());
+
+ fp = mojo::test::FILEFromPlatformHandle(h.Pass(), "rb").Pass();
+ EXPECT_FALSE(h.is_valid());
+ EXPECT_TRUE(fp);
+
+ rewind(fp.get());
+ char read_buffer[1000] = {};
+ EXPECT_EQ(sizeof(kHelloWorld),
+ fread(read_buffer, 1, sizeof(read_buffer), fp.get()));
+ EXPECT_STREQ(kHelloWorld, read_buffer);
+
+ // Try getting the handle again. (It should fail cleanly.)
+ h = dispatcher->PassPlatformHandle().Pass();
+ EXPECT_FALSE(h.is_valid());
+
+ EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
+}
+
+TEST(PlatformHandleDispatcherTest, CreateEquivalentDispatcherAndClose) {
+ static const char kFooBar[] = "foo bar";
+
+ base::FilePath unused;
+ base::ScopedFILE fp(CreateAndOpenTemporaryFile(&unused));
+ EXPECT_EQ(sizeof(kFooBar), fwrite(kFooBar, 1, sizeof(kFooBar), fp.get()));
+
+ scoped_refptr<PlatformHandleDispatcher> dispatcher(
+ new PlatformHandleDispatcher(
+ mojo::test::PlatformHandleFromFILE(fp.Pass())));
+
+ DispatcherTransport transport(
+ test::DispatcherTryStartTransport(dispatcher.get()));
+ EXPECT_TRUE(transport.is_valid());
+ EXPECT_EQ(Dispatcher::kTypePlatformHandle, transport.GetType());
+ EXPECT_FALSE(transport.IsBusy());
+
+ scoped_refptr<Dispatcher> generic_dispatcher =
+ transport.CreateEquivalentDispatcherAndClose();
+ ASSERT_TRUE(generic_dispatcher);
+
+ transport.End();
+ EXPECT_TRUE(dispatcher->HasOneRef());
+ dispatcher = NULL;
+
+ ASSERT_EQ(Dispatcher::kTypePlatformHandle, generic_dispatcher->GetType());
+ dispatcher = static_cast<PlatformHandleDispatcher*>(generic_dispatcher.get());
+
+ fp = mojo::test::FILEFromPlatformHandle(dispatcher->PassPlatformHandle(),
+ "rb").Pass();
+ EXPECT_TRUE(fp);
+
+ rewind(fp.get());
+ char read_buffer[1000] = {};
+ EXPECT_EQ(sizeof(kFooBar),
+ fread(read_buffer, 1, sizeof(read_buffer), fp.get()));
+ EXPECT_STREQ(kFooBar, read_buffer);
+
+ EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
+}
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/proxy_message_pipe_endpoint.cc b/chromium/mojo/system/proxy_message_pipe_endpoint.cc
new file mode 100644
index 00000000000..79074d0222c
--- /dev/null
+++ b/chromium/mojo/system/proxy_message_pipe_endpoint.cc
@@ -0,0 +1,148 @@
+// Copyright 2013 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 "mojo/system/proxy_message_pipe_endpoint.h"
+
+#include <string.h>
+
+#include "base/logging.h"
+#include "mojo/system/channel.h"
+#include "mojo/system/local_message_pipe_endpoint.h"
+#include "mojo/system/message_pipe_dispatcher.h"
+
+namespace mojo {
+namespace system {
+
+ProxyMessagePipeEndpoint::ProxyMessagePipeEndpoint()
+ : local_id_(MessageInTransit::kInvalidEndpointId),
+ remote_id_(MessageInTransit::kInvalidEndpointId),
+ is_peer_open_(true) {
+}
+
+ProxyMessagePipeEndpoint::ProxyMessagePipeEndpoint(
+ LocalMessagePipeEndpoint* local_message_pipe_endpoint,
+ bool is_peer_open)
+ : local_id_(MessageInTransit::kInvalidEndpointId),
+ remote_id_(MessageInTransit::kInvalidEndpointId),
+ is_peer_open_(is_peer_open),
+ paused_message_queue_(MessageInTransitQueue::PassContents(),
+ local_message_pipe_endpoint->message_queue()) {
+ local_message_pipe_endpoint->Close();
+}
+
+ProxyMessagePipeEndpoint::~ProxyMessagePipeEndpoint() {
+ DCHECK(!is_running());
+ DCHECK(!is_attached());
+ AssertConsistentState();
+ DCHECK(paused_message_queue_.IsEmpty());
+}
+
+MessagePipeEndpoint::Type ProxyMessagePipeEndpoint::GetType() const {
+ return kTypeProxy;
+}
+
+bool ProxyMessagePipeEndpoint::OnPeerClose() {
+ DCHECK(is_peer_open_);
+
+ is_peer_open_ = false;
+
+ // If our outgoing message queue isn't empty, we shouldn't be destroyed yet.
+ if (!paused_message_queue_.IsEmpty())
+ return true;
+
+ if (is_attached()) {
+ if (!is_running()) {
+ // If we're not running yet, we can't be destroyed yet, because we're
+ // still waiting for the "run" message from the other side.
+ return true;
+ }
+
+ Detach();
+ }
+
+ return false;
+}
+
+// Note: We may have to enqueue messages even when our (local) peer isn't open
+// -- it may have been written to and closed immediately, before we were ready.
+// This case is handled in |Run()| (which will call us).
+void ProxyMessagePipeEndpoint::EnqueueMessage(
+ scoped_ptr<MessageInTransit> message) {
+ if (is_running()) {
+ message->SerializeAndCloseDispatchers(channel_.get());
+
+ message->set_source_id(local_id_);
+ message->set_destination_id(remote_id_);
+ if (!channel_->WriteMessage(message.Pass()))
+ LOG(WARNING) << "Failed to write message to channel";
+ } else {
+ paused_message_queue_.AddMessage(message.Pass());
+ }
+}
+
+void ProxyMessagePipeEndpoint::Attach(scoped_refptr<Channel> channel,
+ MessageInTransit::EndpointId local_id) {
+ DCHECK(channel);
+ DCHECK_NE(local_id, MessageInTransit::kInvalidEndpointId);
+
+ DCHECK(!is_attached());
+
+ AssertConsistentState();
+ channel_ = channel;
+ local_id_ = local_id;
+ AssertConsistentState();
+}
+
+bool ProxyMessagePipeEndpoint::Run(MessageInTransit::EndpointId remote_id) {
+ // Assertions about arguments:
+ DCHECK_NE(remote_id, MessageInTransit::kInvalidEndpointId);
+
+ // Assertions about current state:
+ DCHECK(is_attached());
+ DCHECK(!is_running());
+
+ AssertConsistentState();
+ remote_id_ = remote_id;
+ AssertConsistentState();
+
+ while (!paused_message_queue_.IsEmpty())
+ EnqueueMessage(paused_message_queue_.GetMessage());
+
+ if (is_peer_open_)
+ return true; // Stay alive.
+
+ // We were just waiting to die.
+ Detach();
+ return false;
+}
+
+void ProxyMessagePipeEndpoint::OnRemove() {
+ Detach();
+}
+
+void ProxyMessagePipeEndpoint::Detach() {
+ DCHECK(is_attached());
+
+ AssertConsistentState();
+ channel_->DetachMessagePipeEndpoint(local_id_, remote_id_);
+ channel_ = NULL;
+ local_id_ = MessageInTransit::kInvalidEndpointId;
+ remote_id_ = MessageInTransit::kInvalidEndpointId;
+ paused_message_queue_.Clear();
+ AssertConsistentState();
+}
+
+#ifndef NDEBUG
+void ProxyMessagePipeEndpoint::AssertConsistentState() const {
+ if (is_attached()) {
+ DCHECK_NE(local_id_, MessageInTransit::kInvalidEndpointId);
+ } else { // Not attached.
+ DCHECK_EQ(local_id_, MessageInTransit::kInvalidEndpointId);
+ DCHECK_EQ(remote_id_, MessageInTransit::kInvalidEndpointId);
+ }
+}
+#endif
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/proxy_message_pipe_endpoint.h b/chromium/mojo/system/proxy_message_pipe_endpoint.h
new file mode 100644
index 00000000000..695967995b1
--- /dev/null
+++ b/chromium/mojo/system/proxy_message_pipe_endpoint.h
@@ -0,0 +1,104 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_PROXY_MESSAGE_PIPE_ENDPOINT_H_
+#define MOJO_SYSTEM_PROXY_MESSAGE_PIPE_ENDPOINT_H_
+
+#include <stdint.h>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/system/message_in_transit.h"
+#include "mojo/system/message_in_transit_queue.h"
+#include "mojo/system/message_pipe_endpoint.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class Channel;
+class LocalMessagePipeEndpoint;
+class MessagePipe;
+
+// A |ProxyMessagePipeEndpoint| connects an end of a |MessagePipe| to a
+// |Channel|, over which it transmits and receives data (to/from another
+// |ProxyMessagePipeEndpoint|). So a |MessagePipe| with one endpoint local and
+// the other endpoint remote consists of a |LocalMessagePipeEndpoint| and a
+// |ProxyMessagePipeEndpoint|, with only the local endpoint being accessible via
+// a |MessagePipeDispatcher|.
+//
+// Like any |MessagePipeEndpoint|, a |ProxyMessagePipeEndpoint| is owned by a
+// |MessagePipe|.
+// - A |ProxyMessagePipeEndpoint| starts out *detached*, i.e., not associated
+// to any |Channel|. When *attached*, it gets a reference to a |Channel| and
+// is assigned a local ID. A |ProxyMessagePipeEndpoint| must be detached
+// before destruction; this is done inside |Close()|.
+// - When attached, a |ProxyMessagePipeEndpoint| starts out not running. When
+// run, it gets a remote ID.
+class MOJO_SYSTEM_IMPL_EXPORT ProxyMessagePipeEndpoint
+ : public MessagePipeEndpoint {
+ public:
+ ProxyMessagePipeEndpoint();
+ // Constructs a |ProxyMessagePipeEndpoint| that replaces the given
+ // |LocalMessagePipeEndpoint| (which this constructor will close), taking its
+ // message queue's contents. This is done when transferring a message pipe
+ // handle over a remote message pipe.
+ ProxyMessagePipeEndpoint(
+ LocalMessagePipeEndpoint* local_message_pipe_endpoint,
+ bool is_peer_open);
+ virtual ~ProxyMessagePipeEndpoint();
+
+ // |MessagePipeEndpoint| implementation:
+ virtual Type GetType() const OVERRIDE;
+ virtual bool OnPeerClose() OVERRIDE;
+ virtual void EnqueueMessage(scoped_ptr<MessageInTransit> message) OVERRIDE;
+ virtual void Attach(scoped_refptr<Channel> channel,
+ MessageInTransit::EndpointId local_id) OVERRIDE;
+ virtual bool Run(MessageInTransit::EndpointId remote_id) OVERRIDE;
+ virtual void OnRemove() OVERRIDE;
+
+ private:
+ void Detach();
+
+#ifdef NDEBUG
+ void AssertConsistentState() const {}
+#else
+ void AssertConsistentState() const;
+#endif
+
+ bool is_attached() const {
+ return !!channel_;
+ }
+
+ bool is_running() const {
+ return remote_id_ != MessageInTransit::kInvalidEndpointId;
+ }
+
+ // This should only be set if we're attached.
+ scoped_refptr<Channel> channel_;
+
+ // |local_id_| should be set to something other than
+ // |MessageInTransit::kInvalidEndpointId| when we're attached.
+ MessageInTransit::EndpointId local_id_;
+
+ // |remote_id_| being set to anything other than
+ // |MessageInTransit::kInvalidEndpointId| indicates that we're "running",
+ // i.e., actively able to send messages. We should only ever be running if
+ // we're attached.
+ MessageInTransit::EndpointId remote_id_;
+
+ bool is_peer_open_;
+
+ // This queue is only used while we're detached, to store messages while we're
+ // not ready to send them yet.
+ MessageInTransitQueue paused_message_queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProxyMessagePipeEndpoint);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_PROXY_MESSAGE_PIPE_ENDPOINT_H_
diff --git a/chromium/mojo/system/raw_channel.cc b/chromium/mojo/system/raw_channel.cc
new file mode 100644
index 00000000000..f7b8029b49a
--- /dev/null
+++ b/chromium/mojo/system/raw_channel.cc
@@ -0,0 +1,500 @@
+// Copyright 2014 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 "mojo/system/raw_channel.h"
+
+#include <string.h>
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "mojo/system/message_in_transit.h"
+#include "mojo/system/transport_data.h"
+
+namespace mojo {
+namespace system {
+
+const size_t kReadSize = 4096;
+
+// RawChannel::ReadBuffer ------------------------------------------------------
+
+RawChannel::ReadBuffer::ReadBuffer()
+ : buffer_(kReadSize),
+ num_valid_bytes_(0) {
+}
+
+RawChannel::ReadBuffer::~ReadBuffer() {
+}
+
+void RawChannel::ReadBuffer::GetBuffer(char** addr, size_t* size) {
+ DCHECK_GE(buffer_.size(), num_valid_bytes_ + kReadSize);
+ *addr = &buffer_[0] + num_valid_bytes_;
+ *size = kReadSize;
+}
+
+// RawChannel::WriteBuffer -----------------------------------------------------
+
+RawChannel::WriteBuffer::WriteBuffer(size_t serialized_platform_handle_size)
+ : serialized_platform_handle_size_(serialized_platform_handle_size),
+ platform_handles_offset_(0),
+ data_offset_(0) {
+}
+
+RawChannel::WriteBuffer::~WriteBuffer() {
+ STLDeleteElements(&message_queue_);
+}
+
+bool RawChannel::WriteBuffer::HavePlatformHandlesToSend() const {
+ if (message_queue_.empty())
+ return false;
+
+ const TransportData* transport_data =
+ message_queue_.front()->transport_data();
+ if (!transport_data)
+ return false;
+
+ const embedder::PlatformHandleVector* all_platform_handles =
+ transport_data->platform_handles();
+ if (!all_platform_handles) {
+ DCHECK_EQ(platform_handles_offset_, 0u);
+ return false;
+ }
+ if (platform_handles_offset_ >= all_platform_handles->size()) {
+ DCHECK_EQ(platform_handles_offset_, all_platform_handles->size());
+ return false;
+ }
+
+ return true;
+}
+
+void RawChannel::WriteBuffer::GetPlatformHandlesToSend(
+ size_t* num_platform_handles,
+ embedder::PlatformHandle** platform_handles,
+ void** serialization_data) {
+ DCHECK(HavePlatformHandlesToSend());
+
+ TransportData* transport_data = message_queue_.front()->transport_data();
+ embedder::PlatformHandleVector* all_platform_handles =
+ transport_data->platform_handles();
+ *num_platform_handles =
+ all_platform_handles->size() - platform_handles_offset_;
+ *platform_handles = &(*all_platform_handles)[platform_handles_offset_];
+ size_t serialization_data_offset =
+ transport_data->platform_handle_table_offset();
+ DCHECK_GT(serialization_data_offset, 0u);
+ serialization_data_offset +=
+ platform_handles_offset_ * serialized_platform_handle_size_;
+ *serialization_data =
+ static_cast<char*>(transport_data->buffer()) + serialization_data_offset;
+}
+
+void RawChannel::WriteBuffer::GetBuffers(std::vector<Buffer>* buffers) const {
+ buffers->clear();
+
+ if (message_queue_.empty())
+ return;
+
+ MessageInTransit* message = message_queue_.front();
+ DCHECK_LT(data_offset_, message->total_size());
+ size_t bytes_to_write = message->total_size() - data_offset_;
+
+ size_t transport_data_buffer_size = message->transport_data() ?
+ message->transport_data()->buffer_size() : 0;
+
+ if (!transport_data_buffer_size) {
+ // Only write from the main buffer.
+ DCHECK_LT(data_offset_, message->main_buffer_size());
+ DCHECK_LE(bytes_to_write, message->main_buffer_size());
+ Buffer buffer = {
+ static_cast<const char*>(message->main_buffer()) + data_offset_,
+ bytes_to_write};
+ buffers->push_back(buffer);
+ return;
+ }
+
+ if (data_offset_ >= message->main_buffer_size()) {
+ // Only write from the transport data buffer.
+ DCHECK_LT(data_offset_ - message->main_buffer_size(),
+ transport_data_buffer_size);
+ DCHECK_LE(bytes_to_write, transport_data_buffer_size);
+ Buffer buffer = {
+ static_cast<const char*>(message->transport_data()->buffer()) +
+ (data_offset_ - message->main_buffer_size()),
+ bytes_to_write};
+ buffers->push_back(buffer);
+ return;
+ }
+
+ // TODO(vtl): We could actually send out buffers from multiple messages, with
+ // the "stopping" condition being reaching a message with platform handles
+ // attached.
+
+ // Write from both buffers.
+ DCHECK_EQ(bytes_to_write, message->main_buffer_size() - data_offset_ +
+ transport_data_buffer_size);
+ Buffer buffer1 = {
+ static_cast<const char*>(message->main_buffer()) + data_offset_,
+ message->main_buffer_size() - data_offset_
+ };
+ buffers->push_back(buffer1);
+ Buffer buffer2 = {
+ static_cast<const char*>(message->transport_data()->buffer()),
+ transport_data_buffer_size
+ };
+ buffers->push_back(buffer2);
+}
+
+// RawChannel ------------------------------------------------------------------
+
+RawChannel::RawChannel()
+ : message_loop_for_io_(NULL),
+ delegate_(NULL),
+ read_stopped_(false),
+ write_stopped_(false),
+ weak_ptr_factory_(this) {
+}
+
+RawChannel::~RawChannel() {
+ DCHECK(!read_buffer_);
+ DCHECK(!write_buffer_);
+
+ // No need to take the |write_lock_| here -- if there are still weak pointers
+ // outstanding, then we're hosed anyway (since we wouldn't be able to
+ // invalidate them cleanly, since we might not be on the I/O thread).
+ DCHECK(!weak_ptr_factory_.HasWeakPtrs());
+}
+
+bool RawChannel::Init(Delegate* delegate) {
+ DCHECK(delegate);
+
+ DCHECK(!delegate_);
+ delegate_ = delegate;
+
+ CHECK_EQ(base::MessageLoop::current()->type(), base::MessageLoop::TYPE_IO);
+ DCHECK(!message_loop_for_io_);
+ message_loop_for_io_ =
+ static_cast<base::MessageLoopForIO*>(base::MessageLoop::current());
+
+ // No need to take the lock. No one should be using us yet.
+ DCHECK(!read_buffer_);
+ read_buffer_.reset(new ReadBuffer);
+ DCHECK(!write_buffer_);
+ write_buffer_.reset(new WriteBuffer(GetSerializedPlatformHandleSize()));
+
+ if (!OnInit()) {
+ delegate_ = NULL;
+ message_loop_for_io_ = NULL;
+ read_buffer_.reset();
+ write_buffer_.reset();
+ return false;
+ }
+
+ if (ScheduleRead() != IO_PENDING) {
+ // This will notify the delegate about the read failure. Although we're on
+ // the I/O thread, don't call it in the nested context.
+ message_loop_for_io_->PostTask(
+ FROM_HERE,
+ base::Bind(&RawChannel::OnReadCompleted, weak_ptr_factory_.GetWeakPtr(),
+ false, 0));
+ }
+
+ // ScheduleRead() failure is treated as a read failure (by notifying the
+ // delegate), not as an init failure.
+ return true;
+}
+
+void RawChannel::Shutdown() {
+ DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io_);
+
+ base::AutoLock locker(write_lock_);
+
+ LOG_IF(WARNING, !write_buffer_->message_queue_.empty())
+ << "Shutting down RawChannel with write buffer nonempty";
+
+ // Reset the delegate so that it won't receive further calls.
+ delegate_ = NULL;
+ read_stopped_ = true;
+ write_stopped_ = true;
+ weak_ptr_factory_.InvalidateWeakPtrs();
+
+ OnShutdownNoLock(read_buffer_.Pass(), write_buffer_.Pass());
+}
+
+// Reminder: This must be thread-safe.
+bool RawChannel::WriteMessage(scoped_ptr<MessageInTransit> message) {
+ DCHECK(message);
+
+ base::AutoLock locker(write_lock_);
+ if (write_stopped_)
+ return false;
+
+ if (!write_buffer_->message_queue_.empty()) {
+ EnqueueMessageNoLock(message.Pass());
+ return true;
+ }
+
+ EnqueueMessageNoLock(message.Pass());
+ DCHECK_EQ(write_buffer_->data_offset_, 0u);
+
+ size_t platform_handles_written = 0;
+ size_t bytes_written = 0;
+ IOResult io_result = WriteNoLock(&platform_handles_written, &bytes_written);
+ if (io_result == IO_PENDING)
+ return true;
+
+ bool result = OnWriteCompletedNoLock(io_result == IO_SUCCEEDED,
+ platform_handles_written,
+ bytes_written);
+ if (!result) {
+ // Even if we're on the I/O thread, don't call |OnFatalError()| in the
+ // nested context.
+ message_loop_for_io_->PostTask(
+ FROM_HERE,
+ base::Bind(&RawChannel::CallOnFatalError,
+ weak_ptr_factory_.GetWeakPtr(),
+ Delegate::FATAL_ERROR_WRITE));
+ }
+
+ return result;
+}
+
+// Reminder: This must be thread-safe.
+bool RawChannel::IsWriteBufferEmpty() {
+ base::AutoLock locker(write_lock_);
+ return write_buffer_->message_queue_.empty();
+}
+
+void RawChannel::OnReadCompleted(bool result, size_t bytes_read) {
+ DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io_);
+
+ if (read_stopped_) {
+ NOTREACHED();
+ return;
+ }
+
+ IOResult io_result = result ? IO_SUCCEEDED : IO_FAILED;
+
+ // Keep reading data in a loop, and dispatch messages if enough data is
+ // received. Exit the loop if any of the following happens:
+ // - one or more messages were dispatched;
+ // - the last read failed, was a partial read or would block;
+ // - |Shutdown()| was called.
+ do {
+ if (io_result != IO_SUCCEEDED) {
+ read_stopped_ = true;
+ CallOnFatalError(Delegate::FATAL_ERROR_READ);
+ return;
+ }
+
+ read_buffer_->num_valid_bytes_ += bytes_read;
+
+ // Dispatch all the messages that we can.
+ bool did_dispatch_message = false;
+ // Tracks the offset of the first undispatched message in |read_buffer_|.
+ // Currently, we copy data to ensure that this is zero at the beginning.
+ size_t read_buffer_start = 0;
+ size_t remaining_bytes = read_buffer_->num_valid_bytes_;
+ size_t message_size;
+ // Note that we rely on short-circuit evaluation here:
+ // - |read_buffer_start| may be an invalid index into
+ // |read_buffer_->buffer_| if |remaining_bytes| is zero.
+ // - |message_size| is only valid if |GetNextMessageSize()| returns true.
+ // TODO(vtl): Use |message_size| more intelligently (e.g., to request the
+ // next read).
+ // TODO(vtl): Validate that |message_size| is sane.
+ while (remaining_bytes > 0 &&
+ MessageInTransit::GetNextMessageSize(
+ &read_buffer_->buffer_[read_buffer_start], remaining_bytes,
+ &message_size) &&
+ remaining_bytes >= message_size) {
+ MessageInTransit::View
+ message_view(message_size, &read_buffer_->buffer_[read_buffer_start]);
+ DCHECK_EQ(message_view.total_size(), message_size);
+
+ const char* error_message = NULL;
+ if (!message_view.IsValid(GetSerializedPlatformHandleSize(),
+ &error_message)) {
+ DCHECK(error_message);
+ LOG(WARNING) << "Received invalid message: " << error_message;
+ read_stopped_ = true;
+ CallOnFatalError(Delegate::FATAL_ERROR_READ);
+ return;
+ }
+
+ if (message_view.type() == MessageInTransit::kTypeRawChannel) {
+ if (!OnReadMessageForRawChannel(message_view)) {
+ read_stopped_ = true;
+ CallOnFatalError(Delegate::FATAL_ERROR_READ);
+ return;
+ }
+ } else {
+ embedder::ScopedPlatformHandleVectorPtr platform_handles;
+ if (message_view.transport_data_buffer()) {
+ size_t num_platform_handles;
+ const void* platform_handle_table;
+ TransportData::GetPlatformHandleTable(
+ message_view.transport_data_buffer(),
+ &num_platform_handles,
+ &platform_handle_table);
+
+ if (num_platform_handles > 0) {
+ platform_handles =
+ GetReadPlatformHandles(num_platform_handles,
+ platform_handle_table).Pass();
+ if (!platform_handles) {
+ LOG(WARNING) << "Invalid number of platform handles received";
+ read_stopped_ = true;
+ CallOnFatalError(Delegate::FATAL_ERROR_READ);
+ return;
+ }
+ }
+ }
+
+ // TODO(vtl): In the case that we aren't expecting any platform handles,
+ // for the POSIX implementation, we should confirm that none are stored.
+
+ // Dispatch the message.
+ DCHECK(delegate_);
+ delegate_->OnReadMessage(message_view, platform_handles.Pass());
+ if (read_stopped_) {
+ // |Shutdown()| was called in |OnReadMessage()|.
+ // TODO(vtl): Add test for this case.
+ return;
+ }
+ }
+
+ did_dispatch_message = true;
+
+ // Update our state.
+ read_buffer_start += message_size;
+ remaining_bytes -= message_size;
+ }
+
+ if (read_buffer_start > 0) {
+ // Move data back to start.
+ read_buffer_->num_valid_bytes_ = remaining_bytes;
+ if (read_buffer_->num_valid_bytes_ > 0) {
+ memmove(&read_buffer_->buffer_[0],
+ &read_buffer_->buffer_[read_buffer_start], remaining_bytes);
+ }
+ read_buffer_start = 0;
+ }
+
+ if (read_buffer_->buffer_.size() - read_buffer_->num_valid_bytes_ <
+ kReadSize) {
+ // Use power-of-2 buffer sizes.
+ // TODO(vtl): Make sure the buffer doesn't get too large (and enforce the
+ // maximum message size to whatever extent necessary).
+ // TODO(vtl): We may often be able to peek at the header and get the real
+ // required extra space (which may be much bigger than |kReadSize|).
+ size_t new_size = std::max(read_buffer_->buffer_.size(), kReadSize);
+ while (new_size < read_buffer_->num_valid_bytes_ + kReadSize)
+ new_size *= 2;
+
+ // TODO(vtl): It's suboptimal to zero out the fresh memory.
+ read_buffer_->buffer_.resize(new_size, 0);
+ }
+
+ // (1) If we dispatched any messages, stop reading for now (and let the
+ // message loop do its thing for another round).
+ // TODO(vtl): Is this the behavior we want? (Alternatives: i. Dispatch only
+ // a single message. Risks: slower, more complex if we want to avoid lots of
+ // copying. ii. Keep reading until there's no more data and dispatch all the
+ // messages we can. Risks: starvation of other users of the message loop.)
+ // (2) If we didn't max out |kReadSize|, stop reading for now.
+ bool schedule_for_later = did_dispatch_message || bytes_read < kReadSize;
+ bytes_read = 0;
+ io_result = schedule_for_later ? ScheduleRead() : Read(&bytes_read);
+ } while (io_result != IO_PENDING);
+}
+
+void RawChannel::OnWriteCompleted(bool result,
+ size_t platform_handles_written,
+ size_t bytes_written) {
+ DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io_);
+
+ bool did_fail = false;
+ {
+ base::AutoLock locker(write_lock_);
+ DCHECK_EQ(write_stopped_, write_buffer_->message_queue_.empty());
+
+ if (write_stopped_) {
+ NOTREACHED();
+ return;
+ }
+
+ did_fail = !OnWriteCompletedNoLock(result,
+ platform_handles_written,
+ bytes_written);
+ }
+
+ if (did_fail)
+ CallOnFatalError(Delegate::FATAL_ERROR_WRITE);
+}
+
+void RawChannel::EnqueueMessageNoLock(scoped_ptr<MessageInTransit> message) {
+ write_lock_.AssertAcquired();
+ write_buffer_->message_queue_.push_back(message.release());
+}
+
+bool RawChannel::OnReadMessageForRawChannel(
+ const MessageInTransit::View& message_view) {
+ // No non-implementation specific |RawChannel| control messages.
+ LOG(ERROR) << "Invalid control message (subtype " << message_view.subtype()
+ << ")";
+ return false;
+}
+
+void RawChannel::CallOnFatalError(Delegate::FatalError fatal_error) {
+ DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io_);
+ // TODO(vtl): Add a "write_lock_.AssertNotAcquired()"?
+ if (delegate_)
+ delegate_->OnFatalError(fatal_error);
+}
+
+bool RawChannel::OnWriteCompletedNoLock(bool result,
+ size_t platform_handles_written,
+ size_t bytes_written) {
+ write_lock_.AssertAcquired();
+
+ DCHECK(!write_stopped_);
+ DCHECK(!write_buffer_->message_queue_.empty());
+
+ if (result) {
+ write_buffer_->platform_handles_offset_ += platform_handles_written;
+ write_buffer_->data_offset_ += bytes_written;
+
+ MessageInTransit* message = write_buffer_->message_queue_.front();
+ if (write_buffer_->data_offset_ >= message->total_size()) {
+ // Complete write.
+ DCHECK_EQ(write_buffer_->data_offset_, message->total_size());
+ write_buffer_->message_queue_.pop_front();
+ delete message;
+ write_buffer_->platform_handles_offset_ = 0;
+ write_buffer_->data_offset_ = 0;
+
+ if (write_buffer_->message_queue_.empty())
+ return true;
+ }
+
+ // Schedule the next write.
+ IOResult io_result = ScheduleWriteNoLock();
+ if (io_result == IO_PENDING)
+ return true;
+ DCHECK_EQ(io_result, IO_FAILED);
+ }
+
+ write_stopped_ = true;
+ STLDeleteElements(&write_buffer_->message_queue_);
+ write_buffer_->platform_handles_offset_ = 0;
+ write_buffer_->data_offset_ = 0;
+ return false;
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/raw_channel.h b/chromium/mojo/system/raw_channel.h
new file mode 100644
index 00000000000..965e10903aa
--- /dev/null
+++ b/chromium/mojo/system/raw_channel.h
@@ -0,0 +1,315 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_RAW_CHANNEL_H_
+#define MOJO_SYSTEM_RAW_CHANNEL_H_
+
+#include <deque>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/synchronization/lock.h"
+#include "mojo/embedder/platform_handle_vector.h"
+#include "mojo/embedder/scoped_platform_handle.h"
+#include "mojo/system/constants.h"
+#include "mojo/system/message_in_transit.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace base {
+class MessageLoopForIO;
+}
+
+namespace mojo {
+namespace system {
+
+// |RawChannel| is an interface and base class for objects that wrap an OS
+// "pipe". It presents the following interface to users:
+// - Receives and dispatches messages on an I/O thread (running a
+// |MessageLoopForIO|.
+// - Provides a thread-safe way of writing messages (|WriteMessage()|);
+// writing/queueing messages will not block and is atomic from the point of
+// view of the caller. If necessary, messages are queued (to be written on
+// the aforementioned thread).
+//
+// OS-specific implementation subclasses are to be instantiated using the
+// |Create()| static factory method.
+//
+// With the exception of |WriteMessage()|, this class is thread-unsafe (and in
+// general its methods should only be used on the I/O thread, i.e., the thread
+// on which |Init()| is called).
+class MOJO_SYSTEM_IMPL_EXPORT RawChannel {
+ public:
+ virtual ~RawChannel();
+
+ // The |Delegate| is only accessed on the same thread as the message loop
+ // (passed in on creation).
+ class MOJO_SYSTEM_IMPL_EXPORT Delegate {
+ public:
+ enum FatalError {
+ FATAL_ERROR_READ = 0,
+ FATAL_ERROR_WRITE
+ };
+
+ // Called when a message is read. This may call |Shutdown()| (on the
+ // |RawChannel|), but must not destroy it.
+ virtual void OnReadMessage(
+ const MessageInTransit::View& message_view,
+ embedder::ScopedPlatformHandleVectorPtr platform_handles) = 0;
+
+ // Called when there's a fatal error, which leads to the channel no longer
+ // being viable. This may call |Shutdown()| (on the |RawChannel()|), but
+ // must not destroy it.
+ //
+ // For each raw channel, at most one |FATAL_ERROR_READ| and at most one
+ // |FATAL_ERROR_WRITE| notification will be issued (both may be issued).
+ // After a |OnFatalError(FATAL_ERROR_READ)|, there will be no further calls
+ // to |OnReadMessage()|.
+ virtual void OnFatalError(FatalError fatal_error) = 0;
+
+ protected:
+ virtual ~Delegate() {}
+ };
+
+ // Static factory method. |handle| should be a handle to a
+ // (platform-appropriate) bidirectional communication channel (e.g., a socket
+ // on POSIX, a named pipe on Windows).
+ static scoped_ptr<RawChannel> Create(embedder::ScopedPlatformHandle handle);
+
+ // This must be called (on an I/O thread) before this object is used. Does
+ // *not* take ownership of |delegate|. Both the I/O thread and |delegate| must
+ // remain alive until |Shutdown()| is called (unless this fails); |delegate|
+ // will no longer be used after |Shutdown()|. Returns true on success. On
+ // failure, |Shutdown()| should *not* be called.
+ bool Init(Delegate* delegate);
+
+ // This must be called (on the I/O thread) before this object is destroyed.
+ void Shutdown();
+
+ // Writes the given message (or schedules it to be written). |message| must
+ // have no |Dispatcher|s still attached (i.e.,
+ // |SerializeAndCloseDispatchers()| should have been called). This method is
+ // thread-safe and may be called from any thread. Returns true on success.
+ bool WriteMessage(scoped_ptr<MessageInTransit> message);
+
+ // Returns true if the write buffer is empty (i.e., all messages written using
+ // |WriteMessage()| have actually been sent.
+ // TODO(vtl): We should really also notify our delegate when the write buffer
+ // becomes empty (or something like that).
+ bool IsWriteBufferEmpty();
+
+ // Returns the amount of space needed in the |MessageInTransit|'s
+ // |TransportData|'s "platform handle table" per platform handle (to be
+ // attached to a message). (This amount may be zero.)
+ virtual size_t GetSerializedPlatformHandleSize() const = 0;
+
+ protected:
+ // Return values of |[Schedule]Read()| and |[Schedule]WriteNoLock()|.
+ enum IOResult {
+ IO_SUCCEEDED,
+ IO_FAILED,
+ IO_PENDING
+ };
+
+ class MOJO_SYSTEM_IMPL_EXPORT ReadBuffer {
+ public:
+ ReadBuffer();
+ ~ReadBuffer();
+
+ void GetBuffer(char** addr, size_t* size);
+
+ private:
+ friend class RawChannel;
+
+ // We store data from |[Schedule]Read()|s in |buffer_|. The start of
+ // |buffer_| is always aligned with a message boundary (we will copy memory
+ // to ensure this), but |buffer_| may be larger than the actual number of
+ // bytes we have.
+ std::vector<char> buffer_;
+ size_t num_valid_bytes_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadBuffer);
+ };
+
+ class MOJO_SYSTEM_IMPL_EXPORT WriteBuffer {
+ public:
+ struct Buffer {
+ const char* addr;
+ size_t size;
+ };
+
+ explicit WriteBuffer(size_t serialized_platform_handle_size);
+ ~WriteBuffer();
+
+ // Returns true if there are (more) platform handles to be sent (from the
+ // front of |message_queue_|).
+ bool HavePlatformHandlesToSend() const;
+ // Gets platform handles to be sent (from the front of |message_queue_|).
+ // This should only be called if |HavePlatformHandlesToSend()| returned
+ // true. There are two components to this: the actual |PlatformHandle|s
+ // (which should be closed once sent) and any additional serialization
+ // information (which will be embedded in the message's data; there are
+ // |GetSerializedPlatformHandleSize()| bytes per handle). Once all platform
+ // handles have been sent, the message data should be written next (see
+ // |GetBuffers()|).
+ void GetPlatformHandlesToSend(size_t* num_platform_handles,
+ embedder::PlatformHandle** platform_handles,
+ void** serialization_data);
+
+ // Gets buffers to be written. These buffers will always come from the front
+ // of |message_queue_|. Once they are completely written, the front
+ // |MessageInTransit| should be popped (and destroyed); this is done in
+ // |OnWriteCompletedNoLock()|.
+ void GetBuffers(std::vector<Buffer>* buffers) const;
+
+ private:
+ friend class RawChannel;
+
+ const size_t serialized_platform_handle_size_;
+
+ // TODO(vtl): When C++11 is available, switch this to a deque of
+ // |scoped_ptr|/|unique_ptr|s.
+ std::deque<MessageInTransit*> message_queue_;
+ // Platform handles are sent before the message data, but doing so may
+ // require several passes. |platform_handles_offset_| indicates the position
+ // in the first message's vector of platform handles to send next.
+ size_t platform_handles_offset_;
+ // The first message's data may have been partially sent. |data_offset_|
+ // indicates the position in the first message's data to start the next
+ // write.
+ size_t data_offset_;
+
+ DISALLOW_COPY_AND_ASSIGN(WriteBuffer);
+ };
+
+ RawChannel();
+
+ // Must be called on the I/O thread WITHOUT |write_lock_| held.
+ void OnReadCompleted(bool result, size_t bytes_read);
+ // Must be called on the I/O thread WITHOUT |write_lock_| held.
+ void OnWriteCompleted(bool result,
+ size_t platform_handles_written,
+ size_t bytes_written);
+
+ base::MessageLoopForIO* message_loop_for_io() { return message_loop_for_io_; }
+ base::Lock& write_lock() { return write_lock_; }
+
+ // Should only be called on the I/O thread.
+ ReadBuffer* read_buffer() { return read_buffer_.get(); }
+
+ // Only called under |write_lock_|.
+ WriteBuffer* write_buffer_no_lock() {
+ write_lock_.AssertAcquired();
+ return write_buffer_.get();
+ }
+
+ // Adds |message| to the write message queue. Implementation subclasses may
+ // override this to add any additional "control" messages needed. This is
+ // called (on any thread) with |write_lock_| held.
+ virtual void EnqueueMessageNoLock(scoped_ptr<MessageInTransit> message);
+
+ // Handles any control messages targeted to the |RawChannel| (or
+ // implementation subclass). Implementation subclasses may override this to
+ // handle any implementation-specific control messages, but should call
+ // |RawChannel::OnReadMessageForRawChannel()| for any remaining messages.
+ // Returns true on success and false on error (e.g., invalid control message).
+ // This is only called on the I/O thread.
+ virtual bool OnReadMessageForRawChannel(
+ const MessageInTransit::View& message_view);
+
+ // Reads into |read_buffer()|.
+ // This class guarantees that:
+ // - the area indicated by |GetBuffer()| will stay valid until read completion
+ // (but please also see the comments for |OnShutdownNoLock()|);
+ // - a second read is not started if there is a pending read;
+ // - the method is called on the I/O thread WITHOUT |write_lock_| held.
+ //
+ // The implementing subclass must guarantee that:
+ // - |bytes_read| is untouched unless |Read()| returns |IO_SUCCEEDED|;
+ // - if the method returns |IO_PENDING|, |OnReadCompleted()| will be called on
+ // the I/O thread to report the result, unless |Shutdown()| is called.
+ virtual IOResult Read(size_t* bytes_read) = 0;
+ // Similar to |Read()|, except that the implementing subclass must also
+ // guarantee that the method doesn't succeed synchronously, i.e., it only
+ // returns |IO_FAILED| or |IO_PENDING|.
+ virtual IOResult ScheduleRead() = 0;
+
+ // Called by |OnReadCompleted()| to get the platform handles associated with
+ // the given platform handle table (from a message). This should only be
+ // called when |num_platform_handles| is nonzero. Returns null if the
+ // |num_platform_handles| handles are not available. Only called on the I/O
+ // thread (without |write_lock_| held).
+ virtual embedder::ScopedPlatformHandleVectorPtr GetReadPlatformHandles(
+ size_t num_platform_handles,
+ const void* platform_handle_table) = 0;
+
+ // Writes contents in |write_buffer_no_lock()|.
+ // This class guarantees that:
+ // - the |PlatformHandle|s given by |GetPlatformHandlesToSend()| and the
+ // buffer(s) given by |GetBuffers()| will remain valid until write
+ // completion (see also the comments for |OnShutdownNoLock()|);
+ // - a second write is not started if there is a pending write;
+ // - the method is called under |write_lock_|.
+ //
+ // The implementing subclass must guarantee that:
+ // - |platform_handles_written| and |bytes_written| are untouched unless
+ // |WriteNoLock()| returns |IO_SUCCEEDED|;
+ // - if the method returns |IO_PENDING|, |OnWriteCompleted()| will be called
+ // on the I/O thread to report the result, unless |Shutdown()| is called.
+ virtual IOResult WriteNoLock(size_t* platform_handles_written,
+ size_t* bytes_written) = 0;
+ // Similar to |WriteNoLock()|, except that the implementing subclass must also
+ // guarantee that the method doesn't succeed synchronously, i.e., it only
+ // returns |IO_FAILED| or |IO_PENDING|.
+ virtual IOResult ScheduleWriteNoLock() = 0;
+
+ // Must be called on the I/O thread WITHOUT |write_lock_| held.
+ virtual bool OnInit() = 0;
+ // On shutdown, passes the ownership of the buffers to subclasses, which may
+ // want to preserve them if there are pending read/write. Must be called on
+ // the I/O thread under |write_lock_|.
+ virtual void OnShutdownNoLock(
+ scoped_ptr<ReadBuffer> read_buffer,
+ scoped_ptr<WriteBuffer> write_buffer) = 0;
+
+ private:
+ // Calls |delegate_->OnFatalError(fatal_error)|. Must be called on the I/O
+ // thread WITHOUT |write_lock_| held.
+ void CallOnFatalError(Delegate::FatalError fatal_error);
+
+ // If |result| is true, updates the write buffer and schedules a write
+ // operation to run later if there are more contents to write. If |result| is
+ // false or any error occurs during the method execution, cancels pending
+ // writes and returns false.
+ // Must be called only if |write_stopped_| is false and under |write_lock_|.
+ bool OnWriteCompletedNoLock(bool result,
+ size_t platform_handles_written,
+ size_t bytes_written);
+
+ // Set in |Init()| and never changed (hence usable on any thread without
+ // locking):
+ base::MessageLoopForIO* message_loop_for_io_;
+
+ // Only used on the I/O thread:
+ Delegate* delegate_;
+ bool read_stopped_;
+ scoped_ptr<ReadBuffer> read_buffer_;
+
+ base::Lock write_lock_; // Protects the following members.
+ bool write_stopped_;
+ scoped_ptr<WriteBuffer> write_buffer_;
+
+ // This is used for posting tasks from write threads to the I/O thread. It
+ // must only be accessed under |write_lock_|. The weak pointers it produces
+ // are only used/invalidated on the I/O thread.
+ base::WeakPtrFactory<RawChannel> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(RawChannel);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_RAW_CHANNEL_H_
diff --git a/chromium/mojo/system/raw_channel_posix.cc b/chromium/mojo/system/raw_channel_posix.cc
new file mode 100644
index 00000000000..ba23599f8f7
--- /dev/null
+++ b/chromium/mojo/system/raw_channel_posix.cc
@@ -0,0 +1,469 @@
+// Copyright 2013 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 "mojo/system/raw_channel.h"
+
+#include <errno.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <deque>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/synchronization/lock.h"
+#include "mojo/embedder/platform_channel_utils_posix.h"
+#include "mojo/embedder/platform_handle.h"
+#include "mojo/embedder/platform_handle_vector.h"
+#include "mojo/system/transport_data.h"
+
+namespace mojo {
+namespace system {
+
+namespace {
+
+class RawChannelPosix : public RawChannel,
+ public base::MessageLoopForIO::Watcher {
+ public:
+ explicit RawChannelPosix(embedder::ScopedPlatformHandle handle);
+ virtual ~RawChannelPosix();
+
+ // |RawChannel| public methods:
+ virtual size_t GetSerializedPlatformHandleSize() const OVERRIDE;
+
+ private:
+ // |RawChannel| protected methods:
+ // Actually override this so that we can send multiple messages with (only)
+ // FDs if necessary.
+ virtual void EnqueueMessageNoLock(
+ scoped_ptr<MessageInTransit> message) OVERRIDE;
+ // Override this to handle those extra FD-only messages.
+ virtual bool OnReadMessageForRawChannel(
+ const MessageInTransit::View& message_view) OVERRIDE;
+ virtual IOResult Read(size_t* bytes_read) OVERRIDE;
+ virtual IOResult ScheduleRead() OVERRIDE;
+ virtual embedder::ScopedPlatformHandleVectorPtr GetReadPlatformHandles(
+ size_t num_platform_handles,
+ const void* platform_handle_table) OVERRIDE;
+ virtual IOResult WriteNoLock(size_t* platform_handles_written,
+ size_t* bytes_written) OVERRIDE;
+ virtual IOResult ScheduleWriteNoLock() OVERRIDE;
+ virtual bool OnInit() OVERRIDE;
+ virtual void OnShutdownNoLock(
+ scoped_ptr<ReadBuffer> read_buffer,
+ scoped_ptr<WriteBuffer> write_buffer) OVERRIDE;
+
+ // |base::MessageLoopForIO::Watcher| implementation:
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
+ virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
+
+ // Watches for |fd_| to become writable. Must be called on the I/O thread.
+ void WaitToWrite();
+
+ embedder::ScopedPlatformHandle fd_;
+
+ // The following members are only used on the I/O thread:
+ scoped_ptr<base::MessageLoopForIO::FileDescriptorWatcher> read_watcher_;
+ scoped_ptr<base::MessageLoopForIO::FileDescriptorWatcher> write_watcher_;
+
+ bool pending_read_;
+
+ std::deque<embedder::PlatformHandle> read_platform_handles_;
+
+ // The following members are used on multiple threads and protected by
+ // |write_lock()|:
+ bool pending_write_;
+
+ // This is used for posting tasks from write threads to the I/O thread. It
+ // must only be accessed under |write_lock_|. The weak pointers it produces
+ // are only used/invalidated on the I/O thread.
+ base::WeakPtrFactory<RawChannelPosix> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(RawChannelPosix);
+};
+
+RawChannelPosix::RawChannelPosix(embedder::ScopedPlatformHandle handle)
+ : fd_(handle.Pass()),
+ pending_read_(false),
+ pending_write_(false),
+ weak_ptr_factory_(this) {
+ DCHECK(fd_.is_valid());
+}
+
+RawChannelPosix::~RawChannelPosix() {
+ DCHECK(!pending_read_);
+ DCHECK(!pending_write_);
+
+ // No need to take the |write_lock()| here -- if there are still weak pointers
+ // outstanding, then we're hosed anyway (since we wouldn't be able to
+ // invalidate them cleanly, since we might not be on the I/O thread).
+ DCHECK(!weak_ptr_factory_.HasWeakPtrs());
+
+ // These must have been shut down/destroyed on the I/O thread.
+ DCHECK(!read_watcher_);
+ DCHECK(!write_watcher_);
+
+ embedder::CloseAllPlatformHandles(&read_platform_handles_);
+}
+
+size_t RawChannelPosix::GetSerializedPlatformHandleSize() const {
+ // We don't actually need any space on POSIX (since we just send FDs).
+ return 0;
+}
+
+void RawChannelPosix::EnqueueMessageNoLock(
+ scoped_ptr<MessageInTransit> message) {
+ if (message->transport_data()) {
+ embedder::PlatformHandleVector* const platform_handles =
+ message->transport_data()->platform_handles();
+ if (platform_handles &&
+ platform_handles->size() > embedder::kPlatformChannelMaxNumHandles) {
+ // We can't attach all the FDs to a single message, so we have to "split"
+ // the message. Send as many control messages as needed first with FDs
+ // attached (and no data).
+ size_t i = 0;
+ for (; platform_handles->size() - i >
+ embedder::kPlatformChannelMaxNumHandles;
+ i += embedder::kPlatformChannelMaxNumHandles) {
+ scoped_ptr<MessageInTransit> fd_message(
+ new MessageInTransit(
+ MessageInTransit::kTypeRawChannel,
+ MessageInTransit::kSubtypeRawChannelPosixExtraPlatformHandles,
+ 0,
+ NULL));
+ embedder::ScopedPlatformHandleVectorPtr fds(
+ new embedder::PlatformHandleVector(
+ platform_handles->begin() + i,
+ platform_handles->begin() + i +
+ embedder::kPlatformChannelMaxNumHandles));
+ fd_message->SetTransportData(
+ make_scoped_ptr(new TransportData(fds.Pass())));
+ RawChannel::EnqueueMessageNoLock(fd_message.Pass());
+ }
+
+ // Remove the handles that we "moved" into the other messages.
+ platform_handles->erase(platform_handles->begin(),
+ platform_handles->begin() + i);
+ }
+ }
+
+ RawChannel::EnqueueMessageNoLock(message.Pass());
+}
+
+bool RawChannelPosix::OnReadMessageForRawChannel(
+ const MessageInTransit::View& message_view) {
+ DCHECK_EQ(message_view.type(), MessageInTransit::kTypeRawChannel);
+
+ if (message_view.subtype() ==
+ MessageInTransit::kSubtypeRawChannelPosixExtraPlatformHandles) {
+ // We don't need to do anything. |RawChannel| won't extract the platform
+ // handles, and they'll be accumulated in |Read()|.
+ return true;
+ }
+
+ return RawChannel::OnReadMessageForRawChannel(message_view);
+}
+
+RawChannel::IOResult RawChannelPosix::Read(size_t* bytes_read) {
+ DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+ DCHECK(!pending_read_);
+
+ char* buffer = NULL;
+ size_t bytes_to_read = 0;
+ read_buffer()->GetBuffer(&buffer, &bytes_to_read);
+
+ size_t old_num_platform_handles = read_platform_handles_.size();
+ ssize_t read_result =
+ embedder::PlatformChannelRecvmsg(fd_.get(),
+ buffer,
+ bytes_to_read,
+ &read_platform_handles_);
+ if (read_platform_handles_.size() > old_num_platform_handles) {
+ DCHECK_LE(read_platform_handles_.size() - old_num_platform_handles,
+ embedder::kPlatformChannelMaxNumHandles);
+
+ // We should never accumulate more than |TransportData::kMaxPlatformHandles
+ // + embedder::kPlatformChannelMaxNumHandles| handles. (The latter part is
+ // possible because we could have accumulated all the handles for a message,
+ // then received the message data plus the first set of handles for the next
+ // message in the subsequent |recvmsg()|.)
+ if (read_platform_handles_.size() > (TransportData::kMaxPlatformHandles +
+ embedder::kPlatformChannelMaxNumHandles)) {
+ LOG(WARNING) << "Received too many platform handles";
+ embedder::CloseAllPlatformHandles(&read_platform_handles_);
+ read_platform_handles_.clear();
+ return IO_FAILED;
+ }
+ }
+
+ if (read_result > 0) {
+ *bytes_read = static_cast<size_t>(read_result);
+ return IO_SUCCEEDED;
+ }
+
+ // |read_result == 0| means "end of file".
+ if (read_result == 0 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
+ PLOG_IF(WARNING, read_result != 0) << "recvmsg";
+
+ // Make sure that |OnFileCanReadWithoutBlocking()| won't be called again.
+ read_watcher_.reset();
+
+ return IO_FAILED;
+ }
+
+ return ScheduleRead();
+}
+
+RawChannel::IOResult RawChannelPosix::ScheduleRead() {
+ DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+ DCHECK(!pending_read_);
+
+ pending_read_ = true;
+
+ return IO_PENDING;
+}
+
+embedder::ScopedPlatformHandleVectorPtr RawChannelPosix::GetReadPlatformHandles(
+ size_t num_platform_handles,
+ const void* /*platform_handle_table*/) {
+ DCHECK_GT(num_platform_handles, 0u);
+
+ if (read_platform_handles_.size() < num_platform_handles) {
+ embedder::CloseAllPlatformHandles(&read_platform_handles_);
+ read_platform_handles_.clear();
+ return embedder::ScopedPlatformHandleVectorPtr();
+ }
+
+ embedder::ScopedPlatformHandleVectorPtr rv(
+ new embedder::PlatformHandleVector(num_platform_handles));
+ rv->assign(read_platform_handles_.begin(),
+ read_platform_handles_.begin() + num_platform_handles);
+ read_platform_handles_.erase(
+ read_platform_handles_.begin(),
+ read_platform_handles_.begin() + num_platform_handles);
+ return rv.Pass();
+}
+
+RawChannel::IOResult RawChannelPosix::WriteNoLock(
+ size_t* platform_handles_written,
+ size_t* bytes_written) {
+ write_lock().AssertAcquired();
+
+ DCHECK(!pending_write_);
+
+ size_t num_platform_handles = 0;
+ ssize_t write_result;
+ if (write_buffer_no_lock()->HavePlatformHandlesToSend()) {
+ embedder::PlatformHandle* platform_handles;
+ void* serialization_data; // Actually unused.
+ write_buffer_no_lock()->GetPlatformHandlesToSend(&num_platform_handles,
+ &platform_handles,
+ &serialization_data);
+ DCHECK_GT(num_platform_handles, 0u);
+ DCHECK_LE(num_platform_handles, embedder::kPlatformChannelMaxNumHandles);
+ DCHECK(platform_handles);
+
+ // TODO(vtl): Reduce code duplication. (This is duplicated from below.)
+ std::vector<WriteBuffer::Buffer> buffers;
+ write_buffer_no_lock()->GetBuffers(&buffers);
+ DCHECK(!buffers.empty());
+ const size_t kMaxBufferCount = 10;
+ iovec iov[kMaxBufferCount];
+ size_t buffer_count = std::min(buffers.size(), kMaxBufferCount);
+ for (size_t i = 0; i < buffer_count; ++i) {
+ iov[i].iov_base = const_cast<char*>(buffers[i].addr);
+ iov[i].iov_len = buffers[i].size;
+ }
+
+ write_result = embedder::PlatformChannelSendmsgWithHandles(
+ fd_.get(), iov, buffer_count, platform_handles, num_platform_handles);
+ for (size_t i = 0; i < num_platform_handles; i++)
+ platform_handles[i].CloseIfNecessary();
+ } else {
+ std::vector<WriteBuffer::Buffer> buffers;
+ write_buffer_no_lock()->GetBuffers(&buffers);
+ DCHECK(!buffers.empty());
+
+ if (buffers.size() == 1) {
+ write_result = embedder::PlatformChannelWrite(fd_.get(), buffers[0].addr,
+ buffers[0].size);
+ } else {
+ const size_t kMaxBufferCount = 10;
+ iovec iov[kMaxBufferCount];
+ size_t buffer_count = std::min(buffers.size(), kMaxBufferCount);
+ for (size_t i = 0; i < buffer_count; ++i) {
+ iov[i].iov_base = const_cast<char*>(buffers[i].addr);
+ iov[i].iov_len = buffers[i].size;
+ }
+
+ write_result = embedder::PlatformChannelWritev(fd_.get(), iov,
+ buffer_count);
+ }
+ }
+
+ if (write_result >= 0) {
+ *platform_handles_written = num_platform_handles;
+ *bytes_written = static_cast<size_t>(write_result);
+ return IO_SUCCEEDED;
+ }
+
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ PLOG(ERROR) << "sendmsg/write/writev";
+ return IO_FAILED;
+ }
+
+ return ScheduleWriteNoLock();
+}
+
+RawChannel::IOResult RawChannelPosix::ScheduleWriteNoLock() {
+ write_lock().AssertAcquired();
+
+ DCHECK(!pending_write_);
+
+ // Set up to wait for the FD to become writable.
+ // If we're not on the I/O thread, we have to post a task to do this.
+ if (base::MessageLoop::current() != message_loop_for_io()) {
+ message_loop_for_io()->PostTask(
+ FROM_HERE,
+ base::Bind(&RawChannelPosix::WaitToWrite,
+ weak_ptr_factory_.GetWeakPtr()));
+ pending_write_ = true;
+ return IO_PENDING;
+ }
+
+ if (message_loop_for_io()->WatchFileDescriptor(fd_.get().fd, false,
+ base::MessageLoopForIO::WATCH_WRITE, write_watcher_.get(), this)) {
+ pending_write_ = true;
+ return IO_PENDING;
+ }
+
+ return IO_FAILED;
+}
+
+bool RawChannelPosix::OnInit() {
+ DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+
+ DCHECK(!read_watcher_);
+ read_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher());
+ DCHECK(!write_watcher_);
+ write_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher());
+
+ if (!message_loop_for_io()->WatchFileDescriptor(fd_.get().fd, true,
+ base::MessageLoopForIO::WATCH_READ, read_watcher_.get(), this)) {
+ // TODO(vtl): I'm not sure |WatchFileDescriptor()| actually fails cleanly
+ // (in the sense of returning the message loop's state to what it was before
+ // it was called).
+ read_watcher_.reset();
+ write_watcher_.reset();
+ return false;
+ }
+
+ return true;
+}
+
+void RawChannelPosix::OnShutdownNoLock(
+ scoped_ptr<ReadBuffer> /*read_buffer*/,
+ scoped_ptr<WriteBuffer> /*write_buffer*/) {
+ DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+ write_lock().AssertAcquired();
+
+ read_watcher_.reset(); // This will stop watching (if necessary).
+ write_watcher_.reset(); // This will stop watching (if necessary).
+
+ pending_read_ = false;
+ pending_write_ = false;
+
+ DCHECK(fd_.is_valid());
+ fd_.reset();
+
+ weak_ptr_factory_.InvalidateWeakPtrs();
+}
+
+void RawChannelPosix::OnFileCanReadWithoutBlocking(int fd) {
+ DCHECK_EQ(fd, fd_.get().fd);
+ DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+
+ if (!pending_read_) {
+ NOTREACHED();
+ return;
+ }
+
+ pending_read_ = false;
+ size_t bytes_read = 0;
+ IOResult result = Read(&bytes_read);
+ if (result != IO_PENDING)
+ OnReadCompleted(result == IO_SUCCEEDED, bytes_read);
+
+ // On failure, |read_watcher_| must have been reset; on success,
+ // we assume that |OnReadCompleted()| always schedules another read.
+ // Otherwise, we could end up spinning -- getting
+ // |OnFileCanReadWithoutBlocking()| again and again but not doing any actual
+ // read.
+ // TODO(yzshen): An alternative is to stop watching if RawChannel doesn't
+ // schedule a new read. But that code won't be reached under the current
+ // RawChannel implementation.
+ DCHECK(!read_watcher_ || pending_read_);
+}
+
+void RawChannelPosix::OnFileCanWriteWithoutBlocking(int fd) {
+ DCHECK_EQ(fd, fd_.get().fd);
+ DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+
+ IOResult result = IO_FAILED;
+ size_t platform_handles_written = 0;
+ size_t bytes_written = 0;
+ {
+ base::AutoLock locker(write_lock());
+
+ DCHECK(pending_write_);
+
+ pending_write_ = false;
+ result = WriteNoLock(&platform_handles_written, &bytes_written);
+ }
+
+ if (result != IO_PENDING) {
+ OnWriteCompleted(result == IO_SUCCEEDED,
+ platform_handles_written,
+ bytes_written);
+ }
+}
+
+void RawChannelPosix::WaitToWrite() {
+ DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+
+ DCHECK(write_watcher_);
+
+ if (!message_loop_for_io()->WatchFileDescriptor(
+ fd_.get().fd, false, base::MessageLoopForIO::WATCH_WRITE,
+ write_watcher_.get(), this)) {
+ {
+ base::AutoLock locker(write_lock());
+
+ DCHECK(pending_write_);
+ pending_write_ = false;
+ }
+ OnWriteCompleted(false, 0, 0);
+ }
+}
+
+} // namespace
+
+// -----------------------------------------------------------------------------
+
+// Static factory method declared in raw_channel.h.
+// static
+scoped_ptr<RawChannel> RawChannel::Create(
+ embedder::ScopedPlatformHandle handle) {
+ return scoped_ptr<RawChannel>(new RawChannelPosix(handle.Pass()));
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/raw_channel_unittest.cc b/chromium/mojo/system/raw_channel_unittest.cc
new file mode 100644
index 00000000000..2a386833e8a
--- /dev/null
+++ b/chromium/mojo/system/raw_channel_unittest.cc
@@ -0,0 +1,689 @@
+// Copyright 2014 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 "mojo/system/raw_channel.h"
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/rand_util.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/platform_thread.h" // For |Sleep()|.
+#include "base/threading/simple_thread.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "mojo/common/test/test_utils.h"
+#include "mojo/embedder/platform_channel_pair.h"
+#include "mojo/embedder/platform_handle.h"
+#include "mojo/embedder/scoped_platform_handle.h"
+#include "mojo/system/message_in_transit.h"
+#include "mojo/system/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+scoped_ptr<MessageInTransit> MakeTestMessage(uint32_t num_bytes) {
+ std::vector<unsigned char> bytes(num_bytes, 0);
+ for (size_t i = 0; i < num_bytes; i++)
+ bytes[i] = static_cast<unsigned char>(i + num_bytes);
+ return make_scoped_ptr(
+ new MessageInTransit(MessageInTransit::kTypeMessagePipeEndpoint,
+ MessageInTransit::kSubtypeMessagePipeEndpointData,
+ num_bytes, bytes.empty() ? NULL : &bytes[0]));
+}
+
+bool CheckMessageData(const void* bytes, uint32_t num_bytes) {
+ const unsigned char* b = static_cast<const unsigned char*>(bytes);
+ for (uint32_t i = 0; i < num_bytes; i++) {
+ if (b[i] != static_cast<unsigned char>(i + num_bytes))
+ return false;
+ }
+ return true;
+}
+
+void InitOnIOThread(RawChannel* raw_channel, RawChannel::Delegate* delegate) {
+ CHECK(raw_channel->Init(delegate));
+}
+
+bool WriteTestMessageToHandle(const embedder::PlatformHandle& handle,
+ uint32_t num_bytes) {
+ scoped_ptr<MessageInTransit> message(MakeTestMessage(num_bytes));
+
+ size_t write_size = 0;
+ mojo::test::BlockingWrite(
+ handle, message->main_buffer(), message->main_buffer_size(), &write_size);
+ return write_size == message->main_buffer_size();
+}
+
+// -----------------------------------------------------------------------------
+
+class RawChannelTest : public testing::Test {
+ public:
+ RawChannelTest() : io_thread_(test::TestIOThread::kManualStart) {}
+ virtual ~RawChannelTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ embedder::PlatformChannelPair channel_pair;
+ handles[0] = channel_pair.PassServerHandle();
+ handles[1] = channel_pair.PassClientHandle();
+ io_thread_.Start();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ io_thread_.Stop();
+ handles[0].reset();
+ handles[1].reset();
+ }
+
+ protected:
+ test::TestIOThread* io_thread() { return &io_thread_; }
+
+ embedder::ScopedPlatformHandle handles[2];
+
+ private:
+ test::TestIOThread io_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(RawChannelTest);
+};
+
+// RawChannelTest.WriteMessage -------------------------------------------------
+
+class WriteOnlyRawChannelDelegate : public RawChannel::Delegate {
+ public:
+ WriteOnlyRawChannelDelegate() {}
+ virtual ~WriteOnlyRawChannelDelegate() {}
+
+ // |RawChannel::Delegate| implementation:
+ virtual void OnReadMessage(
+ const MessageInTransit::View& /*message_view*/,
+ embedder::ScopedPlatformHandleVectorPtr /*platform_handles*/) OVERRIDE {
+ CHECK(false); // Should not get called.
+ }
+ virtual void OnFatalError(FatalError fatal_error) OVERRIDE {
+ // We'll get a read error when the connection is closed.
+ CHECK_EQ(fatal_error, FATAL_ERROR_READ);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WriteOnlyRawChannelDelegate);
+};
+
+static const int64_t kMessageReaderSleepMs = 1;
+static const size_t kMessageReaderMaxPollIterations = 3000;
+
+class TestMessageReaderAndChecker {
+ public:
+ explicit TestMessageReaderAndChecker(embedder::PlatformHandle handle)
+ : handle_(handle) {}
+ ~TestMessageReaderAndChecker() { CHECK(bytes_.empty()); }
+
+ bool ReadAndCheckNextMessage(uint32_t expected_size) {
+ unsigned char buffer[4096];
+
+ for (size_t i = 0; i < kMessageReaderMaxPollIterations;) {
+ size_t read_size = 0;
+ CHECK(mojo::test::NonBlockingRead(handle_, buffer, sizeof(buffer),
+ &read_size));
+
+ // Append newly-read data to |bytes_|.
+ bytes_.insert(bytes_.end(), buffer, buffer + read_size);
+
+ // If we have the header....
+ size_t message_size;
+ if (MessageInTransit::GetNextMessageSize(
+ bytes_.empty() ? NULL : &bytes_[0],
+ bytes_.size(),
+ &message_size)) {
+ // If we've read the whole message....
+ if (bytes_.size() >= message_size) {
+ bool rv = true;
+ MessageInTransit::View message_view(message_size, &bytes_[0]);
+ CHECK_EQ(message_view.main_buffer_size(), message_size);
+
+ if (message_view.num_bytes() != expected_size) {
+ LOG(ERROR) << "Wrong size: " << message_size << " instead of "
+ << expected_size << " bytes.";
+ rv = false;
+ } else if (!CheckMessageData(message_view.bytes(),
+ message_view.num_bytes())) {
+ LOG(ERROR) << "Incorrect message bytes.";
+ rv = false;
+ }
+
+ // Erase message data.
+ bytes_.erase(bytes_.begin(),
+ bytes_.begin() +
+ message_view.main_buffer_size());
+ return rv;
+ }
+ }
+
+ if (static_cast<size_t>(read_size) < sizeof(buffer)) {
+ i++;
+ base::PlatformThread::Sleep(
+ base::TimeDelta::FromMilliseconds(kMessageReaderSleepMs));
+ }
+ }
+
+ LOG(ERROR) << "Too many iterations.";
+ return false;
+ }
+
+ private:
+ const embedder::PlatformHandle handle_;
+
+ // The start of the received data should always be on a message boundary.
+ std::vector<unsigned char> bytes_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestMessageReaderAndChecker);
+};
+
+// Tests writing (and verifies reading using our own custom reader).
+TEST_F(RawChannelTest, WriteMessage) {
+ WriteOnlyRawChannelDelegate delegate;
+ scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
+ TestMessageReaderAndChecker checker(handles[1].get());
+ io_thread()->PostTaskAndWait(FROM_HERE,
+ base::Bind(&InitOnIOThread, rc.get(),
+ base::Unretained(&delegate)));
+
+ // Write and read, for a variety of sizes.
+ for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1) {
+ EXPECT_TRUE(rc->WriteMessage(MakeTestMessage(size)));
+ EXPECT_TRUE(checker.ReadAndCheckNextMessage(size)) << size;
+ }
+
+ // Write/queue and read afterwards, for a variety of sizes.
+ for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1)
+ EXPECT_TRUE(rc->WriteMessage(MakeTestMessage(size)));
+ for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1)
+ EXPECT_TRUE(checker.ReadAndCheckNextMessage(size)) << size;
+
+ io_thread()->PostTaskAndWait(FROM_HERE,
+ base::Bind(&RawChannel::Shutdown,
+ base::Unretained(rc.get())));
+}
+
+// RawChannelTest.OnReadMessage ------------------------------------------------
+
+class ReadCheckerRawChannelDelegate : public RawChannel::Delegate {
+ public:
+ ReadCheckerRawChannelDelegate()
+ : done_event_(false, false),
+ position_(0) {}
+ virtual ~ReadCheckerRawChannelDelegate() {}
+
+ // |RawChannel::Delegate| implementation (called on the I/O thread):
+ virtual void OnReadMessage(
+ const MessageInTransit::View& message_view,
+ embedder::ScopedPlatformHandleVectorPtr platform_handles) OVERRIDE {
+ EXPECT_FALSE(platform_handles);
+
+ size_t position;
+ size_t expected_size;
+ bool should_signal = false;
+ {
+ base::AutoLock locker(lock_);
+ CHECK_LT(position_, expected_sizes_.size());
+ position = position_;
+ expected_size = expected_sizes_[position];
+ position_++;
+ if (position_ >= expected_sizes_.size())
+ should_signal = true;
+ }
+
+ EXPECT_EQ(expected_size, message_view.num_bytes()) << position;
+ if (message_view.num_bytes() == expected_size) {
+ EXPECT_TRUE(CheckMessageData(message_view.bytes(),
+ message_view.num_bytes())) << position;
+ }
+
+ if (should_signal)
+ done_event_.Signal();
+ }
+ virtual void OnFatalError(FatalError fatal_error) OVERRIDE {
+ // We'll get a read error when the connection is closed.
+ CHECK_EQ(fatal_error, FATAL_ERROR_READ);
+ }
+
+ // Waits for all the messages (of sizes |expected_sizes_|) to be seen.
+ void Wait() {
+ done_event_.Wait();
+ }
+
+ void SetExpectedSizes(const std::vector<uint32_t>& expected_sizes) {
+ base::AutoLock locker(lock_);
+ CHECK_EQ(position_, expected_sizes_.size());
+ expected_sizes_ = expected_sizes;
+ position_ = 0;
+ }
+
+ private:
+ base::WaitableEvent done_event_;
+
+ base::Lock lock_; // Protects the following members.
+ std::vector<uint32_t> expected_sizes_;
+ size_t position_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadCheckerRawChannelDelegate);
+};
+
+// Tests reading (writing using our own custom writer).
+TEST_F(RawChannelTest, OnReadMessage) {
+ ReadCheckerRawChannelDelegate delegate;
+ scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
+ io_thread()->PostTaskAndWait(FROM_HERE,
+ base::Bind(&InitOnIOThread, rc.get(),
+ base::Unretained(&delegate)));
+
+ // Write and read, for a variety of sizes.
+ for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1) {
+ delegate.SetExpectedSizes(std::vector<uint32_t>(1, size));
+
+ EXPECT_TRUE(WriteTestMessageToHandle(handles[1].get(), size));
+
+ delegate.Wait();
+ }
+
+ // Set up reader and write as fast as we can.
+ // Write/queue and read afterwards, for a variety of sizes.
+ std::vector<uint32_t> expected_sizes;
+ for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1)
+ expected_sizes.push_back(size);
+ delegate.SetExpectedSizes(expected_sizes);
+ for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1)
+ EXPECT_TRUE(WriteTestMessageToHandle(handles[1].get(), size));
+ delegate.Wait();
+
+ io_thread()->PostTaskAndWait(FROM_HERE,
+ base::Bind(&RawChannel::Shutdown,
+ base::Unretained(rc.get())));
+}
+
+// RawChannelTest.WriteMessageAndOnReadMessage ---------------------------------
+
+class RawChannelWriterThread : public base::SimpleThread {
+ public:
+ RawChannelWriterThread(RawChannel* raw_channel, size_t write_count)
+ : base::SimpleThread("raw_channel_writer_thread"),
+ raw_channel_(raw_channel),
+ left_to_write_(write_count) {
+ }
+
+ virtual ~RawChannelWriterThread() {
+ Join();
+ }
+
+ private:
+ virtual void Run() OVERRIDE {
+ static const int kMaxRandomMessageSize = 25000;
+
+ while (left_to_write_-- > 0) {
+ EXPECT_TRUE(raw_channel_->WriteMessage(MakeTestMessage(
+ static_cast<uint32_t>(base::RandInt(1, kMaxRandomMessageSize)))));
+ }
+ }
+
+ RawChannel* const raw_channel_;
+ size_t left_to_write_;
+
+ DISALLOW_COPY_AND_ASSIGN(RawChannelWriterThread);
+};
+
+class ReadCountdownRawChannelDelegate : public RawChannel::Delegate {
+ public:
+ explicit ReadCountdownRawChannelDelegate(size_t expected_count)
+ : done_event_(false, false),
+ expected_count_(expected_count),
+ count_(0) {}
+ virtual ~ReadCountdownRawChannelDelegate() {}
+
+ // |RawChannel::Delegate| implementation (called on the I/O thread):
+ virtual void OnReadMessage(
+ const MessageInTransit::View& message_view,
+ embedder::ScopedPlatformHandleVectorPtr platform_handles) OVERRIDE {
+ EXPECT_FALSE(platform_handles);
+
+ EXPECT_LT(count_, expected_count_);
+ count_++;
+
+ EXPECT_TRUE(CheckMessageData(message_view.bytes(),
+ message_view.num_bytes()));
+
+ if (count_ >= expected_count_)
+ done_event_.Signal();
+ }
+ virtual void OnFatalError(FatalError fatal_error) OVERRIDE {
+ // We'll get a read error when the connection is closed.
+ CHECK_EQ(fatal_error, FATAL_ERROR_READ);
+ }
+
+ // Waits for all the messages to have been seen.
+ void Wait() {
+ done_event_.Wait();
+ }
+
+ private:
+ base::WaitableEvent done_event_;
+ size_t expected_count_;
+ size_t count_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadCountdownRawChannelDelegate);
+};
+
+TEST_F(RawChannelTest, WriteMessageAndOnReadMessage) {
+ static const size_t kNumWriterThreads = 10;
+ static const size_t kNumWriteMessagesPerThread = 4000;
+
+ WriteOnlyRawChannelDelegate writer_delegate;
+ scoped_ptr<RawChannel> writer_rc(RawChannel::Create(handles[0].Pass()));
+ io_thread()->PostTaskAndWait(FROM_HERE,
+ base::Bind(&InitOnIOThread, writer_rc.get(),
+ base::Unretained(&writer_delegate)));
+
+ ReadCountdownRawChannelDelegate reader_delegate(
+ kNumWriterThreads * kNumWriteMessagesPerThread);
+ scoped_ptr<RawChannel> reader_rc(RawChannel::Create(handles[1].Pass()));
+ io_thread()->PostTaskAndWait(FROM_HERE,
+ base::Bind(&InitOnIOThread, reader_rc.get(),
+ base::Unretained(&reader_delegate)));
+
+ {
+ ScopedVector<RawChannelWriterThread> writer_threads;
+ for (size_t i = 0; i < kNumWriterThreads; i++) {
+ writer_threads.push_back(new RawChannelWriterThread(
+ writer_rc.get(), kNumWriteMessagesPerThread));
+ }
+ for (size_t i = 0; i < writer_threads.size(); i++)
+ writer_threads[i]->Start();
+ } // Joins all the writer threads.
+
+ // Sleep a bit, to let any extraneous reads be processed. (There shouldn't be
+ // any, but we want to know about them.)
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
+
+ // Wait for reading to finish.
+ reader_delegate.Wait();
+
+ io_thread()->PostTaskAndWait(FROM_HERE,
+ base::Bind(&RawChannel::Shutdown,
+ base::Unretained(reader_rc.get())));
+
+ io_thread()->PostTaskAndWait(FROM_HERE,
+ base::Bind(&RawChannel::Shutdown,
+ base::Unretained(writer_rc.get())));
+}
+
+// RawChannelTest.OnFatalError -------------------------------------------------
+
+class FatalErrorRecordingRawChannelDelegate
+ : public ReadCountdownRawChannelDelegate {
+ public:
+ FatalErrorRecordingRawChannelDelegate(size_t expected_read_count,
+ bool expect_read_error,
+ bool expect_write_error)
+ : ReadCountdownRawChannelDelegate(expected_read_count),
+ got_read_fatal_error_event_(false, false),
+ got_write_fatal_error_event_(false, false),
+ expecting_read_error_(expect_read_error),
+ expecting_write_error_(expect_write_error) {
+ }
+
+ virtual ~FatalErrorRecordingRawChannelDelegate() {}
+
+ virtual void OnFatalError(FatalError fatal_error) OVERRIDE {
+ switch (fatal_error) {
+ case FATAL_ERROR_READ:
+ ASSERT_TRUE(expecting_read_error_);
+ expecting_read_error_ = false;
+ got_read_fatal_error_event_.Signal();
+ break;
+ case FATAL_ERROR_WRITE:
+ ASSERT_TRUE(expecting_write_error_);
+ expecting_write_error_ = false;
+ got_write_fatal_error_event_.Signal();
+ break;
+ }
+ }
+
+ void WaitForReadFatalError() { got_read_fatal_error_event_.Wait(); }
+ void WaitForWriteFatalError() { got_write_fatal_error_event_.Wait(); }
+
+ private:
+ base::WaitableEvent got_read_fatal_error_event_;
+ base::WaitableEvent got_write_fatal_error_event_;
+
+ bool expecting_read_error_;
+ bool expecting_write_error_;
+
+ DISALLOW_COPY_AND_ASSIGN(FatalErrorRecordingRawChannelDelegate);
+};
+
+// Tests fatal errors.
+TEST_F(RawChannelTest, OnFatalError) {
+ FatalErrorRecordingRawChannelDelegate delegate(0, true, true);
+ scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
+ io_thread()->PostTaskAndWait(FROM_HERE,
+ base::Bind(&InitOnIOThread, rc.get(),
+ base::Unretained(&delegate)));
+
+ // Close the handle of the other end, which should make writing fail.
+ handles[1].reset();
+
+ EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1)));
+
+ // We should get a write fatal error.
+ delegate.WaitForWriteFatalError();
+
+ // We should also get a read fatal error.
+ delegate.WaitForReadFatalError();
+
+ EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(2)));
+
+ // Sleep a bit, to make sure we don't get another |OnFatalError()|
+ // notification. (If we actually get another one, |OnFatalError()| crashes.)
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
+
+ io_thread()->PostTaskAndWait(FROM_HERE,
+ base::Bind(&RawChannel::Shutdown,
+ base::Unretained(rc.get())));
+}
+
+// RawChannelTest.ReadUnaffectedByWriteFatalError ------------------------------
+
+TEST_F(RawChannelTest, ReadUnaffectedByWriteFatalError) {
+ const size_t kMessageCount = 5;
+
+ // Write a few messages into the other end.
+ uint32_t message_size = 1;
+ for (size_t i = 0; i < kMessageCount;
+ i++, message_size += message_size / 2 + 1)
+ EXPECT_TRUE(WriteTestMessageToHandle(handles[1].get(), message_size));
+
+ // Close the other end, which should make writing fail.
+ handles[1].reset();
+
+ // Only start up reading here. The system buffer should still contain the
+ // messages that were written.
+ FatalErrorRecordingRawChannelDelegate delegate(kMessageCount, true, true);
+ scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
+ io_thread()->PostTaskAndWait(FROM_HERE,
+ base::Bind(&InitOnIOThread, rc.get(),
+ base::Unretained(&delegate)));
+
+ EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1)));
+
+ // We should definitely get a write fatal error.
+ delegate.WaitForWriteFatalError();
+
+ // Wait for reading to finish. A writing failure shouldn't affect reading.
+ delegate.Wait();
+
+ // And then we should get a read fatal error.
+ delegate.WaitForReadFatalError();
+
+ io_thread()->PostTaskAndWait(FROM_HERE,
+ base::Bind(&RawChannel::Shutdown,
+ base::Unretained(rc.get())));
+}
+
+// RawChannelTest.WriteMessageAfterShutdown ------------------------------------
+
+// Makes sure that calling |WriteMessage()| after |Shutdown()| behaves
+// correctly.
+TEST_F(RawChannelTest, WriteMessageAfterShutdown) {
+ WriteOnlyRawChannelDelegate delegate;
+ scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
+ io_thread()->PostTaskAndWait(FROM_HERE,
+ base::Bind(&InitOnIOThread, rc.get(),
+ base::Unretained(&delegate)));
+ io_thread()->PostTaskAndWait(FROM_HERE,
+ base::Bind(&RawChannel::Shutdown,
+ base::Unretained(rc.get())));
+
+ EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1)));
+}
+
+// RawChannelTest.ShutdownOnReadMessage ----------------------------------------
+
+class ShutdownOnReadMessageRawChannelDelegate : public RawChannel::Delegate {
+ public:
+ explicit ShutdownOnReadMessageRawChannelDelegate(RawChannel* raw_channel)
+ : raw_channel_(raw_channel),
+ done_event_(false, false),
+ did_shutdown_(false) {}
+ virtual ~ShutdownOnReadMessageRawChannelDelegate() {}
+
+ // |RawChannel::Delegate| implementation (called on the I/O thread):
+ virtual void OnReadMessage(
+ const MessageInTransit::View& message_view,
+ embedder::ScopedPlatformHandleVectorPtr platform_handles) OVERRIDE {
+ EXPECT_FALSE(platform_handles);
+ EXPECT_FALSE(did_shutdown_);
+ EXPECT_TRUE(CheckMessageData(message_view.bytes(),
+ message_view.num_bytes()));
+ raw_channel_->Shutdown();
+ did_shutdown_ = true;
+ done_event_.Signal();
+ }
+ virtual void OnFatalError(FatalError /*fatal_error*/) OVERRIDE {
+ CHECK(false); // Should not get called.
+ }
+
+ // Waits for shutdown.
+ void Wait() {
+ done_event_.Wait();
+ EXPECT_TRUE(did_shutdown_);
+ }
+
+ private:
+ RawChannel* const raw_channel_;
+ base::WaitableEvent done_event_;
+ bool did_shutdown_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShutdownOnReadMessageRawChannelDelegate);
+};
+
+TEST_F(RawChannelTest, ShutdownOnReadMessage) {
+ // Write a few messages into the other end.
+ for (size_t count = 0; count < 5; count++)
+ EXPECT_TRUE(WriteTestMessageToHandle(handles[1].get(), 10));
+
+ scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
+ ShutdownOnReadMessageRawChannelDelegate delegate(rc.get());
+ io_thread()->PostTaskAndWait(FROM_HERE,
+ base::Bind(&InitOnIOThread, rc.get(),
+ base::Unretained(&delegate)));
+
+ // Wait for the delegate, which will shut the |RawChannel| down.
+ delegate.Wait();
+}
+
+// RawChannelTest.ShutdownOnFatalError{Read, Write} ----------------------------
+
+class ShutdownOnFatalErrorRawChannelDelegate : public RawChannel::Delegate {
+ public:
+ ShutdownOnFatalErrorRawChannelDelegate(RawChannel* raw_channel,
+ FatalError shutdown_on_error_type)
+ : raw_channel_(raw_channel),
+ shutdown_on_error_type_(shutdown_on_error_type),
+ done_event_(false, false),
+ did_shutdown_(false) {}
+ virtual ~ShutdownOnFatalErrorRawChannelDelegate() {}
+
+ // |RawChannel::Delegate| implementation (called on the I/O thread):
+ virtual void OnReadMessage(
+ const MessageInTransit::View& /*message_view*/,
+ embedder::ScopedPlatformHandleVectorPtr /*platform_handles*/) OVERRIDE {
+ CHECK(false); // Should not get called.
+ }
+ virtual void OnFatalError(FatalError fatal_error) OVERRIDE {
+ EXPECT_FALSE(did_shutdown_);
+ if (fatal_error != shutdown_on_error_type_)
+ return;
+ raw_channel_->Shutdown();
+ did_shutdown_ = true;
+ done_event_.Signal();
+ }
+
+ // Waits for shutdown.
+ void Wait() {
+ done_event_.Wait();
+ EXPECT_TRUE(did_shutdown_);
+ }
+
+ private:
+ RawChannel* const raw_channel_;
+ const FatalError shutdown_on_error_type_;
+ base::WaitableEvent done_event_;
+ bool did_shutdown_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShutdownOnFatalErrorRawChannelDelegate);
+};
+
+TEST_F(RawChannelTest, ShutdownOnFatalErrorRead) {
+ scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
+ ShutdownOnFatalErrorRawChannelDelegate delegate(
+ rc.get(), RawChannel::Delegate::FATAL_ERROR_READ);
+ io_thread()->PostTaskAndWait(FROM_HERE,
+ base::Bind(&InitOnIOThread, rc.get(),
+ base::Unretained(&delegate)));
+
+ // Close the handle of the other end, which should stuff fail.
+ handles[1].reset();
+
+ // Wait for the delegate, which will shut the |RawChannel| down.
+ delegate.Wait();
+}
+
+TEST_F(RawChannelTest, ShutdownOnFatalErrorWrite) {
+ scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
+ ShutdownOnFatalErrorRawChannelDelegate delegate(
+ rc.get(), RawChannel::Delegate::FATAL_ERROR_WRITE);
+ io_thread()->PostTaskAndWait(FROM_HERE,
+ base::Bind(&InitOnIOThread, rc.get(),
+ base::Unretained(&delegate)));
+
+ // Close the handle of the other end, which should stuff fail.
+ handles[1].reset();
+
+ EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1)));
+
+ // Wait for the delegate, which will shut the |RawChannel| down.
+ delegate.Wait();
+}
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/raw_channel_win.cc b/chromium/mojo/system/raw_channel_win.cc
new file mode 100644
index 00000000000..005a344907f
--- /dev/null
+++ b/chromium/mojo/system/raw_channel_win.cc
@@ -0,0 +1,568 @@
+// Copyright 2013 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 "mojo/system/raw_channel.h"
+
+#include <windows.h>
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/lazy_instance.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/synchronization/lock.h"
+#include "base/win/windows_version.h"
+#include "mojo/embedder/platform_handle.h"
+
+namespace mojo {
+namespace system {
+
+namespace {
+
+class VistaOrHigherFunctions {
+ public:
+ VistaOrHigherFunctions();
+
+ bool is_vista_or_higher() const { return is_vista_or_higher_; }
+
+ BOOL SetFileCompletionNotificationModes(HANDLE handle, UCHAR flags) {
+ return set_file_completion_notification_modes_(handle, flags);
+ }
+
+ BOOL CancelIoEx(HANDLE handle, LPOVERLAPPED overlapped) {
+ return cancel_io_ex_(handle, overlapped);
+ }
+
+ private:
+ typedef BOOL (WINAPI *SetFileCompletionNotificationModesFunc)(HANDLE, UCHAR);
+ typedef BOOL (WINAPI *CancelIoExFunc)(HANDLE, LPOVERLAPPED);
+
+ bool is_vista_or_higher_;
+ SetFileCompletionNotificationModesFunc
+ set_file_completion_notification_modes_;
+ CancelIoExFunc cancel_io_ex_;
+};
+
+VistaOrHigherFunctions::VistaOrHigherFunctions()
+ : is_vista_or_higher_(base::win::GetVersion() >= base::win::VERSION_VISTA),
+ set_file_completion_notification_modes_(NULL),
+ cancel_io_ex_(NULL) {
+ if (!is_vista_or_higher_)
+ return;
+
+ HMODULE module = GetModuleHandleW(L"kernel32.dll");
+ set_file_completion_notification_modes_ =
+ reinterpret_cast<SetFileCompletionNotificationModesFunc>(
+ GetProcAddress(module, "SetFileCompletionNotificationModes"));
+ DCHECK(set_file_completion_notification_modes_);
+
+ cancel_io_ex_ = reinterpret_cast<CancelIoExFunc>(
+ GetProcAddress(module, "CancelIoEx"));
+ DCHECK(cancel_io_ex_);
+}
+
+base::LazyInstance<VistaOrHigherFunctions> g_vista_or_higher_functions =
+ LAZY_INSTANCE_INITIALIZER;
+
+class RawChannelWin : public RawChannel {
+ public:
+ RawChannelWin(embedder::ScopedPlatformHandle handle);
+ virtual ~RawChannelWin();
+
+ // |RawChannel| public methods:
+ virtual size_t GetSerializedPlatformHandleSize() const OVERRIDE;
+
+ private:
+ // RawChannelIOHandler receives OS notifications for I/O completion. It must
+ // be created on the I/O thread.
+ //
+ // It manages its own destruction. Destruction happens on the I/O thread when
+ // all the following conditions are satisfied:
+ // - |DetachFromOwnerNoLock()| has been called;
+ // - there is no pending read;
+ // - there is no pending write.
+ class RawChannelIOHandler : public base::MessageLoopForIO::IOHandler {
+ public:
+ RawChannelIOHandler(RawChannelWin* owner,
+ embedder::ScopedPlatformHandle handle);
+
+ HANDLE handle() const { return handle_.get().handle; }
+
+ // The following methods are only called by the owner on the I/O thread.
+ bool pending_read() const;
+ base::MessageLoopForIO::IOContext* read_context();
+ // Instructs the object to wait for an |OnIOCompleted()| notification.
+ void OnPendingReadStarted();
+
+ // The following methods are only called by the owner under
+ // |owner_->write_lock()|.
+ bool pending_write_no_lock() const;
+ base::MessageLoopForIO::IOContext* write_context_no_lock();
+ // Instructs the object to wait for an |OnIOCompleted()| notification.
+ void OnPendingWriteStartedNoLock();
+
+ // |base::MessageLoopForIO::IOHandler| implementation:
+ // Must be called on the I/O thread. It could be called before or after
+ // detached from the owner.
+ virtual void OnIOCompleted(base::MessageLoopForIO::IOContext* context,
+ DWORD bytes_transferred,
+ DWORD error) OVERRIDE;
+
+ // Must be called on the I/O thread under |owner_->write_lock()|.
+ // After this call, the owner must not make any further calls on this
+ // object, and therefore the object is used on the I/O thread exclusively
+ // (if it stays alive).
+ void DetachFromOwnerNoLock(scoped_ptr<ReadBuffer> read_buffer,
+ scoped_ptr<WriteBuffer> write_buffer);
+
+ private:
+ virtual ~RawChannelIOHandler();
+
+ // Returns true if |owner_| has been reset and there is not pending read or
+ // write.
+ // Must be called on the I/O thread.
+ bool ShouldSelfDestruct() const;
+
+ // Must be called on the I/O thread. It may be called before or after
+ // detaching from the owner.
+ void OnReadCompleted(DWORD bytes_read, DWORD error);
+ // Must be called on the I/O thread. It may be called before or after
+ // detaching from the owner.
+ void OnWriteCompleted(DWORD bytes_written, DWORD error);
+
+ embedder::ScopedPlatformHandle handle_;
+
+ // |owner_| is reset on the I/O thread under |owner_->write_lock()|.
+ // Therefore, it may be used on any thread under lock; or on the I/O thread
+ // without locking.
+ RawChannelWin* owner_;
+
+ // The following members must be used on the I/O thread.
+ scoped_ptr<ReadBuffer> preserved_read_buffer_after_detach_;
+ scoped_ptr<WriteBuffer> preserved_write_buffer_after_detach_;
+ bool suppress_self_destruct_;
+
+ bool pending_read_;
+ base::MessageLoopForIO::IOContext read_context_;
+
+ // The following members must be used under |owner_->write_lock()| while the
+ // object is still attached to the owner, and only on the I/O thread
+ // afterwards.
+ bool pending_write_;
+ base::MessageLoopForIO::IOContext write_context_;
+
+ DISALLOW_COPY_AND_ASSIGN(RawChannelIOHandler);
+ };
+
+ // |RawChannel| private methods:
+ virtual IOResult Read(size_t* bytes_read) OVERRIDE;
+ virtual IOResult ScheduleRead() OVERRIDE;
+ virtual embedder::ScopedPlatformHandleVectorPtr GetReadPlatformHandles(
+ size_t num_platform_handles,
+ const void* platform_handle_table) OVERRIDE;
+ virtual IOResult WriteNoLock(size_t* platform_handles_written,
+ size_t* bytes_written) OVERRIDE;
+ virtual IOResult ScheduleWriteNoLock() OVERRIDE;
+ virtual bool OnInit() OVERRIDE;
+ virtual void OnShutdownNoLock(
+ scoped_ptr<ReadBuffer> read_buffer,
+ scoped_ptr<WriteBuffer> write_buffer) OVERRIDE;
+
+ // Passed to |io_handler_| during initialization.
+ embedder::ScopedPlatformHandle handle_;
+
+ RawChannelIOHandler* io_handler_;
+
+ const bool skip_completion_port_on_success_;
+
+ DISALLOW_COPY_AND_ASSIGN(RawChannelWin);
+};
+
+RawChannelWin::RawChannelIOHandler::RawChannelIOHandler(
+ RawChannelWin* owner,
+ embedder::ScopedPlatformHandle handle) : handle_(handle.Pass()),
+ owner_(owner),
+ suppress_self_destruct_(false),
+ pending_read_(false),
+ pending_write_(false) {
+ memset(&read_context_.overlapped, 0, sizeof(read_context_.overlapped));
+ read_context_.handler = this;
+ memset(&write_context_.overlapped, 0, sizeof(write_context_.overlapped));
+ write_context_.handler = this;
+
+ owner_->message_loop_for_io()->RegisterIOHandler(handle_.get().handle, this);
+}
+
+RawChannelWin::RawChannelIOHandler::~RawChannelIOHandler() {
+ DCHECK(ShouldSelfDestruct());
+}
+
+bool RawChannelWin::RawChannelIOHandler::pending_read() const {
+ DCHECK(owner_);
+ DCHECK_EQ(base::MessageLoop::current(), owner_->message_loop_for_io());
+ return pending_read_;
+}
+
+base::MessageLoopForIO::IOContext*
+ RawChannelWin::RawChannelIOHandler::read_context() {
+ DCHECK(owner_);
+ DCHECK_EQ(base::MessageLoop::current(), owner_->message_loop_for_io());
+ return &read_context_;
+}
+
+void RawChannelWin::RawChannelIOHandler::OnPendingReadStarted() {
+ DCHECK(owner_);
+ DCHECK_EQ(base::MessageLoop::current(), owner_->message_loop_for_io());
+ DCHECK(!pending_read_);
+ pending_read_ = true;
+}
+
+bool RawChannelWin::RawChannelIOHandler::pending_write_no_lock() const {
+ DCHECK(owner_);
+ owner_->write_lock().AssertAcquired();
+ return pending_write_;
+}
+
+base::MessageLoopForIO::IOContext*
+ RawChannelWin::RawChannelIOHandler::write_context_no_lock() {
+ DCHECK(owner_);
+ owner_->write_lock().AssertAcquired();
+ return &write_context_;
+}
+
+void RawChannelWin::RawChannelIOHandler::OnPendingWriteStartedNoLock() {
+ DCHECK(owner_);
+ owner_->write_lock().AssertAcquired();
+ DCHECK(!pending_write_);
+ pending_write_ = true;
+}
+
+void RawChannelWin::RawChannelIOHandler::OnIOCompleted(
+ base::MessageLoopForIO::IOContext* context,
+ DWORD bytes_transferred,
+ DWORD error) {
+ DCHECK(!owner_ ||
+ base::MessageLoop::current() == owner_->message_loop_for_io());
+
+ {
+ // Suppress self-destruction inside |OnReadCompleted()|, etc. (in case they
+ // result in a call to |Shutdown()|).
+ base::AutoReset<bool> resetter(&suppress_self_destruct_, true);
+
+ if (context == &read_context_)
+ OnReadCompleted(bytes_transferred, error);
+ else if (context == &write_context_)
+ OnWriteCompleted(bytes_transferred, error);
+ else
+ NOTREACHED();
+ }
+
+ if (ShouldSelfDestruct())
+ delete this;
+}
+
+void RawChannelWin::RawChannelIOHandler::DetachFromOwnerNoLock(
+ scoped_ptr<ReadBuffer> read_buffer,
+ scoped_ptr<WriteBuffer> write_buffer) {
+ DCHECK(owner_);
+ DCHECK_EQ(base::MessageLoop::current(), owner_->message_loop_for_io());
+ owner_->write_lock().AssertAcquired();
+
+ // If read/write is pending, we have to retain the corresponding buffer.
+ if (pending_read_)
+ preserved_read_buffer_after_detach_ = read_buffer.Pass();
+ if (pending_write_)
+ preserved_write_buffer_after_detach_ = write_buffer.Pass();
+
+ owner_ = NULL;
+ if (ShouldSelfDestruct())
+ delete this;
+}
+
+bool RawChannelWin::RawChannelIOHandler::ShouldSelfDestruct() const {
+ if (owner_ || suppress_self_destruct_)
+ return false;
+
+ // Note: Detached, hence no lock needed for |pending_write_|.
+ return !pending_read_ && !pending_write_;
+}
+
+void RawChannelWin::RawChannelIOHandler::OnReadCompleted(DWORD bytes_read,
+ DWORD error) {
+ DCHECK(!owner_ ||
+ base::MessageLoop::current() == owner_->message_loop_for_io());
+ DCHECK(suppress_self_destruct_);
+
+ CHECK(pending_read_);
+ pending_read_ = false;
+ if (!owner_)
+ return;
+
+ if (error != ERROR_SUCCESS) {
+ DCHECK_EQ(bytes_read, 0u);
+ LOG_IF(ERROR, error != ERROR_BROKEN_PIPE)
+ << "ReadFile: " << logging::SystemErrorCodeToString(error);
+ owner_->OnReadCompleted(false, 0);
+ } else {
+ DCHECK_GT(bytes_read, 0u);
+ owner_->OnReadCompleted(true, bytes_read);
+ }
+}
+
+void RawChannelWin::RawChannelIOHandler::OnWriteCompleted(DWORD bytes_written,
+ DWORD error) {
+ DCHECK(!owner_ ||
+ base::MessageLoop::current() == owner_->message_loop_for_io());
+ DCHECK(suppress_self_destruct_);
+
+ if (!owner_) {
+ // No lock needed.
+ CHECK(pending_write_);
+ pending_write_ = false;
+ return;
+ }
+
+ {
+ base::AutoLock locker(owner_->write_lock());
+ CHECK(pending_write_);
+ pending_write_ = false;
+ }
+
+ if (error != ERROR_SUCCESS) {
+ LOG(ERROR) << "WriteFile: " << logging::SystemErrorCodeToString(error);
+ owner_->OnWriteCompleted(false, 0, 0);
+ } else {
+ owner_->OnWriteCompleted(true, 0, bytes_written);
+ }
+}
+
+RawChannelWin::RawChannelWin(embedder::ScopedPlatformHandle handle)
+ : handle_(handle.Pass()),
+ io_handler_(NULL),
+ skip_completion_port_on_success_(
+ g_vista_or_higher_functions.Get().is_vista_or_higher()) {
+ DCHECK(handle_.is_valid());
+}
+
+RawChannelWin::~RawChannelWin() {
+ DCHECK(!io_handler_);
+}
+
+size_t RawChannelWin::GetSerializedPlatformHandleSize() const {
+ // TODO(vtl): Implement.
+ return 0;
+}
+
+RawChannel::IOResult RawChannelWin::Read(size_t* bytes_read) {
+ DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+ DCHECK(io_handler_);
+ DCHECK(!io_handler_->pending_read());
+
+ char* buffer = NULL;
+ size_t bytes_to_read = 0;
+ read_buffer()->GetBuffer(&buffer, &bytes_to_read);
+
+ DWORD bytes_read_dword = 0;
+ BOOL result = ReadFile(io_handler_->handle(),
+ buffer,
+ static_cast<DWORD>(bytes_to_read),
+ &bytes_read_dword,
+ &io_handler_->read_context()->overlapped);
+ if (!result) {
+ DCHECK_EQ(bytes_read_dword, 0u);
+ DWORD error = GetLastError();
+ if (error != ERROR_IO_PENDING) {
+ LOG_IF(ERROR, error != ERROR_BROKEN_PIPE)
+ << "ReadFile: " << logging::SystemErrorCodeToString(error);
+ return IO_FAILED;
+ }
+ }
+
+ if (result && skip_completion_port_on_success_) {
+ *bytes_read = bytes_read_dword;
+ return IO_SUCCEEDED;
+ }
+
+ // If the read is pending or the read has succeeded but we don't skip
+ // completion port on success, instruct |io_handler_| to wait for the
+ // completion packet.
+ //
+ // TODO(yzshen): It seems there isn't document saying that all error cases
+ // (other than ERROR_IO_PENDING) are guaranteed to *not* queue a completion
+ // packet. If we do get one for errors, |RawChannelIOHandler::OnIOCompleted()|
+ // will crash so we will learn about it.
+
+ io_handler_->OnPendingReadStarted();
+ return IO_PENDING;
+}
+
+RawChannel::IOResult RawChannelWin::ScheduleRead() {
+ DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+ DCHECK(io_handler_);
+ DCHECK(!io_handler_->pending_read());
+
+ size_t bytes_read = 0;
+ IOResult io_result = Read(&bytes_read);
+ if (io_result == IO_SUCCEEDED) {
+ DCHECK(skip_completion_port_on_success_);
+
+ // We have finished reading successfully. Queue a notification manually.
+ io_handler_->OnPendingReadStarted();
+ // |io_handler_| won't go away before the task is run, so it is safe to use
+ // |base::Unretained()|.
+ message_loop_for_io()->PostTask(
+ FROM_HERE,
+ base::Bind(&RawChannelIOHandler::OnIOCompleted,
+ base::Unretained(io_handler_),
+ base::Unretained(io_handler_->read_context()),
+ static_cast<DWORD>(bytes_read),
+ ERROR_SUCCESS));
+ return IO_PENDING;
+ }
+
+ return io_result;
+}
+
+embedder::ScopedPlatformHandleVectorPtr RawChannelWin::GetReadPlatformHandles(
+ size_t num_platform_handles,
+ const void* platform_handle_table) {
+ // TODO(vtl): Implement.
+ NOTIMPLEMENTED();
+ return embedder::ScopedPlatformHandleVectorPtr();
+}
+
+RawChannel::IOResult RawChannelWin::WriteNoLock(
+ size_t* platform_handles_written,
+ size_t* bytes_written) {
+ write_lock().AssertAcquired();
+
+ DCHECK(io_handler_);
+ DCHECK(!io_handler_->pending_write_no_lock());
+
+ if (write_buffer_no_lock()->HavePlatformHandlesToSend()) {
+ // TODO(vtl): Implement.
+ NOTIMPLEMENTED();
+ }
+
+ std::vector<WriteBuffer::Buffer> buffers;
+ write_buffer_no_lock()->GetBuffers(&buffers);
+ DCHECK(!buffers.empty());
+
+ // TODO(yzshen): Handle multi-segment writes more efficiently.
+ DWORD bytes_written_dword = 0;
+ BOOL result = WriteFile(io_handler_->handle(),
+ buffers[0].addr,
+ static_cast<DWORD>(buffers[0].size),
+ &bytes_written_dword,
+ &io_handler_->write_context_no_lock()->overlapped);
+ if (!result && GetLastError() != ERROR_IO_PENDING) {
+ PLOG(ERROR) << "WriteFile";
+ return IO_FAILED;
+ }
+
+ if (result && skip_completion_port_on_success_) {
+ *platform_handles_written = 0;
+ *bytes_written = bytes_written_dword;
+ return IO_SUCCEEDED;
+ }
+
+ // If the write is pending or the write has succeeded but we don't skip
+ // completion port on success, instruct |io_handler_| to wait for the
+ // completion packet.
+ //
+ // TODO(yzshen): it seems there isn't document saying that all error cases
+ // (other than ERROR_IO_PENDING) are guaranteed to *not* queue a completion
+ // packet. If we do get one for errors, |RawChannelIOHandler::OnIOCompleted()|
+ // will crash so we will learn about it.
+
+ io_handler_->OnPendingWriteStartedNoLock();
+ return IO_PENDING;
+}
+
+RawChannel::IOResult RawChannelWin::ScheduleWriteNoLock() {
+ write_lock().AssertAcquired();
+
+ DCHECK(io_handler_);
+ DCHECK(!io_handler_->pending_write_no_lock());
+
+ // TODO(vtl): Do something with |platform_handles_written|.
+ size_t platform_handles_written = 0;
+ size_t bytes_written = 0;
+ IOResult io_result = WriteNoLock(&platform_handles_written, &bytes_written);
+ if (io_result == IO_SUCCEEDED) {
+ DCHECK(skip_completion_port_on_success_);
+
+ // We have finished writing successfully. Queue a notification manually.
+ io_handler_->OnPendingWriteStartedNoLock();
+ // |io_handler_| won't go away before that task is run, so it is safe to use
+ // |base::Unretained()|.
+ message_loop_for_io()->PostTask(
+ FROM_HERE,
+ base::Bind(&RawChannelIOHandler::OnIOCompleted,
+ base::Unretained(io_handler_),
+ base::Unretained(io_handler_->write_context_no_lock()),
+ static_cast<DWORD>(bytes_written),
+ ERROR_SUCCESS));
+ return IO_PENDING;
+ }
+
+ return io_result;
+}
+
+bool RawChannelWin::OnInit() {
+ DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+
+ DCHECK(handle_.is_valid());
+ if (skip_completion_port_on_success_ &&
+ !g_vista_or_higher_functions.Get().SetFileCompletionNotificationModes(
+ handle_.get().handle, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) {
+ return false;
+ }
+
+ DCHECK(!io_handler_);
+ io_handler_ = new RawChannelIOHandler(this, handle_.Pass());
+
+ return true;
+}
+
+void RawChannelWin::OnShutdownNoLock(scoped_ptr<ReadBuffer> read_buffer,
+ scoped_ptr<WriteBuffer> write_buffer) {
+ DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io());
+ DCHECK(io_handler_);
+
+ write_lock().AssertAcquired();
+
+ if (io_handler_->pending_read() || io_handler_->pending_write_no_lock()) {
+ // |io_handler_| will be alive until pending read/write (if any) completes.
+ // Call |CancelIoEx()| or |CancelIo()| so that resources can be freed up as
+ // soon as possible.
+ // Note: |CancelIo()| only cancels read/write requests started from this
+ // thread.
+ if (g_vista_or_higher_functions.Get().is_vista_or_higher())
+ g_vista_or_higher_functions.Get().CancelIoEx(io_handler_->handle(), NULL);
+ else
+ CancelIo(io_handler_->handle());
+ }
+
+ io_handler_->DetachFromOwnerNoLock(read_buffer.Pass(), write_buffer.Pass());
+ io_handler_ = NULL;
+}
+
+} // namespace
+
+// -----------------------------------------------------------------------------
+
+// Static factory method declared in raw_channel.h.
+// static
+scoped_ptr<RawChannel> RawChannel::Create(
+ embedder::ScopedPlatformHandle handle) {
+ return scoped_ptr<RawChannel>(new RawChannelWin(handle.Pass()));
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/raw_shared_buffer.cc b/chromium/mojo/system/raw_shared_buffer.cc
new file mode 100644
index 00000000000..e6e3ebce239
--- /dev/null
+++ b/chromium/mojo/system/raw_shared_buffer.cc
@@ -0,0 +1,86 @@
+// Copyright 2014 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 "mojo/system/raw_shared_buffer.h"
+
+#include "base/logging.h"
+#include "mojo/embedder/platform_handle_utils.h"
+
+namespace mojo {
+namespace system {
+
+// static
+RawSharedBuffer* RawSharedBuffer::Create(size_t num_bytes) {
+ DCHECK_GT(num_bytes, 0u);
+
+ RawSharedBuffer* rv = new RawSharedBuffer(num_bytes);
+ if (!rv->Init()) {
+ // We can't just delete it directly, due to the "in destructor" (debug)
+ // check.
+ scoped_refptr<RawSharedBuffer> deleter(rv);
+ return NULL;
+ }
+
+ return rv;
+}
+
+RawSharedBuffer* RawSharedBuffer::CreateFromPlatformHandle(
+ size_t num_bytes,
+ embedder::ScopedPlatformHandle platform_handle) {
+ DCHECK_GT(num_bytes, 0u);
+
+ RawSharedBuffer* rv = new RawSharedBuffer(num_bytes);
+ if (!rv->InitFromPlatformHandle(platform_handle.Pass())) {
+ // We can't just delete it directly, due to the "in destructor" (debug)
+ // check.
+ scoped_refptr<RawSharedBuffer> deleter(rv);
+ return NULL;
+ }
+
+ return rv;
+}
+
+scoped_ptr<RawSharedBufferMapping> RawSharedBuffer::Map(size_t offset,
+ size_t length) {
+ if (!IsValidMap(offset, length))
+ return scoped_ptr<RawSharedBufferMapping>();
+
+ return MapNoCheck(offset, length);
+}
+
+bool RawSharedBuffer::IsValidMap(size_t offset, size_t length) {
+ if (offset > num_bytes_ || length == 0)
+ return false;
+
+ // Note: This is an overflow-safe check of |offset + length > num_bytes_|
+ // (that |num_bytes >= offset| is verified above).
+ if (length > num_bytes_ - offset)
+ return false;
+
+ return true;
+}
+
+scoped_ptr<RawSharedBufferMapping> RawSharedBuffer::MapNoCheck(size_t offset,
+ size_t length) {
+ DCHECK(IsValidMap(offset, length));
+ return MapImpl(offset, length);
+}
+
+embedder::ScopedPlatformHandle RawSharedBuffer::DuplicatePlatformHandle() {
+ return embedder::DuplicatePlatformHandle(handle_.get());
+}
+
+embedder::ScopedPlatformHandle RawSharedBuffer::PassPlatformHandle() {
+ DCHECK(HasOneRef());
+ return handle_.Pass();
+}
+
+RawSharedBuffer::RawSharedBuffer(size_t num_bytes) : num_bytes_(num_bytes) {
+}
+
+RawSharedBuffer::~RawSharedBuffer() {
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/raw_shared_buffer.h b/chromium/mojo/system/raw_shared_buffer.h
new file mode 100644
index 00000000000..d38acb841c6
--- /dev/null
+++ b/chromium/mojo/system/raw_shared_buffer.h
@@ -0,0 +1,137 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SYSTEM_RAW_SHARED_BUFFER_H_
+#define MOJO_SYSTEM_RAW_SHARED_BUFFER_H_
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/embedder/scoped_platform_handle.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class RawSharedBufferMapping;
+
+// |RawSharedBuffer| is a thread-safe, ref-counted wrapper around OS-specific
+// shared memory. It has the following features:
+// - A |RawSharedBuffer| simply represents a piece of shared memory that *may*
+// be mapped and *may* be shared to another process.
+// - A single |RawSharedBuffer| may be mapped multiple times. The lifetime of
+// the mapping (owned by |RawSharedBufferMapping|) is separate from the
+// lifetime of the |RawSharedBuffer|.
+// - Sizes/offsets (of the shared memory and mappings) are arbitrary, and not
+// restricted by page size. However, more memory may actually be mapped than
+// requested.
+//
+// It currently does NOT support the following:
+// - Sharing read-only. (This will probably eventually be supported.)
+//
+// TODO(vtl): Rectify this with |base::SharedMemory|.
+class MOJO_SYSTEM_IMPL_EXPORT RawSharedBuffer
+ : public base::RefCountedThreadSafe<RawSharedBuffer> {
+ public:
+ // Creates a shared buffer of size |num_bytes| bytes (initially zero-filled).
+ // |num_bytes| must be nonzero. Returns null on failure.
+ static RawSharedBuffer* Create(size_t num_bytes);
+
+ static RawSharedBuffer* CreateFromPlatformHandle(
+ size_t num_bytes,
+ embedder::ScopedPlatformHandle platform_handle);
+
+ // Maps (some) of the shared buffer into memory; [|offset|, |offset + length|]
+ // must be contained in [0, |num_bytes|], and |length| must be at least 1.
+ // Returns null on failure.
+ scoped_ptr<RawSharedBufferMapping> Map(size_t offset, size_t length);
+
+ // Checks if |offset| and |length| are valid arguments.
+ bool IsValidMap(size_t offset, size_t length);
+
+ // Like |Map()|, but doesn't check its arguments (which should have been
+ // preflighted using |IsValidMap()|).
+ scoped_ptr<RawSharedBufferMapping> MapNoCheck(size_t offset, size_t length);
+
+ // Duplicates the underlying platform handle and passes it to the caller.
+ embedder::ScopedPlatformHandle DuplicatePlatformHandle();
+
+ // Passes the underlying platform handle to the caller. This should only be
+ // called if there's a unique reference to this object (owned by the caller).
+ // After calling this, this object should no longer be used, but should only
+ // be disposed of.
+ embedder::ScopedPlatformHandle PassPlatformHandle();
+
+ size_t num_bytes() const { return num_bytes_; }
+
+ private:
+ friend class base::RefCountedThreadSafe<RawSharedBuffer>;
+
+ explicit RawSharedBuffer(size_t num_bytes);
+ ~RawSharedBuffer();
+
+ // Implemented in raw_shared_buffer_{posix,win}.cc:
+
+ // This is called by |Create()| before this object is given to anyone.
+ bool Init();
+
+ // This is like |Init()|, but for |CreateFromPlatformHandle()|. (Note: It
+ // should verify that |platform_handle| is an appropriate handle for the
+ // claimed |num_bytes_|.)
+ bool InitFromPlatformHandle(embedder::ScopedPlatformHandle platform_handle);
+
+ // The platform-dependent part of |Map()|; doesn't check arguments.
+ scoped_ptr<RawSharedBufferMapping> MapImpl(size_t offset, size_t length);
+
+ const size_t num_bytes_;
+
+ // This is set in |Init()|/|InitFromPlatformHandle()| and never modified
+ // (except by |PassPlatformHandle()|; see the comments above its declaration),
+ // hence does not need to be protected by a lock.
+ embedder::ScopedPlatformHandle handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(RawSharedBuffer);
+};
+
+// A mapping of a |RawSharedBuffer| (compararable to a "file view" in Windows);
+// see above. Created by |RawSharedBuffer::Map()|. Automatically unmaps memory
+// on destruction.
+//
+// Mappings are NOT thread-safe.
+//
+// Note: This is an entirely separate class (instead of
+// |RawSharedBuffer::Mapping|) so that it can be forward-declared.
+class MOJO_SYSTEM_IMPL_EXPORT RawSharedBufferMapping {
+ public:
+ ~RawSharedBufferMapping() { Unmap(); }
+
+ void* base() const { return base_; }
+ size_t length() const { return length_; }
+
+ private:
+ friend class RawSharedBuffer;
+
+ RawSharedBufferMapping(void* base,
+ size_t length,
+ void* real_base,
+ size_t real_length)
+ : base_(base), length_(length),
+ real_base_(real_base), real_length_(real_length) {}
+ void Unmap();
+
+ void* const base_;
+ const size_t length_;
+
+ void* const real_base_;
+ const size_t real_length_;
+
+ DISALLOW_COPY_AND_ASSIGN(RawSharedBufferMapping);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_RAW_SHARED_BUFFER_H_
diff --git a/chromium/mojo/system/raw_shared_buffer_posix.cc b/chromium/mojo/system/raw_shared_buffer_posix.cc
new file mode 100644
index 00000000000..5c98737efdc
--- /dev/null
+++ b/chromium/mojo/system/raw_shared_buffer_posix.cc
@@ -0,0 +1,151 @@
+// Copyright 2014 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 "mojo/system/raw_shared_buffer.h"
+
+#include <stdint.h>
+#include <stdio.h> // For |fileno()|.
+#include <sys/mman.h> // For |mmap()|/|munmap()|.
+#include <sys/stat.h>
+#include <sys/types.h> // For |off_t|.
+#include <unistd.h>
+
+#include <limits>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/sys_info.h"
+#include "base/threading/thread_restrictions.h"
+#include "mojo/embedder/platform_handle.h"
+
+// We assume that |size_t| and |off_t| (type for |ftruncate()|) fits in a
+// |uint64_t|.
+COMPILE_ASSERT(sizeof(size_t) <= sizeof(uint64_t), size_t_too_big);
+COMPILE_ASSERT(sizeof(off_t) <= sizeof(uint64_t), off_t_too_big);
+
+namespace mojo {
+namespace system {
+
+// RawSharedBuffer -------------------------------------------------------------
+
+bool RawSharedBuffer::Init() {
+ DCHECK(!handle_.is_valid());
+
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+
+ if (static_cast<uint64_t>(num_bytes_) >
+ static_cast<uint64_t>(std::numeric_limits<off_t>::max())) {
+ return false;
+ }
+
+ // TODO(vtl): This is stupid. The implementation of
+ // |CreateAndOpenTemporaryFileInDir()| starts with an FD, |fdopen()|s to get a
+ // |FILE*|, and then we have to |dup(fileno(fp))| to get back to an FD that we
+ // can own. (base/memory/shared_memory_posix.cc does this too, with more
+ // |fstat()|s thrown in for good measure.)
+ base::FilePath shared_buffer_dir;
+ if (!base::GetShmemTempDir(false, &shared_buffer_dir)) {
+ LOG(ERROR) << "Failed to get temporary directory for shared memory";
+ return false;
+ }
+ base::FilePath shared_buffer_file;
+ base::ScopedFILE fp(base::CreateAndOpenTemporaryFileInDir(
+ shared_buffer_dir, &shared_buffer_file));
+ if (!fp) {
+ LOG(ERROR) << "Failed to create/open temporary file for shared memory";
+ return false;
+ }
+ // Note: |unlink()| is not interruptible.
+ if (unlink(shared_buffer_file.value().c_str()) != 0) {
+ PLOG(WARNING) << "unlink";
+ // This isn't "fatal" (e.g., someone else may have unlinked the file first),
+ // so we may as well continue.
+ }
+
+ // Note: |dup()| is not interruptible (but |dup2()|/|dup3()| are).
+ base::ScopedFD fd(dup(fileno(fp.get())));
+ if (!fd.is_valid()) {
+ PLOG(ERROR) << "dup";
+ return false;
+ }
+
+ if (HANDLE_EINTR(ftruncate(fd.get(), static_cast<off_t>(num_bytes_))) != 0) {
+ PLOG(ERROR) << "ftruncate";
+ return false;
+ }
+
+ handle_.reset(embedder::PlatformHandle(fd.release()));
+ return true;
+}
+
+bool RawSharedBuffer::InitFromPlatformHandle(
+ embedder::ScopedPlatformHandle platform_handle) {
+ DCHECK(!handle_.is_valid());
+
+ if (static_cast<uint64_t>(num_bytes_) >
+ static_cast<uint64_t>(std::numeric_limits<off_t>::max())) {
+ return false;
+ }
+
+ struct stat sb = {};
+ // Note: |fstat()| isn't interruptible.
+ if (fstat(platform_handle.get().fd, &sb) != 0) {
+ PLOG(ERROR) << "fstat";
+ return false;
+ }
+
+ if (!S_ISREG(sb.st_mode)) {
+ LOG(ERROR) << "Platform handle not to a regular file";
+ return false;
+ }
+
+ if (sb.st_size != static_cast<off_t>(num_bytes_)) {
+ LOG(ERROR) << "Shared memory file has the wrong size";
+ return false;
+ }
+
+ // TODO(vtl): More checks?
+
+ handle_ = platform_handle.Pass();
+ return true;
+}
+
+scoped_ptr<RawSharedBufferMapping> RawSharedBuffer::MapImpl(size_t offset,
+ size_t length) {
+ size_t offset_rounding = offset % base::SysInfo::VMAllocationGranularity();
+ size_t real_offset = offset - offset_rounding;
+ size_t real_length = length + offset_rounding;
+
+ // This should hold (since we checked |num_bytes| versus the maximum value of
+ // |off_t| on creation, but it never hurts to be paranoid.
+ DCHECK_LE(static_cast<uint64_t>(real_offset),
+ static_cast<uint64_t>(std::numeric_limits<off_t>::max()));
+
+ void* real_base = mmap(NULL, real_length, PROT_READ | PROT_WRITE, MAP_SHARED,
+ handle_.get().fd, static_cast<off_t>(real_offset));
+ // |mmap()| should return |MAP_FAILED| (a.k.a. -1) on error. But it shouldn't
+ // return null either.
+ if (real_base == MAP_FAILED || !real_base) {
+ PLOG(ERROR) << "mmap";
+ return scoped_ptr<RawSharedBufferMapping>();
+ }
+
+ void* base = static_cast<char*>(real_base) + offset_rounding;
+ return make_scoped_ptr(
+ new RawSharedBufferMapping(base, length, real_base, real_length));
+}
+
+// RawSharedBufferMapping ------------------------------------------------------
+
+void RawSharedBufferMapping::Unmap() {
+ int result = munmap(real_base_, real_length_);
+ PLOG_IF(ERROR, result != 0) << "munmap";
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/raw_shared_buffer_unittest.cc b/chromium/mojo/system/raw_shared_buffer_unittest.cc
new file mode 100644
index 00000000000..07da4f90a5e
--- /dev/null
+++ b/chromium/mojo/system/raw_shared_buffer_unittest.cc
@@ -0,0 +1,181 @@
+// Copyright 2014 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 "mojo/system/raw_shared_buffer.h"
+
+#include <limits>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+TEST(RawSharedBufferTest, Basic) {
+ const size_t kNumInts = 100;
+ const size_t kNumBytes = kNumInts * sizeof(int);
+ // A fudge so that we're not just writing zero bytes 75% of the time.
+ const int kFudge = 1234567890;
+
+ // Make some memory.
+ scoped_refptr<RawSharedBuffer> buffer(RawSharedBuffer::Create(kNumBytes));
+ ASSERT_TRUE(buffer);
+
+ // Map it all, scribble some stuff, and then unmap it.
+ {
+ EXPECT_TRUE(buffer->IsValidMap(0, kNumBytes));
+ scoped_ptr<RawSharedBufferMapping> mapping(buffer->Map(0, kNumBytes));
+ ASSERT_TRUE(mapping);
+ ASSERT_TRUE(mapping->base());
+ int* stuff = static_cast<int*>(mapping->base());
+ for (size_t i = 0; i < kNumInts; i++)
+ stuff[i] = static_cast<int>(i) + kFudge;
+ }
+
+ // Map it all again, check that our scribbling is still there, then do a
+ // partial mapping and scribble on that, check that everything is coherent,
+ // unmap the first mapping, scribble on some of the second mapping, and then
+ // unmap it.
+ {
+ ASSERT_TRUE(buffer->IsValidMap(0, kNumBytes));
+ // Use |MapNoCheck()| this time.
+ scoped_ptr<RawSharedBufferMapping> mapping1(
+ buffer->MapNoCheck(0, kNumBytes));
+ ASSERT_TRUE(mapping1);
+ ASSERT_TRUE(mapping1->base());
+ int* stuff1 = static_cast<int*>(mapping1->base());
+ for (size_t i = 0; i < kNumInts; i++)
+ EXPECT_EQ(static_cast<int>(i) + kFudge, stuff1[i]) << i;
+
+ scoped_ptr<RawSharedBufferMapping> mapping2(
+ buffer->Map((kNumInts / 2) * sizeof(int), 2 * sizeof(int)));
+ ASSERT_TRUE(mapping2);
+ ASSERT_TRUE(mapping2->base());
+ int* stuff2 = static_cast<int*>(mapping2->base());
+ EXPECT_EQ(static_cast<int>(kNumInts / 2) + kFudge, stuff2[0]);
+ EXPECT_EQ(static_cast<int>(kNumInts / 2) + 1 + kFudge, stuff2[1]);
+
+ stuff2[0] = 123;
+ stuff2[1] = 456;
+ EXPECT_EQ(123, stuff1[kNumInts / 2]);
+ EXPECT_EQ(456, stuff1[kNumInts / 2 + 1]);
+
+ mapping1.reset();
+
+ EXPECT_EQ(123, stuff2[0]);
+ EXPECT_EQ(456, stuff2[1]);
+ stuff2[1] = 789;
+ }
+
+ // Do another partial mapping and check that everything is the way we expect
+ // it to be.
+ {
+ EXPECT_TRUE(buffer->IsValidMap(sizeof(int), kNumBytes - sizeof(int)));
+ scoped_ptr<RawSharedBufferMapping> mapping(
+ buffer->Map(sizeof(int), kNumBytes - sizeof(int)));
+ ASSERT_TRUE(mapping);
+ ASSERT_TRUE(mapping->base());
+ int* stuff = static_cast<int*>(mapping->base());
+
+ for (size_t j = 0; j < kNumInts - 1; j++) {
+ int i = static_cast<int>(j) + 1;
+ if (i == kNumInts / 2) {
+ EXPECT_EQ(123, stuff[j]);
+ } else if (i == kNumInts / 2 + 1) {
+ EXPECT_EQ(789, stuff[j]);
+ } else {
+ EXPECT_EQ(i + kFudge, stuff[j]) << i;
+ }
+ }
+ }
+}
+
+// TODO(vtl): Bigger buffers.
+
+TEST(RawSharedBufferTest, InvalidMappings) {
+ scoped_refptr<RawSharedBuffer> buffer(RawSharedBuffer::Create(100));
+ ASSERT_TRUE(buffer);
+
+ // Zero length not allowed.
+ EXPECT_FALSE(buffer->Map(0, 0));
+ EXPECT_FALSE(buffer->IsValidMap(0, 0));
+
+ // Okay:
+ EXPECT_TRUE(buffer->Map(0, 100));
+ EXPECT_TRUE(buffer->IsValidMap(0, 100));
+ // Offset + length too big.
+ EXPECT_FALSE(buffer->Map(0, 101));
+ EXPECT_FALSE(buffer->IsValidMap(0, 101));
+ EXPECT_FALSE(buffer->Map(1, 100));
+ EXPECT_FALSE(buffer->IsValidMap(1, 100));
+
+ // Okay:
+ EXPECT_TRUE(buffer->Map(50, 50));
+ EXPECT_TRUE(buffer->IsValidMap(50, 50));
+ // Offset + length too big.
+ EXPECT_FALSE(buffer->Map(50, 51));
+ EXPECT_FALSE(buffer->IsValidMap(50, 51));
+ EXPECT_FALSE(buffer->Map(51, 50));
+ EXPECT_FALSE(buffer->IsValidMap(51, 50));
+}
+
+TEST(RawSharedBufferTest, TooBig) {
+ // If |size_t| is 32-bit, it's quite possible/likely that |Create()| succeeds
+ // (since it only involves creating a 4 GB file).
+ const size_t kMaxSizeT = std::numeric_limits<size_t>::max();
+ scoped_refptr<RawSharedBuffer> buffer(RawSharedBuffer::Create(kMaxSizeT));
+ // But, assuming |sizeof(size_t) == sizeof(void*)|, mapping all of it should
+ // always fail.
+ if (buffer)
+ EXPECT_FALSE(buffer->Map(0, kMaxSizeT));
+}
+
+// Tests that separate mappings get distinct addresses.
+// Note: It's not inconceivable that the OS could ref-count identical mappings
+// and reuse the same address, in which case we'd have to be more careful about
+// using the address as the key for unmapping.
+TEST(RawSharedBufferTest, MappingsDistinct) {
+ scoped_refptr<RawSharedBuffer> buffer(RawSharedBuffer::Create(100));
+ scoped_ptr<RawSharedBufferMapping> mapping1(buffer->Map(0, 100));
+ scoped_ptr<RawSharedBufferMapping> mapping2(buffer->Map(0, 100));
+ EXPECT_NE(mapping1->base(), mapping2->base());
+}
+
+TEST(RawSharedBufferTest, BufferZeroInitialized) {
+ static const size_t kSizes[] = { 10, 100, 1000, 10000, 100000 };
+ for (size_t i = 0; i < arraysize(kSizes); i++) {
+ scoped_refptr<RawSharedBuffer> buffer(RawSharedBuffer::Create(kSizes[i]));
+ scoped_ptr<RawSharedBufferMapping> mapping(buffer->Map(0, kSizes[i]));
+ for (size_t j = 0; j < kSizes[i]; j++) {
+ // "Assert" instead of "expect" so we don't spam the output with thousands
+ // of failures if we fail.
+ ASSERT_EQ('\0', static_cast<char*>(mapping->base())[j])
+ << "size " << kSizes[i] << ", offset " << j;
+ }
+ }
+}
+
+TEST(RawSharedBufferTest, MappingsOutliveBuffer) {
+ scoped_ptr<RawSharedBufferMapping> mapping1;
+ scoped_ptr<RawSharedBufferMapping> mapping2;
+
+ {
+ scoped_refptr<RawSharedBuffer> buffer(RawSharedBuffer::Create(100));
+ mapping1 = buffer->Map(0, 100).Pass();
+ mapping2 = buffer->Map(50, 50).Pass();
+ static_cast<char*>(mapping1->base())[50] = 'x';
+ }
+
+ EXPECT_EQ('x', static_cast<char*>(mapping2->base())[0]);
+
+ static_cast<char*>(mapping2->base())[1] = 'y';
+ EXPECT_EQ('y', static_cast<char*>(mapping1->base())[51]);
+}
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/raw_shared_buffer_win.cc b/chromium/mojo/system/raw_shared_buffer_win.cc
new file mode 100644
index 00000000000..fdccd363cb5
--- /dev/null
+++ b/chromium/mojo/system/raw_shared_buffer_win.cc
@@ -0,0 +1,88 @@
+// Copyright 2014 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 "mojo/system/raw_shared_buffer.h"
+
+#include <windows.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/sys_info.h"
+#include "mojo/embedder/platform_handle.h"
+#include "mojo/embedder/scoped_platform_handle.h"
+
+namespace mojo {
+namespace system {
+
+// RawSharedBuffer -------------------------------------------------------------
+
+bool RawSharedBuffer::Init() {
+ DCHECK(!handle_.is_valid());
+
+ // TODO(vtl): Currently, we only support mapping up to 2^32-1 bytes.
+ if (static_cast<uint64_t>(num_bytes_) >
+ static_cast<uint64_t>(std::numeric_limits<DWORD>::max())) {
+ return false;
+ }
+
+ // IMPORTANT NOTE: Unnamed objects are NOT SECURABLE. Thus if we ever want to
+ // share read-only to other processes, we'll have to name our file mapping
+ // object.
+ // TODO(vtl): Unlike |base::SharedMemory|, we don't round up the size (to a
+ // multiple of 64 KB). This may cause problems with NaCl. Cross this bridge
+ // when we get there. crbug.com/210609
+ handle_.reset(embedder::PlatformHandle(CreateFileMapping(
+ INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
+ static_cast<DWORD>(num_bytes_), NULL)));
+ if (!handle_.is_valid()) {
+ PLOG(ERROR) << "CreateFileMapping";
+ return false;
+ }
+
+ return true;
+}
+
+bool RawSharedBuffer::InitFromPlatformHandle(
+ embedder::ScopedPlatformHandle platform_handle) {
+ DCHECK(!handle_.is_valid());
+
+ // TODO(vtl): Implement.
+ NOTIMPLEMENTED();
+ return false;
+}
+
+scoped_ptr<RawSharedBufferMapping> RawSharedBuffer::MapImpl(size_t offset,
+ size_t length) {
+ size_t offset_rounding = offset % base::SysInfo::VMAllocationGranularity();
+ size_t real_offset = offset - offset_rounding;
+ size_t real_length = length + offset_rounding;
+
+ // This should hold (since we checked |num_bytes| versus the maximum value of
+ // |off_t| on creation, but it never hurts to be paranoid.
+ DCHECK_LE(static_cast<uint64_t>(real_offset),
+ static_cast<uint64_t>(std::numeric_limits<DWORD>::max()));
+
+ void* real_base = MapViewOfFile(
+ handle_.get().handle, FILE_MAP_READ | FILE_MAP_WRITE, 0,
+ static_cast<DWORD>(real_offset), real_length);
+ if (!real_base) {
+ PLOG(ERROR) << "MapViewOfFile";
+ return scoped_ptr<RawSharedBufferMapping>();
+ }
+
+ void* base = static_cast<char*>(real_base) + offset_rounding;
+ return make_scoped_ptr(
+ new RawSharedBufferMapping(base, length, real_base, real_length));
+}
+
+// RawSharedBufferMapping ------------------------------------------------------
+
+void RawSharedBufferMapping::Unmap() {
+ BOOL result = UnmapViewOfFile(real_base_);
+ PLOG_IF(ERROR, !result) << "UnmapViewOfFile";
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/remote_message_pipe_unittest.cc b/chromium/mojo/system/remote_message_pipe_unittest.cc
new file mode 100644
index 00000000000..e1f55579171
--- /dev/null
+++ b/chromium/mojo/system/remote_message_pipe_unittest.cc
@@ -0,0 +1,843 @@
+// Copyright 2014 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 <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/platform_thread.h" // For |Sleep()|.
+#include "build/build_config.h" // TODO(vtl): Remove this.
+#include "mojo/common/test/test_utils.h"
+#include "mojo/embedder/platform_channel_pair.h"
+#include "mojo/embedder/scoped_platform_handle.h"
+#include "mojo/system/channel.h"
+#include "mojo/system/local_message_pipe_endpoint.h"
+#include "mojo/system/message_pipe.h"
+#include "mojo/system/message_pipe_dispatcher.h"
+#include "mojo/system/platform_handle_dispatcher.h"
+#include "mojo/system/proxy_message_pipe_endpoint.h"
+#include "mojo/system/raw_channel.h"
+#include "mojo/system/shared_buffer_dispatcher.h"
+#include "mojo/system/test_utils.h"
+#include "mojo/system/waiter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+class RemoteMessagePipeTest : public testing::Test {
+ public:
+ RemoteMessagePipeTest() : io_thread_(test::TestIOThread::kAutoStart) {}
+ virtual ~RemoteMessagePipeTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ io_thread_.PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&RemoteMessagePipeTest::SetUpOnIOThread,
+ base::Unretained(this)));
+ }
+
+ virtual void TearDown() OVERRIDE {
+ io_thread_.PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&RemoteMessagePipeTest::TearDownOnIOThread,
+ base::Unretained(this)));
+ }
+
+ protected:
+ // This connects MP 0, port 1 and MP 1, port 0 (leaving MP 0, port 0 and MP 1,
+ // port 1 as the user-visible endpoints) to channel 0 and 1, respectively. MP
+ // 0, port 1 and MP 1, port 0 must have |ProxyMessagePipeEndpoint|s.
+ void ConnectMessagePipes(scoped_refptr<MessagePipe> mp0,
+ scoped_refptr<MessagePipe> mp1) {
+ io_thread_.PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&RemoteMessagePipeTest::ConnectMessagePipesOnIOThread,
+ base::Unretained(this), mp0, mp1));
+ }
+
+ // This connects |mp|'s port |channel_index ^ 1| to channel |channel_index|.
+ // It assumes/requires that this is the bootstrap case, i.e., that the
+ // endpoint IDs are both/will both be |Channel::kBootstrapEndpointId|. This
+ // returns *without* waiting for it to finish connecting.
+ void BootstrapMessagePipeNoWait(unsigned channel_index,
+ scoped_refptr<MessagePipe> mp) {
+ io_thread_.PostTask(
+ FROM_HERE,
+ base::Bind(&RemoteMessagePipeTest::BootstrapMessagePipeOnIOThread,
+ base::Unretained(this), channel_index, mp));
+ }
+
+ void RestoreInitialState() {
+ io_thread_.PostTaskAndWait(
+ FROM_HERE,
+ base::Bind(&RemoteMessagePipeTest::RestoreInitialStateOnIOThread,
+ base::Unretained(this)));
+ }
+
+ test::TestIOThread* io_thread() { return &io_thread_; }
+
+ private:
+ void SetUpOnIOThread() {
+ CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+
+ embedder::PlatformChannelPair channel_pair;
+ platform_handles_[0] = channel_pair.PassServerHandle();
+ platform_handles_[1] = channel_pair.PassClientHandle();
+ }
+
+ void TearDownOnIOThread() {
+ CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+
+ if (channels_[0]) {
+ channels_[0]->Shutdown();
+ channels_[0] = NULL;
+ }
+ if (channels_[1]) {
+ channels_[1]->Shutdown();
+ channels_[1] = NULL;
+ }
+ }
+
+ void CreateAndInitChannel(unsigned channel_index) {
+ CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+ CHECK(channel_index == 0 || channel_index == 1);
+ CHECK(!channels_[channel_index]);
+
+ channels_[channel_index] = new Channel();
+ CHECK(channels_[channel_index]->Init(
+ RawChannel::Create(platform_handles_[channel_index].Pass())));
+ }
+
+ void ConnectMessagePipesOnIOThread(scoped_refptr<MessagePipe> mp0,
+ scoped_refptr<MessagePipe> mp1) {
+ CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+
+ if (!channels_[0])
+ CreateAndInitChannel(0);
+ if (!channels_[1])
+ CreateAndInitChannel(1);
+
+ MessageInTransit::EndpointId local_id0 =
+ channels_[0]->AttachMessagePipeEndpoint(mp0, 1);
+ MessageInTransit::EndpointId local_id1 =
+ channels_[1]->AttachMessagePipeEndpoint(mp1, 0);
+
+ CHECK(channels_[0]->RunMessagePipeEndpoint(local_id0, local_id1));
+ CHECK(channels_[1]->RunMessagePipeEndpoint(local_id1, local_id0));
+ }
+
+ void BootstrapMessagePipeOnIOThread(unsigned channel_index,
+ scoped_refptr<MessagePipe> mp) {
+ CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+ CHECK(channel_index == 0 || channel_index == 1);
+
+ unsigned port = channel_index ^ 1u;
+
+ CreateAndInitChannel(channel_index);
+ MessageInTransit::EndpointId endpoint_id =
+ channels_[channel_index]->AttachMessagePipeEndpoint(mp, port);
+ if (endpoint_id == MessageInTransit::kInvalidEndpointId)
+ return;
+
+ CHECK_EQ(endpoint_id, Channel::kBootstrapEndpointId);
+ CHECK(channels_[channel_index]->RunMessagePipeEndpoint(
+ Channel::kBootstrapEndpointId, Channel::kBootstrapEndpointId));
+ }
+
+ void RestoreInitialStateOnIOThread() {
+ CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
+
+ TearDownOnIOThread();
+ SetUpOnIOThread();
+ }
+
+ test::TestIOThread io_thread_;
+ embedder::ScopedPlatformHandle platform_handles_[2];
+ scoped_refptr<Channel> channels_[2];
+
+ DISALLOW_COPY_AND_ASSIGN(RemoteMessagePipeTest);
+};
+
+TEST_F(RemoteMessagePipeTest, Basic) {
+ static const char kHello[] = "hello";
+ static const char kWorld[] = "world!!!1!!!1!";
+ char buffer[100] = { 0 };
+ uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ Waiter waiter;
+ uint32_t context = 0;
+
+ // Connect message pipes. MP 0, port 1 will be attached to channel 0 and
+ // connected to MP 1, port 0, which will be attached to channel 1. This leaves
+ // MP 0, port 0 and MP 1, port 1 as the "user-facing" endpoints.
+
+ scoped_refptr<MessagePipe> mp0(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+ scoped_refptr<MessagePipe> mp1(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint())));
+ ConnectMessagePipes(mp0, mp1);
+
+ // Write in one direction: MP 0, port 0 -> ... -> MP 1, port 1.
+
+ // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do
+ // it later, it might already be readable.)
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp1->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123));
+
+ // Write to MP 0, port 0.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp0->WriteMessage(0,
+ kHello, sizeof(kHello),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Wait.
+ EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_EQ(123u, context);
+ mp1->RemoveWaiter(1, &waiter);
+
+ // Read from MP 1, port 1.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp1->ReadMessage(1,
+ buffer, &buffer_size,
+ NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(sizeof(kHello), static_cast<size_t>(buffer_size));
+ EXPECT_STREQ(kHello, buffer);
+
+ // Write in the other direction: MP 1, port 1 -> ... -> MP 0, port 0.
+
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp0->AddWaiter(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 456));
+
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp1->WriteMessage(1,
+ kWorld, sizeof(kWorld),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_EQ(456u, context);
+ mp0->RemoveWaiter(0, &waiter);
+
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp0->ReadMessage(0,
+ buffer, &buffer_size,
+ NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(sizeof(kWorld), static_cast<size_t>(buffer_size));
+ EXPECT_STREQ(kWorld, buffer);
+
+ // Close MP 0, port 0.
+ mp0->Close(0);
+
+ // Try to wait for MP 1, port 1 to become readable. This will eventually fail
+ // when it realizes that MP 0, port 0 has been closed. (It may also fail
+ // immediately.)
+ waiter.Init();
+ MojoResult result =
+ mp1->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 789);
+ if (result == MOJO_RESULT_OK) {
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_EQ(789u, context);
+ mp1->RemoveWaiter(1, &waiter);
+ } else {
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+ }
+
+ // And MP 1, port 1.
+ mp1->Close(1);
+}
+
+TEST_F(RemoteMessagePipeTest, Multiplex) {
+ static const char kHello[] = "hello";
+ static const char kWorld[] = "world!!!1!!!1!";
+ char buffer[100] = { 0 };
+ uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ Waiter waiter;
+ uint32_t context = 0;
+
+ // Connect message pipes as in the |Basic| test.
+
+ scoped_refptr<MessagePipe> mp0(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+ scoped_refptr<MessagePipe> mp1(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint())));
+ ConnectMessagePipes(mp0, mp1);
+
+ // Now put another message pipe on the channel.
+
+ scoped_refptr<MessagePipe> mp2(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+ scoped_refptr<MessagePipe> mp3(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint())));
+ ConnectMessagePipes(mp2, mp3);
+
+ // Write: MP 2, port 0 -> MP 3, port 1.
+
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp3->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 789));
+
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp2->WriteMessage(0,
+ kHello, sizeof(kHello),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_EQ(789u, context);
+ mp3->RemoveWaiter(1, &waiter);
+
+ // Make sure there's nothing on MP 0, port 0 or MP 1, port 1 or MP 2, port 0.
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ mp0->ReadMessage(0,
+ buffer, &buffer_size,
+ NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ mp1->ReadMessage(1,
+ buffer, &buffer_size,
+ NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ mp2->ReadMessage(0,
+ buffer, &buffer_size,
+ NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+
+ // Read from MP 3, port 1.
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp3->ReadMessage(1,
+ buffer, &buffer_size,
+ NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(sizeof(kHello), static_cast<size_t>(buffer_size));
+ EXPECT_STREQ(kHello, buffer);
+
+ // Write: MP 0, port 0 -> MP 1, port 1 again.
+
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp1->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123));
+
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp0->WriteMessage(0,
+ kWorld, sizeof(kWorld),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_EQ(123u, context);
+ mp1->RemoveWaiter(1, &waiter);
+
+ // Make sure there's nothing on the other ports.
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ mp0->ReadMessage(0,
+ buffer, &buffer_size,
+ NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ mp2->ReadMessage(0,
+ buffer, &buffer_size,
+ NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT,
+ mp3->ReadMessage(1,
+ buffer, &buffer_size,
+ NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+
+ buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp1->ReadMessage(1,
+ buffer, &buffer_size,
+ NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(sizeof(kWorld), static_cast<size_t>(buffer_size));
+ EXPECT_STREQ(kWorld, buffer);
+
+ mp0->Close(0);
+ mp1->Close(1);
+ mp2->Close(0);
+ mp3->Close(1);
+}
+
+TEST_F(RemoteMessagePipeTest, CloseBeforeConnect) {
+ static const char kHello[] = "hello";
+ char buffer[100] = { 0 };
+ uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ Waiter waiter;
+ uint32_t context = 0;
+
+ // Connect message pipes. MP 0, port 1 will be attached to channel 0 and
+ // connected to MP 1, port 0, which will be attached to channel 1. This leaves
+ // MP 0, port 0 and MP 1, port 1 as the "user-facing" endpoints.
+
+ scoped_refptr<MessagePipe> mp0(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+
+ // Write to MP 0, port 0.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp0->WriteMessage(0,
+ kHello, sizeof(kHello),
+ NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ BootstrapMessagePipeNoWait(0, mp0);
+
+
+ // Close MP 0, port 0 before channel 1 is even connected.
+ mp0->Close(0);
+
+ scoped_refptr<MessagePipe> mp1(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint())));
+
+ // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do
+ // it later, it might already be readable.)
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp1->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123));
+
+ BootstrapMessagePipeNoWait(1, mp1);
+
+ // Wait.
+ EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_EQ(123u, context);
+ mp1->RemoveWaiter(1, &waiter);
+
+ // Read from MP 1, port 1.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp1->ReadMessage(1,
+ buffer, &buffer_size,
+ NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(sizeof(kHello), static_cast<size_t>(buffer_size));
+ EXPECT_STREQ(kHello, buffer);
+
+ // And MP 1, port 1.
+ mp1->Close(1);
+}
+
+TEST_F(RemoteMessagePipeTest, HandlePassing) {
+ static const char kHello[] = "hello";
+ Waiter waiter;
+ uint32_t context = 0;
+
+ scoped_refptr<MessagePipe> mp0(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+ scoped_refptr<MessagePipe> mp1(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint())));
+ ConnectMessagePipes(mp0, mp1);
+
+ // We'll try to pass this dispatcher.
+ scoped_refptr<MessagePipeDispatcher> dispatcher(new MessagePipeDispatcher(
+ MessagePipeDispatcher::kDefaultCreateOptions));
+ scoped_refptr<MessagePipe> local_mp(new MessagePipe());
+ dispatcher->Init(local_mp, 0);
+
+ // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do
+ // it later, it might already be readable.)
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp1->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123));
+
+ // Write to MP 0, port 0.
+ {
+ DispatcherTransport
+ transport(test::DispatcherTryStartTransport(dispatcher.get()));
+ EXPECT_TRUE(transport.is_valid());
+
+ std::vector<DispatcherTransport> transports;
+ transports.push_back(transport);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp0->WriteMessage(0, kHello, sizeof(kHello), &transports,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ transport.End();
+
+ // |dispatcher| should have been closed. This is |DCHECK()|ed when the
+ // |dispatcher| is destroyed.
+ EXPECT_TRUE(dispatcher->HasOneRef());
+ dispatcher = NULL;
+ }
+
+ // Wait.
+ EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_EQ(123u, context);
+ mp1->RemoveWaiter(1, &waiter);
+
+ // Read from MP 1, port 1.
+ char read_buffer[100] = { 0 };
+ uint32_t read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer));
+ DispatcherVector read_dispatchers;
+ uint32_t read_num_dispatchers = 10; // Maximum to get.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp1->ReadMessage(1, read_buffer, &read_buffer_size,
+ &read_dispatchers, &read_num_dispatchers,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size));
+ EXPECT_STREQ(kHello, read_buffer);
+ EXPECT_EQ(1u, read_dispatchers.size());
+ EXPECT_EQ(1u, read_num_dispatchers);
+ ASSERT_TRUE(read_dispatchers[0]);
+ EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
+
+ EXPECT_EQ(Dispatcher::kTypeMessagePipe, read_dispatchers[0]->GetType());
+ dispatcher = static_cast<MessagePipeDispatcher*>(read_dispatchers[0].get());
+
+ // Write to "local_mp", port 1.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ local_mp->WriteMessage(1, kHello, sizeof(kHello), NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // TODO(vtl): FIXME -- We (racily) crash if I close |dispatcher| immediately
+ // here. (We don't crash if I sleep and then close.)
+
+ // Wait for the dispatcher to become readable.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dispatcher->AddWaiter(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 456));
+ EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_EQ(456u, context);
+ dispatcher->RemoveWaiter(&waiter);
+
+ // Read from the dispatcher.
+ memset(read_buffer, 0, sizeof(read_buffer));
+ read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dispatcher->ReadMessage(read_buffer, &read_buffer_size, 0, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size));
+ EXPECT_STREQ(kHello, read_buffer);
+
+ // Prepare to wait on "local_mp", port 1.
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ local_mp->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 789));
+
+ // Write to the dispatcher.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dispatcher->WriteMessage(kHello, sizeof(kHello), NULL,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+
+ // Wait.
+ EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_EQ(789u, context);
+ local_mp->RemoveWaiter(1, &waiter);
+
+ // Read from "local_mp", port 1.
+ memset(read_buffer, 0, sizeof(read_buffer));
+ read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ local_mp->ReadMessage(1, read_buffer, &read_buffer_size, NULL, NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size));
+ EXPECT_STREQ(kHello, read_buffer);
+
+ // TODO(vtl): Also test that messages queued up before the handle was sent are
+ // delivered properly.
+
+ // Close everything that belongs to us.
+ mp0->Close(0);
+ mp1->Close(1);
+ EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
+ // Note that |local_mp|'s port 0 belong to |dispatcher|, which was closed.
+ local_mp->Close(1);
+}
+
+#if defined(OS_POSIX)
+#define MAYBE_SharedBufferPassing SharedBufferPassing
+#else
+// Not yet implemented (on Windows).
+#define MAYBE_SharedBufferPassing DISABLED_SharedBufferPassing
+#endif
+TEST_F(RemoteMessagePipeTest, MAYBE_SharedBufferPassing) {
+ static const char kHello[] = "hello";
+ Waiter waiter;
+ uint32_t context = 0;
+
+ scoped_refptr<MessagePipe> mp0(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+ scoped_refptr<MessagePipe> mp1(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint())));
+ ConnectMessagePipes(mp0, mp1);
+
+ // We'll try to pass this dispatcher.
+ scoped_refptr<SharedBufferDispatcher> dispatcher;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ SharedBufferDispatcher::Create(
+ SharedBufferDispatcher::kDefaultCreateOptions, 100,
+ &dispatcher));
+ ASSERT_TRUE(dispatcher);
+
+ // Make a mapping.
+ scoped_ptr<RawSharedBufferMapping> mapping0;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE,
+ &mapping0));
+ ASSERT_TRUE(mapping0);
+ ASSERT_TRUE(mapping0->base());
+ ASSERT_EQ(100u, mapping0->length());
+ static_cast<char*>(mapping0->base())[0] = 'A';
+ static_cast<char*>(mapping0->base())[50] = 'B';
+ static_cast<char*>(mapping0->base())[99] = 'C';
+
+ // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do
+ // it later, it might already be readable.)
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp1->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123));
+
+ // Write to MP 0, port 0.
+ {
+ DispatcherTransport
+ transport(test::DispatcherTryStartTransport(dispatcher.get()));
+ EXPECT_TRUE(transport.is_valid());
+
+ std::vector<DispatcherTransport> transports;
+ transports.push_back(transport);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp0->WriteMessage(0, kHello, sizeof(kHello), &transports,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ transport.End();
+
+ // |dispatcher| should have been closed. This is |DCHECK()|ed when the
+ // |dispatcher| is destroyed.
+ EXPECT_TRUE(dispatcher->HasOneRef());
+ dispatcher = NULL;
+ }
+
+ // Wait.
+ EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_EQ(123u, context);
+ mp1->RemoveWaiter(1, &waiter);
+
+ // Read from MP 1, port 1.
+ char read_buffer[100] = { 0 };
+ uint32_t read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer));
+ DispatcherVector read_dispatchers;
+ uint32_t read_num_dispatchers = 10; // Maximum to get.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp1->ReadMessage(1, read_buffer, &read_buffer_size,
+ &read_dispatchers, &read_num_dispatchers,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(sizeof(kHello), static_cast<size_t>(read_buffer_size));
+ EXPECT_STREQ(kHello, read_buffer);
+ EXPECT_EQ(1u, read_dispatchers.size());
+ EXPECT_EQ(1u, read_num_dispatchers);
+ ASSERT_TRUE(read_dispatchers[0]);
+ EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
+
+ EXPECT_EQ(Dispatcher::kTypeSharedBuffer, read_dispatchers[0]->GetType());
+ dispatcher =
+ static_cast<SharedBufferDispatcher*>(read_dispatchers[0].get());
+
+ // Make another mapping.
+ scoped_ptr<RawSharedBufferMapping> mapping1;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE,
+ &mapping1));
+ ASSERT_TRUE(mapping1);
+ ASSERT_TRUE(mapping1->base());
+ ASSERT_EQ(100u, mapping1->length());
+ EXPECT_NE(mapping1->base(), mapping0->base());
+ EXPECT_EQ('A', static_cast<char*>(mapping1->base())[0]);
+ EXPECT_EQ('B', static_cast<char*>(mapping1->base())[50]);
+ EXPECT_EQ('C', static_cast<char*>(mapping1->base())[99]);
+
+ // Write stuff either way.
+ static_cast<char*>(mapping1->base())[1] = 'x';
+ EXPECT_EQ('x', static_cast<char*>(mapping0->base())[1]);
+ static_cast<char*>(mapping0->base())[2] = 'y';
+ EXPECT_EQ('y', static_cast<char*>(mapping1->base())[2]);
+
+ // Kill the first mapping; the second should still be valid.
+ mapping0.reset();
+ EXPECT_EQ('A', static_cast<char*>(mapping1->base())[0]);
+
+ // Close everything that belongs to us.
+ mp0->Close(0);
+ mp1->Close(1);
+ EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
+
+ // The second mapping should still be good.
+ EXPECT_EQ('x', static_cast<char*>(mapping1->base())[1]);
+}
+
+#if defined(OS_POSIX)
+#define MAYBE_PlatformHandlePassing PlatformHandlePassing
+#else
+// Not yet implemented (on Windows).
+#define MAYBE_PlatformHandlePassing DISABLED_PlatformHandlePassing
+#endif
+TEST_F(RemoteMessagePipeTest, MAYBE_PlatformHandlePassing) {
+ static const char kHello[] = "hello";
+ static const char kWorld[] = "world";
+ Waiter waiter;
+ uint32_t context = 0;
+
+ scoped_refptr<MessagePipe> mp0(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+ scoped_refptr<MessagePipe> mp1(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint())));
+ ConnectMessagePipes(mp0, mp1);
+
+ base::FilePath unused;
+ base::ScopedFILE fp(CreateAndOpenTemporaryFile(&unused));
+ EXPECT_EQ(sizeof(kHello), fwrite(kHello, 1, sizeof(kHello), fp.get()));
+ // We'll try to pass this dispatcher, which will cause a |PlatformHandle| to
+ // be passed.
+ scoped_refptr<PlatformHandleDispatcher> dispatcher(
+ new PlatformHandleDispatcher(
+ mojo::test::PlatformHandleFromFILE(fp.Pass())));
+
+ // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do
+ // it later, it might already be readable.)
+ waiter.Init();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp1->AddWaiter(1, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123));
+
+ // Write to MP 0, port 0.
+ {
+ DispatcherTransport
+ transport(test::DispatcherTryStartTransport(dispatcher.get()));
+ EXPECT_TRUE(transport.is_valid());
+
+ std::vector<DispatcherTransport> transports;
+ transports.push_back(transport);
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp0->WriteMessage(0, kWorld, sizeof(kWorld), &transports,
+ MOJO_WRITE_MESSAGE_FLAG_NONE));
+ transport.End();
+
+ // |dispatcher| should have been closed. This is |DCHECK()|ed when the
+ // |dispatcher| is destroyed.
+ EXPECT_TRUE(dispatcher->HasOneRef());
+ dispatcher = NULL;
+ }
+
+ // Wait.
+ EXPECT_EQ(MOJO_RESULT_OK, waiter.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_EQ(123u, context);
+ mp1->RemoveWaiter(1, &waiter);
+
+ // Read from MP 1, port 1.
+ char read_buffer[100] = { 0 };
+ uint32_t read_buffer_size = static_cast<uint32_t>(sizeof(read_buffer));
+ DispatcherVector read_dispatchers;
+ uint32_t read_num_dispatchers = 10; // Maximum to get.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ mp1->ReadMessage(1, read_buffer, &read_buffer_size,
+ &read_dispatchers, &read_num_dispatchers,
+ MOJO_READ_MESSAGE_FLAG_NONE));
+ EXPECT_EQ(sizeof(kWorld), static_cast<size_t>(read_buffer_size));
+ EXPECT_STREQ(kWorld, read_buffer);
+ EXPECT_EQ(1u, read_dispatchers.size());
+ EXPECT_EQ(1u, read_num_dispatchers);
+ ASSERT_TRUE(read_dispatchers[0]);
+ EXPECT_TRUE(read_dispatchers[0]->HasOneRef());
+
+ EXPECT_EQ(Dispatcher::kTypePlatformHandle, read_dispatchers[0]->GetType());
+ dispatcher =
+ static_cast<PlatformHandleDispatcher*>(read_dispatchers[0].get());
+
+ embedder::ScopedPlatformHandle h = dispatcher->PassPlatformHandle().Pass();
+ EXPECT_TRUE(h.is_valid());
+
+ fp = mojo::test::FILEFromPlatformHandle(h.Pass(), "rb").Pass();
+ EXPECT_FALSE(h.is_valid());
+ EXPECT_TRUE(fp);
+
+ rewind(fp.get());
+ memset(read_buffer, 0, sizeof(read_buffer));
+ EXPECT_EQ(sizeof(kHello),
+ fread(read_buffer, 1, sizeof(read_buffer), fp.get()));
+ EXPECT_STREQ(kHello, read_buffer);
+
+ // Close everything that belongs to us.
+ mp0->Close(0);
+ mp1->Close(1);
+ EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
+}
+
+// Test racing closes (on each end).
+// Note: A flaky failure would almost certainly indicate a problem in the code
+// itself (not in the test). Also, any logged warnings/errors would also
+// probably be indicative of bugs.
+TEST_F(RemoteMessagePipeTest, RacingClosesStress) {
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(5);
+
+ for (unsigned i = 0; i < 256; i++) {
+ DVLOG(2) << "---------------------------------------- " << i;
+ scoped_refptr<MessagePipe> mp0(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
+ BootstrapMessagePipeNoWait(0, mp0);
+
+ scoped_refptr<MessagePipe> mp1(new MessagePipe(
+ scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint()),
+ scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint())));
+ BootstrapMessagePipeNoWait(1, mp1);
+
+ if (i & 1u) {
+ io_thread()->task_runner()->PostTask(
+ FROM_HERE, base::Bind(&base::PlatformThread::Sleep, delay));
+ }
+ if (i & 2u)
+ base::PlatformThread::Sleep(delay);
+
+ mp0->Close(0);
+
+ if (i & 4u) {
+ io_thread()->task_runner()->PostTask(
+ FROM_HERE, base::Bind(&base::PlatformThread::Sleep, delay));
+ }
+ if (i & 8u)
+ base::PlatformThread::Sleep(delay);
+
+ mp1->Close(1);
+
+ RestoreInitialState();
+ }
+}
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/shared_buffer_dispatcher.cc b/chromium/mojo/system/shared_buffer_dispatcher.cc
new file mode 100644
index 00000000000..1181b01b915
--- /dev/null
+++ b/chromium/mojo/system/shared_buffer_dispatcher.cc
@@ -0,0 +1,272 @@
+// Copyright 2014 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 "mojo/system/shared_buffer_dispatcher.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/public/c/system/macros.h"
+#include "mojo/system/constants.h"
+#include "mojo/system/memory.h"
+#include "mojo/system/options_validation.h"
+#include "mojo/system/raw_shared_buffer.h"
+
+namespace mojo {
+namespace system {
+
+namespace {
+
+struct SerializedSharedBufferDispatcher {
+ size_t num_bytes;
+ size_t platform_handle_index;
+};
+
+} // namespace
+
+// static
+const MojoCreateSharedBufferOptions
+ SharedBufferDispatcher::kDefaultCreateOptions = {
+ static_cast<uint32_t>(sizeof(MojoCreateSharedBufferOptions)),
+ MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE
+};
+
+// static
+MojoResult SharedBufferDispatcher::ValidateCreateOptions(
+ const MojoCreateSharedBufferOptions* in_options,
+ MojoCreateSharedBufferOptions* out_options) {
+ const MojoCreateSharedBufferOptionsFlags kKnownFlags =
+ MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE;
+
+ *out_options = kDefaultCreateOptions;
+ if (!in_options)
+ return MOJO_RESULT_OK;
+
+ MojoResult result =
+ ValidateOptionsStructPointerSizeAndFlags<MojoCreateSharedBufferOptions>(
+ in_options, kKnownFlags, out_options);
+ if (result != MOJO_RESULT_OK)
+ return result;
+
+ // Checks for fields beyond |flags|:
+
+ // (Nothing here yet.)
+
+ return MOJO_RESULT_OK;
+}
+
+// static
+MojoResult SharedBufferDispatcher::Create(
+ const MojoCreateSharedBufferOptions& /*validated_options*/,
+ uint64_t num_bytes,
+ scoped_refptr<SharedBufferDispatcher>* result) {
+ if (!num_bytes)
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (num_bytes > kMaxSharedMemoryNumBytes)
+ return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+ scoped_refptr<RawSharedBuffer> shared_buffer(
+ RawSharedBuffer::Create(static_cast<size_t>(num_bytes)));
+ if (!shared_buffer)
+ return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+ *result = new SharedBufferDispatcher(shared_buffer);
+ return MOJO_RESULT_OK;
+}
+
+Dispatcher::Type SharedBufferDispatcher::GetType() const {
+ return kTypeSharedBuffer;
+}
+
+// static
+scoped_refptr<SharedBufferDispatcher> SharedBufferDispatcher::Deserialize(
+ Channel* channel,
+ const void* source,
+ size_t size,
+ embedder::PlatformHandleVector* platform_handles) {
+ if (size != sizeof(SerializedSharedBufferDispatcher)) {
+ LOG(ERROR) << "Invalid serialized shared buffer dispatcher (bad size)";
+ return scoped_refptr<SharedBufferDispatcher>();
+ }
+
+ const SerializedSharedBufferDispatcher* serialization =
+ static_cast<const SerializedSharedBufferDispatcher*>(source);
+ size_t num_bytes = serialization->num_bytes;
+ size_t platform_handle_index = serialization->platform_handle_index;
+
+ if (!num_bytes) {
+ LOG(ERROR)
+ << "Invalid serialized shared buffer dispatcher (invalid num_bytes)";
+ return scoped_refptr<SharedBufferDispatcher>();
+ }
+
+ if (!platform_handles || platform_handle_index >= platform_handles->size()) {
+ LOG(ERROR)
+ << "Invalid serialized shared buffer dispatcher (missing handles)";
+ return scoped_refptr<SharedBufferDispatcher>();
+ }
+
+ // Starts off invalid, which is what we want.
+ embedder::PlatformHandle platform_handle;
+ // We take ownership of the handle, so we have to invalidate the one in
+ // |platform_handles|.
+ std::swap(platform_handle, (*platform_handles)[platform_handle_index]);
+
+ // Wrapping |platform_handle| in a |ScopedPlatformHandle| means that it'll be
+ // closed even if creation fails.
+ scoped_refptr<RawSharedBuffer> shared_buffer(
+ RawSharedBuffer::CreateFromPlatformHandle(num_bytes,
+ embedder::ScopedPlatformHandle(platform_handle)));
+ if (!shared_buffer) {
+ LOG(ERROR)
+ << "Invalid serialized shared buffer dispatcher (invalid num_bytes?)";
+ return scoped_refptr<SharedBufferDispatcher>();
+ }
+
+ return scoped_refptr<SharedBufferDispatcher>(new SharedBufferDispatcher(
+ shared_buffer));
+}
+
+SharedBufferDispatcher::SharedBufferDispatcher(
+ scoped_refptr<RawSharedBuffer> shared_buffer)
+ : shared_buffer_(shared_buffer) {
+ DCHECK(shared_buffer_);
+}
+
+SharedBufferDispatcher::~SharedBufferDispatcher() {
+}
+
+// static
+MojoResult SharedBufferDispatcher::ValidateDuplicateOptions(
+ const MojoDuplicateBufferHandleOptions* in_options,
+ MojoDuplicateBufferHandleOptions* out_options) {
+ const MojoDuplicateBufferHandleOptionsFlags kKnownFlags =
+ MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE;
+ static const MojoDuplicateBufferHandleOptions kDefaultOptions = {
+ static_cast<uint32_t>(sizeof(MojoDuplicateBufferHandleOptions)),
+ MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE
+ };
+
+ *out_options = kDefaultOptions;
+ if (!in_options)
+ return MOJO_RESULT_OK;
+
+ MojoResult result =
+ ValidateOptionsStructPointerSizeAndFlags<
+ MojoDuplicateBufferHandleOptions>(
+ in_options, kKnownFlags, out_options);
+ if (result != MOJO_RESULT_OK)
+ return result;
+
+ // Checks for fields beyond |flags|:
+
+ // (Nothing here yet.)
+
+ return MOJO_RESULT_OK;
+}
+
+void SharedBufferDispatcher::CloseImplNoLock() {
+ lock().AssertAcquired();
+ DCHECK(shared_buffer_);
+ shared_buffer_ = NULL;
+}
+
+scoped_refptr<Dispatcher>
+ SharedBufferDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() {
+ lock().AssertAcquired();
+ DCHECK(shared_buffer_);
+ scoped_refptr<RawSharedBuffer> shared_buffer;
+ shared_buffer.swap(shared_buffer_);
+ return scoped_refptr<Dispatcher>(new SharedBufferDispatcher(shared_buffer));
+}
+
+MojoResult SharedBufferDispatcher::DuplicateBufferHandleImplNoLock(
+ const MojoDuplicateBufferHandleOptions* options,
+ scoped_refptr<Dispatcher>* new_dispatcher) {
+ lock().AssertAcquired();
+
+ MojoDuplicateBufferHandleOptions validated_options;
+ MojoResult result = ValidateDuplicateOptions(options, &validated_options);
+ if (result != MOJO_RESULT_OK)
+ return result;
+
+ *new_dispatcher = new SharedBufferDispatcher(shared_buffer_);
+ return MOJO_RESULT_OK;
+}
+
+MojoResult SharedBufferDispatcher::MapBufferImplNoLock(
+ uint64_t offset,
+ uint64_t num_bytes,
+ MojoMapBufferFlags flags,
+ scoped_ptr<RawSharedBufferMapping>* mapping) {
+ lock().AssertAcquired();
+ DCHECK(shared_buffer_);
+
+ if (offset > static_cast<uint64_t>(std::numeric_limits<size_t>::max()))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+ if (num_bytes > static_cast<uint64_t>(std::numeric_limits<size_t>::max()))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ if (!shared_buffer_->IsValidMap(static_cast<size_t>(offset),
+ static_cast<size_t>(num_bytes)))
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ DCHECK(mapping);
+ *mapping = shared_buffer_->MapNoCheck(static_cast<size_t>(offset),
+ static_cast<size_t>(num_bytes));
+ if (!*mapping)
+ return MOJO_RESULT_RESOURCE_EXHAUSTED;
+
+ return MOJO_RESULT_OK;
+}
+
+void SharedBufferDispatcher::StartSerializeImplNoLock(
+ Channel* /*channel*/,
+ size_t* max_size,
+ size_t* max_platform_handles) {
+ DCHECK(HasOneRef()); // Only one ref => no need to take the lock.
+ *max_size = sizeof(SerializedSharedBufferDispatcher);
+ *max_platform_handles = 1;
+}
+
+bool SharedBufferDispatcher::EndSerializeAndCloseImplNoLock(
+ Channel* /*channel*/,
+ void* destination,
+ size_t* actual_size,
+ embedder::PlatformHandleVector* platform_handles) {
+ DCHECK(HasOneRef()); // Only one ref => no need to take the lock.
+ DCHECK(shared_buffer_);
+
+ SerializedSharedBufferDispatcher* serialization =
+ static_cast<SerializedSharedBufferDispatcher*>(destination);
+ // If there's only one reference to |shared_buffer_|, then it's ours (and no
+ // one else can make any more references to it), so we can just take its
+ // handle.
+ embedder::ScopedPlatformHandle platform_handle(
+ shared_buffer_->HasOneRef() ?
+ shared_buffer_->PassPlatformHandle() :
+ shared_buffer_->DuplicatePlatformHandle());
+ if (!platform_handle.is_valid()) {
+ shared_buffer_ = NULL;
+ return false;
+ }
+
+ serialization->num_bytes = shared_buffer_->num_bytes();
+ serialization->platform_handle_index = platform_handles->size();
+ platform_handles->push_back(platform_handle.release());
+ *actual_size = sizeof(SerializedSharedBufferDispatcher);
+
+ shared_buffer_ = NULL;
+
+ return true;
+}
+
+HandleSignalsState SharedBufferDispatcher::GetHandleSignalsStateNoLock() const {
+ // TODO(vtl): Add transferrable flag.
+ return HandleSignalsState();
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/shared_buffer_dispatcher.h b/chromium/mojo/system/shared_buffer_dispatcher.h
new file mode 100644
index 00000000000..ff251e3305b
--- /dev/null
+++ b/chromium/mojo/system/shared_buffer_dispatcher.h
@@ -0,0 +1,98 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SYSTEM_SHARED_BUFFER_DISPATCHER_H_
+#define MOJO_SYSTEM_SHARED_BUFFER_DISPATCHER_H_
+
+#include "base/macros.h"
+#include "mojo/system/raw_shared_buffer.h"
+#include "mojo/system/simple_dispatcher.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+// TODO(vtl): We derive from SimpleDispatcher, even though we don't currently
+// have anything that's waitable. I want to add a "transferrable" wait flag.
+class MOJO_SYSTEM_IMPL_EXPORT SharedBufferDispatcher : public SimpleDispatcher {
+ public:
+ // The default options to use for |MojoCreateSharedBuffer()|. (Real uses
+ // should obtain this via |ValidateCreateOptions()| with a null |in_options|;
+ // this is exposed directly for testing convenience.)
+ static const MojoCreateSharedBufferOptions kDefaultCreateOptions;
+
+ // Validates and/or sets default options for |MojoCreateSharedBufferOptions|.
+ // If non-null, |in_options| must point to a struct of at least
+ // |in_options->struct_size| bytes. |out_options| must point to a (current)
+ // |MojoCreateSharedBufferOptions| and will be entirely overwritten on success
+ // (it may be partly overwritten on failure).
+ static MojoResult ValidateCreateOptions(
+ const MojoCreateSharedBufferOptions* in_options,
+ MojoCreateSharedBufferOptions* out_options);
+
+ // Static factory method: |validated_options| must be validated (obviously).
+ // On failure, |*result| will be left as-is.
+ static MojoResult Create(
+ const MojoCreateSharedBufferOptions& validated_options,
+ uint64_t num_bytes,
+ scoped_refptr<SharedBufferDispatcher>* result);
+
+ // |Dispatcher| public methods:
+ virtual Type GetType() const OVERRIDE;
+
+ // The "opposite" of |SerializeAndClose()|. (Typically this is called by
+ // |Dispatcher::Deserialize()|.)
+ static scoped_refptr<SharedBufferDispatcher> Deserialize(
+ Channel* channel,
+ const void* source,
+ size_t size,
+ embedder::PlatformHandleVector* platform_handles);
+
+ private:
+ explicit SharedBufferDispatcher(
+ scoped_refptr<RawSharedBuffer> shared_buffer_);
+ virtual ~SharedBufferDispatcher();
+
+ // Validates and/or sets default options for
+ // |MojoDuplicateBufferHandleOptions|. If non-null, |in_options| must point to
+ // a struct of at least |in_options->struct_size| bytes. |out_options| must
+ // point to a (current) |MojoDuplicateBufferHandleOptions| and will be
+ // entirely overwritten on success (it may be partly overwritten on failure).
+ static MojoResult ValidateDuplicateOptions(
+ const MojoDuplicateBufferHandleOptions* in_options,
+ MojoDuplicateBufferHandleOptions* out_options);
+
+ // |Dispatcher| protected methods:
+ virtual void CloseImplNoLock() OVERRIDE;
+ virtual scoped_refptr<Dispatcher>
+ CreateEquivalentDispatcherAndCloseImplNoLock() OVERRIDE;
+ virtual MojoResult DuplicateBufferHandleImplNoLock(
+ const MojoDuplicateBufferHandleOptions* options,
+ scoped_refptr<Dispatcher>* new_dispatcher) OVERRIDE;
+ virtual MojoResult MapBufferImplNoLock(
+ uint64_t offset,
+ uint64_t num_bytes,
+ MojoMapBufferFlags flags,
+ scoped_ptr<RawSharedBufferMapping>* mapping) OVERRIDE;
+ virtual void StartSerializeImplNoLock(Channel* channel,
+ size_t* max_size,
+ size_t* max_platform_handles) OVERRIDE;
+ virtual bool EndSerializeAndCloseImplNoLock(
+ Channel* channel,
+ void* destination,
+ size_t* actual_size,
+ embedder::PlatformHandleVector* platform_handles) OVERRIDE;
+
+ // |SimpleDispatcher| method:
+ virtual HandleSignalsState GetHandleSignalsStateNoLock() const OVERRIDE;
+
+ scoped_refptr<RawSharedBuffer> shared_buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(SharedBufferDispatcher);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_SHARED_BUFFER_DISPATCHER_H_
diff --git a/chromium/mojo/system/shared_buffer_dispatcher_unittest.cc b/chromium/mojo/system/shared_buffer_dispatcher_unittest.cc
new file mode 100644
index 00000000000..8534ae56ace
--- /dev/null
+++ b/chromium/mojo/system/shared_buffer_dispatcher_unittest.cc
@@ -0,0 +1,268 @@
+// Copyright 2014 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 "mojo/system/shared_buffer_dispatcher.h"
+
+#include <limits>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "mojo/system/dispatcher.h"
+#include "mojo/system/raw_shared_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+// NOTE(vtl): There's currently not much to test for in
+// |SharedBufferDispatcher::ValidateCreateOptions()|, but the tests should be
+// expanded if/when options are added, so I've kept the general form of the
+// tests from data_pipe_unittest.cc.
+
+const uint32_t kSizeOfCreateOptions = sizeof(MojoCreateSharedBufferOptions);
+
+// Does a cursory sanity check of |validated_options|. Calls
+// |ValidateCreateOptions()| on already-validated options. The validated options
+// should be valid, and the revalidated copy should be the same.
+void RevalidateCreateOptions(
+ const MojoCreateSharedBufferOptions& validated_options) {
+ EXPECT_EQ(kSizeOfCreateOptions, validated_options.struct_size);
+ // Nothing to check for flags.
+
+ MojoCreateSharedBufferOptions revalidated_options = {};
+ EXPECT_EQ(MOJO_RESULT_OK,
+ SharedBufferDispatcher::ValidateCreateOptions(
+ &validated_options, &revalidated_options));
+ EXPECT_EQ(validated_options.struct_size, revalidated_options.struct_size);
+ EXPECT_EQ(validated_options.flags, revalidated_options.flags);
+}
+
+// Tests valid inputs to |ValidateCreateOptions()|.
+TEST(SharedBufferDispatcherTest, ValidateCreateOptionsValid) {
+ // Default options.
+ {
+ MojoCreateSharedBufferOptions validated_options = {};
+ EXPECT_EQ(MOJO_RESULT_OK,
+ SharedBufferDispatcher::ValidateCreateOptions(
+ NULL, &validated_options));
+ RevalidateCreateOptions(validated_options);
+ }
+
+ // Different flags.
+ MojoCreateSharedBufferOptionsFlags flags_values[] = {
+ MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE
+ };
+ for (size_t i = 0; i < arraysize(flags_values); i++) {
+ const MojoCreateSharedBufferOptionsFlags flags = flags_values[i];
+
+ // Different capacities (size 1).
+ for (uint32_t capacity = 1; capacity <= 100 * 1000 * 1000; capacity *= 10) {
+ MojoCreateSharedBufferOptions options = {
+ kSizeOfCreateOptions, // |struct_size|.
+ flags // |flags|.
+ };
+ MojoCreateSharedBufferOptions validated_options = {};
+ EXPECT_EQ(MOJO_RESULT_OK,
+ SharedBufferDispatcher::ValidateCreateOptions(
+ &options, &validated_options))
+ << capacity;
+ RevalidateCreateOptions(validated_options);
+ EXPECT_EQ(options.flags, validated_options.flags);
+ }
+ }
+}
+
+TEST(SharedBufferDispatcherTest, ValidateCreateOptionsInvalid) {
+ // Invalid |struct_size|.
+ {
+ MojoCreateSharedBufferOptions options = {
+ 1, // |struct_size|.
+ MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE // |flags|.
+ };
+ MojoCreateSharedBufferOptions unused;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ SharedBufferDispatcher::ValidateCreateOptions(&options, &unused));
+ }
+
+ // Unknown |flags|.
+ {
+ MojoCreateSharedBufferOptions options = {
+ kSizeOfCreateOptions, // |struct_size|.
+ ~0u // |flags|.
+ };
+ MojoCreateSharedBufferOptions unused;
+ EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+ SharedBufferDispatcher::ValidateCreateOptions(&options, &unused));
+ }
+}
+
+TEST(SharedBufferDispatcherTest, CreateAndMapBuffer) {
+ scoped_refptr<SharedBufferDispatcher> dispatcher;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ SharedBufferDispatcher::Create(
+ SharedBufferDispatcher::kDefaultCreateOptions, 100,
+ &dispatcher));
+ ASSERT_TRUE(dispatcher);
+ EXPECT_EQ(Dispatcher::kTypeSharedBuffer, dispatcher->GetType());
+
+ // Make a couple of mappings.
+ scoped_ptr<RawSharedBufferMapping> mapping1;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE,
+ &mapping1));
+ ASSERT_TRUE(mapping1);
+ ASSERT_TRUE(mapping1->base());
+ EXPECT_EQ(100u, mapping1->length());
+ // Write something.
+ static_cast<char*>(mapping1->base())[50] = 'x';
+
+ scoped_ptr<RawSharedBufferMapping> mapping2;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dispatcher->MapBuffer(50, 50, MOJO_MAP_BUFFER_FLAG_NONE,
+ &mapping2));
+ ASSERT_TRUE(mapping2);
+ ASSERT_TRUE(mapping2->base());
+ EXPECT_EQ(50u, mapping2->length());
+ EXPECT_EQ('x', static_cast<char*>(mapping2->base())[0]);
+
+ EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
+
+ // Check that we can still read/write to mappings after the dispatcher has
+ // gone away.
+ static_cast<char*>(mapping2->base())[1] = 'y';
+ EXPECT_EQ('y', static_cast<char*>(mapping1->base())[51]);
+}
+
+TEST(SharedBufferDispatcher, DuplicateBufferHandle) {
+ scoped_refptr<SharedBufferDispatcher> dispatcher1;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ SharedBufferDispatcher::Create(
+ SharedBufferDispatcher::kDefaultCreateOptions, 100,
+ &dispatcher1));
+
+ // Map and write something.
+ scoped_ptr<RawSharedBufferMapping> mapping;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dispatcher1->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE,
+ &mapping));
+ static_cast<char*>(mapping->base())[0] = 'x';
+ mapping.reset();
+
+ // Duplicate |dispatcher1| and then close it.
+ scoped_refptr<Dispatcher> dispatcher2;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dispatcher1->DuplicateBufferHandle(NULL, &dispatcher2));
+ ASSERT_TRUE(dispatcher2);
+ EXPECT_EQ(Dispatcher::kTypeSharedBuffer, dispatcher2->GetType());
+
+ EXPECT_EQ(MOJO_RESULT_OK, dispatcher1->Close());
+
+ // Map |dispatcher2| and read something.
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dispatcher2->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE,
+ &mapping));
+ EXPECT_EQ('x', static_cast<char*>(mapping->base())[0]);
+
+ EXPECT_EQ(MOJO_RESULT_OK, dispatcher2->Close());
+}
+
+TEST(SharedBufferDispatcherTest, DuplicateBufferHandleOptionsValid) {
+ scoped_refptr<SharedBufferDispatcher> dispatcher1;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ SharedBufferDispatcher::Create(
+ SharedBufferDispatcher::kDefaultCreateOptions, 100,
+ &dispatcher1));
+
+ MojoDuplicateBufferHandleOptions options[] = {
+ {sizeof(MojoDuplicateBufferHandleOptions),
+ MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE},
+ {sizeof(MojoDuplicateBufferHandleOptionsFlags), ~0u}
+ };
+ for (size_t i = 0; i < arraysize(options); i++) {
+ scoped_refptr<Dispatcher> dispatcher2;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ dispatcher1->DuplicateBufferHandle(&options[i], &dispatcher2));
+ ASSERT_TRUE(dispatcher2);
+ EXPECT_EQ(Dispatcher::kTypeSharedBuffer, dispatcher2->GetType());
+ EXPECT_EQ(MOJO_RESULT_OK, dispatcher2->Close());
+ }
+
+ EXPECT_EQ(MOJO_RESULT_OK, dispatcher1->Close());
+}
+
+TEST(SharedBufferDispatcherTest, DuplicateBufferHandleOptionsInvalid) {
+ scoped_refptr<SharedBufferDispatcher> dispatcher1;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ SharedBufferDispatcher::Create(
+ SharedBufferDispatcher::kDefaultCreateOptions, 100,
+ &dispatcher1));
+
+ // Invalid |struct_size|.
+ {
+ MojoDuplicateBufferHandleOptions options = {
+ 1u, MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE
+ };
+ scoped_refptr<Dispatcher> dispatcher2;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dispatcher1->DuplicateBufferHandle(&options, &dispatcher2));
+ EXPECT_FALSE(dispatcher2);
+ }
+
+ // Unknown |flags|.
+ {
+ MojoDuplicateBufferHandleOptions options = {
+ sizeof(MojoDuplicateBufferHandleOptions), ~0u
+ };
+ scoped_refptr<Dispatcher> dispatcher2;
+ EXPECT_EQ(MOJO_RESULT_UNIMPLEMENTED,
+ dispatcher1->DuplicateBufferHandle(&options, &dispatcher2));
+ EXPECT_FALSE(dispatcher2);
+ }
+
+ EXPECT_EQ(MOJO_RESULT_OK, dispatcher1->Close());
+}
+
+TEST(SharedBufferDispatcherTest, CreateInvalidNumBytes) {
+ // Size too big.
+ scoped_refptr<SharedBufferDispatcher> dispatcher;
+ EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED,
+ SharedBufferDispatcher::Create(
+ SharedBufferDispatcher::kDefaultCreateOptions,
+ std::numeric_limits<uint64_t>::max(), &dispatcher));
+ EXPECT_FALSE(dispatcher);
+
+ // Zero size.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ SharedBufferDispatcher::Create(
+ SharedBufferDispatcher::kDefaultCreateOptions, 0, &dispatcher));
+ EXPECT_FALSE(dispatcher);
+}
+
+TEST(SharedBufferDispatcherTest, MapBufferInvalidArguments) {
+ scoped_refptr<SharedBufferDispatcher> dispatcher;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ SharedBufferDispatcher::Create(
+ SharedBufferDispatcher::kDefaultCreateOptions, 100,
+ &dispatcher));
+
+ scoped_ptr<RawSharedBufferMapping> mapping;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dispatcher->MapBuffer(0, 101, MOJO_MAP_BUFFER_FLAG_NONE, &mapping));
+ EXPECT_FALSE(mapping);
+
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dispatcher->MapBuffer(1, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping));
+ EXPECT_FALSE(mapping);
+
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ dispatcher->MapBuffer(0, 0, MOJO_MAP_BUFFER_FLAG_NONE, &mapping));
+ EXPECT_FALSE(mapping);
+
+ EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close());
+}
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/simple_dispatcher.cc b/chromium/mojo/system/simple_dispatcher.cc
new file mode 100644
index 00000000000..3595815c571
--- /dev/null
+++ b/chromium/mojo/system/simple_dispatcher.cc
@@ -0,0 +1,49 @@
+// Copyright 2013 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 "mojo/system/simple_dispatcher.h"
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace system {
+
+SimpleDispatcher::SimpleDispatcher() {
+}
+
+SimpleDispatcher::~SimpleDispatcher() {
+}
+
+void SimpleDispatcher::HandleSignalsStateChangedNoLock() {
+ lock().AssertAcquired();
+ waiter_list_.AwakeWaitersForStateChange(GetHandleSignalsStateNoLock());
+}
+
+void SimpleDispatcher::CancelAllWaitersNoLock() {
+ lock().AssertAcquired();
+ waiter_list_.CancelAllWaiters();
+}
+
+MojoResult SimpleDispatcher::AddWaiterImplNoLock(Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context) {
+ lock().AssertAcquired();
+
+ HandleSignalsState state(GetHandleSignalsStateNoLock());
+ if (state.satisfies(signals))
+ return MOJO_RESULT_ALREADY_EXISTS;
+ if (!state.can_satisfy(signals))
+ return MOJO_RESULT_FAILED_PRECONDITION;
+
+ waiter_list_.AddWaiter(waiter, signals, context);
+ return MOJO_RESULT_OK;
+}
+
+void SimpleDispatcher::RemoveWaiterImplNoLock(Waiter* waiter) {
+ lock().AssertAcquired();
+ waiter_list_.RemoveWaiter(waiter);
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/simple_dispatcher.h b/chromium/mojo/system/simple_dispatcher.h
new file mode 100644
index 00000000000..65d52fed5ab
--- /dev/null
+++ b/chromium/mojo/system/simple_dispatcher.h
@@ -0,0 +1,53 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_SIMPLE_DISPATCHER_H_
+#define MOJO_SYSTEM_SIMPLE_DISPATCHER_H_
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "mojo/system/dispatcher.h"
+#include "mojo/system/handle_signals_state.h"
+#include "mojo/system/system_impl_export.h"
+#include "mojo/system/waiter_list.h"
+
+namespace mojo {
+namespace system {
+
+// A base class for simple dispatchers. "Simple" means that there's a one-to-one
+// correspondence between handles and dispatchers (see the explanatory comment
+// in core.cc). This class implements the standard waiter-signalling mechanism
+// in that case.
+class MOJO_SYSTEM_IMPL_EXPORT SimpleDispatcher : public Dispatcher {
+ protected:
+ SimpleDispatcher();
+ virtual ~SimpleDispatcher();
+
+ // To be called by subclasses when the state changes (so
+ // |GetHandleSignalsStateNoLock()| should be checked again). Must be called
+ // under lock.
+ void HandleSignalsStateChangedNoLock();
+
+ // Never called after the dispatcher has been closed; called under |lock_|.
+ virtual HandleSignalsState GetHandleSignalsStateNoLock() const = 0;
+
+ // |Dispatcher| protected methods:
+ virtual void CancelAllWaitersNoLock() OVERRIDE;
+ virtual MojoResult AddWaiterImplNoLock(Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context) OVERRIDE;
+ virtual void RemoveWaiterImplNoLock(Waiter* waiter) OVERRIDE;
+
+ private:
+ // Protected by |lock()|:
+ WaiterList waiter_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(SimpleDispatcher);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_SIMPLE_DISPATCHER_H_
diff --git a/chromium/mojo/system/simple_dispatcher_unittest.cc b/chromium/mojo/system/simple_dispatcher_unittest.cc
new file mode 100644
index 00000000000..00c39654ef5
--- /dev/null
+++ b/chromium/mojo/system/simple_dispatcher_unittest.cc
@@ -0,0 +1,541 @@
+// Copyright 2013 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.
+
+// NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a
+// heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to
+// increase tolerance and reduce observed flakiness (though doing so reduces the
+// meaningfulness of the test).
+
+#include "mojo/system/simple_dispatcher.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_vector.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h" // For |Sleep()|.
+#include "base/time/time.h"
+#include "mojo/system/test_utils.h"
+#include "mojo/system/waiter.h"
+#include "mojo/system/waiter_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+class MockSimpleDispatcher : public SimpleDispatcher {
+ public:
+ MockSimpleDispatcher()
+ : state_(MOJO_HANDLE_SIGNAL_NONE,
+ MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE) {}
+
+ void SetSatisfiedSignals(MojoHandleSignals new_satisfied_signals) {
+ base::AutoLock locker(lock());
+
+ // Any new signals that are set should be satisfiable.
+ CHECK_EQ(new_satisfied_signals & ~state_.satisfied_signals,
+ new_satisfied_signals & ~state_.satisfied_signals &
+ state_.satisfiable_signals);
+
+ if (new_satisfied_signals == state_.satisfied_signals)
+ return;
+
+ state_.satisfied_signals = new_satisfied_signals;
+ HandleSignalsStateChangedNoLock();
+ }
+
+ void SetSatisfiableSignals(MojoHandleSignals new_satisfiable_signals) {
+ base::AutoLock locker(lock());
+
+ // Satisfied implies satisfiable.
+ CHECK_EQ(new_satisfiable_signals & state_.satisfied_signals,
+ state_.satisfied_signals);
+
+ if (new_satisfiable_signals == state_.satisfiable_signals)
+ return;
+
+ state_.satisfiable_signals = new_satisfiable_signals;
+ HandleSignalsStateChangedNoLock();
+ }
+
+ virtual Type GetType() const OVERRIDE {
+ return kTypeUnknown;
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<MockSimpleDispatcher>;
+ virtual ~MockSimpleDispatcher() {}
+
+ virtual scoped_refptr<Dispatcher>
+ CreateEquivalentDispatcherAndCloseImplNoLock() OVERRIDE {
+ scoped_refptr<MockSimpleDispatcher> rv(new MockSimpleDispatcher());
+ rv->state_ = state_;
+ return scoped_refptr<Dispatcher>(rv.get());
+ }
+
+ // |SimpleDispatcher| implementation:
+ virtual HandleSignalsState GetHandleSignalsStateNoLock() const OVERRIDE {
+ lock().AssertAcquired();
+ return state_;
+ }
+
+ // Protected by |lock()|:
+ HandleSignalsState state_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockSimpleDispatcher);
+};
+
+TEST(SimpleDispatcherTest, Basic) {
+ test::Stopwatch stopwatch;
+
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ Waiter w;
+ uint32_t context = 0;
+
+ // Try adding a readable waiter when already readable.
+ w.Init();
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_READABLE, 0));
+ // Shouldn't need to remove the waiter (it was not added).
+
+ // Wait (forever) for writable when already writable.
+ w.Init();
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 1));
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE);
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_OK, w.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+ EXPECT_EQ(1u, context);
+ d->RemoveWaiter(&w);
+
+ // Wait for zero time for writable when already writable.
+ w.Init();
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 2));
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE);
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_OK, w.Wait(0, &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+ EXPECT_EQ(2u, context);
+ d->RemoveWaiter(&w);
+
+ // Wait for non-zero, finite time for writable when already writable.
+ w.Init();
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 3));
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE);
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_OK,
+ w.Wait(2 * test::EpsilonTimeout().InMicroseconds(), &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+ EXPECT_EQ(3u, context);
+ d->RemoveWaiter(&w);
+
+ // Wait for zero time for writable when not writable (will time out).
+ w.Init();
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 4));
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, w.Wait(0, NULL));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+ d->RemoveWaiter(&w);
+
+ // Wait for non-zero, finite time for writable when not writable (will time
+ // out).
+ w.Init();
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 5));
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+ w.Wait(2 * test::EpsilonTimeout().InMicroseconds(), NULL));
+ base::TimeDelta elapsed = stopwatch.Elapsed();
+ EXPECT_GT(elapsed, (2-1) * test::EpsilonTimeout());
+ EXPECT_LT(elapsed, (2+1) * test::EpsilonTimeout());
+ d->RemoveWaiter(&w);
+
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+}
+
+TEST(SimpleDispatcherTest, BasicUnsatisfiable) {
+ test::Stopwatch stopwatch;
+
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ Waiter w;
+ uint32_t context = 0;
+
+ // Try adding a writable waiter when it can never be writable.
+ w.Init();
+ d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ d->SetSatisfiedSignals(0);
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 1));
+ // Shouldn't need to remove the waiter (it was not added).
+
+ // Wait (forever) for writable and then it becomes never writable.
+ w.Init();
+ d->SetSatisfiableSignals(
+ MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 2));
+ d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ w.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+ EXPECT_EQ(2u, context);
+ d->RemoveWaiter(&w);
+
+ // Wait for zero time for writable and then it becomes never writable.
+ w.Init();
+ d->SetSatisfiableSignals(
+ MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 3));
+ d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, w.Wait(0, &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+ EXPECT_EQ(3u, context);
+ d->RemoveWaiter(&w);
+
+ // Wait for non-zero, finite time for writable and then it becomes never
+ // writable.
+ w.Init();
+ d->SetSatisfiableSignals(
+ MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 4));
+ d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ w.Wait(2 * test::EpsilonTimeout().InMicroseconds(), &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+ EXPECT_EQ(4u, context);
+ d->RemoveWaiter(&w);
+
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+}
+
+TEST(SimpleDispatcherTest, BasicClosed) {
+ test::Stopwatch stopwatch;
+
+ scoped_refptr<MockSimpleDispatcher> d;
+ Waiter w;
+ uint32_t context = 0;
+
+ // Try adding a writable waiter when the dispatcher has been closed.
+ d = new MockSimpleDispatcher();
+ w.Init();
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 1));
+ // Shouldn't need to remove the waiter (it was not added).
+
+ // Wait (forever) for writable and then the dispatcher is closed.
+ d = new MockSimpleDispatcher();
+ w.Init();
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 2));
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, w.Wait(MOJO_DEADLINE_INDEFINITE, &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+ EXPECT_EQ(2u, context);
+ // Don't need to remove waiters from closed dispatchers.
+
+ // Wait for zero time for writable and then the dispatcher is closed.
+ d = new MockSimpleDispatcher();
+ w.Init();
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 3));
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, w.Wait(0, &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+ EXPECT_EQ(3u, context);
+ // Don't need to remove waiters from closed dispatchers.
+
+ // Wait for non-zero, finite time for writable and then the dispatcher is
+ // closed.
+ d = new MockSimpleDispatcher();
+ w.Init();
+ EXPECT_EQ(MOJO_RESULT_OK, d->AddWaiter(&w, MOJO_HANDLE_SIGNAL_WRITABLE, 4));
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_CANCELLED,
+ w.Wait(2 * test::EpsilonTimeout().InMicroseconds(), &context));
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+ EXPECT_EQ(4u, context);
+ // Don't need to remove waiters from closed dispatchers.
+}
+
+TEST(SimpleDispatcherTest, BasicThreaded) {
+ test::Stopwatch stopwatch;
+ bool did_wait;
+ MojoResult result;
+ uint32_t context;
+
+ // Wait for readable (already readable).
+ {
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ {
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ test::WaiterThread thread(d,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ 1,
+ &did_wait, &result, &context);
+ stopwatch.Start();
+ thread.Start();
+ } // Joins the thread.
+ // If we closed earlier, then probably we'd get a |MOJO_RESULT_CANCELLED|.
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ }
+ EXPECT_LT(stopwatch.Elapsed(), test::EpsilonTimeout());
+ EXPECT_FALSE(did_wait);
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, result);
+
+ // Wait for readable and becomes readable after some time.
+ {
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ test::WaiterThread thread(d,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ 2,
+ &did_wait, &result, &context);
+ stopwatch.Start();
+ thread.Start();
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ } // Joins the thread.
+ base::TimeDelta elapsed = stopwatch.Elapsed();
+ EXPECT_GT(elapsed, (2-1) * test::EpsilonTimeout());
+ EXPECT_LT(elapsed, (2+1) * test::EpsilonTimeout());
+ EXPECT_TRUE(did_wait);
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(2u, context);
+
+ // Wait for readable and becomes never-readable after some time.
+ {
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ test::WaiterThread thread(d,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ 3,
+ &did_wait, &result, &context);
+ stopwatch.Start();
+ thread.Start();
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_NONE);
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ } // Joins the thread.
+ elapsed = stopwatch.Elapsed();
+ EXPECT_GT(elapsed, (2-1) * test::EpsilonTimeout());
+ EXPECT_LT(elapsed, (2+1) * test::EpsilonTimeout());
+ EXPECT_TRUE(did_wait);
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+ EXPECT_EQ(3u, context);
+
+ // Wait for readable and dispatcher gets closed.
+ {
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ test::WaiterThread thread(d,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ 4,
+ &did_wait, &result, &context);
+ stopwatch.Start();
+ thread.Start();
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ } // Joins the thread.
+ elapsed = stopwatch.Elapsed();
+ EXPECT_GT(elapsed, (2-1) * test::EpsilonTimeout());
+ EXPECT_LT(elapsed, (2+1) * test::EpsilonTimeout());
+ EXPECT_TRUE(did_wait);
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+ EXPECT_EQ(4u, context);
+
+ // Wait for readable and times out.
+ {
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ {
+ test::WaiterThread thread(d,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ 2 * test::EpsilonTimeout().InMicroseconds(),
+ 5,
+ &did_wait, &result, &context);
+ stopwatch.Start();
+ thread.Start();
+ base::PlatformThread::Sleep(1 * test::EpsilonTimeout());
+ // Not what we're waiting for.
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_WRITABLE);
+ } // Joins the thread (after its wait times out).
+ // If we closed earlier, then probably we'd get a |MOJO_RESULT_CANCELLED|.
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ }
+ elapsed = stopwatch.Elapsed();
+ EXPECT_GT(elapsed, (2-1) * test::EpsilonTimeout());
+ EXPECT_LT(elapsed, (2+1) * test::EpsilonTimeout());
+ EXPECT_TRUE(did_wait);
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result);
+}
+
+TEST(SimpleDispatcherTest, MultipleWaiters) {
+ static const uint32_t kNumWaiters = 20;
+
+ bool did_wait[kNumWaiters];
+ MojoResult result[kNumWaiters];
+ uint32_t context[kNumWaiters];
+
+ // All wait for readable and becomes readable after some time.
+ {
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ ScopedVector<test::WaiterThread> threads;
+ for (uint32_t i = 0; i < kNumWaiters; i++) {
+ threads.push_back(new test::WaiterThread(d,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ i,
+ &did_wait[i],
+ &result[i],
+ &context[i]));
+ threads.back()->Start();
+ }
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ } // Joins the threads.
+ for (uint32_t i = 0; i < kNumWaiters; i++) {
+ EXPECT_TRUE(did_wait[i]);
+ EXPECT_EQ(MOJO_RESULT_OK, result[i]);
+ EXPECT_EQ(i, context[i]);
+ }
+
+ // Some wait for readable, some for writable, and becomes readable after some
+ // time.
+ {
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ ScopedVector<test::WaiterThread> threads;
+ for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+ threads.push_back(new test::WaiterThread(d,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ i,
+ &did_wait[i],
+ &result[i],
+ &context[i]));
+ threads.back()->Start();
+ }
+ for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+ threads.push_back(new test::WaiterThread(d,
+ MOJO_HANDLE_SIGNAL_WRITABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ i,
+ &did_wait[i],
+ &result[i],
+ &context[i]));
+ threads.back()->Start();
+ }
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ // This will wake up the ones waiting to write.
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ } // Joins the threads.
+ for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+ EXPECT_TRUE(did_wait[i]);
+ EXPECT_EQ(MOJO_RESULT_OK, result[i]);
+ EXPECT_EQ(i, context[i]);
+ }
+ for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+ EXPECT_TRUE(did_wait[i]);
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result[i]);
+ EXPECT_EQ(i, context[i]);
+ }
+
+ // Some wait for readable, some for writable, and becomes readable and
+ // never-writable after some time.
+ {
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ ScopedVector<test::WaiterThread> threads;
+ for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+ threads.push_back(new test::WaiterThread(d,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ i,
+ &did_wait[i],
+ &result[i],
+ &context[i]));
+ threads.back()->Start();
+ }
+ for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+ threads.push_back(new test::WaiterThread(d,
+ MOJO_HANDLE_SIGNAL_WRITABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ i,
+ &did_wait[i],
+ &result[i],
+ &context[i]));
+ threads.back()->Start();
+ }
+ base::PlatformThread::Sleep(1 * test::EpsilonTimeout());
+ d->SetSatisfiableSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ base::PlatformThread::Sleep(1 * test::EpsilonTimeout());
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ } // Joins the threads.
+ for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+ EXPECT_TRUE(did_wait[i]);
+ EXPECT_EQ(MOJO_RESULT_OK, result[i]);
+ EXPECT_EQ(i, context[i]);
+ }
+ for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+ EXPECT_TRUE(did_wait[i]);
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result[i]);
+ EXPECT_EQ(i, context[i]);
+ }
+
+ // Some wait for readable, some for writable, and becomes readable after some
+ // time.
+ {
+ scoped_refptr<MockSimpleDispatcher> d(new MockSimpleDispatcher());
+ ScopedVector<test::WaiterThread> threads;
+ for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+ threads.push_back(
+ new test::WaiterThread(d,
+ MOJO_HANDLE_SIGNAL_READABLE,
+ 3 * test::EpsilonTimeout().InMicroseconds(),
+ i,
+ &did_wait[i], &result[i], &context[i]));
+ threads.back()->Start();
+ }
+ for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+ threads.push_back(
+ new test::WaiterThread(d,
+ MOJO_HANDLE_SIGNAL_WRITABLE,
+ 1 * test::EpsilonTimeout().InMicroseconds(),
+ i,
+ &did_wait[i], &result[i], &context[i]));
+ threads.back()->Start();
+ }
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ d->SetSatisfiedSignals(MOJO_HANDLE_SIGNAL_READABLE);
+ // All those waiting for writable should have timed out.
+ EXPECT_EQ(MOJO_RESULT_OK, d->Close());
+ } // Joins the threads.
+ for (uint32_t i = 0; i < kNumWaiters / 2; i++) {
+ EXPECT_TRUE(did_wait[i]);
+ EXPECT_EQ(MOJO_RESULT_OK, result[i]);
+ EXPECT_EQ(i, context[i]);
+ }
+ for (uint32_t i = kNumWaiters / 2; i < kNumWaiters; i++) {
+ EXPECT_TRUE(did_wait[i]);
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result[i]);
+ }
+}
+
+// TODO(vtl): Stress test?
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/system_impl_export.h b/chromium/mojo/system/system_impl_export.h
new file mode 100644
index 00000000000..a35eacfe5a5
--- /dev/null
+++ b/chromium/mojo/system/system_impl_export.h
@@ -0,0 +1,29 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_SYSTEM_IMPL_EXPORT_H_
+#define MOJO_SYSTEM_SYSTEM_IMPL_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(MOJO_SYSTEM_IMPL_IMPLEMENTATION)
+#define MOJO_SYSTEM_IMPL_EXPORT __declspec(dllexport)
+#else
+#define MOJO_SYSTEM_IMPL_EXPORT __declspec(dllimport)
+#endif // defined(MOJO_SYSTEM_IMPL_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(MOJO_SYSTEM_IMPL_IMPLEMENTATION)
+#define MOJO_SYSTEM_IMPL_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_SYSTEM_IMPL_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define MOJO_SYSTEM_IMPL_EXPORT
+#endif
+
+#endif // MOJO_SYSTEM_SYSTEM_IMPL_EXPORT_H_
diff --git a/chromium/mojo/system/test_utils.cc b/chromium/mojo/system/test_utils.cc
new file mode 100644
index 00000000000..48f109138f0
--- /dev/null
+++ b/chromium/mojo/system/test_utils.cc
@@ -0,0 +1,96 @@
+// Copyright 2013 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 "mojo/system/test_utils.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/test/test_timeouts.h"
+#include "build/build_config.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+namespace {
+
+void PostTaskAndWaitHelper(base::WaitableEvent* event,
+ const base::Closure& task) {
+ task.Run();
+ event->Signal();
+}
+
+} // namespace
+
+void PostTaskAndWait(scoped_refptr<base::TaskRunner> task_runner,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ base::WaitableEvent event(false, false);
+ task_runner->PostTask(from_here,
+ base::Bind(&PostTaskAndWaitHelper, &event, task));
+ event.Wait();
+}
+
+base::TimeDelta EpsilonTimeout() {
+ // Originally, our epsilon timeout was 10 ms, which was mostly fine but flaky
+ // on some Windows bots. I don't recall ever seeing flakes on other bots. At
+ // 30 ms tests seem reliable on Windows bots, but not at 25 ms. We'd like this
+ // timeout to be as small as possible (see the description in the .h file).
+ //
+ // Currently, |tiny_timeout()| is usually 100 ms (possibly scaled under ASAN,
+ // etc.). Based on this, set it to (usually be) 30 ms on Windows and 20 ms
+ // elsewhere.
+#if defined(OS_WIN)
+ return (TestTimeouts::tiny_timeout() * 3) / 10;
+#else
+ return (TestTimeouts::tiny_timeout() * 2) / 10;
+#endif
+}
+
+// TestIOThread ----------------------------------------------------------------
+
+TestIOThread::TestIOThread(Mode mode)
+ : io_thread_("test_io_thread"),
+ io_thread_started_(false) {
+ switch (mode) {
+ case kAutoStart:
+ Start();
+ return;
+ case kManualStart:
+ return;
+ }
+ CHECK(false) << "Invalid mode";
+}
+
+TestIOThread::~TestIOThread() {
+ Stop();
+}
+
+void TestIOThread::Start() {
+ CHECK(!io_thread_started_);
+ io_thread_started_ = true;
+ CHECK(io_thread_.StartWithOptions(
+ base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
+}
+
+void TestIOThread::Stop() {
+ // Note: It's okay to call |Stop()| even if the thread isn't running.
+ io_thread_.Stop();
+ io_thread_started_ = false;
+}
+
+void TestIOThread::PostTask(const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ task_runner()->PostTask(from_here, task);
+}
+
+void TestIOThread::PostTaskAndWait(const tracked_objects::Location& from_here,
+ const base::Closure& task) {
+ ::mojo::system::test::PostTaskAndWait(task_runner(), from_here, task);
+}
+
+} // namespace test
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/test_utils.h b/chromium/mojo/system/test_utils.h
new file mode 100644
index 00000000000..c1a1a1e0b47
--- /dev/null
+++ b/chromium/mojo/system/test_utils.h
@@ -0,0 +1,100 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_TEST_UTILS_H_
+#define MOJO_SYSTEM_TEST_UTILS_H_
+
+#include <stdint.h>
+
+#include "base/callback_forward.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/task_runner.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+
+namespace tracked_objects {
+class Location;
+}
+
+namespace mojo {
+namespace system {
+namespace test {
+
+// Posts the given task (to the given task runner) and waits for it to complete.
+// (Note: Doesn't spin the current thread's message loop, so if you're careless
+// this could easily deadlock.)
+void PostTaskAndWait(scoped_refptr<base::TaskRunner> task_runner,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task);
+
+// A timeout smaller than |TestTimeouts::tiny_timeout()|. Warning: This may lead
+// to flakiness, but this is unavoidable if, e.g., you're trying to ensure that
+// functions with timeouts are reasonably accurate. We want this to be as small
+// as possible without causing too much flakiness.
+base::TimeDelta EpsilonTimeout();
+
+// Stopwatch -------------------------------------------------------------------
+
+// A simple "stopwatch" for measuring time elapsed from a given starting point.
+class Stopwatch {
+ public:
+ Stopwatch() {}
+ ~Stopwatch() {}
+
+ void Start() {
+ start_time_ = base::TimeTicks::HighResNow();
+ }
+
+ base::TimeDelta Elapsed() {
+ return base::TimeTicks::HighResNow() - start_time_;
+ }
+
+ private:
+ base::TimeTicks start_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(Stopwatch);
+};
+
+// TestIOThread ----------------------------------------------------------------
+
+class TestIOThread {
+ public:
+ enum Mode { kAutoStart, kManualStart };
+ explicit TestIOThread(Mode mode);
+ // Stops the I/O thread if necessary.
+ ~TestIOThread();
+
+ // |Start()|/|Stop()| should only be called from the main (creation) thread.
+ // After |Stop()|, |Start()| may be called again to start a new I/O thread.
+ // |Stop()| may be called even when the I/O thread is not started.
+ void Start();
+ void Stop();
+
+ void PostTask(const tracked_objects::Location& from_here,
+ const base::Closure& task);
+ void PostTaskAndWait(const tracked_objects::Location& from_here,
+ const base::Closure& task);
+
+ base::MessageLoopForIO* message_loop() {
+ return static_cast<base::MessageLoopForIO*>(io_thread_.message_loop());
+ }
+
+ scoped_refptr<base::TaskRunner> task_runner() {
+ return message_loop()->message_loop_proxy();
+ }
+
+ private:
+ base::Thread io_thread_;
+ bool io_thread_started_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestIOThread);
+};
+
+} // namespace test
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_TEST_UTILS_H_
diff --git a/chromium/mojo/system/transport_data.cc b/chromium/mojo/system/transport_data.cc
new file mode 100644
index 00000000000..10a3228ecf8
--- /dev/null
+++ b/chromium/mojo/system/transport_data.cc
@@ -0,0 +1,345 @@
+// Copyright 2014 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 "mojo/system/transport_data.h"
+
+#include <string.h>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "mojo/system/channel.h"
+#include "mojo/system/constants.h"
+#include "mojo/system/message_in_transit.h"
+
+namespace mojo {
+namespace system {
+
+// The maximum amount of space needed per platform handle.
+// (|{Channel,RawChannel}::GetSerializedPlatformHandleSize()| should always
+// return a value which is at most this. This is only used to calculate
+// |TransportData::kMaxBufferSize|. This value should be a multiple of the
+// alignment in order to simplify calculations, even though the actual amount of
+// space needed need not be a multiple of the alignment.
+const size_t kMaxSizePerPlatformHandle = 8;
+COMPILE_ASSERT(kMaxSizePerPlatformHandle %
+ MessageInTransit::kMessageAlignment == 0,
+ kMaxSizePerPlatformHandle_not_a_multiple_of_alignment);
+
+STATIC_CONST_MEMBER_DEFINITION const size_t
+ TransportData::kMaxSerializedDispatcherSize;
+STATIC_CONST_MEMBER_DEFINITION const size_t
+ TransportData::kMaxSerializedDispatcherPlatformHandles;
+
+// static
+const size_t TransportData::kMaxPlatformHandles =
+ kMaxMessageNumHandles * kMaxSerializedDispatcherPlatformHandles;
+
+// In additional to the header, for each attached (Mojo) handle there'll be a
+// handle table entry and serialized dispatcher data.
+// Note: This definition must follow the one for |kMaxPlatformHandles|;
+// otherwise, we get a static initializer with gcc (but not clang).
+// static
+const size_t TransportData::kMaxBufferSize =
+ sizeof(Header) +
+ kMaxMessageNumHandles * (sizeof(HandleTableEntry) +
+ kMaxSerializedDispatcherSize) +
+ kMaxPlatformHandles * kMaxSizePerPlatformHandle;
+
+struct TransportData::PrivateStructForCompileAsserts {
+ // The size of |Header| must be a multiple of the alignment.
+ COMPILE_ASSERT(sizeof(Header) % MessageInTransit::kMessageAlignment == 0,
+ sizeof_MessageInTransit_Header_invalid);
+
+ // The maximum serialized dispatcher size must be a multiple of the alignment.
+ COMPILE_ASSERT(kMaxSerializedDispatcherSize %
+ MessageInTransit::kMessageAlignment == 0,
+ kMaxSerializedDispatcherSize_not_a_multiple_of_alignment);
+
+ // The size of |HandleTableEntry| must be a multiple of the alignment.
+ COMPILE_ASSERT(sizeof(HandleTableEntry) %
+ MessageInTransit::kMessageAlignment == 0,
+ sizeof_MessageInTransit_HandleTableEntry_invalid);
+};
+
+TransportData::TransportData(scoped_ptr<DispatcherVector> dispatchers,
+ Channel* channel) {
+ DCHECK(dispatchers);
+ DCHECK(channel);
+
+ const size_t num_handles = dispatchers->size();
+ DCHECK_GT(num_handles, 0u);
+
+ // The offset to the start of the (Mojo) handle table.
+ const size_t handle_table_start_offset = sizeof(Header);
+ // The offset to the start of the serialized dispatcher data.
+ const size_t serialized_dispatcher_start_offset =
+ handle_table_start_offset + num_handles * sizeof(HandleTableEntry);
+ // The estimated size of the secondary buffer. We compute this estimate below.
+ // It must be at least as big as the (eventual) actual size.
+ size_t estimated_size = serialized_dispatcher_start_offset;
+ size_t estimated_num_platform_handles = 0;
+#if DCHECK_IS_ON
+ std::vector<size_t> all_max_sizes(num_handles);
+ std::vector<size_t> all_max_platform_handles(num_handles);
+#endif
+ for (size_t i = 0; i < num_handles; i++) {
+ if (Dispatcher* dispatcher = (*dispatchers)[i].get()) {
+ size_t max_size = 0;
+ size_t max_platform_handles = 0;
+ Dispatcher::TransportDataAccess::StartSerialize(
+ dispatcher, channel, &max_size, &max_platform_handles);
+
+ DCHECK_LE(max_size, kMaxSerializedDispatcherSize);
+ estimated_size += MessageInTransit::RoundUpMessageAlignment(max_size);
+ DCHECK_LE(estimated_size, kMaxBufferSize);
+
+ DCHECK_LE(max_platform_handles,
+ kMaxSerializedDispatcherPlatformHandles);
+ estimated_num_platform_handles += max_platform_handles;
+ DCHECK_LE(estimated_num_platform_handles, kMaxPlatformHandles);
+
+#if DCHECK_IS_ON
+ all_max_sizes[i] = max_size;
+ all_max_platform_handles[i] = max_platform_handles;
+#endif
+ }
+ }
+
+ size_t size_per_platform_handle = 0;
+ if (estimated_num_platform_handles > 0) {
+ size_per_platform_handle = channel->GetSerializedPlatformHandleSize();
+ DCHECK_LE(size_per_platform_handle, kMaxSizePerPlatformHandle);
+ estimated_size += estimated_num_platform_handles * size_per_platform_handle;
+ estimated_size = MessageInTransit::RoundUpMessageAlignment(estimated_size);
+ DCHECK_LE(estimated_size, kMaxBufferSize);
+ }
+
+ buffer_.reset(static_cast<char*>(
+ base::AlignedAlloc(estimated_size, MessageInTransit::kMessageAlignment)));
+ // Entirely clear out the secondary buffer, since then we won't have to worry
+ // about clearing padding or unused space (e.g., if a dispatcher fails to
+ // serialize).
+ memset(buffer_.get(), 0, estimated_size);
+
+ if (estimated_num_platform_handles > 0) {
+ DCHECK(!platform_handles_);
+ platform_handles_.reset(new embedder::PlatformHandleVector());
+ }
+
+ Header* header = reinterpret_cast<Header*>(buffer_.get());
+ header->num_handles = static_cast<uint32_t>(num_handles);
+ // (Okay to leave |platform_handle_table_offset|, |num_platform_handles|, and
+ // |unused| be zero; we'll set the former two later if necessary.)
+
+ HandleTableEntry* handle_table = reinterpret_cast<HandleTableEntry*>(
+ buffer_.get() + handle_table_start_offset);
+ size_t current_offset = serialized_dispatcher_start_offset;
+ for (size_t i = 0; i < num_handles; i++) {
+ Dispatcher* dispatcher = (*dispatchers)[i].get();
+ if (!dispatcher) {
+ COMPILE_ASSERT(Dispatcher::kTypeUnknown == 0,
+ value_of_Dispatcher_kTypeUnknown_must_be_zero);
+ continue;
+ }
+
+#if DCHECK_IS_ON
+ size_t old_platform_handles_size =
+ platform_handles_ ? platform_handles_->size() : 0;
+#endif
+
+ void* destination = buffer_.get() + current_offset;
+ size_t actual_size = 0;
+ if (Dispatcher::TransportDataAccess::EndSerializeAndClose(
+ dispatcher, channel, destination, &actual_size,
+ platform_handles_.get())) {
+ handle_table[i].type = static_cast<int32_t>(dispatcher->GetType());
+ handle_table[i].offset = static_cast<uint32_t>(current_offset);
+ handle_table[i].size = static_cast<uint32_t>(actual_size);
+ // (Okay to not set |unused| since we cleared the entire buffer.)
+
+#if DCHECK_IS_ON
+ DCHECK_LE(actual_size, all_max_sizes[i]);
+ DCHECK_LE(platform_handles_ ? (platform_handles_->size() -
+ old_platform_handles_size) : 0,
+ all_max_platform_handles[i]);
+#endif
+ } else {
+ // Nothing to do on failure, since |buffer_| was cleared, and
+ // |kTypeUnknown| is zero. The handle was simply closed.
+ LOG(ERROR) << "Failed to serialize handle to remote message pipe";
+ }
+
+ current_offset += MessageInTransit::RoundUpMessageAlignment(actual_size);
+ DCHECK_LE(current_offset, estimated_size);
+ DCHECK_LE(platform_handles_ ? platform_handles_->size() : 0,
+ estimated_num_platform_handles);
+ }
+
+ if (platform_handles_ && platform_handles_->size() > 0) {
+ header->platform_handle_table_offset =
+ static_cast<uint32_t>(current_offset);
+ header->num_platform_handles =
+ static_cast<uint32_t>(platform_handles_->size());
+ current_offset += platform_handles_->size() * size_per_platform_handle;
+ current_offset = MessageInTransit::RoundUpMessageAlignment(current_offset);
+ }
+
+ // There's no aligned realloc, so it's no good way to release unused space (if
+ // we overshot our estimated space requirements).
+ buffer_size_ = current_offset;
+
+ // |dispatchers_| will be destroyed as it goes out of scope.
+}
+
+#if defined(OS_POSIX)
+TransportData::TransportData(
+ embedder::ScopedPlatformHandleVectorPtr platform_handles)
+ : buffer_size_(sizeof(Header)),
+ platform_handles_(platform_handles.Pass()) {
+ buffer_.reset(static_cast<char*>(
+ base::AlignedAlloc(buffer_size_, MessageInTransit::kMessageAlignment)));
+ memset(buffer_.get(), 0, buffer_size_);
+}
+#endif // defined(OS_POSIX)
+
+TransportData::~TransportData() {
+}
+
+// static
+const char* TransportData::ValidateBuffer(
+ size_t serialized_platform_handle_size,
+ const void* buffer,
+ size_t buffer_size) {
+ DCHECK(buffer);
+ DCHECK_GT(buffer_size, 0u);
+
+ // Always make sure that the buffer size is sane; if it's not, someone's
+ // messing with us.
+ if (buffer_size < sizeof(Header) || buffer_size > kMaxBufferSize ||
+ buffer_size % MessageInTransit::kMessageAlignment != 0)
+ return "Invalid message secondary buffer size";
+
+ const Header* header = static_cast<const Header*>(buffer);
+ const size_t num_handles = header->num_handles;
+
+#if !defined(OS_POSIX)
+ // On POSIX, we send control messages with platform handles (but no handles)
+ // attached (see the comments for
+ // |TransportData(embedder::ScopedPlatformHandleVectorPtr)|. (This check isn't
+ // important security-wise anyway.)
+ if (num_handles == 0)
+ return "Message has no handles attached, but secondary buffer present";
+#endif
+
+ // Sanity-check |num_handles| (before multiplying it against anything).
+ if (num_handles > kMaxMessageNumHandles)
+ return "Message handle payload too large";
+
+ if (buffer_size < sizeof(Header) + num_handles * sizeof(HandleTableEntry))
+ return "Message secondary buffer too small";
+
+ if (header->num_platform_handles == 0) {
+ // Then |platform_handle_table_offset| should also be zero.
+ if (header->platform_handle_table_offset != 0) {
+ return
+ "Message has no handles attached, but platform handle table present";
+ }
+ } else {
+ // |num_handles| has already been validated, so the multiplication is okay.
+ if (header->num_platform_handles >
+ num_handles * kMaxSerializedDispatcherPlatformHandles)
+ return "Message has too many platform handles attached";
+
+ static const char kInvalidPlatformHandleTableOffset[] =
+ "Message has invalid platform handle table offset";
+ // This doesn't check that the platform handle table doesn't alias other
+ // stuff, but it doesn't matter, since it's all read-only.
+ if (header->platform_handle_table_offset %
+ MessageInTransit::kMessageAlignment != 0)
+ return kInvalidPlatformHandleTableOffset;
+
+ // ">" instead of ">=" since the size per handle may be zero.
+ if (header->platform_handle_table_offset > buffer_size)
+ return kInvalidPlatformHandleTableOffset;
+
+ // We already checked |platform_handle_table_offset| and
+ // |num_platform_handles|, so the addition and multiplication are okay.
+ if (header->platform_handle_table_offset +
+ header->num_platform_handles * serialized_platform_handle_size >
+ buffer_size)
+ return kInvalidPlatformHandleTableOffset;
+ }
+
+ const HandleTableEntry* handle_table =
+ reinterpret_cast<const HandleTableEntry*>(
+ static_cast<const char*>(buffer) + sizeof(Header));
+ static const char kInvalidSerializedDispatcher[] =
+ "Message contains invalid serialized dispatcher";
+ for (size_t i = 0; i < num_handles; i++) {
+ size_t offset = handle_table[i].offset;
+ if (offset % MessageInTransit::kMessageAlignment != 0)
+ return kInvalidSerializedDispatcher;
+
+ size_t size = handle_table[i].size;
+ if (size > kMaxSerializedDispatcherSize || size > buffer_size)
+ return kInvalidSerializedDispatcher;
+
+ // Note: This is an overflow-safe check for |offset + size > buffer_size|
+ // (we know that |size <= buffer_size| from the previous check).
+ if (offset > buffer_size - size)
+ return kInvalidSerializedDispatcher;
+ }
+
+ return NULL;
+}
+
+// static
+void TransportData::GetPlatformHandleTable(const void* transport_data_buffer,
+ size_t* num_platform_handles,
+ const void** platform_handle_table) {
+ DCHECK(transport_data_buffer);
+ DCHECK(num_platform_handles);
+ DCHECK(platform_handle_table);
+
+ const Header* header = static_cast<const Header*>(transport_data_buffer);
+ *num_platform_handles = header->num_platform_handles;
+ *platform_handle_table = static_cast<const char*>(transport_data_buffer) +
+ header->platform_handle_table_offset;
+}
+
+// static
+scoped_ptr<DispatcherVector> TransportData::DeserializeDispatchers(
+ const void* buffer,
+ size_t buffer_size,
+ embedder::ScopedPlatformHandleVectorPtr platform_handles,
+ Channel* channel) {
+ DCHECK(buffer);
+ DCHECK_GT(buffer_size, 0u);
+ DCHECK(channel);
+
+ const Header* header = static_cast<const Header*>(buffer);
+ const size_t num_handles = header->num_handles;
+ scoped_ptr<DispatcherVector> dispatchers(new DispatcherVector(num_handles));
+
+ const HandleTableEntry* handle_table =
+ reinterpret_cast<const HandleTableEntry*>(
+ static_cast<const char*>(buffer) + sizeof(Header));
+ for (size_t i = 0; i < num_handles; i++) {
+ size_t offset = handle_table[i].offset;
+ size_t size = handle_table[i].size;
+ // Should already have been checked by |ValidateBuffer()|:
+ DCHECK_EQ(offset % MessageInTransit::kMessageAlignment, 0u);
+ DCHECK_LE(offset, buffer_size);
+ DCHECK_LE(offset + size, buffer_size);
+
+ const void* source = static_cast<const char*>(buffer) + offset;
+ (*dispatchers)[i] = Dispatcher::TransportDataAccess::Deserialize(
+ channel, handle_table[i].type, source, size, platform_handles.get());
+ }
+
+ return dispatchers.Pass();
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/transport_data.h b/chromium/mojo/system/transport_data.h
new file mode 100644
index 00000000000..ddaa20b616b
--- /dev/null
+++ b/chromium/mojo/system/transport_data.h
@@ -0,0 +1,192 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_SYSTEM_TRANSPORT_DATA_H_
+#define MOJO_SYSTEM_TRANSPORT_DATA_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/aligned_memory.h"
+#include "base/memory/scoped_ptr.h"
+#include "build/build_config.h"
+#include "mojo/embedder/platform_handle.h"
+#include "mojo/embedder/platform_handle_vector.h"
+#include "mojo/system/dispatcher.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class Channel;
+
+// This class is used by |MessageInTransit| to represent handles (|Dispatcher|s)
+// in various stages of serialization.
+//
+// The stages are:
+// - Before reaching |TransportData|: Turn |DispatcherTransport|s into
+// |Dispatcher|s that are "owned" by (and attached to) a |MessageInTransit|.
+// This invalidates the handles in the space of the sending application
+// (and, e.g., if another thread is waiting on such a handle, it'll be
+// notified of this invalidation).
+// - Serialize these dispatchers into the |TransportData|: First, for each
+// attached dispatcher, there's an entry in the |TransportData|'s "handle
+// table", which points to a segment of (dispatcher-type-dependent) data.
+// - During the serialization of the dispatchers, |PlatformHandle|s may be
+// detached from the dispatchers and attached to the |TransportData|.
+// - Before sending the |MessageInTransit|, including its main buffer and the
+// |TransportData|'s buffer, the |Channel| sends any |PlatformHandle|s (in a
+// platform-, and possibly sandbox-situation-, specific way) first. In doing
+// so, it appends a "platform handle table" to the |TransportData|
+// containing information about how to deserialize these |PlatformHandle|s.
+// - Finally, at this point, to send the |MessageInTransit|, there only
+// remains "inert" data: the |MessageInTransit|'s main buffer and data from
+// the |TransportData|, consisting of the "handle table" (one entry for each
+// attached dispatcher), dispatcher-type-specific data (one segment for each
+// entry in the "handle table"), and the "platform handle table" (one entry
+// for each attached |PlatformHandle|).
+//
+// To receive a message (|MessageInTransit|), the "reverse" happens:
+// - On POSIX, receive and buffer |PlatformHandle|s (i.e., FDs), which were
+// sent before the "inert" data.
+// - Receive the "inert" data from the |MessageInTransit|. Examine its
+// "platform handle table". On POSIX, match its entries with the buffered
+// |PlatformHandle|s, which were previously received. On Windows, do what's
+// necessary to obtain |PlatformHandle|s (e.g.: i. if the sender is fully
+// trusted and able to duplicate handle into the receiver, then just pick
+// out the |HANDLE| value; ii. if the receiver is fully trusted and able to
+// duplicate handles from the receiver, do the |DuplicateHandle()|; iii.
+// otherwise, talk to a broker to get handles). Reattach all the
+// |PlatformHandle|s to the |MessageInTransit|.
+// - For each entry in the "handle table", use serialized dispatcher data to
+// reconstitute a dispatcher, taking ownership of associated
+// |PlatformHandle|s (and detaching them). Attach these dispatchers to the
+// |MessageInTransit|.
+// - At this point, the |MessageInTransit| consists of its main buffer
+// (primarily the data payload) and the attached dispatchers; the
+// |TransportData| can be discarded.
+// - When |MojoReadMessage()| is to give data to the application, attach the
+// dispatchers to the (global, "core") handle table, getting handles; give
+// the application the data payload and these handles.
+//
+// TODO(vtl): Everything above involving |PlatformHandle|s.
+class MOJO_SYSTEM_IMPL_EXPORT TransportData {
+ public:
+ // The maximum size of a single serialized dispatcher. This must be a multiple
+ // of |kMessageAlignment|.
+ static const size_t kMaxSerializedDispatcherSize = 10000;
+
+ // The maximum number of platform handles to attach for a single serialized
+ // dispatcher.
+ static const size_t kMaxSerializedDispatcherPlatformHandles = 2;
+
+ // The maximum possible size of a valid transport data buffer.
+ static const size_t kMaxBufferSize;
+
+ // The maximum total number of platform handles that may be attached.
+ static const size_t kMaxPlatformHandles;
+
+ TransportData(scoped_ptr<DispatcherVector> dispatchers, Channel* channel);
+
+#if defined(OS_POSIX)
+ // This is a hacky POSIX-only constructor to directly attach only platform
+ // handles to a message, used by |RawChannelPosix| to split messages with too
+ // many platform handles into multiple messages. |Header| will be present, but
+ // be zero. (No other information will be attached, and
+ // |RawChannel::GetSerializedPlatformHandleSize()| should return zero.)
+ explicit TransportData(
+ embedder::ScopedPlatformHandleVectorPtr platform_handles);
+#endif
+
+ ~TransportData();
+
+ const void* buffer() const { return buffer_.get(); }
+ void* buffer() { return buffer_.get(); }
+ size_t buffer_size() const { return buffer_size_; }
+
+ uint32_t platform_handle_table_offset() const {
+ return header()->platform_handle_table_offset;
+ }
+
+ // Gets attached platform-specific handles; this may return null if there are
+ // none. Note that the caller may mutate the set of platform-specific handles.
+ const embedder::PlatformHandleVector* platform_handles() const {
+ return platform_handles_.get();
+ }
+ embedder::PlatformHandleVector* platform_handles() {
+ return platform_handles_.get();
+ }
+
+ // Receive-side functions:
+
+ // Checks if the given buffer (from the "wire") looks like a valid
+ // |TransportData| buffer. (Should only be called if |buffer_size| is
+ // nonzero.) Returns null if valid, and a pointer to a human-readable error
+ // message (for debug/logging purposes) on error. Note: This checks the
+ // validity of the handle table entries (i.e., does range checking), but does
+ // not check that the validity of the actual serialized dispatcher
+ // information.
+ static const char* ValidateBuffer(size_t serialized_platform_handle_size,
+ const void* buffer,
+ size_t buffer_size);
+
+ // Gets the platform handle table from a (valid) |TransportData| buffer (which
+ // should have been validated using |ValidateBuffer()| first).
+ static void GetPlatformHandleTable(const void* transport_data_buffer,
+ size_t* num_platform_handles,
+ const void** platform_handle_table);
+
+ // Deserializes dispatchers from the given (serialized) transport data buffer
+ // (typically from a |MessageInTransit::View|) and vector of platform handles.
+ // |buffer| should be non-null and |buffer_size| should be nonzero.
+ static scoped_ptr<DispatcherVector> DeserializeDispatchers(
+ const void* buffer,
+ size_t buffer_size,
+ embedder::ScopedPlatformHandleVectorPtr platform_handles,
+ Channel* channel);
+
+ private:
+ // To allow us to make compile-assertions about |Header|, etc. in the .cc
+ // file.
+ struct PrivateStructForCompileAsserts;
+
+ // Header for the "secondary buffer"/"transport data". Must be a multiple of
+ // |MessageInTransit::kMessageAlignment| in size. Must be POD.
+ struct Header {
+ uint32_t num_handles;
+ // TODO(vtl): Not used yet:
+ uint32_t platform_handle_table_offset;
+ uint32_t num_platform_handles;
+ uint32_t unused;
+ };
+
+ struct HandleTableEntry {
+ int32_t type; // From |Dispatcher::Type| (|kTypeUnknown| for "invalid").
+ uint32_t offset; // Relative to the start of the "secondary buffer".
+ uint32_t size; // (Not including any padding.)
+ uint32_t unused;
+ };
+
+ const Header* header() const {
+ return reinterpret_cast<const Header*>(buffer_.get());
+ }
+
+ size_t buffer_size_;
+ scoped_ptr<char, base::AlignedFreeDeleter> buffer_; // Never null.
+
+ // Any platform-specific handles attached to this message (for inter-process
+ // transport). The vector (if any) owns the handles that it contains (and is
+ // responsible for closing them).
+ // TODO(vtl): With C++11, change it to a vector of |ScopedPlatformHandles|.
+ embedder::ScopedPlatformHandleVectorPtr platform_handles_;
+
+ DISALLOW_COPY_AND_ASSIGN(TransportData);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_TRANSPORT_DATA_H_
diff --git a/chromium/mojo/system/waiter.cc b/chromium/mojo/system/waiter.cc
new file mode 100644
index 00000000000..5a3150d3bce
--- /dev/null
+++ b/chromium/mojo/system/waiter.cc
@@ -0,0 +1,98 @@
+// Copyright 2013 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 "mojo/system/waiter.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/time/time.h"
+
+namespace mojo {
+namespace system {
+
+Waiter::Waiter()
+ : cv_(&lock_),
+#ifndef NDEBUG
+ initialized_(false),
+#endif
+ awoken_(false),
+ awake_result_(MOJO_RESULT_INTERNAL),
+ awake_context_(static_cast<uint32_t>(-1)) {
+}
+
+Waiter::~Waiter() {
+}
+
+void Waiter::Init() {
+#ifndef NDEBUG
+ initialized_ = true;
+#endif
+ awoken_ = false;
+ // NOTE(vtl): If performance ever becomes an issue, we can disable the setting
+ // of |awake_result_| (except the first one in |Awake()|) in Release builds.
+ awake_result_ = MOJO_RESULT_INTERNAL;
+}
+
+// TODO(vtl): Fast-path the |deadline == 0| case?
+MojoResult Waiter::Wait(MojoDeadline deadline, uint32_t* context) {
+ base::AutoLock locker(lock_);
+
+#ifndef NDEBUG
+ DCHECK(initialized_);
+ // It'll need to be re-initialized after this.
+ initialized_ = false;
+#endif
+
+ // Fast-path the already-awoken case:
+ if (awoken_) {
+ DCHECK_NE(awake_result_, MOJO_RESULT_INTERNAL);
+ if (context)
+ *context = awake_context_;
+ return awake_result_;
+ }
+
+ // |MojoDeadline| is actually a |uint64_t|, but we need a signed quantity.
+ // Treat any out-of-range deadline as "forever" (which is wrong, but okay
+ // since 2^63 microseconds is ~300000 years). Note that this also takes care
+ // of the |MOJO_DEADLINE_INDEFINITE| (= 2^64 - 1) case.
+ if (deadline > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
+ do {
+ cv_.Wait();
+ } while (!awoken_);
+ } else {
+ // NOTE(vtl): This is very inefficient on POSIX, since pthreads condition
+ // variables take an absolute deadline.
+ const base::TimeTicks end_time = base::TimeTicks::HighResNow() +
+ base::TimeDelta::FromMicroseconds(static_cast<int64_t>(deadline));
+ do {
+ base::TimeTicks now_time = base::TimeTicks::HighResNow();
+ if (now_time >= end_time)
+ return MOJO_RESULT_DEADLINE_EXCEEDED;
+
+ cv_.TimedWait(end_time - now_time);
+ } while (!awoken_);
+ }
+
+ DCHECK_NE(awake_result_, MOJO_RESULT_INTERNAL);
+ if (context)
+ *context = awake_context_;
+ return awake_result_;
+}
+
+void Waiter::Awake(MojoResult result, uint32_t context) {
+ base::AutoLock locker(lock_);
+
+ if (awoken_)
+ return;
+
+ awoken_ = true;
+ awake_result_ = result;
+ awake_context_ = context;
+ cv_.Signal();
+ // |cv_.Wait()|/|cv_.TimedWait()| will return after |lock_| is released.
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/waiter.h b/chromium/mojo/system/waiter.h
new file mode 100644
index 00000000000..66fe6e7ee88
--- /dev/null
+++ b/chromium/mojo/system/waiter.h
@@ -0,0 +1,81 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_WAITER_H_
+#define MOJO_SYSTEM_WAITER_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+// IMPORTANT (all-caps gets your attention, right?): |Waiter| methods are called
+// under other locks, in particular, |Dispatcher::lock_|s, so |Waiter| methods
+// must never call out to other objects (in particular, |Dispatcher|s). This
+// class is thread-safe.
+class MOJO_SYSTEM_IMPL_EXPORT Waiter {
+ public:
+ Waiter();
+ ~Waiter();
+
+ // A |Waiter| can be used multiple times; |Init()| should be called before
+ // each time it's used.
+ void Init();
+
+ // Waits until a suitable |Awake()| is called. (|context| may be null, in
+ // which case, obviously no context is ever returned.)
+ // Returns:
+ // - The result given to the first call to |Awake()| (possibly before this
+ // call to |Wait()|); in this case, |*context| is set to the value passed
+ // to that call to |Awake()|.
+ // - |MOJO_RESULT_DEADLINE_EXCEEDED| if the deadline was exceeded; in this
+ // case |*context| is not modified.
+ //
+ // Usually, the context passed to |Awake()| will be the value passed to
+ // |Dispatcher::AddWaiter()|, which is usually the index to the array of
+ // handles passed to |MojoWaitMany()| (or 0 for |MojoWait()|).
+ //
+ // Typical |Awake()| results are:
+ // - |MOJO_RESULT_OK| if one of the flags passed to
+ // |MojoWait()|/|MojoWaitMany()| (hence |Dispatcher::AddWaiter()|) was
+ // satisfied;
+ // - |MOJO_RESULT_CANCELLED| if a handle (on which
+ // |MojoWait()|/|MojoWaitMany()| was called) was closed (hence the
+ // dispatcher closed); and
+ // - |MOJO_RESULT_FAILED_PRECONDITION| if one of the set of flags passed to
+ // |MojoWait()|/|MojoWaitMany()| cannot or can no longer be satisfied by
+ // the corresponding handle (e.g., if the other end of a message or data
+ // pipe is closed).
+ MojoResult Wait(MojoDeadline deadline, uint32_t* context);
+
+ // Wake the waiter up with the given result and context (or no-op if it's been
+ // woken up already).
+ void Awake(MojoResult result, uint32_t context);
+
+ private:
+ base::ConditionVariable cv_; // Associated to |lock_|.
+ base::Lock lock_; // Protects the following members.
+#ifndef NDEBUG
+ bool initialized_;
+#endif
+ bool awoken_;
+ MojoResult awake_result_;
+ // This is a |uint32_t| because we really only need to store an index (for
+ // |MojoWaitMany()|). But in tests, it's convenient to use this for other
+ // purposes (e.g., to distinguish between different wake-up reasons).
+ uint32_t awake_context_;
+
+ DISALLOW_COPY_AND_ASSIGN(Waiter);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_WAITER_H_
diff --git a/chromium/mojo/system/waiter_list.cc b/chromium/mojo/system/waiter_list.cc
new file mode 100644
index 00000000000..6ee90100aa8
--- /dev/null
+++ b/chromium/mojo/system/waiter_list.cc
@@ -0,0 +1,57 @@
+// Copyright 2013 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 "mojo/system/waiter_list.h"
+
+#include "base/logging.h"
+#include "mojo/system/handle_signals_state.h"
+#include "mojo/system/waiter.h"
+
+namespace mojo {
+namespace system {
+
+WaiterList::WaiterList() {
+}
+
+WaiterList::~WaiterList() {
+ DCHECK(waiters_.empty());
+}
+
+void WaiterList::AwakeWaitersForStateChange(const HandleSignalsState& state) {
+ for (WaiterInfoList::iterator it = waiters_.begin(); it != waiters_.end();
+ ++it) {
+ if (state.satisfies(it->signals))
+ it->waiter->Awake(MOJO_RESULT_OK, it->context);
+ else if (!state.can_satisfy(it->signals))
+ it->waiter->Awake(MOJO_RESULT_FAILED_PRECONDITION, it->context);
+ }
+}
+
+void WaiterList::CancelAllWaiters() {
+ for (WaiterInfoList::iterator it = waiters_.begin(); it != waiters_.end();
+ ++it) {
+ it->waiter->Awake(MOJO_RESULT_CANCELLED, it->context);
+ }
+ waiters_.clear();
+}
+
+void WaiterList::AddWaiter(Waiter* waiter,
+ MojoHandleSignals signals,
+ uint32_t context) {
+ waiters_.push_back(WaiterInfo(waiter, signals, context));
+}
+
+void WaiterList::RemoveWaiter(Waiter* waiter) {
+ // We allow a thread to wait on the same handle multiple times simultaneously,
+ // so we need to scan the entire list and remove all occurrences of |waiter|.
+ for (WaiterInfoList::iterator it = waiters_.begin(); it != waiters_.end();) {
+ WaiterInfoList::iterator maybe_delete = it;
+ ++it;
+ if (maybe_delete->waiter == waiter)
+ waiters_.erase(maybe_delete);
+ }
+}
+
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/waiter_list.h b/chromium/mojo/system/waiter_list.h
new file mode 100644
index 00000000000..39ac7b00100
--- /dev/null
+++ b/chromium/mojo/system/waiter_list.h
@@ -0,0 +1,58 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_WAITER_LIST_H_
+#define MOJO_SYSTEM_WAITER_LIST_H_
+
+#include <stdint.h>
+
+#include <list>
+
+#include "base/macros.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/system/system_impl_export.h"
+
+namespace mojo {
+namespace system {
+
+class Waiter;
+struct HandleSignalsState;
+
+// |WaiterList| tracks all the |Waiter|s that are waiting on a given
+// handle/|Dispatcher|. There should be a |WaiterList| for each handle that can
+// be waited on (in any way). In the simple case, the |WaiterList| is owned by
+// the |Dispatcher|, whereas in more complex cases it is owned by the secondary
+// object (see simple_dispatcher.* and the explanatory comment in core.cc). This
+// class is thread-unsafe (all concurrent access must be protected by some
+// lock).
+class MOJO_SYSTEM_IMPL_EXPORT WaiterList {
+ public:
+ WaiterList();
+ ~WaiterList();
+
+ void AwakeWaitersForStateChange(const HandleSignalsState& state);
+ void CancelAllWaiters();
+ void AddWaiter(Waiter* waiter, MojoHandleSignals signals, uint32_t context);
+ void RemoveWaiter(Waiter* waiter);
+
+ private:
+ struct WaiterInfo {
+ WaiterInfo(Waiter* waiter, MojoHandleSignals signals, uint32_t context)
+ : waiter(waiter), signals(signals), context(context) {}
+
+ Waiter* waiter;
+ MojoHandleSignals signals;
+ uint32_t context;
+ };
+ typedef std::list<WaiterInfo> WaiterInfoList;
+
+ WaiterInfoList waiters_;
+
+ DISALLOW_COPY_AND_ASSIGN(WaiterList);
+};
+
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_WAITER_LIST_H_
diff --git a/chromium/mojo/system/waiter_list_unittest.cc b/chromium/mojo/system/waiter_list_unittest.cc
new file mode 100644
index 00000000000..1abe5e7eb4b
--- /dev/null
+++ b/chromium/mojo/system/waiter_list_unittest.cc
@@ -0,0 +1,297 @@
+// Copyright 2013 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.
+
+// NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a
+// heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to
+// increase tolerance and reduce observed flakiness (though doing so reduces the
+// meaningfulness of the test).
+
+#include "mojo/system/waiter_list.h"
+
+#include "base/threading/platform_thread.h" // For |Sleep()|.
+#include "base/time/time.h"
+#include "mojo/system/handle_signals_state.h"
+#include "mojo/system/test_utils.h"
+#include "mojo/system/waiter.h"
+#include "mojo/system/waiter_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+TEST(WaiterListTest, BasicCancel) {
+ MojoResult result;
+ uint32_t context;
+
+ // Cancel immediately after thread start.
+ {
+ WaiterList waiter_list;
+ test::SimpleWaiterThread thread(&result, &context);
+ waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1);
+ thread.Start();
+ waiter_list.CancelAllWaiters();
+ waiter_list.RemoveWaiter(thread.waiter()); // Double-remove okay.
+ } // Join |thread|.
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+ EXPECT_EQ(1u, context);
+
+ // Cancel before after thread start.
+ {
+ WaiterList waiter_list;
+ test::SimpleWaiterThread thread(&result, &context);
+ waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2);
+ waiter_list.CancelAllWaiters();
+ thread.Start();
+ } // Join |thread|.
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+ EXPECT_EQ(2u, context);
+
+ // Cancel some time after thread start.
+ {
+ WaiterList waiter_list;
+ test::SimpleWaiterThread thread(&result, &context);
+ waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3);
+ thread.Start();
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ waiter_list.CancelAllWaiters();
+ } // Join |thread|.
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+ EXPECT_EQ(3u, context);
+}
+
+TEST(WaiterListTest, BasicAwakeSatisfied) {
+ MojoResult result;
+ uint32_t context;
+
+ // Awake immediately after thread start.
+ {
+ WaiterList waiter_list;
+ test::SimpleWaiterThread thread(&result, &context);
+ waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1);
+ thread.Start();
+ waiter_list.AwakeWaitersForStateChange(
+ HandleSignalsState(MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_HANDLE_SIGNAL_READABLE |
+ MOJO_HANDLE_SIGNAL_WRITABLE));
+ waiter_list.RemoveWaiter(thread.waiter());
+ } // Join |thread|.
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(1u, context);
+
+ // Awake before after thread start.
+ {
+ WaiterList waiter_list;
+ test::SimpleWaiterThread thread(&result, &context);
+ waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2);
+ waiter_list.AwakeWaitersForStateChange(
+ HandleSignalsState(MOJO_HANDLE_SIGNAL_WRITABLE,
+ MOJO_HANDLE_SIGNAL_READABLE |
+ MOJO_HANDLE_SIGNAL_WRITABLE));
+ waiter_list.RemoveWaiter(thread.waiter());
+ waiter_list.RemoveWaiter(thread.waiter()); // Double-remove okay.
+ thread.Start();
+ } // Join |thread|.
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(2u, context);
+
+ // Awake some time after thread start.
+ {
+ WaiterList waiter_list;
+ test::SimpleWaiterThread thread(&result, &context);
+ waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3);
+ thread.Start();
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ waiter_list.AwakeWaitersForStateChange(
+ HandleSignalsState(MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_HANDLE_SIGNAL_READABLE |
+ MOJO_HANDLE_SIGNAL_WRITABLE));
+ waiter_list.RemoveWaiter(thread.waiter());
+ } // Join |thread|.
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(3u, context);
+}
+
+TEST(WaiterListTest, BasicAwakeUnsatisfiable) {
+ MojoResult result;
+ uint32_t context;
+
+ // Awake (for unsatisfiability) immediately after thread start.
+ {
+ WaiterList waiter_list;
+ test::SimpleWaiterThread thread(&result, &context);
+ waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1);
+ thread.Start();
+ waiter_list.AwakeWaitersForStateChange(
+ HandleSignalsState(MOJO_HANDLE_SIGNAL_NONE,
+ MOJO_HANDLE_SIGNAL_WRITABLE));
+ waiter_list.RemoveWaiter(thread.waiter());
+ } // Join |thread|.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+ EXPECT_EQ(1u, context);
+
+ // Awake (for unsatisfiability) before after thread start.
+ {
+ WaiterList waiter_list;
+ test::SimpleWaiterThread thread(&result, &context);
+ waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2);
+ waiter_list.AwakeWaitersForStateChange(
+ HandleSignalsState(MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_HANDLE_SIGNAL_READABLE));
+ waiter_list.RemoveWaiter(thread.waiter());
+ thread.Start();
+ } // Join |thread|.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+ EXPECT_EQ(2u, context);
+
+ // Awake (for unsatisfiability) some time after thread start.
+ {
+ WaiterList waiter_list;
+ test::SimpleWaiterThread thread(&result, &context);
+ waiter_list.AddWaiter(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3);
+ thread.Start();
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ waiter_list.AwakeWaitersForStateChange(
+ HandleSignalsState(MOJO_HANDLE_SIGNAL_NONE,
+ MOJO_HANDLE_SIGNAL_WRITABLE));
+ waiter_list.RemoveWaiter(thread.waiter());
+ waiter_list.RemoveWaiter(thread.waiter()); // Double-remove okay.
+ } // Join |thread|.
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+ EXPECT_EQ(3u, context);
+}
+
+TEST(WaiterListTest, MultipleWaiters) {
+ MojoResult result1;
+ MojoResult result2;
+ MojoResult result3;
+ MojoResult result4;
+ uint32_t context1;
+ uint32_t context2;
+ uint32_t context3;
+ uint32_t context4;
+
+ // Cancel two waiters.
+ {
+ WaiterList waiter_list;
+ test::SimpleWaiterThread thread1(&result1, &context1);
+ waiter_list.AddWaiter(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1);
+ thread1.Start();
+ test::SimpleWaiterThread thread2(&result2, &context2);
+ waiter_list.AddWaiter(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2);
+ thread2.Start();
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ waiter_list.CancelAllWaiters();
+ } // Join threads.
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result1);
+ EXPECT_EQ(1u, context1);
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result2);
+ EXPECT_EQ(2u, context2);
+
+ // Awake one waiter, cancel other.
+ {
+ WaiterList waiter_list;
+ test::SimpleWaiterThread thread1(&result1, &context1);
+ waiter_list.AddWaiter(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3);
+ thread1.Start();
+ test::SimpleWaiterThread thread2(&result2, &context2);
+ waiter_list.AddWaiter(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 4);
+ thread2.Start();
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ waiter_list.AwakeWaitersForStateChange(
+ HandleSignalsState(MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_HANDLE_SIGNAL_READABLE |
+ MOJO_HANDLE_SIGNAL_WRITABLE));
+ waiter_list.RemoveWaiter(thread1.waiter());
+ waiter_list.CancelAllWaiters();
+ } // Join threads.
+ EXPECT_EQ(MOJO_RESULT_OK, result1);
+ EXPECT_EQ(3u, context1);
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result2);
+ EXPECT_EQ(4u, context2);
+
+ // Cancel one waiter, awake other for unsatisfiability.
+ {
+ WaiterList waiter_list;
+ test::SimpleWaiterThread thread1(&result1, &context1);
+ waiter_list.AddWaiter(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 5);
+ thread1.Start();
+ test::SimpleWaiterThread thread2(&result2, &context2);
+ waiter_list.AddWaiter(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 6);
+ thread2.Start();
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ waiter_list.AwakeWaitersForStateChange(
+ HandleSignalsState(MOJO_HANDLE_SIGNAL_NONE,
+ MOJO_HANDLE_SIGNAL_READABLE));
+ waiter_list.RemoveWaiter(thread2.waiter());
+ waiter_list.CancelAllWaiters();
+ } // Join threads.
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result1);
+ EXPECT_EQ(5u, context1);
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result2);
+ EXPECT_EQ(6u, context2);
+
+ // Cancel one waiter, awake other for unsatisfiability.
+ {
+ WaiterList waiter_list;
+ test::SimpleWaiterThread thread1(&result1, &context1);
+ waiter_list.AddWaiter(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 7);
+ thread1.Start();
+
+ base::PlatformThread::Sleep(1 * test::EpsilonTimeout());
+
+ // Should do nothing.
+ waiter_list.AwakeWaitersForStateChange(
+ HandleSignalsState(MOJO_HANDLE_SIGNAL_NONE,
+ MOJO_HANDLE_SIGNAL_READABLE |
+ MOJO_HANDLE_SIGNAL_WRITABLE));
+
+ test::SimpleWaiterThread thread2(&result2, &context2);
+ waiter_list.AddWaiter(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 8);
+ thread2.Start();
+
+ base::PlatformThread::Sleep(1 * test::EpsilonTimeout());
+
+ // Awake #1.
+ waiter_list.AwakeWaitersForStateChange(
+ HandleSignalsState(MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_HANDLE_SIGNAL_READABLE |
+ MOJO_HANDLE_SIGNAL_WRITABLE));
+ waiter_list.RemoveWaiter(thread1.waiter());
+
+ base::PlatformThread::Sleep(1 * test::EpsilonTimeout());
+
+ test::SimpleWaiterThread thread3(&result3, &context3);
+ waiter_list.AddWaiter(thread3.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 9);
+ thread3.Start();
+
+ test::SimpleWaiterThread thread4(&result4, &context4);
+ waiter_list.AddWaiter(thread4.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 10);
+ thread4.Start();
+
+ base::PlatformThread::Sleep(1 * test::EpsilonTimeout());
+
+ // Awake #2 and #3 for unsatisfiability.
+ waiter_list.AwakeWaitersForStateChange(
+ HandleSignalsState(MOJO_HANDLE_SIGNAL_NONE,
+ MOJO_HANDLE_SIGNAL_READABLE));
+ waiter_list.RemoveWaiter(thread2.waiter());
+ waiter_list.RemoveWaiter(thread3.waiter());
+
+ // Cancel #4.
+ waiter_list.CancelAllWaiters();
+ } // Join threads.
+ EXPECT_EQ(MOJO_RESULT_OK, result1);
+ EXPECT_EQ(7u, context1);
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result2);
+ EXPECT_EQ(8u, context2);
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result3);
+ EXPECT_EQ(9u, context3);
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result4);
+ EXPECT_EQ(10u, context4);
+}
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/waiter_test_utils.cc b/chromium/mojo/system/waiter_test_utils.cc
new file mode 100644
index 00000000000..f762e985f90
--- /dev/null
+++ b/chromium/mojo/system/waiter_test_utils.cc
@@ -0,0 +1,66 @@
+// Copyright 2013 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 "mojo/system/waiter_test_utils.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+SimpleWaiterThread::SimpleWaiterThread(MojoResult* result, uint32_t* context)
+ : base::SimpleThread("waiter_thread"),
+ result_(result),
+ context_(context) {
+ waiter_.Init();
+ *result_ = -5420734; // Totally invalid result.
+ *context_ = 23489023; // "Random".
+}
+
+SimpleWaiterThread::~SimpleWaiterThread() {
+ Join();
+}
+
+void SimpleWaiterThread::Run() {
+ *result_ = waiter_.Wait(MOJO_DEADLINE_INDEFINITE, context_);
+}
+
+WaiterThread::WaiterThread(scoped_refptr<Dispatcher> dispatcher,
+ MojoHandleSignals handle_signals,
+ MojoDeadline deadline,
+ uint32_t context,
+ bool* did_wait_out,
+ MojoResult* result_out,
+ uint32_t* context_out)
+ : base::SimpleThread("waiter_thread"),
+ dispatcher_(dispatcher),
+ handle_signals_(handle_signals),
+ deadline_(deadline),
+ context_(context),
+ did_wait_out_(did_wait_out),
+ result_out_(result_out),
+ context_out_(context_out) {
+ *did_wait_out_ = false;
+ *result_out_ = -8542346; // Totally invalid result.
+ *context_out_ = 89023444; // "Random".
+}
+
+WaiterThread::~WaiterThread() {
+ Join();
+}
+
+void WaiterThread::Run() {
+ waiter_.Init();
+
+ *result_out_ = dispatcher_->AddWaiter(&waiter_, handle_signals_, context_);
+ if (*result_out_ != MOJO_RESULT_OK)
+ return;
+
+ *did_wait_out_ = true;
+ *result_out_ = waiter_.Wait(deadline_, context_out_);
+ dispatcher_->RemoveWaiter(&waiter_);
+}
+
+} // namespace test
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/system/waiter_test_utils.h b/chromium/mojo/system/waiter_test_utils.h
new file mode 100644
index 00000000000..95e0f51f90f
--- /dev/null
+++ b/chromium/mojo/system/waiter_test_utils.h
@@ -0,0 +1,101 @@
+// Copyright 2013 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.
+
+#ifndef MOJO_SYSTEM_WAITER_TEST_UTILS_H_
+#define MOJO_SYSTEM_WAITER_TEST_UTILS_H_
+
+#include <stdint.h>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/simple_thread.h"
+#include "mojo/public/c/system/types.h"
+#include "mojo/system/dispatcher.h"
+#include "mojo/system/waiter.h"
+
+namespace mojo {
+namespace system {
+namespace test {
+
+// This is a very simple thread that has a |Waiter|, on which it waits
+// indefinitely (and records the result). It will create and initialize the
+// |Waiter| on creation, but the caller must start the thread with |Start()|. It
+// will join the thread on destruction.
+//
+// One usually uses it like:
+//
+// MojoResult result;
+// {
+// WaiterList waiter_list;
+// test::SimpleWaiterThread thread(&result);
+// waiter_list.AddWaiter(thread.waiter(), ...);
+// thread.Start();
+// ... some stuff to wake the waiter ...
+// waiter_list.RemoveWaiter(thread.waiter());
+// } // Join |thread|.
+// EXPECT_EQ(..., result);
+//
+// There's a bit of unrealism in its use: In this sort of usage, calls such as
+// |Waiter::Init()|, |AddWaiter()|, and |RemoveWaiter()| are done in the main
+// (test) thread, not the waiter thread (as would actually happen in real code).
+// (We accept this unrealism for simplicity, since |WaiterList| is
+// thread-unsafe so making it more realistic would require adding nontrivial
+// synchronization machinery.)
+class SimpleWaiterThread : public base::SimpleThread {
+ public:
+ // For the duration of the lifetime of this object, |*result| belongs to it
+ // (in the sense that it will write to it whenever it wants).
+ SimpleWaiterThread(MojoResult* result, uint32_t* context);
+ virtual ~SimpleWaiterThread(); // Joins the thread.
+
+ Waiter* waiter() { return &waiter_; }
+
+ private:
+ virtual void Run() OVERRIDE;
+
+ MojoResult* const result_;
+ uint32_t* const context_;
+ Waiter waiter_;
+
+ DISALLOW_COPY_AND_ASSIGN(SimpleWaiterThread);
+};
+
+// This is a more complex and realistic thread that has a |Waiter|, on which it
+// waits for the given deadline (with the given flags). Unlike
+// |SimpleWaiterThread|, it requires the machinery of |Dispatcher|.
+class WaiterThread : public base::SimpleThread {
+ public:
+ // Note: |*did_wait_out|, |*result_out|, and |*context_out| "belong" to this
+ // object (i.e., may be modified by, on some other thread) while it's alive.
+ WaiterThread(scoped_refptr<Dispatcher> dispatcher,
+ MojoHandleSignals handle_signals,
+ MojoDeadline deadline,
+ uint32_t context,
+ bool* did_wait_out,
+ MojoResult* result_out,
+ uint32_t* context_out);
+ virtual ~WaiterThread();
+
+ private:
+ virtual void Run() OVERRIDE;
+
+ const scoped_refptr<Dispatcher> dispatcher_;
+ const MojoHandleSignals handle_signals_;
+ const MojoDeadline deadline_;
+ const uint32_t context_;
+ bool* const did_wait_out_;
+ MojoResult* const result_out_;
+ uint32_t* const context_out_;
+
+ Waiter waiter_;
+
+ DISALLOW_COPY_AND_ASSIGN(WaiterThread);
+};
+
+} // namespace test
+} // namespace system
+} // namespace mojo
+
+#endif // MOJO_SYSTEM_WAITER_TEST_UTILS_H_
diff --git a/chromium/mojo/system/waiter_unittest.cc b/chromium/mojo/system/waiter_unittest.cc
new file mode 100644
index 00000000000..3afef28aa20
--- /dev/null
+++ b/chromium/mojo/system/waiter_unittest.cc
@@ -0,0 +1,303 @@
+// Copyright 2013 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.
+
+// NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a
+// heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to
+// increase tolerance and reduce observed flakiness (though doing so reduces the
+// meaningfulness of the test).
+
+#include "mojo/system/waiter.h"
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h" // For |Sleep()|.
+#include "base/threading/simple_thread.h"
+#include "base/time/time.h"
+#include "mojo/system/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace system {
+namespace {
+
+const int64_t kMicrosPerMs = 1000;
+const int64_t kPollTimeMicros = 10 * kMicrosPerMs; // 10 ms.
+
+class WaitingThread : public base::SimpleThread {
+ public:
+ explicit WaitingThread(MojoDeadline deadline)
+ : base::SimpleThread("waiting_thread"),
+ deadline_(deadline),
+ done_(false),
+ result_(MOJO_RESULT_UNKNOWN),
+ context_(static_cast<uint32_t>(-1)) {
+ waiter_.Init();
+ }
+
+ virtual ~WaitingThread() {
+ Join();
+ }
+
+ void WaitUntilDone(MojoResult* result,
+ uint32_t* context,
+ base::TimeDelta* elapsed) {
+ for (;;) {
+ {
+ base::AutoLock locker(lock_);
+ if (done_) {
+ *result = result_;
+ *context = context_;
+ *elapsed = elapsed_;
+ break;
+ }
+ }
+
+ base::PlatformThread::Sleep(
+ base::TimeDelta::FromMicroseconds(kPollTimeMicros));
+ }
+ }
+
+ Waiter* waiter() { return &waiter_; }
+
+ private:
+ virtual void Run() OVERRIDE {
+ test::Stopwatch stopwatch;
+ MojoResult result;
+ uint32_t context = static_cast<uint32_t>(-1);
+ base::TimeDelta elapsed;
+
+ stopwatch.Start();
+ result = waiter_.Wait(deadline_, &context);
+ elapsed = stopwatch.Elapsed();
+
+ {
+ base::AutoLock locker(lock_);
+ done_ = true;
+ result_ = result;
+ context_ = context;
+ elapsed_ = elapsed;
+ }
+ }
+
+ const MojoDeadline deadline_;
+ Waiter waiter_; // Thread-safe.
+
+ base::Lock lock_; // Protects the following members.
+ bool done_;
+ MojoResult result_;
+ uint32_t context_;
+ base::TimeDelta elapsed_;
+
+ DISALLOW_COPY_AND_ASSIGN(WaitingThread);
+};
+
+TEST(WaiterTest, Basic) {
+ MojoResult result;
+ uint32_t context;
+ base::TimeDelta elapsed;
+
+ // Finite deadline.
+
+ // Awake immediately after thread start.
+ {
+ WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds());
+ thread.Start();
+ thread.waiter()->Awake(MOJO_RESULT_OK, 1);
+ thread.WaitUntilDone(&result, &context, &elapsed);
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(1u, context);
+ EXPECT_LT(elapsed, test::EpsilonTimeout());
+ }
+
+ // Awake before after thread start.
+ {
+ WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds());
+ thread.waiter()->Awake(MOJO_RESULT_CANCELLED, 2);
+ thread.Start();
+ thread.WaitUntilDone(&result, &context, &elapsed);
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+ EXPECT_EQ(2u, context);
+ EXPECT_LT(elapsed, test::EpsilonTimeout());
+ }
+
+ // Awake some time after thread start.
+ {
+ WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds());
+ thread.Start();
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ thread.waiter()->Awake(1, 3);
+ thread.WaitUntilDone(&result, &context, &elapsed);
+ EXPECT_EQ(1, result);
+ EXPECT_EQ(3u, context);
+ EXPECT_GT(elapsed, (2-1) * test::EpsilonTimeout());
+ EXPECT_LT(elapsed, (2+1) * test::EpsilonTimeout());
+ }
+
+ // Awake some longer time after thread start.
+ {
+ WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds());
+ thread.Start();
+ base::PlatformThread::Sleep(5 * test::EpsilonTimeout());
+ thread.waiter()->Awake(2, 4);
+ thread.WaitUntilDone(&result, &context, &elapsed);
+ EXPECT_EQ(2, result);
+ EXPECT_EQ(4u, context);
+ EXPECT_GT(elapsed, (5-1) * test::EpsilonTimeout());
+ EXPECT_LT(elapsed, (5+1) * test::EpsilonTimeout());
+ }
+
+ // Don't awake -- time out (on another thread).
+ {
+ WaitingThread thread(2 * test::EpsilonTimeout().InMicroseconds());
+ thread.Start();
+ thread.WaitUntilDone(&result, &context, &elapsed);
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result);
+ EXPECT_EQ(static_cast<uint32_t>(-1), context);
+ EXPECT_GT(elapsed, (2-1) * test::EpsilonTimeout());
+ EXPECT_LT(elapsed, (2+1) * test::EpsilonTimeout());
+ }
+
+ // No (indefinite) deadline.
+
+ // Awake immediately after thread start.
+ {
+ WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
+ thread.Start();
+ thread.waiter()->Awake(MOJO_RESULT_OK, 5);
+ thread.WaitUntilDone(&result, &context, &elapsed);
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(5u, context);
+ EXPECT_LT(elapsed, test::EpsilonTimeout());
+ }
+
+ // Awake before after thread start.
+ {
+ WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
+ thread.waiter()->Awake(MOJO_RESULT_CANCELLED, 6);
+ thread.Start();
+ thread.WaitUntilDone(&result, &context, &elapsed);
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+ EXPECT_EQ(6u, context);
+ EXPECT_LT(elapsed, test::EpsilonTimeout());
+ }
+
+ // Awake some time after thread start.
+ {
+ WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
+ thread.Start();
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ thread.waiter()->Awake(1, 7);
+ thread.WaitUntilDone(&result, &context, &elapsed);
+ EXPECT_EQ(1, result);
+ EXPECT_EQ(7u, context);
+ EXPECT_GT(elapsed, (2-1) * test::EpsilonTimeout());
+ EXPECT_LT(elapsed, (2+1) * test::EpsilonTimeout());
+ }
+
+ // Awake some longer time after thread start.
+ {
+ WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
+ thread.Start();
+ base::PlatformThread::Sleep(5 * test::EpsilonTimeout());
+ thread.waiter()->Awake(2, 8);
+ thread.WaitUntilDone(&result, &context, &elapsed);
+ EXPECT_EQ(2, result);
+ EXPECT_EQ(8u, context);
+ EXPECT_GT(elapsed, (5-1) * test::EpsilonTimeout());
+ EXPECT_LT(elapsed, (5+1) * test::EpsilonTimeout());
+ }
+}
+
+TEST(WaiterTest, TimeOut) {
+ test::Stopwatch stopwatch;
+ base::TimeDelta elapsed;
+
+ Waiter waiter;
+ uint32_t context = 123;
+
+ waiter.Init();
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, &context));
+ elapsed = stopwatch.Elapsed();
+ EXPECT_LT(elapsed, test::EpsilonTimeout());
+ EXPECT_EQ(123u, context);
+
+ waiter.Init();
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+ waiter.Wait(2 * test::EpsilonTimeout().InMicroseconds(), &context));
+ elapsed = stopwatch.Elapsed();
+ EXPECT_GT(elapsed, (2-1) * test::EpsilonTimeout());
+ EXPECT_LT(elapsed, (2+1) * test::EpsilonTimeout());
+ EXPECT_EQ(123u, context);
+
+ waiter.Init();
+ stopwatch.Start();
+ EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
+ waiter.Wait(5 * test::EpsilonTimeout().InMicroseconds(), &context));
+ elapsed = stopwatch.Elapsed();
+ EXPECT_GT(elapsed, (5-1) * test::EpsilonTimeout());
+ EXPECT_LT(elapsed, (5+1) * test::EpsilonTimeout());
+ EXPECT_EQ(123u, context);
+}
+
+// The first |Awake()| should always win.
+TEST(WaiterTest, MultipleAwakes) {
+ MojoResult result;
+ uint32_t context;
+ base::TimeDelta elapsed;
+
+ {
+ WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
+ thread.Start();
+ thread.waiter()->Awake(MOJO_RESULT_OK, 1);
+ thread.waiter()->Awake(1, 2);
+ thread.WaitUntilDone(&result, &context, &elapsed);
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(1u, context);
+ EXPECT_LT(elapsed, test::EpsilonTimeout());
+ }
+
+ {
+ WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
+ thread.waiter()->Awake(1, 3);
+ thread.Start();
+ thread.waiter()->Awake(MOJO_RESULT_OK, 4);
+ thread.WaitUntilDone(&result, &context, &elapsed);
+ EXPECT_EQ(1, result);
+ EXPECT_EQ(3u, context);
+ EXPECT_LT(elapsed, test::EpsilonTimeout());
+ }
+
+ {
+ WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
+ thread.Start();
+ thread.waiter()->Awake(10, 5);
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ thread.waiter()->Awake(20, 6);
+ thread.WaitUntilDone(&result, &context, &elapsed);
+ EXPECT_EQ(10, result);
+ EXPECT_EQ(5u, context);
+ EXPECT_LT(elapsed, test::EpsilonTimeout());
+ }
+
+ {
+ WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds());
+ thread.Start();
+ base::PlatformThread::Sleep(1 * test::EpsilonTimeout());
+ thread.waiter()->Awake(MOJO_RESULT_FAILED_PRECONDITION, 7);
+ base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
+ thread.waiter()->Awake(MOJO_RESULT_OK, 8);
+ thread.WaitUntilDone(&result, &context, &elapsed);
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+ EXPECT_EQ(7u, context);
+ EXPECT_GT(elapsed, (1-1) * test::EpsilonTimeout());
+ EXPECT_LT(elapsed, (1+1) * test::EpsilonTimeout());
+ }
+}
+
+} // namespace
+} // namespace system
+} // namespace mojo
diff --git a/chromium/mojo/tools/check_mojom_golden_files.py b/chromium/mojo/tools/check_mojom_golden_files.py
new file mode 100755
index 00000000000..6c2e187c8ae
--- /dev/null
+++ b/chromium/mojo/tools/check_mojom_golden_files.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+# Copyright 2014 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.
+
+import argparse
+import os.path
+import sys
+from filecmp import dircmp
+from shutil import rmtree
+from tempfile import mkdtemp
+
+_script_dir = os.path.dirname(os.path.abspath(__file__))
+_mojo_dir = os.path.join(_script_dir, os.pardir)
+sys.path.insert(0, os.path.join(_mojo_dir, "public", "tools", "bindings",
+ "pylib"))
+from mojom_tests.support.find_files import FindFiles
+from mojom_tests.support.run_bindings_generator import RunBindingsGenerator
+
+
+def _ProcessDircmpResults(results, verbose=False):
+ """Prints results of directory comparison and returns true if they are
+ identical (note: the "left" directory should be the golden directory)."""
+ rv = not (bool(results.left_only) or bool(results.right_only) or \
+ bool(results.common_funny) or bool(results.funny_files) or \
+ bool(results.diff_files))
+ if verbose:
+ for f in results.left_only:
+ print "%s exists in golden directory but not in current output" % f
+ for f in results.right_only:
+ print "%s exists in current output but not in golden directory" % f
+ for f in results.common_funny + results.funny_files:
+ print "Unable to compare %s between golden directory and current output" \
+ % f
+ for f in results.diff_files:
+ print "%s differs between golden directory and current output" % f
+ for r in results.subdirs.values():
+ # If we're being verbose, check subdirectories even if we know that there
+ # are differences. Note that it's "... and rv" to avoid the short-circuit.
+ if rv or verbose:
+ rv = _ProcessDircmpResults(r, verbose=verbose) and rv
+ return rv
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--generate_golden_files", action="store_true",
+ help=("generate golden files (does not obliterate "
+ "directory"))
+ parser.add_argument("--keep_temp_dir", action="store_true",
+ help="don't delete the temporary directory")
+ parser.add_argument("--verbose", action="store_true",
+ help="spew excess verbiage")
+ parser.add_argument("golden_dir", metavar="GOLDEN_DIR",
+ help="directory with the golden files")
+ args = parser.parse_args()
+
+ if args.generate_golden_files:
+ if os.path.exists(args.golden_dir):
+ print "WARNING: golden directory %s already exists" % args.golden_dir
+ out_dir = args.golden_dir
+ else:
+ if not os.path.exists(args.golden_dir):
+ print "ERROR: golden directory %s does not exist" % args.golden_dir
+ return 1
+ out_dir = mkdtemp()
+ if args.verbose:
+ print "Generating files to %s ..." % out_dir
+
+ mojom_files = FindFiles(_mojo_dir, "*.mojom")
+ for mojom_file in mojom_files:
+ if args.verbose:
+ print " Processing %s ..." % os.path.relpath(mojom_file, _mojo_dir)
+ RunBindingsGenerator(out_dir, _mojo_dir, mojom_file)
+
+ if args.generate_golden_files:
+ return 0
+
+ identical = _ProcessDircmpResults(dircmp(args.golden_dir, out_dir, ignore=[]),
+ verbose=args.verbose)
+
+ if args.keep_temp_dir:
+ if args.verbose:
+ print "Not removing %s ..." % out_dir
+ else:
+ if args.verbose:
+ print "Removing %s ..." % out_dir
+ rmtree(out_dir)
+
+ if not identical:
+ print "FAILURE: current output differs from golden files"
+ return 1
+
+ print "SUCCESS: current output identical to golden files"
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/chromium/mojo/tools/data/unittests b/chromium/mojo/tools/data/unittests
new file mode 100644
index 00000000000..7939ae24970
--- /dev/null
+++ b/chromium/mojo/tools/data/unittests
@@ -0,0 +1,26 @@
+# This file contains a list of Mojo gtest unit tests.
+# Prepend * to indicate that results shouldn't be cached (e.g., if the test has
+# other data dependencies).
+# TODO(vtl): Add a way of specifying data dependencies instead.
+
+# System tests:
+mojo_system_unittests
+
+# Public tests:
+*mojo_public_bindings_unittests
+mojo_public_environment_unittests
+mojo_public_system_unittests
+mojo_public_utility_unittests
+
+# Non-system, non-public tests:
+mojo_common_unittests
+mojo_service_manager_unittests
+mojo_view_manager_lib_unittests
+mojo_view_manager_unittests
+
+# JavaScript tests:
+*mojo_apps_js_unittests
+*mojo_js_unittests
+
+# Shell integration tests:
+*mojo_shell_tests
diff --git a/chromium/mojo/tools/generate_java_callback_interfaces.py b/chromium/mojo/tools/generate_java_callback_interfaces.py
new file mode 100644
index 00000000000..fdf1f74d8a9
--- /dev/null
+++ b/chromium/mojo/tools/generate_java_callback_interfaces.py
@@ -0,0 +1,59 @@
+"""Generate the org.chromium.mojo.bindings.Callbacks interface"""
+
+import argparse
+import sys
+
+CALLBACK_TEMPLATE = ("""
+ /**
+ * A generic %d-argument callback.
+ *
+ * %s
+ */
+ interface Callback%d<%s> {
+ /**
+ * Call the callback.
+ */
+ public void call(%s);
+ }
+""")
+
+INTERFACE_TEMPLATE = (
+"""// Copyright 2014 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.
+
+// This file was generated using
+// mojo/tools/generate_java_callback_interfaces.py
+
+package org.chromium.mojo.bindings;
+
+/**
+ * Contains a generic interface for callbacks.
+ */
+public interface Callbacks {
+%s
+}""")
+
+def GenerateCallback(nb_args):
+ params = '\n * '.join(
+ ['@param <T%d> the type of argument %d.' % (i+1, i+1)
+ for i in xrange(nb_args)])
+ template_parameters = ', '.join(['T%d' % (i+1) for i in xrange(nb_args)])
+ callback_parameters = ', '.join(['T%d arg%d' % ((i+1), (i+1))
+ for i in xrange(nb_args)])
+ return CALLBACK_TEMPLATE % (nb_args, params, nb_args, template_parameters,
+ callback_parameters)
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Generate org.chromium.mojo.bindings.Callbacks")
+ parser.add_argument("max_args", nargs=1, type=int,
+ help="maximal number of arguments to generate callbacks for")
+ args = parser.parse_args()
+ max_args = args.max_args[0]
+ print INTERFACE_TEMPLATE % ''.join([GenerateCallback(i+1)
+ for i in xrange(max_args)])
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/chromium/mojo/tools/message_generator.cc b/chromium/mojo/tools/message_generator.cc
new file mode 100644
index 00000000000..86fcab21af1
--- /dev/null
+++ b/chromium/mojo/tools/message_generator.cc
@@ -0,0 +1,63 @@
+// Copyright 2014 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 "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/lib/message_internal.h"
+#include "mojo/public/cpp/bindings/message.h"
+
+// This file is used to generate various files corresponding to mojo
+// messages. The various binding implementations can parse these to verify they
+// correctly decode messages.
+//
+// The output consists of each byte of the message encoded in a hex string with
+// a newline after it.
+namespace mojo {
+namespace {
+
+std::string BinaryToHex(const base::StringPiece& piece) {
+ std::string result("// File generated by mojo_message_generator.\n");;
+ result.reserve(result.size() + (piece.size() * 5));
+ for (size_t i = 0; i < piece.size(); ++i)
+ base::StringAppendF(&result, "0X%.2X\n", static_cast<int>(piece.data()[i]));
+ return result;
+}
+
+void WriteMessageToFile(const Message& message, const base::FilePath& path) {
+ const std::string hex_message(BinaryToHex(
+ base::StringPiece(reinterpret_cast<const char*>(message.data()),
+ message.data_num_bytes())));
+ CHECK_EQ(static_cast<int>(hex_message.size()),
+ base::WriteFile(path, hex_message.data(),
+ static_cast<int>(hex_message.size())));
+}
+
+// Generates a message of type MessageData. The message uses the name 21,
+// with 4 bytes of payload: 0x9, 0x8, 0x7, 0x6.
+void GenerateMessageDataMessage() {
+ internal::MessageBuilder builder(static_cast<uint32_t>(21),
+ static_cast<size_t>(4));
+ char* data = static_cast<char*>(builder.buffer()->Allocate(4));
+ DCHECK(data);
+ data[0] = 9;
+ data[1] = 8;
+ data[2] = 7;
+ data[3] = 6;
+
+ Message message;
+ builder.Finish(&message);
+ WriteMessageToFile(message,
+ base::FilePath(FILE_PATH_LITERAL("message_data")));
+}
+
+} // namespace
+} // namespace mojo
+
+int main(int argc, char** argv) {
+ mojo::GenerateMessageDataMessage();
+ return 0;
+}
diff --git a/chromium/mojo/tools/mojob.sh b/chromium/mojo/tools/mojob.sh
new file mode 100755
index 00000000000..b870cd13014
--- /dev/null
+++ b/chromium/mojo/tools/mojob.sh
@@ -0,0 +1,186 @@
+#!/bin/bash
+# Copyright 2013 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.
+
+# This a simple script to make building/testing Mojo components easier (on
+# Linux).
+
+# TODO(vtl): Maybe make the test runner smart and not run unchanged test
+# binaries.
+# TODO(vtl) Maybe also provide a way to pass command-line arguments to the test
+# binaries.
+
+do_help() {
+ cat << EOF
+Usage: $(basename "$0") [command|option ...]
+
+command should be one of:
+ build - Build.
+ test - Run unit tests (does not build).
+ perftest - Run perf tests (does not build).
+ pytest - Run Python unit tests.
+ gyp - Run gyp for mojo (does not sync), with clang.
+ sync - Sync using gclient (does not run gyp).
+ show-bash-alias - Outputs an appropriate bash alias for mojob. In bash do:
+ \$ eval \`mojo/tools/mojob.sh show-bash-alias\`
+
+option (which will only apply to following commands) should be one of:
+ Build/test options (specified before build/test/perftest):
+ --debug - Build/test in Debug mode.
+ --release - Build/test in Release mode.
+ --debug-and-release - Build/test in both Debug and Release modes (default).
+ Compiler options (specified before gyp):
+ --clang - Use clang (default).
+ --gcc - Use gcc.
+ Component options:
+ --shared Build components as shared libraries (default).
+ --static Build components as static libraries.
+ Mojo in chromium/content (crbug.com/353602):
+ --use-mojo - Enabled (default).
+ --no-use-mojo - Disabled.
+
+Note: It will abort on the first failure (if any).
+EOF
+}
+
+do_build() {
+ echo "Building in out/$1 ..."
+ ninja -C "out/$1" mojo || exit 1
+}
+
+do_unittests() {
+ echo "Running unit tests in out/$1 ..."
+ mojo/tools/test_runner.py mojo/tools/data/unittests "out/$1" \
+ mojob_test_successes || exit 1
+}
+
+do_perftests() {
+ echo "Running perf tests in out/$1 ..."
+ "out/$1/mojo_public_system_perftests" || exit 1
+}
+
+do_pytests() {
+ python mojo/tools/run_mojo_python_tests.py || exit 1
+}
+
+do_gyp() {
+ local gyp_defines="$(make_gyp_defines)"
+ echo "Running gyp with GYP_DEFINES=$gyp_defines ..."
+ GYP_DEFINES="$gyp_defines" build/gyp_chromium mojo/mojo.gyp || exit 1
+}
+
+do_sync() {
+ # Note: sync only (with hooks, but no gyp-ing).
+ GYP_CHROMIUM_NO_ACTION=1 gclient sync || exit 1
+}
+
+# Valid values: Debug, Release, or Debug_and_Release.
+BUILD_TEST_TYPE=Debug_and_Release
+should_do_Debug() {
+ test "$BUILD_TEST_TYPE" = Debug -o "$BUILD_TEST_TYPE" = Debug_and_Release
+}
+should_do_Release() {
+ test "$BUILD_TEST_TYPE" = Release -o "$BUILD_TEST_TYPE" = Debug_and_Release
+}
+
+# Valid values: clang or gcc.
+COMPILER=clang
+# Valid values: shared or static.
+COMPONENT=shared
+make_gyp_defines() {
+ local options=()
+ # Always include these options.
+ options+=("use_aura=1")
+ case "$COMPILER" in
+ clang)
+ options+=("clang=1")
+ ;;
+ gcc)
+ options+=("clang=0")
+ ;;
+ esac
+ case "$COMPONENT" in
+ shared)
+ options+=("component=shared_library")
+ ;;
+ static)
+ options+=("component=static_library")
+ ;;
+ esac
+ echo ${options[*]}
+}
+
+# We're in src/mojo/tools. We want to get to src.
+cd "$(realpath "$(dirname "$0")")/../.."
+
+if [ $# -eq 0 ]; then
+ do_help
+ exit 0
+fi
+
+for arg in "$@"; do
+ case "$arg" in
+ # Commands -----------------------------------------------------------------
+ help|--help)
+ do_help
+ exit 0
+ ;;
+ build)
+ should_do_Debug && do_build Debug
+ should_do_Release && do_build Release
+ ;;
+ test)
+ should_do_Debug && do_unittests Debug
+ should_do_Release && do_unittests Release
+ ;;
+ perftest)
+ should_do_Debug && do_perftests Debug
+ should_do_Release && do_perftests Release
+ ;;
+ pytest)
+ do_pytests
+ ;;
+ gyp)
+ do_gyp
+ ;;
+ sync)
+ do_sync
+ ;;
+ show-bash-alias)
+ # You want to type something like:
+ # alias mojob=\
+ # '"$(pwd | sed '"'"'s/\(.*\/src\).*/\1/'"'"')/mojo/tools/mojob.sh"'
+ # This is quoting hell, so we simply escape every non-alphanumeric
+ # character.
+ echo alias\ mojob\=\'\"\$\(pwd\ \|\ sed\ \'\"\'\"\'s\/\\\(\.\*\\\/src\\\)\
+\.\*\/\\1\/\'\"\'\"\'\)\/mojo\/tools\/mojob\.sh\"\'
+ ;;
+ # Options ------------------------------------------------------------------
+ --debug)
+ BUILD_TEST_TYPE=Debug
+ ;;
+ --release)
+ BUILD_TEST_TYPE=Release
+ ;;
+ --debug-and-release)
+ BUILD_TEST_TYPE=Debug_and_Release
+ ;;
+ --clang)
+ COMPILER=clang
+ ;;
+ --gcc)
+ COMPILER=gcc
+ ;;
+ --shared)
+ COMPONENT=shared
+ ;;
+ --static)
+ COMPONENT=static
+ ;;
+ *)
+ echo "Unknown command \"${arg}\". Try \"$(basename "$0") help\"."
+ exit 1
+ ;;
+ esac
+done
diff --git a/chromium/mojo/tools/mojosh.sh b/chromium/mojo/tools/mojosh.sh
new file mode 100755
index 00000000000..bb8a2cdca4a
--- /dev/null
+++ b/chromium/mojo/tools/mojosh.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+# Copyright 2014 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.
+
+# This a simple script to make running Mojo shell easier (on Linux).
+
+DIRECTORY="$(dirname "$0")"/../../out/Debug
+PORT=$(($RANDOM % 8192 + 2000))
+
+do_help() {
+ cat << EOF
+Usage: $(basename "$0") [-d DIRECTORY] [-|--] <mojo_shell arguments ...>
+
+DIRECTORY defaults to $DIRECTORY.
+
+Example:
+ $(basename "$0") mojo:mojo_sample_app
+EOF
+}
+
+kill_http_server() {
+ echo "Killing SimpleHTTPServer ..."
+ kill $HTTP_SERVER_PID
+ wait $HTTP_SERVER_PID
+}
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -h|--help)
+ do_help
+ exit 0
+ ;;
+ -d)
+ shift
+ if [ $# -eq 0 ]; then
+ do_help
+ exit 1
+ fi
+ DIRECTORY="$1"
+ ;;
+ --show-bash-alias)
+ echo alias\ mojosh\=\'\"\$\(pwd\ \|\ sed\ \'\"\'\"\'s\/\\\(\.\*\\\/src\\\
+\)\.\*\/\\1\/\'\"\'\"\'\)\/mojo\/tools\/mojosh\.sh\"\'
+ exit 0
+ ;;
+ # Separate arguments to mojo_shell (e.g., in case you want to pass it -d).
+ -|--)
+ shift
+ break
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+done
+
+echo "Base directory: $DIRECTORY"
+
+echo "Running SimpleHTTPServer in directory $DIRECTORY/lib on port $PORT"
+cd $DIRECTORY/lib || exit 1
+python -m SimpleHTTPServer $PORT &
+# Kill the HTTP server on exit (even if the user kills everything using ^C).
+HTTP_SERVER_PID=$!
+trap kill_http_server EXIT
+cd ..
+
+echo "Running:"
+echo "./mojo_shell --origin=http://127.0.0.1:$PORT --disable-cache $*"
+./mojo_shell --origin=http://127.0.0.1:$PORT --disable-cache $*
diff --git a/chromium/mojo/tools/pylib/transitive_hash.py b/chromium/mojo/tools/pylib/transitive_hash.py
new file mode 100644
index 00000000000..93e8dc4e75e
--- /dev/null
+++ b/chromium/mojo/tools/pylib/transitive_hash.py
@@ -0,0 +1,89 @@
+# Copyright 2014 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.
+
+import logging
+import subprocess
+import sys
+
+from hashlib import sha256
+from os.path import basename, realpath
+
+_logging = logging.getLogger()
+
+# Based on/taken from
+# http://code.activestate.com/recipes/578231-probably-the-fastest-memoization-decorator-in-the-/
+# (with cosmetic changes).
+def _memoize(f):
+ """Memoization decorator for a function taking a single argument."""
+ class Memoize(dict):
+ def __missing__(self, key):
+ rv = self[key] = f(key)
+ return rv
+ return Memoize().__getitem__
+
+@_memoize
+def _file_hash(filename):
+ """Returns a string representing the hash of the given file."""
+ _logging.debug("Hashing %s ...", filename)
+ rv = subprocess.check_output(['sha256sum', '-b', filename]).split(None, 1)[0]
+ _logging.debug(" => %s", rv)
+ return rv
+
+@_memoize
+def _get_dependencies(filename):
+ """Returns a list of filenames for files that the given file depends on."""
+ _logging.debug("Getting dependencies for %s ...", filename)
+ lines = subprocess.check_output(['ldd', filename]).splitlines()
+ rv = []
+ for line in lines:
+ i = line.find('/')
+ if i < 0:
+ _logging.debug(" => no file found in line: %s", line)
+ continue
+ rv.append(line[i:].split(None, 1)[0])
+ _logging.debug(" => %s", rv)
+ return rv
+
+def transitive_hash(filename):
+ """Returns a string that represents the "transitive" hash of the given
+ file. The transitive hash is a hash of the file and all the shared libraries
+ on which it depends (done in an order-independent way)."""
+ hashes = set()
+ to_hash = [filename]
+ while to_hash:
+ current_filename = realpath(to_hash.pop())
+ current_hash = _file_hash(current_filename)
+ if current_hash in hashes:
+ _logging.debug("Already seen %s (%s) ...", current_filename, current_hash)
+ continue
+ _logging.debug("Haven't seen %s (%s) ...", current_filename, current_hash)
+ hashes.add(current_hash)
+ to_hash.extend(_get_dependencies(current_filename))
+ return sha256('|'.join(sorted(hashes))).hexdigest()
+
+def main(argv):
+ logging.basicConfig()
+ # Uncomment to debug:
+ # _logging.setLevel(logging.DEBUG)
+
+ if len(argv) < 2:
+ print """\
+Usage: %s [file] ...
+
+Prints the \"transitive\" hash of each (executable) file. The transitive
+hash is a hash of the file and all the shared libraries on which it
+depends (done in an order-independent way).""" % basename(argv[0])
+ return 0
+
+ rv = 0
+ for filename in argv[1:]:
+ try:
+ print transitive_hash(filename), filename
+ except:
+ print "ERROR", filename
+ rv = 1
+ return rv
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/chromium/mojo/tools/run_mojo_python_tests.py b/chromium/mojo/tools/run_mojo_python_tests.py
new file mode 100755
index 00000000000..c96aba36d2c
--- /dev/null
+++ b/chromium/mojo/tools/run_mojo_python_tests.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python
+# Copyright 2014 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.
+
+import optparse
+import os
+import re
+import sys
+import unittest
+
+
+def main():
+ parser = optparse.OptionParser()
+ parser.usage = 'run_mojo_python_tests.py [options] [tests...]'
+ parser.add_option('-v', '--verbose', action='count', default=0)
+ parser.add_option('--unexpected-failures', metavar='FILENAME', action='store',
+ help=('path to write a list of any tests that fail '
+ 'unexpectedly.'))
+ parser.epilog = ('If --unexpected-failures is passed, a list of the tests '
+ 'that failed (one per line) will be written to the file. '
+ 'If no tests failed, the file will be truncated (empty). '
+ 'If the test run did not completely properly, or something '
+ 'else weird happened, any existing file will be left '
+ 'unmodified. '
+ 'If --unexpected-failures is *not* passed, any existing '
+ 'file will be ignored and left unmodified.')
+ options, args = parser.parse_args()
+
+ chromium_src_dir = os.path.join(os.path.dirname(__file__),
+ os.pardir,
+ os.pardir)
+
+ loader = unittest.loader.TestLoader()
+ print "Running Python unit tests under mojo/public/tools/bindings/pylib ..."
+
+ pylib_dir = os.path.join(chromium_src_dir, 'mojo', 'public',
+ 'tools', 'bindings', 'pylib')
+ if args:
+ if not pylib_dir in sys.path:
+ sys.path.append(pylib_dir)
+ suite = unittest.TestSuite()
+ for test_name in args:
+ suite.addTests(loader.loadTestsFromName(test_name))
+ else:
+ suite = loader.discover(pylib_dir, pattern='*_unittest.py')
+
+ runner = unittest.runner.TextTestRunner(verbosity=(options.verbose + 1))
+ result = runner.run(suite)
+
+ if options.unexpected_failures:
+ WriteUnexpectedFailures(result, options.unexpected_failures)
+
+ return 0 if result.wasSuccessful() else 1
+
+
+def WriteUnexpectedFailures(result, path):
+
+ # This regex and UnitTestName() extracts the test_name in a way
+ # that can be handed back to the loader successfully.
+
+ test_description = re.compile("(\w+) \(([\w.]+)\)")
+
+ def UnitTestName(test):
+ m = test_description.match(str(test))
+ return "%s.%s" % (m.group(2), m.group(1))
+
+ with open(path, 'w') as fp:
+ for (test, _) in result.failures + result.errors:
+ fp.write(UnitTestName(test) + '\n')
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/chromium/mojo/tools/test_runner.py b/chromium/mojo/tools/test_runner.py
new file mode 100755
index 00000000000..b0bb4d9f94b
--- /dev/null
+++ b/chromium/mojo/tools/test_runner.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+# Copyright 2014 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.
+
+"""A "smart" test runner for gtest unit tests (that caches successes)."""
+
+import logging
+import os
+import subprocess
+import sys
+
+_logging = logging.getLogger()
+
+_script_dir = os.path.dirname(os.path.abspath(__file__))
+sys.path.insert(0, os.path.join(_script_dir, "pylib"))
+
+from transitive_hash import transitive_hash
+
+def main(argv):
+ logging.basicConfig()
+ # Uncomment to debug:
+ # _logging.setLevel(logging.DEBUG)
+
+ if len(argv) < 3 or len(argv) > 4:
+ print "Usage: %s gtest_list_file root_dir [successes_cache_file]" % \
+ os.path.basename(argv[0])
+ return 0 if len(argv) < 2 else 1
+
+ _logging.debug("Test list file: %s", argv[1])
+ with open(argv[1], 'rb') as f:
+ gtest_list = [y for y in [x.strip() for x in f.readlines()] \
+ if y and y[0] != '#']
+ _logging.debug("Test list: %s" % gtest_list)
+
+ print "Running tests in directory: %s" % argv[2]
+ os.chdir(argv[2])
+
+ if len(argv) == 4 and argv[3]:
+ successes_cache_filename = argv[3]
+ print "Successes cache file: %s" % successes_cache_filename
+ else:
+ successes_cache_filename = None
+ print "No successes cache file (will run all tests unconditionally)"
+
+ if successes_cache_filename:
+ # This file simply contains a list of transitive hashes of tests that
+ # succeeded.
+ try:
+ _logging.debug("Trying to read successes cache file: %s",
+ successes_cache_filename)
+ with open(argv[3], 'rb') as f:
+ successes = set([x.strip() for x in f.readlines()])
+ _logging.debug("Successes: %s", successes)
+ except:
+ # Just assume that it didn't exist, or whatever.
+ print "Failed to read successes cache file %s (will create)" % argv[3]
+ successes = set()
+
+ # Run gtests with color if we're on a TTY (and we're not being told explicitly
+ # what to do).
+ if sys.stdout.isatty() and 'GTEST_COLOR' not in os.environ:
+ _logging.debug("Setting GTEST_COLOR=yes")
+ os.environ['GTEST_COLOR'] = 'yes'
+
+ # TODO(vtl): We may not close this file on failure.
+ successes_cache_file = open(successes_cache_filename, 'ab') \
+ if successes_cache_filename else None
+ for gtest in gtest_list:
+ if gtest[0] == '*':
+ gtest = gtest[1:]
+ _logging.debug("%s is marked as non-cacheable" % gtest)
+ cacheable = False
+ else:
+ cacheable = True
+
+ if successes_cache_file and cacheable:
+ _logging.debug("Getting transitive hash for %s ... " % gtest)
+ try:
+ gtest_hash = transitive_hash(gtest)
+ except:
+ print "Failed to get transitive hash for %s" % gtest
+ return 1
+ _logging.debug(" Transitive hash: %s" % gtest_hash)
+
+ if gtest_hash in successes:
+ print "Skipping %s (previously succeeded)" % gtest
+ continue
+
+ print "Running %s...." % gtest,
+ sys.stdout.flush()
+ try:
+ subprocess.check_output(["./" + gtest], stderr=subprocess.STDOUT)
+ print "Succeeded"
+ # Record success.
+ if successes_cache_filename and cacheable:
+ successes.add(gtest_hash)
+ successes_cache_file.write(gtest_hash + '\n')
+ successes_cache_file.flush()
+ except subprocess.CalledProcessError as e:
+ print "Failed with exit code %d and output:" % e.returncode
+ print 72 * '-'
+ print e.output
+ print 72 * '-'
+ return 1
+ except OSError as e:
+ print " Failed to start test"
+ return 1
+ print "All tests succeeded"
+ if successes_cache_file:
+ successes_cache_file.close()
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/chromium/mojo/views/DEPS b/chromium/mojo/views/DEPS
new file mode 100644
index 00000000000..a182521d711
--- /dev/null
+++ b/chromium/mojo/views/DEPS
@@ -0,0 +1,9 @@
+include_rules = [
+ "+base",
+ "+skia",
+ "+ui/aura",
+ "+ui/base",
+ "+ui/events",
+ "+ui/views",
+ "+ui/wm",
+]
diff --git a/chromium/mojo/views/native_widget_view_manager.cc b/chromium/mojo/views/native_widget_view_manager.cc
new file mode 100644
index 00000000000..c5d32e1c498
--- /dev/null
+++ b/chromium/mojo/views/native_widget_view_manager.cc
@@ -0,0 +1,131 @@
+// Copyright 2014 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 "mojo/views/native_widget_view_manager.h"
+
+#include "mojo/aura/window_tree_host_mojo.h"
+#include "mojo/services/public/cpp/input_events/input_events_type_converters.h"
+#include "mojo/services/public/cpp/view_manager/view.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_event_dispatcher.h"
+#include "ui/base/ime/input_method.h"
+#include "ui/base/ime/input_method_delegate.h"
+#include "ui/base/ime/input_method_factory.h"
+#include "ui/base/ime/text_input_client.h"
+#include "ui/wm/core/base_focus_rules.h"
+#include "ui/wm/core/focus_controller.h"
+
+namespace mojo {
+namespace {
+
+// TODO: figure out what this should be.
+class FocusRulesImpl : public wm::BaseFocusRules {
+ public:
+ FocusRulesImpl() {}
+ virtual ~FocusRulesImpl() {}
+
+ virtual bool SupportsChildActivation(aura::Window* window) const OVERRIDE {
+ return true;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FocusRulesImpl);
+};
+
+class MinimalInputEventFilter : public ui::internal::InputMethodDelegate,
+ public ui::EventHandler {
+ public:
+ explicit MinimalInputEventFilter(aura::Window* root)
+ : root_(root),
+ input_method_(
+ ui::CreateInputMethod(this, gfx::kNullAcceleratedWidget).Pass()) {
+ ui::InitializeInputMethodForTesting();
+ input_method_->Init(true);
+ root_->AddPreTargetHandler(this);
+ root_->SetProperty(aura::client::kRootWindowInputMethodKey,
+ input_method_.get());
+ }
+
+ virtual ~MinimalInputEventFilter() {
+ root_->RemovePreTargetHandler(this);
+ root_->SetProperty(aura::client::kRootWindowInputMethodKey,
+ static_cast<ui::InputMethod*>(NULL));
+ }
+
+ private:
+ // ui::EventHandler:
+ virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE {
+ // See the comment in InputMethodEventFilter::OnKeyEvent() for details.
+ if (event->IsTranslated()) {
+ event->SetTranslated(false);
+ } else {
+ if (input_method_->DispatchKeyEvent(*event))
+ event->StopPropagation();
+ }
+ }
+
+ // ui::internal::InputMethodDelegate:
+ virtual bool DispatchKeyEventPostIME(const ui::KeyEvent& event) OVERRIDE {
+ // See the comment in InputMethodEventFilter::DispatchKeyEventPostIME() for
+ // details.
+ ui::KeyEvent aura_event(event);
+ aura_event.SetTranslated(true);
+ ui::EventDispatchDetails details =
+ root_->GetHost()->dispatcher()->OnEventFromSource(&aura_event);
+ return aura_event.handled() || details.dispatcher_destroyed;
+ }
+
+ aura::Window* root_;
+ scoped_ptr<ui::InputMethod> input_method_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinimalInputEventFilter);
+};
+
+} // namespace
+
+NativeWidgetViewManager::NativeWidgetViewManager(
+ views::internal::NativeWidgetDelegate* delegate, view_manager::Node* node)
+ : NativeWidgetAura(delegate),
+ node_(node) {
+ node_->active_view()->AddObserver(this);
+ window_tree_host_.reset(new WindowTreeHostMojo(node_, this));
+ window_tree_host_->InitHost();
+
+ ime_filter_.reset(
+ new MinimalInputEventFilter(window_tree_host_->window()));
+
+ focus_client_.reset(new wm::FocusController(new FocusRulesImpl));
+
+ aura::client::SetFocusClient(window_tree_host_->window(),
+ focus_client_.get());
+ aura::client::SetActivationClient(window_tree_host_->window(),
+ focus_client_.get());
+ window_tree_host_->window()->AddPreTargetHandler(focus_client_.get());
+}
+
+NativeWidgetViewManager::~NativeWidgetViewManager() {
+ node_->active_view()->RemoveObserver(this);
+}
+
+void NativeWidgetViewManager::InitNativeWidget(
+ const views::Widget::InitParams& in_params) {
+ views::Widget::InitParams params(in_params);
+ params.parent = window_tree_host_->window();
+ NativeWidgetAura::InitNativeWidget(params);
+}
+
+void NativeWidgetViewManager::CompositorContentsChanged(
+ const SkBitmap& bitmap) {
+ node_->active_view()->SetContents(bitmap);
+}
+
+void NativeWidgetViewManager::OnViewInputEvent(view_manager::View* view,
+ const EventPtr& event) {
+ scoped_ptr<ui::Event> ui_event(event.To<scoped_ptr<ui::Event> >());
+ if (ui_event.get())
+ window_tree_host_->SendEventToProcessor(ui_event.get());
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/views/native_widget_view_manager.h b/chromium/mojo/views/native_widget_view_manager.h
new file mode 100644
index 00000000000..5981200bc20
--- /dev/null
+++ b/chromium/mojo/views/native_widget_view_manager.h
@@ -0,0 +1,61 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_VIEWS_NATIVE_WIDGET_VIEW_MANAGER_H_
+#define MOJO_VIEWS_NATIVE_WIDGET_VIEW_MANAGER_H_
+
+#include "mojo/aura/window_tree_host_mojo_delegate.h"
+#include "mojo/services/public/cpp/view_manager/node_observer.h"
+#include "mojo/services/public/cpp/view_manager/view_observer.h"
+#include "ui/views/widget/native_widget_aura.h"
+
+namespace ui {
+namespace internal {
+class InputMethodDelegate;
+}
+}
+
+namespace wm {
+class FocusController;
+}
+
+namespace mojo {
+
+class WindowTreeHostMojo;
+
+class NativeWidgetViewManager : public views::NativeWidgetAura,
+ public WindowTreeHostMojoDelegate,
+ public view_manager::ViewObserver,
+ public view_manager::NodeObserver {
+ public:
+ NativeWidgetViewManager(views::internal::NativeWidgetDelegate* delegate,
+ view_manager::Node* node);
+ virtual ~NativeWidgetViewManager();
+
+ private:
+ // Overridden from internal::NativeWidgetPrivate:
+ virtual void InitNativeWidget(
+ const views::Widget::InitParams& in_params) OVERRIDE;
+
+ // WindowTreeHostMojoDelegate:
+ virtual void CompositorContentsChanged(const SkBitmap& bitmap) OVERRIDE;
+
+ // view_manager::ViewObserver
+ virtual void OnViewInputEvent(view_manager::View* view,
+ const EventPtr& event) OVERRIDE;
+
+ scoped_ptr<WindowTreeHostMojo> window_tree_host_;
+
+ scoped_ptr<wm::FocusController> focus_client_;
+
+ scoped_ptr<ui::internal::InputMethodDelegate> ime_filter_;
+
+ view_manager::Node* node_;
+
+ DISALLOW_COPY_AND_ASSIGN(NativeWidgetViewManager);
+};
+
+} // namespace mojo
+
+#endif // MOJO_VIEWS_NATIVE_WIDGET_VIEW_MANAGER_H_
diff --git a/chromium/mojo/views/views_init.cc b/chromium/mojo/views/views_init.cc
new file mode 100644
index 00000000000..50651ab1ae8
--- /dev/null
+++ b/chromium/mojo/views/views_init.cc
@@ -0,0 +1,27 @@
+// Copyright 2014 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 "mojo/views/views_init.h"
+
+#include "base/i18n/icu_util.h"
+#include "base/path_service.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/base/ui_base_paths.h"
+
+namespace mojo {
+
+ViewsInit::ViewsInit() {
+ base::i18n::InitializeICU();
+
+ ui::RegisterPathProvider();
+
+ base::FilePath ui_test_pak_path;
+ CHECK(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
+ ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
+}
+
+ViewsInit::~ViewsInit() {
+}
+
+} // namespace mojo
diff --git a/chromium/mojo/views/views_init.h b/chromium/mojo/views/views_init.h
new file mode 100644
index 00000000000..ad3b95e25cb
--- /dev/null
+++ b/chromium/mojo/views/views_init.h
@@ -0,0 +1,26 @@
+// Copyright 2014 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.
+
+#ifndef MOJO_VIEWS_VIEWS_INIT_H_
+#define MOJO_VIEWS_VIEWS_INIT_H_
+
+#include "mojo/aura/aura_init.h"
+
+namespace mojo {
+
+// Sets up necessary state for views when run with the viewmanager.
+class ViewsInit {
+ public:
+ ViewsInit();
+ ~ViewsInit();
+
+ private:
+ AuraInit aura_init_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewsInit);
+};
+
+} // namespace mojo
+
+#endif // MOJO_VIEWS_VIEWS_INIT_H_