aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHidehiko Abe <hidehiko@google.com>2018-04-17 16:34:13 +0900
committerHidehiko Abe <hidehiko@google.com>2018-04-17 16:56:42 +0900
commit1e4a1e50aed7593adac78af6f319adb67f03d7bb (patch)
tree1398145f7694eac739142d9bcbfcdf3d8cf4b9a8
parent0325b56b0518f5ca16701e5048fcf81cc96fd725 (diff)
downloadlibmojo-1e4a1e50aed7593adac78af6f319adb67f03d7bb.tar.gz
Reduce diffs from Chrome r456626.
Currently, mojo/ directory is more diverged than it should be. This CL fills the unnecessary gaps. Bug: 73606903 Test: Built locally. Treehugger. Made sure the diff on eyes. Change-Id: I65877b0ff853ecfd6661259f97b0078631a158a7
-rw-r--r--mojo/android/javatests/src/org/chromium/mojo/system/impl/WatcherImplTest.java239
-rw-r--r--mojo/android/system/base_run_loop.cc1
-rw-r--r--mojo/common/file_path.mojom8
-rw-r--r--mojo/common/file_path.typemap12
-rw-r--r--mojo/common/struct_traits_unittest.cc57
-rw-r--r--mojo/common/traits_test_service.mojom14
-rw-r--r--mojo/common/typemaps.gni14
-rw-r--r--mojo/common/values.mojom32
-rw-r--r--mojo/common/values.typemap25
-rw-r--r--mojo/common/values_struct_traits.cc109
-rw-r--r--mojo/common/values_struct_traits.h257
-rw-r--r--mojo/edk/embedder/named_platform_channel_pair_win.cc89
-rw-r--r--mojo/edk/embedder/named_platform_handle_utils.h56
-rw-r--r--mojo/edk/embedder/named_platform_handle_utils_posix.cc141
-rw-r--r--mojo/edk/embedder/named_platform_handle_utils_win.cc95
-rw-r--r--mojo/edk/embedder/platform_shared_buffer_unittest.cc227
-rw-r--r--mojo/edk/embedder/scoped_ipc_support.cc39
-rw-r--r--mojo/edk/embedder/scoped_ipc_support.h118
-rw-r--r--mojo/edk/js/BUILD.gn18
-rw-r--r--mojo/edk/js/core.cc95
-rw-r--r--mojo/edk/js/core.h3
-rw-r--r--mojo/edk/js/drain_data.cc8
-rw-r--r--mojo/edk/js/drain_data.h6
-rw-r--r--mojo/edk/js/handle.cc3
-rw-r--r--mojo/edk/js/handle.h13
-rw-r--r--mojo/edk/js/js_export.h32
-rw-r--r--mojo/edk/js/mojo_runner_delegate.h3
-rw-r--r--mojo/edk/js/support.h3
-rw-r--r--mojo/edk/js/tests/run_js_unittests.cc83
-rw-r--r--mojo/edk/js/threading.h3
-rw-r--r--mojo/edk/js/waiting_callback.cc1
-rw-r--r--mojo/edk/system/broker_win.cc155
-rw-r--r--mojo/edk/system/channel_unittest.cc177
-rw-r--r--mojo/edk/system/mach_port_relay.cc248
-rw-r--r--mojo/edk/system/mach_port_relay.h94
-rw-r--r--mojo/edk/system/platform_wrapper_unittest.cc212
-rw-r--r--mojo/edk/system/watch_unittest.cc480
-rw-r--r--mojo/public/c/README.md2
-rw-r--r--mojo/public/cpp/bindings/associated_binding_set.h29
-rw-r--r--mojo/public/cpp/bindings/lib/associated_binding.cc62
-rw-r--r--mojo/public/cpp/bindings/lib/wtf_hash_util.h132
-rw-r--r--mojo/public/cpp/bindings/strong_associated_binding.h125
-rw-r--r--mojo/public/cpp/bindings/strong_binding_set.h26
-rw-r--r--mojo/public/cpp/bindings/tests/BUILD.gn24
-rw-r--r--mojo/public/cpp/bindings/tests/array_common_test.h404
-rw-r--r--mojo/public/cpp/bindings/tests/array_unittest.cc131
-rw-r--r--mojo/public/cpp/bindings/tests/associated_interface_unittest.cc693
-rw-r--r--mojo/public/cpp/bindings/tests/bind_task_runner_unittest.cc10
-rw-r--r--mojo/public/cpp/bindings/tests/binding_callback_unittest.cc26
-rw-r--r--mojo/public/cpp/bindings/tests/binding_set_unittest.cc416
-rw-r--r--mojo/public/cpp/bindings/tests/binding_unittest.cc325
-rw-r--r--mojo/public/cpp/bindings/tests/bindings_perftest.cc155
-rw-r--r--mojo/public/cpp/bindings/tests/connector_unittest.cc26
-rw-r--r--mojo/public/cpp/bindings/tests/constant_unittest.cc28
-rw-r--r--mojo/public/cpp/bindings/tests/data_view_unittest.cc303
-rw-r--r--mojo/public/cpp/bindings/tests/e2e_perftest.cc18
-rw-r--r--mojo/public/cpp/bindings/tests/equals_unittest.cc57
-rw-r--r--mojo/public/cpp/bindings/tests/handle_passing_unittest.cc42
-rw-r--r--mojo/public/cpp/bindings/tests/hash_unittest.cc35
-rw-r--r--mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc309
-rw-r--r--mojo/public/cpp/bindings/tests/map_common_test.h230
-rw-r--r--mojo/public/cpp/bindings/tests/map_unittest.cc225
-rw-r--r--mojo/public/cpp/bindings/tests/message_queue.cc8
-rw-r--r--mojo/public/cpp/bindings/tests/message_queue.h5
-rw-r--r--mojo/public/cpp/bindings/tests/mojo_test_blink_export.h29
-rw-r--r--mojo/public/cpp/bindings/tests/mojo_test_export.h29
-rw-r--r--mojo/public/cpp/bindings/tests/multiplex_router_unittest.cc96
-rw-r--r--mojo/public/cpp/bindings/tests/pickle_unittest.cc75
-rw-r--r--mojo/public/cpp/bindings/tests/pickled_types_blink.h5
-rw-r--r--mojo/public/cpp/bindings/tests/rect_blink.h19
-rw-r--r--mojo/public/cpp/bindings/tests/rect_blink.typemap15
-rw-r--r--mojo/public/cpp/bindings/tests/rect_blink_traits.h5
-rw-r--r--mojo/public/cpp/bindings/tests/rect_chromium.h19
-rw-r--r--mojo/public/cpp/bindings/tests/rect_chromium.typemap15
-rw-r--r--mojo/public/cpp/bindings/tests/rect_chromium_traits.h2
-rw-r--r--mojo/public/cpp/bindings/tests/report_bad_message_unittest.cc194
-rw-r--r--mojo/public/cpp/bindings/tests/request_response_unittest.cc8
-rw-r--r--mojo/public/cpp/bindings/tests/router_test_util.cc13
-rw-r--r--mojo/public/cpp/bindings/tests/router_unittest.cc316
-rw-r--r--mojo/public/cpp/bindings/tests/sample_service_unittest.cc36
-rw-r--r--mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc95
-rw-r--r--mojo/public/cpp/bindings/tests/shared_rect.h43
-rw-r--r--mojo/public/cpp/bindings/tests/shared_rect_traits.h33
-rw-r--r--mojo/public/cpp/bindings/tests/stl_converters_unittest.cc93
-rw-r--r--mojo/public/cpp/bindings/tests/string_unittest.cc131
-rw-r--r--mojo/public/cpp/bindings/tests/struct_traits_unittest.cc211
-rw-r--r--mojo/public/cpp/bindings/tests/struct_unittest.cc274
-rw-r--r--mojo/public/cpp/bindings/tests/struct_with_traits.typemap6
-rw-r--r--mojo/public/cpp/bindings/tests/struct_with_traits_impl.cc15
-rw-r--r--mojo/public/cpp/bindings/tests/struct_with_traits_impl.h68
-rw-r--r--mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.cc32
-rw-r--r--mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.h99
-rw-r--r--mojo/public/cpp/bindings/tests/sync_method_unittest.cc33
-rw-r--r--mojo/public/cpp/bindings/tests/test_native_types_blink.typemap2
-rw-r--r--mojo/public/cpp/bindings/tests/test_native_types_chromium.typemap5
-rw-r--r--mojo/public/cpp/bindings/tests/type_conversion_unittest.cc75
-rw-r--r--mojo/public/cpp/bindings/tests/union_unittest.cc374
-rw-r--r--mojo/public/cpp/bindings/tests/validation_context_unittest.cc109
-rw-r--r--mojo/public/cpp/bindings/tests/validation_unittest.cc25
-rw-r--r--mojo/public/cpp/bindings/tests/versioning_apptest.cc6
-rw-r--r--mojo/public/cpp/bindings/tests/versioning_test_service.cc16
-rw-r--r--mojo/public/cpp/bindings/tests/wtf_array_unittest.cc62
-rw-r--r--mojo/public/cpp/bindings/tests/wtf_hash_unittest.cc60
-rw-r--r--mojo/public/cpp/bindings/tests/wtf_map_unittest.cc68
-rw-r--r--mojo/public/cpp/bindings/tests/wtf_types_unittest.cc139
-rw-r--r--mojo/public/cpp/bindings/unique_ptr_impl_ref_traits.h22
-rw-r--r--mojo/public/cpp/system/tests/watcher_unittest.cc49
-rw-r--r--mojo/public/interfaces/bindings/pipe_control_messages.mojom1
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_invalid_interface_id.data24
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_invalid_interface_id.expected1
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_master_interface_id.data24
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_master_interface_id.expected1
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_interface_id_index_out_of_range.data27
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_interface_id_index_out_of_range.expected1
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_good.data18
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_good.expected1
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_collided_interface_id_indices.data36
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_collided_interface_id_indices.expected1
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_wrong_interface_id_index_order.data38
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_wrong_interface_id_index_order.expected1
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_empy_enum_array.data22
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_empy_enum_array.expected1
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd18_good.data14
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd18_good.expected1
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd19_exceed_recursion_limit.data612
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd19_exceed_recursion_limit.expected1
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd20_good.data57
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd20_good.expected1
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd21_empty_extensible_enum_accepts_any_value.data13
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd21_empty_extensible_enum_accepts_any_value.expected1
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd22_empty_nonextensible_enum_accepts_no_values.data14
-rw-r--r--mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd22_empty_nonextensible_enum_accepts_no_values.expected1
-rw-r--r--mojo/public/interfaces/bindings/tests/test_bad_messages.mojom13
-rw-r--r--mojo/public/interfaces/bindings/tests/test_data_view.mojom41
-rw-r--r--mojo/public/interfaces/bindings/tests/test_export.mojom20
-rw-r--r--mojo/public/interfaces/bindings/tests/test_import.mojom11
-rw-r--r--mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java4
-rw-r--r--mojo/public/js/interface_types.js52
-rw-r--r--mojo/public/js/lib/control_message_handler.js111
-rw-r--r--mojo/public/js/lib/control_message_proxy.js102
-rw-r--r--mojo/public/js/new_bindings/bindings.js285
-rw-r--r--mojo/public/js/new_bindings/buffer.js156
-rw-r--r--mojo/public/js/new_bindings/codec.js922
-rw-r--r--mojo/public/js/new_bindings/connector.js115
-rw-r--r--mojo/public/js/new_bindings/interface_types.js52
-rw-r--r--mojo/public/js/new_bindings/lib/control_message_handler.js111
-rw-r--r--mojo/public/js/new_bindings/lib/control_message_proxy.js102
-rw-r--r--mojo/public/js/new_bindings/router.js203
-rw-r--r--mojo/public/js/new_bindings/unicode.js51
-rw-r--r--mojo/public/js/new_bindings/validator.js512
-rw-r--r--mojo/public/js/tests/codec_unittest.js314
-rw-r--r--mojo/public/js/tests/connection_unittest.js136
-rw-r--r--mojo/public/js/tests/core_unittest.js246
-rw-r--r--mojo/public/js/tests/interface_ptr_unittest.js240
-rw-r--r--mojo/public/js/tests/sample_service_unittest.js173
-rw-r--r--mojo/public/js/tests/struct_unittest.js279
-rw-r--r--mojo/public/js/tests/union_unittest.js194
-rw-r--r--mojo/public/js/tests/validation_test_input_parser.js299
-rw-r--r--mojo/public/js/tests/validation_unittest.js338
-rw-r--r--mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl1
-rw-r--r--mojo/public/tools/bindings/generators/mojom_java_generator.py1
161 files changed, 13224 insertions, 2809 deletions
diff --git a/mojo/android/javatests/src/org/chromium/mojo/system/impl/WatcherImplTest.java b/mojo/android/javatests/src/org/chromium/mojo/system/impl/WatcherImplTest.java
new file mode 100644
index 0000000..6a99fe1
--- /dev/null
+++ b/mojo/android/javatests/src/org/chromium/mojo/system/impl/WatcherImplTest.java
@@ -0,0 +1,239 @@
+// Copyright 2016 The Chromium 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 org.chromium.mojo.system.impl;
+
+import android.support.test.filters.SmallTest;
+
+import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.system.Core;
+import org.chromium.mojo.system.Handle;
+import org.chromium.mojo.system.InvalidHandle;
+import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MojoException;
+import org.chromium.mojo.system.MojoResult;
+import org.chromium.mojo.system.Pair;
+import org.chromium.mojo.system.Watcher;
+import org.chromium.mojo.system.Watcher.Callback;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Testing the Watcher.
+ */
+public class WatcherImplTest extends MojoTestCase {
+ private List<Handle> mHandlesToClose = new ArrayList<Handle>();
+ private Watcher mWatcher;
+ private Core mCore;
+
+ /**
+ * @see MojoTestCase#setUp()
+ */
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mWatcher = new WatcherImpl();
+ mCore = CoreImpl.getInstance();
+ }
+
+ /**
+ * @see MojoTestCase#tearDown()
+ */
+ @Override
+ protected void tearDown() throws Exception {
+ mWatcher.destroy();
+ MojoException toThrow = null;
+ for (Handle handle : mHandlesToClose) {
+ try {
+ handle.close();
+ } catch (MojoException e) {
+ if (toThrow == null) {
+ toThrow = e;
+ }
+ }
+ }
+ if (toThrow != null) {
+ throw toThrow;
+ }
+ super.tearDown();
+ }
+
+ private void addHandlePairToClose(Pair<? extends Handle, ? extends Handle> handles) {
+ mHandlesToClose.add(handles.first);
+ mHandlesToClose.add(handles.second);
+ }
+
+ private static class WatcherResult implements Callback {
+ private int mResult = Integer.MIN_VALUE;
+
+ /**
+ * @see Callback#onResult(int)
+ */
+ @Override
+ public void onResult(int result) {
+ this.mResult = result;
+ }
+
+ /**
+ * @return the result
+ */
+ public int getResult() {
+ return mResult;
+ }
+ }
+
+ /**
+ * Testing {@link Watcher} implementation.
+ */
+ @SmallTest
+ public void testCorrectResult() {
+ // Checking a correct result.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = mCore.createMessagePipe(null);
+ addHandlePairToClose(handles);
+ final WatcherResult watcherResult = new WatcherResult();
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+
+ mWatcher.start(handles.first, Core.HandleSignals.READABLE, watcherResult);
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+
+ handles.second.writeMessage(
+ ByteBuffer.allocateDirect(1), null, MessagePipeHandle.WriteFlags.NONE);
+ runLoopUntilIdle();
+ assertEquals(MojoResult.OK, watcherResult.getResult());
+ }
+
+ /**
+ * Testing {@link Watcher} implementation.
+ */
+ @SmallTest
+ public void testClosingPeerHandle() {
+ // Closing the peer handle.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = mCore.createMessagePipe(null);
+ addHandlePairToClose(handles);
+
+ final WatcherResult watcherResult = new WatcherResult();
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+
+ mWatcher.start(handles.first, Core.HandleSignals.READABLE, watcherResult);
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+
+ runLoopUntilIdle();
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+
+ handles.second.close();
+ runLoopUntilIdle();
+ assertEquals(MojoResult.FAILED_PRECONDITION, watcherResult.getResult());
+ }
+
+ /**
+ * Testing {@link Watcher} implementation.
+ */
+ @SmallTest
+ public void testClosingWatchedHandle() {
+ // Closing the peer handle.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = mCore.createMessagePipe(null);
+ addHandlePairToClose(handles);
+
+ final WatcherResult watcherResult = new WatcherResult();
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+
+ mWatcher.start(handles.first, Core.HandleSignals.READABLE, watcherResult);
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+
+ runLoopUntilIdle();
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+
+ handles.first.close();
+ runLoopUntilIdle();
+ assertEquals(MojoResult.CANCELLED, watcherResult.getResult());
+ }
+
+ /**
+ * Testing {@link Watcher} implementation.
+ */
+ @SmallTest
+ public void testInvalidHandle() {
+ // Closing the peer handle.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = mCore.createMessagePipe(null);
+ addHandlePairToClose(handles);
+
+ final WatcherResult watcherResult = new WatcherResult();
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+
+ handles.first.close();
+ assertEquals(MojoResult.INVALID_ARGUMENT,
+ mWatcher.start(handles.first, Core.HandleSignals.READABLE, watcherResult));
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+
+ runLoopUntilIdle();
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+ }
+
+ /**
+ * Testing {@link Watcher} implementation.
+ */
+ @SmallTest
+ public void testDefaultInvalidHandle() {
+ final WatcherResult watcherResult = new WatcherResult();
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+
+ assertEquals(MojoResult.INVALID_ARGUMENT,
+ mWatcher.start(InvalidHandle.INSTANCE, Core.HandleSignals.READABLE, watcherResult));
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+
+ runLoopUntilIdle();
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+ }
+
+ /**
+ * Testing {@link Watcher} implementation.
+ */
+ @SmallTest
+ public void testCancel() {
+ // Closing the peer handle.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = mCore.createMessagePipe(null);
+ addHandlePairToClose(handles);
+
+ final WatcherResult watcherResult = new WatcherResult();
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+
+ mWatcher.start(handles.first, Core.HandleSignals.READABLE, watcherResult);
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+
+ runLoopUntilIdle();
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+
+ mWatcher.cancel();
+ runLoopUntilIdle();
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+
+ handles.second.writeMessage(
+ ByteBuffer.allocateDirect(1), null, MessagePipeHandle.WriteFlags.NONE);
+ runLoopUntilIdle();
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+ }
+
+ /**
+ * Testing {@link Watcher} implementation.
+ */
+ @SmallTest
+ public void testImmediateCancelOnInvalidHandle() {
+ // Closing the peer handle.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = mCore.createMessagePipe(null);
+ addHandlePairToClose(handles);
+
+ final WatcherResult watcherResult = new WatcherResult();
+ handles.first.close();
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+
+ mWatcher.start(handles.first, Core.HandleSignals.READABLE, watcherResult);
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+ mWatcher.cancel();
+
+ runLoopUntilIdle();
+ assertEquals(Integer.MIN_VALUE, watcherResult.getResult());
+ }
+}
diff --git a/mojo/android/system/base_run_loop.cc b/mojo/android/system/base_run_loop.cc
index 7993ba8..6d9bd78 100644
--- a/mojo/android/system/base_run_loop.cc
+++ b/mojo/android/system/base_run_loop.cc
@@ -80,3 +80,4 @@ bool RegisterBaseRunLoop(JNIEnv* env) {
} // namespace android
} // namespace mojo
+
diff --git a/mojo/common/file_path.mojom b/mojo/common/file_path.mojom
new file mode 100644
index 0000000..10ebe05
--- /dev/null
+++ b/mojo/common/file_path.mojom
@@ -0,0 +1,8 @@
+// Copyright 2016 The Chromium 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.common.mojom;
+
+[Native]
+struct FilePath;
diff --git a/mojo/common/file_path.typemap b/mojo/common/file_path.typemap
new file mode 100644
index 0000000..66d8c54
--- /dev/null
+++ b/mojo/common/file_path.typemap
@@ -0,0 +1,12 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/common/file_path.mojom"
+public_headers = [ "//base/files/file_path.h" ]
+traits_headers = [ "//ipc/ipc_message_utils.h" ]
+public_deps = [
+ "//ipc",
+]
+
+type_mappings = [ "mojo.common.mojom.FilePath=base::FilePath" ]
diff --git a/mojo/common/struct_traits_unittest.cc b/mojo/common/struct_traits_unittest.cc
new file mode 100644
index 0000000..5ac4bc9
--- /dev/null
+++ b/mojo/common/struct_traits_unittest.cc
@@ -0,0 +1,57 @@
+// Copyright 2016 The Chromium 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 "mojo/common/traits_test_service.mojom.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace common {
+namespace {
+
+class StructTraitsTest : public testing::Test, public mojom::TraitsTestService {
+ public:
+ StructTraitsTest() {}
+
+ protected:
+ mojom::TraitsTestServicePtr GetTraitsTestProxy() {
+ return traits_test_bindings_.CreateInterfacePtrAndBind(this);
+ }
+
+ private:
+ // TraitsTestService:
+ void EchoVersion(const base::Optional<base::Version>& m,
+ const EchoVersionCallback& callback) override {
+ callback.Run(m);
+ }
+
+ base::MessageLoop loop_;
+ mojo::BindingSet<TraitsTestService> traits_test_bindings_;
+
+ DISALLOW_COPY_AND_ASSIGN(StructTraitsTest);
+};
+
+TEST_F(StructTraitsTest, Version) {
+ const std::string& version_str = "1.2.3.4";
+ base::Version input(version_str);
+ mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy();
+ base::Optional<base::Version> output;
+ proxy->EchoVersion(input, &output);
+ EXPECT_TRUE(output.has_value());
+ EXPECT_EQ(version_str, output->GetString());
+}
+
+TEST_F(StructTraitsTest, InvalidVersion) {
+ const std::string invalid_version_str;
+ base::Version input(invalid_version_str);
+ mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy();
+ base::Optional<base::Version> output;
+ proxy->EchoVersion(input, &output);
+ EXPECT_FALSE(output.has_value());
+}
+
+} // namespace
+} // namespace common
+} // namespace mojo
diff --git a/mojo/common/traits_test_service.mojom b/mojo/common/traits_test_service.mojom
new file mode 100644
index 0000000..7659eea
--- /dev/null
+++ b/mojo/common/traits_test_service.mojom
@@ -0,0 +1,14 @@
+// Copyright 2016 The Chromium 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.common.mojom;
+
+import "mojo/common/version.mojom";
+
+// All functions on this interface echo their arguments to test StructTraits
+// serialization and deserialization.
+interface TraitsTestService {
+ [Sync]
+ EchoVersion(Version? v) => (Version? pass);
+};
diff --git a/mojo/common/typemaps.gni b/mojo/common/typemaps.gni
new file mode 100644
index 0000000..ae36031
--- /dev/null
+++ b/mojo/common/typemaps.gni
@@ -0,0 +1,14 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+typemaps = [
+ "//mojo/common/file.typemap",
+ "//mojo/common/file_path.typemap",
+ "//mojo/common/string16.typemap",
+ "//mojo/common/text_direction.typemap",
+ "//mojo/common/time.typemap",
+ "//mojo/common/unguessable_token.typemap",
+ "//mojo/common/values.typemap",
+ "//mojo/common/version.typemap",
+]
diff --git a/mojo/common/values.mojom b/mojo/common/values.mojom
new file mode 100644
index 0000000..722198c
--- /dev/null
+++ b/mojo/common/values.mojom
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium 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.common.mojom;
+
+union Value {
+ NullValue? null_value;
+ bool bool_value;
+ int32 int_value;
+ double double_value;
+ string string_value;
+ array<uint8> binary_value;
+ DictionaryValue dictionary_value;
+ ListValue list_value;
+};
+
+struct ListValue {
+ array<Value> values;
+};
+
+struct DictionaryValue {
+ map<string, Value> values;
+};
+
+// An empty struct representing a null base::Value.
+struct NullValue {
+};
+
+// To avoid versioning problems for arc. TODO(sammc): Remove ASAP.
+[Native]
+struct LegacyListValue;
diff --git a/mojo/common/values.typemap b/mojo/common/values.typemap
new file mode 100644
index 0000000..f1f3fc2
--- /dev/null
+++ b/mojo/common/values.typemap
@@ -0,0 +1,25 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+mojom = "//mojo/common/values.mojom"
+public_headers = [ "//base/values.h" ]
+traits_headers = [
+ "//ipc/ipc_message_utils.h",
+ "//mojo/common/values_struct_traits.h",
+]
+public_deps = [
+ "//base",
+ "//ipc",
+]
+sources = [
+ "values_struct_traits.cc",
+ "values_struct_traits.h",
+]
+
+type_mappings = [
+ "mojo.common.mojom.DictionaryValue=std::unique_ptr<base::DictionaryValue>[move_only,nullable_is_same_type]",
+ "mojo.common.mojom.LegacyListValue=base::ListValue[non_copyable_non_movable]",
+ "mojo.common.mojom.ListValue=std::unique_ptr<base::ListValue>[move_only,nullable_is_same_type]",
+ "mojo.common.mojom.Value=std::unique_ptr<base::Value>[move_only,nullable_is_same_type]",
+]
diff --git a/mojo/common/values_struct_traits.cc b/mojo/common/values_struct_traits.cc
new file mode 100644
index 0000000..6af7a39
--- /dev/null
+++ b/mojo/common/values_struct_traits.cc
@@ -0,0 +1,109 @@
+// Copyright 2016 The Chromium 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/ptr_util.h"
+#include "mojo/common/values_struct_traits.h"
+
+namespace mojo {
+
+bool StructTraits<common::mojom::ListValueDataView,
+ std::unique_ptr<base::ListValue>>::
+ Read(common::mojom::ListValueDataView data,
+ std::unique_ptr<base::ListValue>* value_out) {
+ mojo::ArrayDataView<common::mojom::ValueDataView> view;
+ data.GetValuesDataView(&view);
+
+ auto list_value = base::MakeUnique<base::ListValue>();
+ for (size_t i = 0; i < view.size(); ++i) {
+ std::unique_ptr<base::Value> value;
+ if (!view.Read(i, &value))
+ return false;
+
+ list_value->Append(std::move(value));
+ }
+ *value_out = std::move(list_value);
+ return true;
+}
+
+bool StructTraits<common::mojom::DictionaryValueDataView,
+ std::unique_ptr<base::DictionaryValue>>::
+ Read(common::mojom::DictionaryValueDataView data,
+ std::unique_ptr<base::DictionaryValue>* value_out) {
+ mojo::MapDataView<mojo::StringDataView, common::mojom::ValueDataView> view;
+ data.GetValuesDataView(&view);
+ auto dictionary_value = base::MakeUnique<base::DictionaryValue>();
+ for (size_t i = 0; i < view.size(); ++i) {
+ base::StringPiece key;
+ std::unique_ptr<base::Value> value;
+ if (!view.keys().Read(i, &key) || !view.values().Read(i, &value))
+ return false;
+
+ dictionary_value->SetWithoutPathExpansion(key, std::move(value));
+ }
+ *value_out = std::move(dictionary_value);
+ return true;
+}
+
+std::unique_ptr<base::DictionaryValue>
+CloneTraits<std::unique_ptr<base::DictionaryValue>, false>::Clone(
+ const std::unique_ptr<base::DictionaryValue>& input) {
+ auto result = base::MakeUnique<base::DictionaryValue>();
+ result->MergeDictionary(input.get());
+ return result;
+}
+
+bool UnionTraits<common::mojom::ValueDataView, std::unique_ptr<base::Value>>::
+ Read(common::mojom::ValueDataView data,
+ std::unique_ptr<base::Value>* value_out) {
+ switch (data.tag()) {
+ case common::mojom::ValueDataView::Tag::NULL_VALUE: {
+ *value_out = base::Value::CreateNullValue();
+ return true;
+ }
+ case common::mojom::ValueDataView::Tag::BOOL_VALUE: {
+ *value_out = base::MakeUnique<base::Value>(data.bool_value());
+ return true;
+ }
+ case common::mojom::ValueDataView::Tag::INT_VALUE: {
+ *value_out = base::MakeUnique<base::Value>(data.int_value());
+ return true;
+ }
+ case common::mojom::ValueDataView::Tag::DOUBLE_VALUE: {
+ *value_out = base::MakeUnique<base::Value>(data.double_value());
+ return true;
+ }
+ case common::mojom::ValueDataView::Tag::STRING_VALUE: {
+ base::StringPiece string_value;
+ if (!data.ReadStringValue(&string_value))
+ return false;
+ *value_out = base::MakeUnique<base::Value>(string_value);
+ return true;
+ }
+ case common::mojom::ValueDataView::Tag::BINARY_VALUE: {
+ mojo::ArrayDataView<uint8_t> binary_data;
+ data.GetBinaryValueDataView(&binary_data);
+ *value_out = base::BinaryValue::CreateWithCopiedBuffer(
+ reinterpret_cast<const char*>(binary_data.data()),
+ binary_data.size());
+ return true;
+ }
+ case common::mojom::ValueDataView::Tag::DICTIONARY_VALUE: {
+ std::unique_ptr<base::DictionaryValue> dictionary_value;
+ if (!data.ReadDictionaryValue(&dictionary_value))
+ return false;
+ *value_out = std::move(dictionary_value);
+ return true;
+ }
+ case common::mojom::ValueDataView::Tag::LIST_VALUE: {
+ std::unique_ptr<base::ListValue> list_value;
+ if (!data.ReadListValue(&list_value))
+ return false;
+ *value_out = std::move(list_value);
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace mojo
diff --git a/mojo/common/values_struct_traits.h b/mojo/common/values_struct_traits.h
new file mode 100644
index 0000000..befcf3a
--- /dev/null
+++ b/mojo/common/values_struct_traits.h
@@ -0,0 +1,257 @@
+// Copyright 2016 The Chromium 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_VALUES_STRUCT_TRAITS_H_
+#define MOJO_COMMON_VALUES_STRUCT_TRAITS_H_
+
+#include "base/values.h"
+#include "mojo/common/values.mojom.h"
+#include "mojo/public/cpp/bindings/array_traits.h"
+#include "mojo/public/cpp/bindings/clone_traits.h"
+#include "mojo/public/cpp/bindings/map_traits.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+#include "mojo/public/cpp/bindings/union_traits.h"
+
+namespace mojo {
+
+template <>
+struct ArrayTraits<base::ListValue> {
+ using Element = std::unique_ptr<base::Value>;
+ using ConstIterator = base::ListValue::const_iterator;
+
+ static size_t GetSize(const base::ListValue& input) {
+ return input.GetSize();
+ }
+
+ static ConstIterator GetBegin(const base::ListValue& input) {
+ return input.begin();
+ }
+
+ static void AdvanceIterator(ConstIterator& iterator) { ++iterator; }
+
+ static const Element& GetValue(ConstIterator& iterator) { return *iterator; }
+};
+
+template <>
+struct StructTraits<common::mojom::ListValueDataView, base::ListValue> {
+ static const base::ListValue& values(const base::ListValue& value) {
+ return value;
+ }
+};
+
+template <>
+struct StructTraits<common::mojom::ListValueDataView,
+ std::unique_ptr<base::ListValue>> {
+ static bool IsNull(const std::unique_ptr<base::ListValue>& value) {
+ return !value;
+ }
+
+ static void SetToNull(std::unique_ptr<base::ListValue>* value) {
+ value->reset();
+ }
+
+ static const base::ListValue& values(
+ const std::unique_ptr<base::ListValue>& value) {
+ return *value;
+ }
+
+ static bool Read(common::mojom::ListValueDataView data,
+ std::unique_ptr<base::ListValue>* value);
+};
+
+template <>
+struct MapTraits<base::DictionaryValue> {
+ using Key = std::string;
+ using Value = base::Value;
+ using Iterator = base::DictionaryValue::Iterator;
+
+ static size_t GetSize(const base::DictionaryValue& input) {
+ return input.size();
+ }
+
+ static Iterator GetBegin(const base::DictionaryValue& input) {
+ return Iterator(input);
+ }
+
+ static void AdvanceIterator(Iterator& iterator) { iterator.Advance(); }
+
+ static const Key& GetKey(Iterator& iterator) { return iterator.key(); }
+
+ static const Value& GetValue(Iterator& iterator) { return iterator.value(); }
+};
+
+template <>
+struct StructTraits<common::mojom::DictionaryValueDataView,
+ base::DictionaryValue> {
+ static const base::DictionaryValue& values(
+ const base::DictionaryValue& value) {
+ return value;
+ }
+};
+
+template <>
+struct StructTraits<common::mojom::DictionaryValueDataView,
+ std::unique_ptr<base::DictionaryValue>> {
+ static bool IsNull(const std::unique_ptr<base::DictionaryValue>& value) {
+ return !value;
+ }
+
+ static void SetToNull(std::unique_ptr<base::DictionaryValue>* value) {
+ value->reset();
+ }
+
+ static const base::DictionaryValue& values(
+ const std::unique_ptr<base::DictionaryValue>& value) {
+ return *value;
+ }
+ static bool Read(common::mojom::DictionaryValueDataView data,
+ std::unique_ptr<base::DictionaryValue>* value);
+};
+
+template <>
+struct CloneTraits<std::unique_ptr<base::DictionaryValue>, false> {
+ static std::unique_ptr<base::DictionaryValue> Clone(
+ const std::unique_ptr<base::DictionaryValue>& input);
+};
+
+template <>
+struct UnionTraits<common::mojom::ValueDataView, base::Value> {
+ static common::mojom::ValueDataView::Tag GetTag(const base::Value& data) {
+ switch (data.GetType()) {
+ case base::Value::Type::NONE:
+ return common::mojom::ValueDataView::Tag::NULL_VALUE;
+ case base::Value::Type::BOOLEAN:
+ return common::mojom::ValueDataView::Tag::BOOL_VALUE;
+ case base::Value::Type::INTEGER:
+ return common::mojom::ValueDataView::Tag::INT_VALUE;
+ case base::Value::Type::DOUBLE:
+ return common::mojom::ValueDataView::Tag::DOUBLE_VALUE;
+ case base::Value::Type::STRING:
+ return common::mojom::ValueDataView::Tag::STRING_VALUE;
+ case base::Value::Type::BINARY:
+ return common::mojom::ValueDataView::Tag::BINARY_VALUE;
+ case base::Value::Type::DICTIONARY:
+ return common::mojom::ValueDataView::Tag::DICTIONARY_VALUE;
+ case base::Value::Type::LIST:
+ return common::mojom::ValueDataView::Tag::LIST_VALUE;
+ }
+ NOTREACHED();
+ return common::mojom::ValueDataView::Tag::NULL_VALUE;
+ }
+
+ static common::mojom::NullValuePtr null_value(const base::Value& value) {
+ return common::mojom::NullValuePtr();
+ }
+
+ static bool bool_value(const base::Value& value) {
+ bool bool_value{};
+ if (!value.GetAsBoolean(&bool_value))
+ NOTREACHED();
+ return bool_value;
+ }
+
+ static int32_t int_value(const base::Value& value) {
+ int int_value{};
+ if (!value.GetAsInteger(&int_value))
+ NOTREACHED();
+ return int_value;
+ }
+
+ static double double_value(const base::Value& value) {
+ double double_value{};
+ if (!value.GetAsDouble(&double_value))
+ NOTREACHED();
+ return double_value;
+ }
+
+ static base::StringPiece string_value(const base::Value& value) {
+ base::StringPiece string_piece;
+ if (!value.GetAsString(&string_piece))
+ NOTREACHED();
+ return string_piece;
+ }
+
+ static mojo::ConstCArray<uint8_t> binary_value(const base::Value& value) {
+ const base::BinaryValue* binary_value = nullptr;
+ if (!value.GetAsBinary(&binary_value))
+ NOTREACHED();
+ return mojo::ConstCArray<uint8_t>(
+ binary_value->GetSize(),
+ reinterpret_cast<const uint8_t*>(binary_value->GetBuffer()));
+ }
+
+ static const base::ListValue& list_value(const base::Value& value) {
+ const base::ListValue* list_value = nullptr;
+ if (!value.GetAsList(&list_value))
+ NOTREACHED();
+ return *list_value;
+ }
+ static const base::DictionaryValue& dictionary_value(
+ const base::Value& value) {
+ const base::DictionaryValue* dictionary_value = nullptr;
+ if (!value.GetAsDictionary(&dictionary_value))
+ NOTREACHED();
+ return *dictionary_value;
+ }
+};
+
+template <>
+struct UnionTraits<common::mojom::ValueDataView, std::unique_ptr<base::Value>> {
+ static bool IsNull(const std::unique_ptr<base::Value>& value) {
+ return !value;
+ }
+
+ static void SetToNull(std::unique_ptr<base::Value>* value) { value->reset(); }
+
+ static common::mojom::ValueDataView::Tag GetTag(
+ const std::unique_ptr<base::Value>& value) {
+ return UnionTraits<common::mojom::ValueDataView, base::Value>::GetTag(
+ *value);
+ }
+
+ static common::mojom::NullValuePtr null_value(
+ const std::unique_ptr<base::Value>& value) {
+ return UnionTraits<common::mojom::ValueDataView, base::Value>::null_value(
+ *value);
+ }
+ static bool bool_value(const std::unique_ptr<base::Value>& value) {
+ return UnionTraits<common::mojom::ValueDataView, base::Value>::bool_value(
+ *value);
+ }
+ static int32_t int_value(const std::unique_ptr<base::Value>& value) {
+ return UnionTraits<common::mojom::ValueDataView, base::Value>::int_value(
+ *value);
+ }
+ static double double_value(const std::unique_ptr<base::Value>& value) {
+ return UnionTraits<common::mojom::ValueDataView, base::Value>::double_value(
+ *value);
+ }
+ static base::StringPiece string_value(
+ const std::unique_ptr<base::Value>& value) {
+ return UnionTraits<common::mojom::ValueDataView, base::Value>::string_value(
+ *value);
+ }
+ static mojo::ConstCArray<uint8_t> binary_value(
+ const std::unique_ptr<base::Value>& value) {
+ return UnionTraits<common::mojom::ValueDataView, base::Value>::binary_value(
+ *value);
+ }
+ static const base::ListValue& list_value(
+ const std::unique_ptr<base::Value>& value) {
+ return UnionTraits<common::mojom::ValueDataView, base::Value>::list_value(
+ *value);
+ }
+ static const base::DictionaryValue& dictionary_value(
+ const std::unique_ptr<base::Value>& value) {
+ return UnionTraits<common::mojom::ValueDataView,
+ base::Value>::dictionary_value(*value);
+ }
+
+ static bool Read(common::mojom::ValueDataView data,
+ std::unique_ptr<base::Value>* value);
+};
+
+} // namespace mojo
+
+#endif // MOJO_COMMON_VALUES_STRUCT_TRAITS_H_
diff --git a/mojo/edk/embedder/named_platform_channel_pair_win.cc b/mojo/edk/embedder/named_platform_channel_pair_win.cc
new file mode 100644
index 0000000..96589ff
--- /dev/null
+++ b/mojo/edk/embedder/named_platform_channel_pair_win.cc
@@ -0,0 +1,89 @@
+// Copyright 2016 The Chromium 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/edk/embedder/named_platform_channel_pair.h"
+
+#include <windows.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#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/strings/utf_string_conversions.h"
+#include "base/win/windows_version.h"
+#include "mojo/edk/embedder/named_platform_handle_utils.h"
+#include "mojo/edk/embedder/platform_handle.h"
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+const char kMojoNamedPlatformChannelPipeSwitch[] =
+ "mojo-named-platform-channel-pipe";
+
+std::wstring GeneratePipeName() {
+ return base::StringPrintf(L"%u.%u.%I64u", GetCurrentProcessId(),
+ GetCurrentThreadId(), base::RandUint64());
+}
+
+} // namespace
+
+NamedPlatformChannelPair::NamedPlatformChannelPair(
+ const NamedPlatformChannelPair::Options& options)
+ : pipe_handle_(GeneratePipeName()) {
+ CreateServerHandleOptions server_handle_options;
+ server_handle_options.security_descriptor = options.security_descriptor;
+ server_handle_options.enforce_uniqueness = true;
+ server_handle_ = CreateServerHandle(pipe_handle_, server_handle_options);
+ PCHECK(server_handle_.is_valid());
+}
+
+NamedPlatformChannelPair::~NamedPlatformChannelPair() {}
+
+ScopedPlatformHandle NamedPlatformChannelPair::PassServerHandle() {
+ return std::move(server_handle_);
+}
+
+// static
+ScopedPlatformHandle
+NamedPlatformChannelPair::PassClientHandleFromParentProcess(
+ const base::CommandLine& command_line) {
+ // In order to support passing the pipe name on the command line, the pipe
+ // handle is lazily created from the pipe name when requested.
+ NamedPlatformHandle handle(
+ command_line.GetSwitchValueNative(kMojoNamedPlatformChannelPipeSwitch));
+
+ if (!handle.is_valid())
+ return ScopedPlatformHandle();
+
+ return CreateClientHandle(handle);
+}
+
+void NamedPlatformChannelPair::PrepareToPassClientHandleToChildProcess(
+ base::CommandLine* command_line) const {
+ DCHECK(command_line);
+
+ // 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(kMojoNamedPlatformChannelPipeSwitch))
+ << "Child command line already has switch --"
+ << kMojoNamedPlatformChannelPipeSwitch << "="
+ << command_line->GetSwitchValueNative(
+ kMojoNamedPlatformChannelPipeSwitch);
+ // (Any existing switch won't actually be removed from the command line, but
+ // the last one appended takes precedence.)
+ command_line->AppendSwitchNative(kMojoNamedPlatformChannelPipeSwitch,
+ pipe_handle_.name);
+}
+
+} // namespace edk
+} // namespace mojo
diff --git a/mojo/edk/embedder/named_platform_handle_utils.h b/mojo/edk/embedder/named_platform_handle_utils.h
new file mode 100644
index 0000000..b767ea0
--- /dev/null
+++ b/mojo/edk/embedder/named_platform_handle_utils.h
@@ -0,0 +1,56 @@
+// Copyright 2016 The Chromium 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_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_UTILS_H_
+#define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_UTILS_H_
+
+#include "build/build_config.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+#if defined(OS_WIN)
+#include "base/strings/string16.h"
+#endif
+
+namespace mojo {
+namespace edk {
+
+struct NamedPlatformHandle;
+
+#if defined(OS_POSIX)
+
+// The maximum length of the name of a unix domain socket. The standard size on
+// linux is 108, mac is 104. To maintain consistency across platforms we
+// standardize on the smaller value.
+const size_t kMaxSocketNameLength = 104;
+
+#endif
+
+struct CreateServerHandleOptions {
+#if defined(OS_WIN)
+ // If true, creating a server handle will fail if another pipe with the same
+ // name exists.
+ bool enforce_uniqueness = true;
+
+ // If non-empty, a security descriptor to use when creating the pipe. If
+ // empty, a default security descriptor will be used. See
+ // kDefaultSecurityDescriptor in named_platform_handle_utils_win.cc.
+ base::string16 security_descriptor;
+#endif
+};
+
+// Creates a client platform handle from |handle|. This may block until |handle|
+// is ready to receive connections.
+MOJO_SYSTEM_IMPL_EXPORT ScopedPlatformHandle
+CreateClientHandle(const NamedPlatformHandle& handle);
+
+// Creates a server platform handle from |handle|.
+MOJO_SYSTEM_IMPL_EXPORT ScopedPlatformHandle
+CreateServerHandle(const NamedPlatformHandle& handle,
+ const CreateServerHandleOptions& options = {});
+
+} // namespace edk
+} // namespace mojo
+
+#endif // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_UTILS_H_
diff --git a/mojo/edk/embedder/named_platform_handle_utils_posix.cc b/mojo/edk/embedder/named_platform_handle_utils_posix.cc
new file mode 100644
index 0000000..056f4d6
--- /dev/null
+++ b/mojo/edk/embedder/named_platform_handle_utils_posix.cc
@@ -0,0 +1,141 @@
+// Copyright 2016 The Chromium 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/edk/embedder/named_platform_handle_utils.h"
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "mojo/edk/embedder/named_platform_handle.h"
+
+namespace mojo {
+namespace edk {
+namespace {
+
+// This function fills in |unix_addr| with the appropriate data for the socket,
+// and sets |unix_addr_len| to the length of the data therein.
+// Returns true on success, or false on failure (typically because |handle.name|
+// violated the naming rules).
+bool MakeUnixAddr(const NamedPlatformHandle& handle,
+ struct sockaddr_un* unix_addr,
+ size_t* unix_addr_len) {
+ DCHECK(unix_addr);
+ DCHECK(unix_addr_len);
+ DCHECK(handle.is_valid());
+
+ // We reject handle.name.length() == kMaxSocketNameLength to make room for the
+ // NUL terminator at the end of the string.
+ if (handle.name.length() >= kMaxSocketNameLength) {
+ LOG(ERROR) << "Socket name too long: " << handle.name;
+ return false;
+ }
+
+ // Create unix_addr structure.
+ memset(unix_addr, 0, sizeof(struct sockaddr_un));
+ unix_addr->sun_family = AF_UNIX;
+ strncpy(unix_addr->sun_path, handle.name.c_str(), kMaxSocketNameLength);
+ *unix_addr_len =
+ offsetof(struct sockaddr_un, sun_path) + handle.name.length();
+ return true;
+}
+
+// This function creates a unix domain socket, and set it as non-blocking.
+// If successful, this returns a ScopedPlatformHandle containing the socket.
+// Otherwise, this returns an invalid ScopedPlatformHandle.
+ScopedPlatformHandle CreateUnixDomainSocket(bool needs_connection) {
+ // Create the unix domain socket.
+ PlatformHandle socket_handle(socket(AF_UNIX, SOCK_STREAM, 0));
+ socket_handle.needs_connection = needs_connection;
+ ScopedPlatformHandle handle(socket_handle);
+ if (!handle.is_valid()) {
+ PLOG(ERROR) << "Failed to create AF_UNIX socket.";
+ return ScopedPlatformHandle();
+ }
+
+ // Now set it as non-blocking.
+ if (!base::SetNonBlocking(handle.get().handle)) {
+ PLOG(ERROR) << "base::SetNonBlocking() failed " << handle.get().handle;
+ return ScopedPlatformHandle();
+ }
+ return handle;
+}
+
+} // namespace
+
+ScopedPlatformHandle CreateClientHandle(
+ const NamedPlatformHandle& named_handle) {
+ if (!named_handle.is_valid())
+ return ScopedPlatformHandle();
+
+ struct sockaddr_un unix_addr;
+ size_t unix_addr_len;
+ if (!MakeUnixAddr(named_handle, &unix_addr, &unix_addr_len))
+ return ScopedPlatformHandle();
+
+ ScopedPlatformHandle handle = CreateUnixDomainSocket(false);
+ if (!handle.is_valid())
+ return ScopedPlatformHandle();
+
+ if (HANDLE_EINTR(connect(handle.get().handle,
+ reinterpret_cast<sockaddr*>(&unix_addr),
+ unix_addr_len)) < 0) {
+ PLOG(ERROR) << "connect " << named_handle.name;
+ return ScopedPlatformHandle();
+ }
+
+ return handle;
+}
+
+ScopedPlatformHandle CreateServerHandle(
+ const NamedPlatformHandle& named_handle,
+ const CreateServerHandleOptions& options) {
+ if (!named_handle.is_valid())
+ return ScopedPlatformHandle();
+
+ // Make sure the path we need exists.
+ base::FilePath socket_dir = base::FilePath(named_handle.name).DirName();
+ if (!base::CreateDirectory(socket_dir)) {
+ LOG(ERROR) << "Couldn't create directory: " << socket_dir.value();
+ return ScopedPlatformHandle();
+ }
+
+ // Delete any old FS instances.
+ if (unlink(named_handle.name.c_str()) < 0 && errno != ENOENT) {
+ PLOG(ERROR) << "unlink " << named_handle.name;
+ return ScopedPlatformHandle();
+ }
+
+ struct sockaddr_un unix_addr;
+ size_t unix_addr_len;
+ if (!MakeUnixAddr(named_handle, &unix_addr, &unix_addr_len))
+ return ScopedPlatformHandle();
+
+ ScopedPlatformHandle handle = CreateUnixDomainSocket(true);
+ if (!handle.is_valid())
+ return ScopedPlatformHandle();
+
+ // Bind the socket.
+ if (bind(handle.get().handle, reinterpret_cast<const sockaddr*>(&unix_addr),
+ unix_addr_len) < 0) {
+ PLOG(ERROR) << "bind " << named_handle.name;
+ return ScopedPlatformHandle();
+ }
+
+ // Start listening on the socket.
+ if (listen(handle.get().handle, SOMAXCONN) < 0) {
+ PLOG(ERROR) << "listen " << named_handle.name;
+ unlink(named_handle.name.c_str());
+ return ScopedPlatformHandle();
+ }
+ return handle;
+}
+
+} // namespace edk
+} // namespace mojo
diff --git a/mojo/edk/embedder/named_platform_handle_utils_win.cc b/mojo/edk/embedder/named_platform_handle_utils_win.cc
new file mode 100644
index 0000000..a145847
--- /dev/null
+++ b/mojo/edk/embedder/named_platform_handle_utils_win.cc
@@ -0,0 +1,95 @@
+// Copyright 2016 The Chromium 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/edk/embedder/named_platform_handle_utils.h"
+
+#include <sddl.h>
+#include <windows.h>
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/win/windows_version.h"
+#include "mojo/edk/embedder/named_platform_handle.h"
+
+namespace mojo {
+namespace edk {
+namespace {
+
+// A DACL to grant:
+// GA = Generic All
+// access to:
+// SY = LOCAL_SYSTEM
+// BA = BUILTIN_ADMINISTRATORS
+// OW = OWNER_RIGHTS
+constexpr base::char16 kDefaultSecurityDescriptor[] =
+ L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;OW)";
+
+} // namespace
+
+ScopedPlatformHandle CreateClientHandle(
+ const NamedPlatformHandle& named_handle) {
+ if (!named_handle.is_valid())
+ return ScopedPlatformHandle();
+
+ base::string16 pipe_name = named_handle.pipe_name();
+
+ // Note: This may block.
+ if (!WaitNamedPipeW(pipe_name.c_str(), NMPWAIT_USE_DEFAULT_WAIT))
+ return ScopedPlatformHandle();
+
+ 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;
+ ScopedPlatformHandle handle(
+ PlatformHandle(CreateFileW(pipe_name.c_str(), kDesiredAccess,
+ 0, // No sharing.
+ nullptr, OPEN_EXISTING, kFlags,
+ nullptr))); // No template file.
+ // The server may have stopped accepting a connection between the
+ // WaitNamedPipe() and CreateFile(). If this occurs, an invalid handle is
+ // returned.
+ DPLOG_IF(ERROR, !handle.is_valid())
+ << "Named pipe " << named_handle.pipe_name()
+ << " could not be opened after WaitNamedPipe succeeded";
+ return handle;
+}
+
+ScopedPlatformHandle CreateServerHandle(
+ const NamedPlatformHandle& named_handle,
+ const CreateServerHandleOptions& options) {
+ if (!named_handle.is_valid())
+ return ScopedPlatformHandle();
+
+ PSECURITY_DESCRIPTOR security_desc = nullptr;
+ ULONG security_desc_len = 0;
+ PCHECK(ConvertStringSecurityDescriptorToSecurityDescriptor(
+ options.security_descriptor.empty() ? kDefaultSecurityDescriptor
+ : options.security_descriptor.c_str(),
+ SDDL_REVISION_1, &security_desc, &security_desc_len));
+ std::unique_ptr<void, decltype(::LocalFree)*> p(security_desc, ::LocalFree);
+ SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES),
+ security_desc, FALSE};
+
+ const DWORD kOpenMode = options.enforce_uniqueness
+ ? PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
+ FILE_FLAG_FIRST_PIPE_INSTANCE
+ : PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED;
+ const DWORD kPipeMode =
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS;
+ PlatformHandle handle(
+ CreateNamedPipeW(named_handle.pipe_name().c_str(), kOpenMode, kPipeMode,
+ options.enforce_uniqueness ? 1 : 255, // Max instances.
+ 4096, // Out buffer size.
+ 4096, // In buffer size.
+ 5000, // Timeout in milliseconds.
+ &security_attributes));
+ handle.needs_connection = true;
+ return ScopedPlatformHandle(handle);
+}
+
+} // namespace edk
+} // namespace mojo
diff --git a/mojo/edk/embedder/platform_shared_buffer_unittest.cc b/mojo/edk/embedder/platform_shared_buffer_unittest.cc
new file mode 100644
index 0000000..f1593f0
--- /dev/null
+++ b/mojo/edk/embedder/platform_shared_buffer_unittest.cc
@@ -0,0 +1,227 @@
+// Copyright 2014 The Chromium 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/edk/embedder/platform_shared_buffer.h"
+
+#include <stddef.h>
+
+#include <limits>
+#include <memory>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/shared_memory.h"
+#include "base/sys_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
+namespace mojo {
+namespace edk {
+namespace {
+
+TEST(PlatformSharedBufferTest, 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<PlatformSharedBuffer> buffer(
+ PlatformSharedBuffer::Create(kNumBytes));
+ ASSERT_TRUE(buffer);
+
+ // Map it all, scribble some stuff, and then unmap it.
+ {
+ EXPECT_TRUE(buffer->IsValidMap(0, kNumBytes));
+ std::unique_ptr<PlatformSharedBufferMapping> mapping(
+ buffer->Map(0, kNumBytes));
+ ASSERT_TRUE(mapping);
+ ASSERT_TRUE(mapping->GetBase());
+ int* stuff = static_cast<int*>(mapping->GetBase());
+ 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.
+ std::unique_ptr<PlatformSharedBufferMapping> mapping1(
+ buffer->MapNoCheck(0, kNumBytes));
+ ASSERT_TRUE(mapping1);
+ ASSERT_TRUE(mapping1->GetBase());
+ int* stuff1 = static_cast<int*>(mapping1->GetBase());
+ for (size_t i = 0; i < kNumInts; i++)
+ EXPECT_EQ(static_cast<int>(i) + kFudge, stuff1[i]) << i;
+
+ std::unique_ptr<PlatformSharedBufferMapping> mapping2(
+ buffer->Map((kNumInts / 2) * sizeof(int), 2 * sizeof(int)));
+ ASSERT_TRUE(mapping2);
+ ASSERT_TRUE(mapping2->GetBase());
+ int* stuff2 = static_cast<int*>(mapping2->GetBase());
+ 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)));
+ std::unique_ptr<PlatformSharedBufferMapping> mapping(
+ buffer->Map(sizeof(int), kNumBytes - sizeof(int)));
+ ASSERT_TRUE(mapping);
+ ASSERT_TRUE(mapping->GetBase());
+ int* stuff = static_cast<int*>(mapping->GetBase());
+
+ 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(PlatformSharedBufferTest, InvalidMappings) {
+ scoped_refptr<PlatformSharedBuffer> buffer(PlatformSharedBuffer::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(PlatformSharedBufferTest, TooBig) {
+ // If |size_t| is 32-bit, it's quite possible/likely that |Create()| succeeds
+ // (since it only involves creating a 4 GB file).
+ size_t max_size = std::numeric_limits<size_t>::max();
+ if (base::SysInfo::AmountOfVirtualMemory() &&
+ max_size > static_cast<size_t>(base::SysInfo::AmountOfVirtualMemory()))
+ max_size = static_cast<size_t>(base::SysInfo::AmountOfVirtualMemory());
+ scoped_refptr<PlatformSharedBuffer> buffer(
+ PlatformSharedBuffer::Create(max_size));
+ // But, assuming |sizeof(size_t) == sizeof(void*)|, mapping all of it should
+ // always fail.
+ if (buffer)
+ EXPECT_FALSE(buffer->Map(0, max_size));
+}
+
+// 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(PlatformSharedBufferTest, MappingsDistinct) {
+ scoped_refptr<PlatformSharedBuffer> buffer(PlatformSharedBuffer::Create(100));
+ std::unique_ptr<PlatformSharedBufferMapping> mapping1(buffer->Map(0, 100));
+ std::unique_ptr<PlatformSharedBufferMapping> mapping2(buffer->Map(0, 100));
+ EXPECT_NE(mapping1->GetBase(), mapping2->GetBase());
+}
+
+TEST(PlatformSharedBufferTest, BufferZeroInitialized) {
+ static const size_t kSizes[] = {10, 100, 1000, 10000, 100000};
+ for (size_t i = 0; i < arraysize(kSizes); i++) {
+ scoped_refptr<PlatformSharedBuffer> buffer(
+ PlatformSharedBuffer::Create(kSizes[i]));
+ std::unique_ptr<PlatformSharedBufferMapping> 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->GetBase())[j])
+ << "size " << kSizes[i] << ", offset " << j;
+ }
+ }
+}
+
+TEST(PlatformSharedBufferTest, MappingsOutliveBuffer) {
+ std::unique_ptr<PlatformSharedBufferMapping> mapping1;
+ std::unique_ptr<PlatformSharedBufferMapping> mapping2;
+
+ {
+ scoped_refptr<PlatformSharedBuffer> buffer(
+ PlatformSharedBuffer::Create(100));
+ mapping1 = buffer->Map(0, 100);
+ mapping2 = buffer->Map(50, 50);
+ static_cast<char*>(mapping1->GetBase())[50] = 'x';
+ }
+
+ EXPECT_EQ('x', static_cast<char*>(mapping2->GetBase())[0]);
+
+ static_cast<char*>(mapping2->GetBase())[1] = 'y';
+ EXPECT_EQ('y', static_cast<char*>(mapping1->GetBase())[51]);
+}
+
+TEST(PlatformSharedBufferTest, FromSharedMemoryHandle) {
+ const size_t kBufferSize = 1234;
+ base::SharedMemoryCreateOptions options;
+ options.size = kBufferSize;
+ base::SharedMemory shared_memory;
+ ASSERT_TRUE(shared_memory.Create(options));
+ ASSERT_TRUE(shared_memory.Map(kBufferSize));
+
+ base::SharedMemoryHandle shm_handle =
+ base::SharedMemory::DuplicateHandle(shared_memory.handle());
+ scoped_refptr<PlatformSharedBuffer> simple_buffer(
+ PlatformSharedBuffer::CreateFromSharedMemoryHandle(
+ kBufferSize, false /* read_only */, shm_handle));
+ ASSERT_TRUE(simple_buffer);
+
+ std::unique_ptr<PlatformSharedBufferMapping> mapping =
+ simple_buffer->Map(0, kBufferSize);
+ ASSERT_TRUE(mapping);
+
+ const int kOffset = 123;
+ char* base_memory = static_cast<char*>(shared_memory.memory());
+ char* mojo_memory = static_cast<char*>(mapping->GetBase());
+ base_memory[kOffset] = 0;
+ EXPECT_EQ(0, mojo_memory[kOffset]);
+ base_memory[kOffset] = 'a';
+ EXPECT_EQ('a', mojo_memory[kOffset]);
+ mojo_memory[kOffset] = 'z';
+ EXPECT_EQ('z', base_memory[kOffset]);
+}
+
+} // namespace
+} // namespace edk
+} // namespace mojo
diff --git a/mojo/edk/embedder/scoped_ipc_support.cc b/mojo/edk/embedder/scoped_ipc_support.cc
new file mode 100644
index 0000000..f67210a
--- /dev/null
+++ b/mojo/edk/embedder/scoped_ipc_support.cc
@@ -0,0 +1,39 @@
+// Copyright 2015 The Chromium 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/edk/embedder/scoped_ipc_support.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread_restrictions.h"
+#include "mojo/edk/embedder/embedder.h"
+
+namespace mojo {
+namespace edk {
+
+ScopedIPCSupport::ScopedIPCSupport(
+ scoped_refptr<base::TaskRunner> io_thread_task_runner,
+ ShutdownPolicy shutdown_policy) : shutdown_policy_(shutdown_policy) {
+ InitIPCSupport(io_thread_task_runner);
+}
+
+ScopedIPCSupport::~ScopedIPCSupport() {
+ if (shutdown_policy_ == ShutdownPolicy::FAST) {
+ ShutdownIPCSupport(base::Bind(&base::DoNothing));
+ return;
+ }
+
+ base::WaitableEvent shutdown_event(
+ base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ ShutdownIPCSupport(base::Bind(&base::WaitableEvent::Signal,
+ base::Unretained(&shutdown_event)));
+
+ base::ThreadRestrictions::ScopedAllowWait allow_io;
+ shutdown_event.Wait();
+}
+
+} // namespace edk
+} // namespace mojo
diff --git a/mojo/edk/embedder/scoped_ipc_support.h b/mojo/edk/embedder/scoped_ipc_support.h
new file mode 100644
index 0000000..22d8e50
--- /dev/null
+++ b/mojo/edk/embedder/scoped_ipc_support.h
@@ -0,0 +1,118 @@
+// Copyright 2015 The Chromium 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_EDK_EMBEDDER_SCOPED_IPC_SUPPORT_H_
+#define MOJO_EDK_EMBEDDER_SCOPED_IPC_SUPPORT_H_
+
+#include "base/memory/ref_counted.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace base {
+class TaskRunner;
+}
+
+namespace mojo {
+namespace edk {
+
+// A simple class that calls |InitIPCSupport()| on construction and
+// |ShutdownIPCSupport()| on destruction, blocking the destructor on clean IPC
+// shutdown completion.
+class MOJO_SYSTEM_IMPL_EXPORT ScopedIPCSupport {
+ public:
+ // ShutdownPolicy is a type for specifying the desired Mojo IPC support
+ // shutdown behavior used during ScopedIPCSupport destruction.
+ //
+ // What follows is a quick overview of why shutdown behavior is interesting
+ // and how you might decide which behavior is right for your use case.
+ //
+ // BACKGROUND
+ // ==========
+ //
+ // In order to facilitate efficient and reliable transfer of Mojo message pipe
+ // endpoints across process boundaries, the underlying model for a message
+ // pipe is actually a self-collapsing cycle of "ports." See
+ // //mojo/edk/system/ports for gritty implementation details.
+ //
+ // Ports are essentially globally unique identifiers used for system-wide
+ // message routing. Every message pipe consists of at least two such ports:
+ // the pipe's two concrete endpoints.
+ //
+ // When a message pipe endpoint is transferred over another message pipe, that
+ // endpoint's port (which subsequently exists only internally with no
+ // publicly-reachable handle) enters a transient proxying state for the
+ // remainder of its lifetime. Once sufficient information has been
+ // proagated throughout the system and this proxying port can be safely
+ // bypassed, it is garbage-collected.
+ //
+ // If a process is terminated while hosting any active proxy ports, this
+ // will necessarily break the message pipe(s) to which those ports belong.
+ //
+ // WHEN TO USE CLEAN SHUTDOWN
+ // ==========================
+ //
+ // Consider three processes, A, B, and C. Suppose A creates a message pipe,
+ // sending one end to B and the other to C. For some brief period of time,
+ // messages sent by B or C over this pipe may be proxied through A.
+ //
+ // If A is suddenly terminated, there may be no way for B's messages to reach
+ // C (and vice versa), since the message pipe state may not have been fully
+ // propagated to all concerned processes in the system. As such, both B and C
+ // may have no choice but to signal peer closure on their respective ends of
+ // the pipe, and thus the pipe may be broken despite a lack of intent by
+ // either B or C.
+ //
+ // This can also happen if A creates a pipe and passes one end to B, who then
+ // passes it along to C. B may temporarily proxy messages for this pipe
+ // between A and C, and B's sudden demise will in turn beget the pipe's
+ // own sudden demise.
+ //
+ // In situations where these sort of arrangements may occur, potentially
+ // proxying processes must ensure they are shut down cleanly in order to avoid
+ // flaky system behavior.
+ //
+ // WHEN TO USE FAST SHUTDOWN
+ // =========================
+ //
+ // As a general rule of thumb, if your process never creates a message pipe
+ // where both ends are passed to other processes, or never forwards a pipe
+ // endpoint from one process to another, fast shutdown is safe. Satisfaction
+ // of these constraints can be difficult to prove though, so clean shutdown is
+ // a safe default choice.
+ //
+ // Content renderer processes are a good example of a case where fast shutdown
+ // is safe, because as a matter of security and stability, a renderer cannot
+ // be trusted to do any proxying on behalf of two other processes anyway.
+ //
+ // There are other practical scenarios where fast shutdown is safe even if
+ // the process may have live proxies. For example, content's browser process
+ // is treated as a sort of master process in the system, in the sense that if
+ // the browser is terminated, no other part of the system is expected to
+ // continue normal operation anyway. In this case the side-effects of fast
+ // shutdown are irrelevant, so fast shutdown is preferred.
+ enum class ShutdownPolicy {
+ // Clean shutdown. This causes the ScopedIPCSupport destructor to *block*
+ // the calling thread until clean shutdown is complete. See explanation
+ // above for details.
+ CLEAN,
+
+ // Fast shutdown. In this case a cheap best-effort attempt is made to
+ // shut down the IPC system, but no effort is made to wait for its
+ // completion. See explanation above for details.
+ FAST,
+ };
+
+ ScopedIPCSupport(scoped_refptr<base::TaskRunner> io_thread_task_runner,
+ ShutdownPolicy shutdown_policy);
+ ~ScopedIPCSupport();
+
+ private:
+ const ShutdownPolicy shutdown_policy_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedIPCSupport);
+};
+
+} // namespace edk
+} // namespace mojo
+
+#endif // MOJO_EDK_EMBEDDER_SCOPED_IPC_SUPPORT_H_
diff --git a/mojo/edk/js/BUILD.gn b/mojo/edk/js/BUILD.gn
index 429a8c0..fc1e03c 100644
--- a/mojo/edk/js/BUILD.gn
+++ b/mojo/edk/js/BUILD.gn
@@ -2,21 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-# TODO(hansmuller): The organization of tests in this directory is weird:
-# * Really, js_unittests tests public stuff, so that should live in public
-# and be reworked as some sort of apptest.
-# * Both js_unittests and js_integration_tests should auto-generate their
-# tests somehow. The .cc files are just test runner stubs, including
-# explicit lists of .js files.
-group("tests") {
- testonly = true
- deps = [
- "test:js_integration_tests",
- "test:js_unittests",
- ]
-}
-
-source_set("js") {
+component("js") {
sources = [
"core.cc",
"core.h",
@@ -25,6 +11,7 @@ source_set("js") {
"handle.cc",
"handle.h",
"handle_close_observer.h",
+ "js_export.h",
"mojo_runner_delegate.cc",
"mojo_runner_delegate.h",
"support.cc",
@@ -44,4 +31,5 @@ source_set("js") {
deps = [
"//mojo/public/cpp/system",
]
+ defines = [ "MOJO_JS_IMPLEMENTATION" ]
}
diff --git a/mojo/edk/js/core.cc b/mojo/edk/js/core.cc
index c8ccc37..f3eec8c 100644
--- a/mojo/edk/js/core.cc
+++ b/mojo/edk/js/core.cc
@@ -289,6 +289,87 @@ bool IsHandle(gin::Arguments* args, v8::Handle<v8::Value> val) {
args->isolate(), val, &ignore_handle);
}
+gin::Dictionary CreateSharedBuffer(const gin::Arguments& args,
+ uint64_t num_bytes,
+ MojoCreateSharedBufferOptionsFlags flags) {
+ gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+ MojoHandle handle = MOJO_HANDLE_INVALID;
+ MojoCreateSharedBufferOptions options;
+ // The |flags| is mandatory parameter for CreateSharedBuffer, and it will
+ // be always initialized in MojoCreateSharedBufferOptions struct. For
+ // forward compatibility, set struct_size to be 8 bytes (struct_size + flags),
+ // so that validator will only check the field that is set.
+ options.struct_size = 8;
+ options.flags = flags;
+ MojoResult result = MojoCreateSharedBuffer(&options, num_bytes, &handle);
+ if (result != MOJO_RESULT_OK) {
+ dictionary.Set("result", result);
+ return dictionary;
+ }
+
+ dictionary.Set("result", result);
+ dictionary.Set("handle", mojo::Handle(handle));
+
+ return dictionary;
+}
+
+gin::Dictionary DuplicateBufferHandle(
+ const gin::Arguments& args,
+ mojo::Handle handle,
+ MojoDuplicateBufferHandleOptionsFlags flags) {
+ gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+ MojoHandle duped = MOJO_HANDLE_INVALID;
+ MojoDuplicateBufferHandleOptions options;
+ // The |flags| is mandatory parameter for DuplicateBufferHandle, and it will
+ // be always initialized in MojoDuplicateBufferHandleOptions struct. For
+ // forward compatibility, set struct_size to be 8 bytes (struct_size + flags),
+ // so that validator will only check the field that is set.
+ options.struct_size = 8;
+ options.flags = flags;
+ MojoResult result =
+ MojoDuplicateBufferHandle(handle.value(), &options, &duped);
+ if (result != MOJO_RESULT_OK) {
+ dictionary.Set("result", result);
+ return dictionary;
+ }
+
+ dictionary.Set("result", result);
+ dictionary.Set("handle", mojo::Handle(duped));
+
+ return dictionary;
+}
+
+gin::Dictionary MapBuffer(const gin::Arguments& args,
+ mojo::Handle handle,
+ uint64_t offset,
+ uint64_t num_bytes,
+ MojoMapBufferFlags flags) {
+ gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
+ void* data = nullptr;
+ MojoResult result =
+ MojoMapBuffer(handle.value(), offset, num_bytes, &data, flags);
+ if (result != MOJO_RESULT_OK) {
+ dictionary.Set("result", result);
+ return dictionary;
+ }
+
+ v8::Handle<v8::ArrayBuffer> array_buffer =
+ v8::ArrayBuffer::New(args.isolate(), data, num_bytes);
+
+ dictionary.Set("result", result);
+ dictionary.Set("buffer", array_buffer);
+
+ return dictionary;
+}
+
+MojoResult UnmapBuffer(const gin::Arguments& args,
+ const v8::Handle<v8::ArrayBuffer>& buffer) {
+ // Buffer must be external, created by MapBuffer
+ if (!buffer->IsExternal())
+ return MOJO_RESULT_INVALID_ARGUMENT;
+
+ return MojoUnmapBuffer(buffer->GetContents().Data());
+}
gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
@@ -317,6 +398,10 @@ v8::Local<v8::Value> Core::GetModule(v8::Isolate* isolate) {
.SetMethod("readData", ReadData)
.SetMethod("drainData", DoDrainData)
.SetMethod("isHandle", IsHandle)
+ .SetMethod("createSharedBuffer", CreateSharedBuffer)
+ .SetMethod("duplicateBufferHandle", DuplicateBufferHandle)
+ .SetMethod("mapBuffer", MapBuffer)
+ .SetMethod("unmapBuffer", UnmapBuffer)
.SetValue("RESULT_OK", MOJO_RESULT_OK)
.SetValue("RESULT_CANCELLED", MOJO_RESULT_CANCELLED)
@@ -369,6 +454,16 @@ v8::Local<v8::Value> Core::GetModule(v8::Isolate* isolate) {
.SetValue("READ_DATA_FLAG_DISCARD", MOJO_READ_DATA_FLAG_DISCARD)
.SetValue("READ_DATA_FLAG_QUERY", MOJO_READ_DATA_FLAG_QUERY)
.SetValue("READ_DATA_FLAG_PEEK", MOJO_READ_DATA_FLAG_PEEK)
+ .SetValue("CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE",
+ MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE)
+
+ .SetValue("DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE",
+ MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE)
+
+ .SetValue("DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY",
+ MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY)
+
+ .SetValue("MAP_BUFFER_FLAG_NONE", MOJO_MAP_BUFFER_FLAG_NONE)
.Build();
data->SetObjectTemplate(&g_wrapper_info, templ);
diff --git a/mojo/edk/js/core.h b/mojo/edk/js/core.h
index ca203ea..97ef5df 100644
--- a/mojo/edk/js/core.h
+++ b/mojo/edk/js/core.h
@@ -5,13 +5,14 @@
#ifndef MOJO_EDK_JS_CORE_H_
#define MOJO_EDK_JS_CORE_H_
+#include "mojo/edk/js/js_export.h"
#include "v8/include/v8.h"
namespace mojo {
namespace edk {
namespace js {
-class Core {
+class MOJO_JS_EXPORT Core {
public:
static const char kModuleName[];
static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
diff --git a/mojo/edk/js/drain_data.cc b/mojo/edk/js/drain_data.cc
index ca5fdf4..cfd0bb5 100644
--- a/mojo/edk/js/drain_data.cc
+++ b/mojo/edk/js/drain_data.cc
@@ -8,6 +8,7 @@
#include <stdint.h>
#include "base/bind.h"
+#include "base/memory/ptr_util.h"
#include "gin/array_buffer.h"
#include "gin/converter.h"
#include "gin/dictionary.h"
@@ -20,7 +21,9 @@ namespace edk {
namespace js {
DrainData::DrainData(v8::Isolate* isolate, mojo::Handle handle)
- : isolate_(isolate), handle_(DataPipeConsumerHandle(handle.value())) {
+ : isolate_(isolate),
+ handle_(DataPipeConsumerHandle(handle.value())),
+ handle_watcher_(FROM_HERE) {
v8::Handle<v8::Context> context(isolate_->GetCurrentContext());
runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr();
@@ -67,8 +70,7 @@ MojoResult DrainData::ReadData() {
if (result != MOJO_RESULT_OK)
return result;
const char* p = static_cast<const char*>(buffer);
- DataBuffer* data_buffer = new DataBuffer(p, p + num_bytes);
- data_buffers_.push_back(data_buffer);
+ data_buffers_.push_back(base::MakeUnique<DataBuffer>(p, p + num_bytes));
return EndReadDataRaw(handle_.get(), num_bytes);
}
diff --git a/mojo/edk/js/drain_data.h b/mojo/edk/js/drain_data.h
index 917ca05..6e8555c 100644
--- a/mojo/edk/js/drain_data.h
+++ b/mojo/edk/js/drain_data.h
@@ -5,7 +5,9 @@
#ifndef MOJO_EDK_JS_DRAIN_DATA_H_
#define MOJO_EDK_JS_DRAIN_DATA_H_
-#include "base/memory/scoped_vector.h"
+#include <memory>
+#include <vector>
+
#include "gin/runner.h"
#include "mojo/public/cpp/system/core.h"
#include "mojo/public/cpp/system/watcher.h"
@@ -53,7 +55,7 @@ class DrainData {
Watcher handle_watcher_;
base::WeakPtr<gin::Runner> runner_;
v8::UniquePersistent<v8::Promise::Resolver> resolver_;
- ScopedVector<DataBuffer> data_buffers_;
+ std::vector<std::unique_ptr<DataBuffer>> data_buffers_;
};
} // namespace js
diff --git a/mojo/edk/js/handle.cc b/mojo/edk/js/handle.cc
index 9f9f161..7da8e9f 100644
--- a/mojo/edk/js/handle.cc
+++ b/mojo/edk/js/handle.cc
@@ -37,7 +37,8 @@ void HandleWrapper::NotifyCloseObservers() {
if (!handle_.is_valid())
return;
- FOR_EACH_OBSERVER(HandleCloseObserver, close_observers_, OnWillCloseHandle());
+ for (auto& observer : close_observers_)
+ observer.OnWillCloseHandle();
}
} // namespace js
diff --git a/mojo/edk/js/handle.h b/mojo/edk/js/handle.h
index 2470fac..60652ed 100644
--- a/mojo/edk/js/handle.h
+++ b/mojo/edk/js/handle.h
@@ -11,6 +11,7 @@
#include "gin/converter.h"
#include "gin/handle.h"
#include "gin/wrappable.h"
+#include "mojo/edk/js/js_export.h"
#include "mojo/public/cpp/system/core.h"
namespace mojo {
@@ -21,7 +22,7 @@ class HandleCloseObserver;
// 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> {
+class MOJO_JS_EXPORT HandleWrapper : public gin::Wrappable<HandleWrapper> {
public:
static gin::WrapperInfo kWrapperInfo;
@@ -56,16 +57,16 @@ namespace gin {
// MojoHandle, since that will do a simple int32_t 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> {
+template <>
+struct MOJO_JS_EXPORT 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);
};
-template<>
-struct Converter<mojo::MessagePipeHandle> {
+template <>
+struct MOJO_JS_EXPORT Converter<mojo::MessagePipeHandle> {
static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate,
mojo::MessagePipeHandle val);
static bool FromV8(v8::Isolate* isolate,
@@ -76,7 +77,7 @@ struct Converter<mojo::MessagePipeHandle> {
// 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<mojo::edk::js::HandleWrapper>> {
+struct MOJO_JS_EXPORT Converter<gin::Handle<mojo::edk::js::HandleWrapper>> {
static v8::Handle<v8::Value> ToV8(
v8::Isolate* isolate,
const gin::Handle<mojo::edk::js::HandleWrapper>& val) {
diff --git a/mojo/edk/js/js_export.h b/mojo/edk/js/js_export.h
new file mode 100644
index 0000000..179113c
--- /dev/null
+++ b/mojo/edk/js/js_export.h
@@ -0,0 +1,32 @@
+// Copyright 2016 The Chromium 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_EDK_JS_JS_EXPORT_H_
+#define MOJO_EDK_JS_JS_EXPORT_H_
+
+// Defines MOJO_JS_EXPORT so that functionality implemented by //mojo/edk/js can
+// be exported to consumers.
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(MOJO_JS_IMPLEMENTATION)
+#define MOJO_JS_EXPORT __declspec(dllexport)
+#else
+#define MOJO_JS_EXPORT __declspec(dllimport)
+#endif // defined(MOJO_JS_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(MOJO_JS_IMPLEMENTATION)
+#define MOJO_JS_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_JS_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define MOJO_JS_EXPORT
+#endif
+
+#endif // MOJO_EDK_JS_JS_EXPORT_H_
diff --git a/mojo/edk/js/mojo_runner_delegate.h b/mojo/edk/js/mojo_runner_delegate.h
index a76460c..9ab325c 100644
--- a/mojo/edk/js/mojo_runner_delegate.h
+++ b/mojo/edk/js/mojo_runner_delegate.h
@@ -7,13 +7,14 @@
#include "base/macros.h"
#include "gin/modules/module_runner_delegate.h"
+#include "mojo/edk/js/js_export.h"
#include "mojo/public/c/system/core.h"
namespace mojo {
namespace edk {
namespace js {
-class MojoRunnerDelegate : public gin::ModuleRunnerDelegate {
+class MOJO_JS_EXPORT MojoRunnerDelegate : public gin::ModuleRunnerDelegate {
public:
MojoRunnerDelegate();
~MojoRunnerDelegate() override;
diff --git a/mojo/edk/js/support.h b/mojo/edk/js/support.h
index c57cf7a..551f5ac 100644
--- a/mojo/edk/js/support.h
+++ b/mojo/edk/js/support.h
@@ -5,13 +5,14 @@
#ifndef MOJO_EDK_JS_SUPPORT_H_
#define MOJO_EDK_JS_SUPPORT_H_
+#include "mojo/edk/js/js_export.h"
#include "v8/include/v8.h"
namespace mojo {
namespace edk {
namespace js {
-class Support {
+class MOJO_JS_EXPORT Support {
public:
static const char kModuleName[];
static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
diff --git a/mojo/edk/js/tests/run_js_unittests.cc b/mojo/edk/js/tests/run_js_unittests.cc
new file mode 100644
index 0000000..a7b70b7
--- /dev/null
+++ b/mojo/edk/js/tests/run_js_unittests.cc
@@ -0,0 +1,83 @@
+// Copyright 2014 The Chromium 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/files/file_path.h"
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "gin/modules/console.h"
+#include "gin/modules/module_registry.h"
+#include "gin/test/file_runner.h"
+#include "gin/test/gtest.h"
+#include "mojo/edk/js/core.h"
+#include "mojo/edk/js/support.h"
+#include "mojo/edk/js/threading.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+namespace js {
+namespace {
+
+class TestRunnerDelegate : public gin::FileRunnerDelegate {
+ public:
+ TestRunnerDelegate() {
+ AddBuiltinModule(gin::Console::kModuleName, gin::Console::GetModule);
+ AddBuiltinModule(Core::kModuleName, Core::GetModule);
+ AddBuiltinModule(Threading::kModuleName, Threading::GetModule);
+ AddBuiltinModule(Support::kModuleName, Support::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("public")
+ .AppendASCII("js")
+ .AppendASCII("tests")
+ .AppendASCII(test);
+ TestRunnerDelegate delegate;
+ gin::RunTestFromFile(path, &delegate, run_until_idle);
+}
+
+// TODO(abarth): Should we autogenerate these stubs from GYP?
+TEST(JSTest, Codec) {
+ RunTest("codec_unittest.js", true);
+}
+
+TEST(JSTest, Connection) {
+ RunTest("connection_unittest.js", false);
+}
+
+TEST(JSTest, Core) {
+ RunTest("core_unittest.js", true);
+}
+
+TEST(JSTest, InterfacePtr) {
+ RunTest("interface_ptr_unittest.js", false);
+}
+
+TEST(JSTest, SampleService) {
+ RunTest("sample_service_unittest.js", false);
+}
+
+TEST(JSTest, Struct) {
+ RunTest("struct_unittest.js", true);
+}
+
+TEST(JSTest, Union) {
+ RunTest("union_unittest.js", true);
+}
+
+TEST(JSTest, Validation) {
+ RunTest("validation_unittest.js", true);
+}
+
+} // namespace
+} // namespace js
+} // namespace edk
+} // namespace mojo
diff --git a/mojo/edk/js/threading.h b/mojo/edk/js/threading.h
index 10404d5..653d076 100644
--- a/mojo/edk/js/threading.h
+++ b/mojo/edk/js/threading.h
@@ -6,13 +6,14 @@
#define MOJO_EDK_JS_THREADING_H_
#include "gin/public/wrapper_info.h"
+#include "mojo/edk/js/js_export.h"
#include "v8/include/v8.h"
namespace mojo {
namespace edk {
namespace js {
-class Threading {
+class MOJO_JS_EXPORT Threading {
public:
static const char kModuleName[];
static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
diff --git a/mojo/edk/js/waiting_callback.cc b/mojo/edk/js/waiting_callback.cc
index 4ba2c61..fada039 100644
--- a/mojo/edk/js/waiting_callback.cc
+++ b/mojo/edk/js/waiting_callback.cc
@@ -53,6 +53,7 @@ WaitingCallback::WaitingCallback(v8::Isolate* isolate,
v8::Handle<v8::Function> callback,
bool one_shot)
: one_shot_(one_shot),
+ watcher_(FROM_HERE),
weak_factory_(this) {
v8::Handle<v8::Context> context = isolate->GetCurrentContext();
runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr();
diff --git a/mojo/edk/system/broker_win.cc b/mojo/edk/system/broker_win.cc
new file mode 100644
index 0000000..063282c
--- /dev/null
+++ b/mojo/edk/system/broker_win.cc
@@ -0,0 +1,155 @@
+// Copyright 2016 The Chromium 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 <windows.h>
+
+#include <limits>
+#include <utility>
+
+#include "base/debug/alias.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/string_piece.h"
+#include "mojo/edk/embedder/named_platform_handle.h"
+#include "mojo/edk/embedder/named_platform_handle_utils.h"
+#include "mojo/edk/embedder/platform_handle.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/system/broker.h"
+#include "mojo/edk/system/broker_messages.h"
+#include "mojo/edk/system/channel.h"
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+// 256 bytes should be enough for anyone!
+const size_t kMaxBrokerMessageSize = 256;
+
+bool TakeHandlesFromBrokerMessage(Channel::Message* message,
+ size_t num_handles,
+ ScopedPlatformHandle* out_handles) {
+ if (message->num_handles() != num_handles) {
+ DLOG(ERROR) << "Received unexpected number of handles in broker message";
+ return false;
+ }
+
+ ScopedPlatformHandleVectorPtr handles = message->TakeHandles();
+ DCHECK(handles);
+ DCHECK_EQ(handles->size(), num_handles);
+ DCHECK(out_handles);
+
+ for (size_t i = 0; i < num_handles; ++i)
+ out_handles[i] = ScopedPlatformHandle((*handles)[i]);
+ handles->clear();
+ return true;
+}
+
+Channel::MessagePtr WaitForBrokerMessage(PlatformHandle platform_handle,
+ BrokerMessageType expected_type) {
+ char buffer[kMaxBrokerMessageSize];
+ DWORD bytes_read = 0;
+ BOOL result = ::ReadFile(platform_handle.handle, buffer,
+ kMaxBrokerMessageSize, &bytes_read, nullptr);
+ if (!result) {
+ // The pipe may be broken if the browser side has been closed, e.g. during
+ // browser shutdown. In that case the ReadFile call will fail and we
+ // shouldn't continue waiting.
+ PLOG(ERROR) << "Error reading broker pipe";
+ return nullptr;
+ }
+
+ Channel::MessagePtr message =
+ Channel::Message::Deserialize(buffer, static_cast<size_t>(bytes_read));
+ if (!message || message->payload_size() < sizeof(BrokerMessageHeader)) {
+ LOG(ERROR) << "Invalid broker message";
+
+ base::debug::Alias(&buffer[0]);
+ base::debug::Alias(&bytes_read);
+ base::debug::Alias(message.get());
+ CHECK(false);
+ return nullptr;
+ }
+
+ const BrokerMessageHeader* header =
+ reinterpret_cast<const BrokerMessageHeader*>(message->payload());
+ if (header->type != expected_type) {
+ LOG(ERROR) << "Unexpected broker message type";
+
+ base::debug::Alias(&buffer[0]);
+ base::debug::Alias(&bytes_read);
+ base::debug::Alias(message.get());
+ CHECK(false);
+ return nullptr;
+ }
+
+ return message;
+}
+
+} // namespace
+
+Broker::Broker(ScopedPlatformHandle handle) : sync_channel_(std::move(handle)) {
+ CHECK(sync_channel_.is_valid());
+ Channel::MessagePtr message =
+ WaitForBrokerMessage(sync_channel_.get(), BrokerMessageType::INIT);
+
+ // If we fail to read a message (broken pipe), just return early. The parent
+ // handle will be null and callers must handle this gracefully.
+ if (!message)
+ return;
+
+ if (!TakeHandlesFromBrokerMessage(message.get(), 1, &parent_channel_)) {
+ // If the message has no handles, we expect it to carry pipe name instead.
+ const BrokerMessageHeader* header =
+ static_cast<const BrokerMessageHeader*>(message->payload());
+ CHECK_GE(message->payload_size(),
+ sizeof(BrokerMessageHeader) + sizeof(InitData));
+ const InitData* data = reinterpret_cast<const InitData*>(header + 1);
+ CHECK_EQ(message->payload_size(),
+ sizeof(BrokerMessageHeader) + sizeof(InitData) +
+ data->pipe_name_length * sizeof(base::char16));
+ const base::char16* name_data =
+ reinterpret_cast<const base::char16*>(data + 1);
+ CHECK(data->pipe_name_length);
+ parent_channel_ = CreateClientHandle(NamedPlatformHandle(
+ base::StringPiece16(name_data, data->pipe_name_length)));
+ }
+}
+
+Broker::~Broker() {}
+
+ScopedPlatformHandle Broker::GetParentPlatformHandle() {
+ return std::move(parent_channel_);
+}
+
+scoped_refptr<PlatformSharedBuffer> Broker::GetSharedBuffer(size_t num_bytes) {
+ base::AutoLock lock(lock_);
+ BufferRequestData* buffer_request;
+ Channel::MessagePtr out_message = CreateBrokerMessage(
+ BrokerMessageType::BUFFER_REQUEST, 0, 0, &buffer_request);
+ buffer_request->size = base::checked_cast<uint32_t>(num_bytes);
+ DWORD bytes_written = 0;
+ BOOL result = ::WriteFile(sync_channel_.get().handle, out_message->data(),
+ static_cast<DWORD>(out_message->data_num_bytes()),
+ &bytes_written, nullptr);
+ if (!result ||
+ static_cast<size_t>(bytes_written) != out_message->data_num_bytes()) {
+ LOG(ERROR) << "Error sending sync broker message";
+ return nullptr;
+ }
+
+ ScopedPlatformHandle handles[2];
+ Channel::MessagePtr response = WaitForBrokerMessage(
+ sync_channel_.get(), BrokerMessageType::BUFFER_RESPONSE);
+ if (response &&
+ TakeHandlesFromBrokerMessage(response.get(), 2, &handles[0])) {
+ return PlatformSharedBuffer::CreateFromPlatformHandlePair(
+ num_bytes, std::move(handles[0]), std::move(handles[1]));
+ }
+
+ return nullptr;
+}
+
+} // namespace edk
+} // namespace mojo
diff --git a/mojo/edk/system/channel_unittest.cc b/mojo/edk/system/channel_unittest.cc
new file mode 100644
index 0000000..ce2c804
--- /dev/null
+++ b/mojo/edk/system/channel_unittest.cc
@@ -0,0 +1,177 @@
+// Copyright 2017 The Chromium 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/edk/system/channel.h"
+#include "base/memory/ptr_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+namespace {
+
+class TestChannel : public Channel {
+ public:
+ TestChannel(Channel::Delegate* delegate) : Channel(delegate) {}
+
+ char* GetReadBufferTest(size_t* buffer_capacity) {
+ return GetReadBuffer(buffer_capacity);
+ }
+
+ bool OnReadCompleteTest(size_t bytes_read, size_t* next_read_size_hint) {
+ return OnReadComplete(bytes_read, next_read_size_hint);
+ }
+
+ MOCK_METHOD4(GetReadPlatformHandles,
+ bool(size_t num_handles,
+ const void* extra_header,
+ size_t extra_header_size,
+ ScopedPlatformHandleVectorPtr* handles));
+ MOCK_METHOD0(Start, void());
+ MOCK_METHOD0(ShutDownImpl, void());
+ MOCK_METHOD0(LeakHandle, void());
+
+ void Write(MessagePtr message) {}
+
+ protected:
+ ~TestChannel() override {}
+};
+
+// Not using GMock as I don't think it supports movable types.
+class MockChannelDelegate : public Channel::Delegate {
+ public:
+ MockChannelDelegate() {}
+
+ size_t GetReceivedPayloadSize() const { return payload_size_; }
+
+ const void* GetReceivedPayload() const { return payload_.get(); }
+
+ protected:
+ void OnChannelMessage(const void* payload,
+ size_t payload_size,
+ ScopedPlatformHandleVectorPtr handles) override {
+ payload_.reset(new char[payload_size]);
+ memcpy(payload_.get(), payload, payload_size);
+ payload_size_ = payload_size;
+ }
+
+ // Notify that an error has occured and the Channel will cease operation.
+ void OnChannelError() override {}
+
+ private:
+ size_t payload_size_ = 0;
+ std::unique_ptr<char[]> payload_;
+};
+
+Channel::MessagePtr CreateDefaultMessage(bool legacy_message) {
+ const size_t payload_size = 100;
+ Channel::MessagePtr message = base::MakeUnique<Channel::Message>(
+ payload_size, 0,
+ legacy_message ? Channel::Message::MessageType::NORMAL_LEGACY
+ : Channel::Message::MessageType::NORMAL);
+ char* payload = static_cast<char*>(message->mutable_payload());
+ for (size_t i = 0; i < payload_size; i++) {
+ payload[i] = static_cast<char>(i);
+ }
+ return message;
+}
+
+void TestMemoryEqual(const void* data1,
+ size_t data1_size,
+ const void* data2,
+ size_t data2_size) {
+ ASSERT_EQ(data1_size, data2_size);
+ const unsigned char* data1_char = static_cast<const unsigned char*>(data1);
+ const unsigned char* data2_char = static_cast<const unsigned char*>(data2);
+ for (size_t i = 0; i < data1_size; i++) {
+ // ASSERT so we don't log tons of errors if the data is different.
+ ASSERT_EQ(data1_char[i], data2_char[i]);
+ }
+}
+
+void TestMessagesAreEqual(Channel::Message* message1,
+ Channel::Message* message2,
+ bool legacy_messages) {
+ // If any of the message is null, this is probably not what you wanted to
+ // test.
+ ASSERT_NE(nullptr, message1);
+ ASSERT_NE(nullptr, message2);
+
+ ASSERT_EQ(message1->payload_size(), message2->payload_size());
+ EXPECT_EQ(message1->has_handles(), message2->has_handles());
+
+ TestMemoryEqual(message1->payload(), message1->payload_size(),
+ message2->payload(), message2->payload_size());
+
+ if (legacy_messages)
+ return;
+
+ ASSERT_EQ(message1->extra_header_size(), message2->extra_header_size());
+ TestMemoryEqual(message1->extra_header(), message1->extra_header_size(),
+ message2->extra_header(), message2->extra_header_size());
+}
+
+TEST(ChannelTest, LegacyMessageDeserialization) {
+ Channel::MessagePtr message = CreateDefaultMessage(true /* legacy_message */);
+ Channel::MessagePtr deserialized_message =
+ Channel::Message::Deserialize(message->data(), message->data_num_bytes());
+ TestMessagesAreEqual(message.get(), deserialized_message.get(),
+ true /* legacy_message */);
+}
+
+TEST(ChannelTest, NonLegacyMessageDeserialization) {
+ Channel::MessagePtr message =
+ CreateDefaultMessage(false /* legacy_message */);
+ Channel::MessagePtr deserialized_message =
+ Channel::Message::Deserialize(message->data(), message->data_num_bytes());
+ TestMessagesAreEqual(message.get(), deserialized_message.get(),
+ false /* legacy_message */);
+}
+
+TEST(ChannelTest, OnReadLegacyMessage) {
+ size_t buffer_size = 100 * 1024;
+ Channel::MessagePtr message = CreateDefaultMessage(true /* legacy_message */);
+
+ MockChannelDelegate channel_delegate;
+ scoped_refptr<TestChannel> channel = new TestChannel(&channel_delegate);
+ char* read_buffer = channel->GetReadBufferTest(&buffer_size);
+ ASSERT_LT(message->data_num_bytes(),
+ buffer_size); // Bad test. Increase buffer
+ // size.
+ memcpy(read_buffer, message->data(), message->data_num_bytes());
+
+ size_t next_read_size_hint = 0;
+ EXPECT_TRUE(channel->OnReadCompleteTest(message->data_num_bytes(),
+ &next_read_size_hint));
+
+ TestMemoryEqual(message->payload(), message->payload_size(),
+ channel_delegate.GetReceivedPayload(),
+ channel_delegate.GetReceivedPayloadSize());
+}
+
+TEST(ChannelTest, OnReadNonLegacyMessage) {
+ size_t buffer_size = 100 * 1024;
+ Channel::MessagePtr message =
+ CreateDefaultMessage(false /* legacy_message */);
+
+ MockChannelDelegate channel_delegate;
+ scoped_refptr<TestChannel> channel = new TestChannel(&channel_delegate);
+ char* read_buffer = channel->GetReadBufferTest(&buffer_size);
+ ASSERT_LT(message->data_num_bytes(),
+ buffer_size); // Bad test. Increase buffer
+ // size.
+ memcpy(read_buffer, message->data(), message->data_num_bytes());
+
+ size_t next_read_size_hint = 0;
+ EXPECT_TRUE(channel->OnReadCompleteTest(message->data_num_bytes(),
+ &next_read_size_hint));
+
+ TestMemoryEqual(message->payload(), message->payload_size(),
+ channel_delegate.GetReceivedPayload(),
+ channel_delegate.GetReceivedPayloadSize());
+}
+
+} // namespace
+} // namespace edk
+} // namespace mojo
diff --git a/mojo/edk/system/mach_port_relay.cc b/mojo/edk/system/mach_port_relay.cc
new file mode 100644
index 0000000..f05cf22
--- /dev/null
+++ b/mojo/edk/system/mach_port_relay.cc
@@ -0,0 +1,248 @@
+// Copyright 2016 The Chromium 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/edk/system/mach_port_relay.h"
+
+#include <mach/mach.h>
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/mac/mach_port_util.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/process/process.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+
+namespace mojo {
+namespace edk {
+
+namespace {
+
+// Errors that can occur in the broker (privileged parent) process.
+// These match tools/metrics/histograms.xml.
+// This enum is append-only.
+enum class BrokerUMAError : int {
+ SUCCESS = 0,
+ // Couldn't get a task port for the process with a given pid.
+ ERROR_TASK_FOR_PID = 1,
+ // Couldn't make a port with receive rights in the destination process.
+ ERROR_MAKE_RECEIVE_PORT = 2,
+ // Couldn't change the attributes of a Mach port.
+ ERROR_SET_ATTRIBUTES = 3,
+ // Couldn't extract a right from the destination.
+ ERROR_EXTRACT_DEST_RIGHT = 4,
+ // Couldn't send a Mach port in a call to mach_msg().
+ ERROR_SEND_MACH_PORT = 5,
+ // Couldn't extract a right from the source.
+ ERROR_EXTRACT_SOURCE_RIGHT = 6,
+ ERROR_MAX
+};
+
+// Errors that can occur in a child process.
+// These match tools/metrics/histograms.xml.
+// This enum is append-only.
+enum class ChildUMAError : int {
+ SUCCESS = 0,
+ // An error occurred while trying to receive a Mach port with mach_msg().
+ ERROR_RECEIVE_MACH_MESSAGE = 1,
+ ERROR_MAX
+};
+
+void ReportBrokerError(BrokerUMAError error) {
+ UMA_HISTOGRAM_ENUMERATION("Mojo.MachPortRelay.BrokerError",
+ static_cast<int>(error),
+ static_cast<int>(BrokerUMAError::ERROR_MAX));
+}
+
+void ReportChildError(ChildUMAError error) {
+ UMA_HISTOGRAM_ENUMERATION("Mojo.MachPortRelay.ChildError",
+ static_cast<int>(error),
+ static_cast<int>(ChildUMAError::ERROR_MAX));
+}
+
+} // namespace
+
+// static
+bool MachPortRelay::ReceivePorts(PlatformHandleVector* handles) {
+ DCHECK(handles);
+
+ for (size_t i = 0; i < handles->size(); i++) {
+ PlatformHandle* handle = handles->data() + i;
+ DCHECK(handle->type != PlatformHandle::Type::MACH);
+ if (handle->type != PlatformHandle::Type::MACH_NAME)
+ continue;
+
+ if (handle->port == MACH_PORT_NULL) {
+ handle->type = PlatformHandle::Type::MACH;
+ continue;
+ }
+
+ base::mac::ScopedMachReceiveRight message_port(handle->port);
+ base::mac::ScopedMachSendRight received_port(
+ base::ReceiveMachPort(message_port.get()));
+ if (received_port.get() == MACH_PORT_NULL) {
+ ReportChildError(ChildUMAError::ERROR_RECEIVE_MACH_MESSAGE);
+ handle->port = MACH_PORT_NULL;
+ LOG(ERROR) << "Error receiving mach port";
+ return false;
+ }
+
+ ReportChildError(ChildUMAError::SUCCESS);
+ handle->port = received_port.release();
+ handle->type = PlatformHandle::Type::MACH;
+ }
+
+ return true;
+}
+
+MachPortRelay::MachPortRelay(base::PortProvider* port_provider)
+ : port_provider_(port_provider) {
+ DCHECK(port_provider);
+ port_provider_->AddObserver(this);
+}
+
+MachPortRelay::~MachPortRelay() {
+ port_provider_->RemoveObserver(this);
+}
+
+bool MachPortRelay::SendPortsToProcess(Channel::Message* message,
+ base::ProcessHandle process) {
+ DCHECK(message);
+ mach_port_t task_port = port_provider_->TaskForPid(process);
+ if (task_port == MACH_PORT_NULL) {
+ // Callers check the port provider for the task port before calling this
+ // function, in order to queue pending messages. Therefore, if this fails,
+ // it should be considered a genuine, bona fide, electrified, six-car error.
+ ReportBrokerError(BrokerUMAError::ERROR_TASK_FOR_PID);
+ return false;
+ }
+
+ size_t num_sent = 0;
+ bool error = false;
+ ScopedPlatformHandleVectorPtr handles = message->TakeHandles();
+ // Message should have handles, otherwise there's no point in calling this
+ // function.
+ DCHECK(handles);
+ for (size_t i = 0; i < handles->size(); i++) {
+ PlatformHandle* handle = &(*handles)[i];
+ DCHECK(handle->type != PlatformHandle::Type::MACH_NAME);
+ if (handle->type != PlatformHandle::Type::MACH)
+ continue;
+
+ if (handle->port == MACH_PORT_NULL) {
+ handle->type = PlatformHandle::Type::MACH_NAME;
+ num_sent++;
+ continue;
+ }
+
+ mach_port_name_t intermediate_port;
+ base::MachCreateError error_code;
+ intermediate_port = base::CreateIntermediateMachPort(
+ task_port, base::mac::ScopedMachSendRight(handle->port), &error_code);
+ if (intermediate_port == MACH_PORT_NULL) {
+ BrokerUMAError uma_error;
+ switch (error_code) {
+ case base::MachCreateError::ERROR_MAKE_RECEIVE_PORT:
+ uma_error = BrokerUMAError::ERROR_MAKE_RECEIVE_PORT;
+ break;
+ case base::MachCreateError::ERROR_SET_ATTRIBUTES:
+ uma_error = BrokerUMAError::ERROR_SET_ATTRIBUTES;
+ break;
+ case base::MachCreateError::ERROR_EXTRACT_DEST_RIGHT:
+ uma_error = BrokerUMAError::ERROR_EXTRACT_DEST_RIGHT;
+ break;
+ case base::MachCreateError::ERROR_SEND_MACH_PORT:
+ uma_error = BrokerUMAError::ERROR_SEND_MACH_PORT;
+ break;
+ }
+ ReportBrokerError(uma_error);
+ handle->port = MACH_PORT_NULL;
+ error = true;
+ break;
+ }
+
+ ReportBrokerError(BrokerUMAError::SUCCESS);
+ handle->port = intermediate_port;
+ handle->type = PlatformHandle::Type::MACH_NAME;
+ num_sent++;
+ }
+ DCHECK(error || num_sent);
+ message->SetHandles(std::move(handles));
+
+ return !error;
+}
+
+bool MachPortRelay::ExtractPortRights(Channel::Message* message,
+ base::ProcessHandle process) {
+ DCHECK(message);
+
+ mach_port_t task_port = port_provider_->TaskForPid(process);
+ if (task_port == MACH_PORT_NULL) {
+ ReportBrokerError(BrokerUMAError::ERROR_TASK_FOR_PID);
+ return false;
+ }
+
+ size_t num_received = 0;
+ bool error = false;
+ ScopedPlatformHandleVectorPtr handles = message->TakeHandles();
+ // Message should have handles, otherwise there's no point in calling this
+ // function.
+ DCHECK(handles);
+ for (size_t i = 0; i < handles->size(); i++) {
+ PlatformHandle* handle = handles->data() + i;
+ DCHECK(handle->type != PlatformHandle::Type::MACH);
+ if (handle->type != PlatformHandle::Type::MACH_NAME)
+ continue;
+
+ if (handle->port == MACH_PORT_NULL) {
+ handle->type = PlatformHandle::Type::MACH;
+ num_received++;
+ continue;
+ }
+
+ mach_port_t extracted_right = MACH_PORT_NULL;
+ mach_msg_type_name_t extracted_right_type;
+ kern_return_t kr =
+ mach_port_extract_right(task_port, handle->port,
+ MACH_MSG_TYPE_MOVE_SEND,
+ &extracted_right, &extracted_right_type);
+ if (kr != KERN_SUCCESS) {
+ ReportBrokerError(BrokerUMAError::ERROR_EXTRACT_SOURCE_RIGHT);
+ error = true;
+ break;
+ }
+
+ ReportBrokerError(BrokerUMAError::SUCCESS);
+ DCHECK_EQ(static_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND),
+ extracted_right_type);
+ handle->port = extracted_right;
+ handle->type = PlatformHandle::Type::MACH;
+ num_received++;
+ }
+ DCHECK(error || num_received);
+ message->SetHandles(std::move(handles));
+
+ return !error;
+}
+
+void MachPortRelay::AddObserver(Observer* observer) {
+ base::AutoLock locker(observers_lock_);
+ bool inserted = observers_.insert(observer).second;
+ DCHECK(inserted);
+}
+
+void MachPortRelay::RemoveObserver(Observer* observer) {
+ base::AutoLock locker(observers_lock_);
+ observers_.erase(observer);
+}
+
+void MachPortRelay::OnReceivedTaskPort(base::ProcessHandle process) {
+ base::AutoLock locker(observers_lock_);
+ for (auto* observer : observers_)
+ observer->OnProcessReady(process);
+}
+
+} // namespace edk
+} // namespace mojo
diff --git a/mojo/edk/system/mach_port_relay.h b/mojo/edk/system/mach_port_relay.h
new file mode 100644
index 0000000..87bc56c
--- /dev/null
+++ b/mojo/edk/system/mach_port_relay.h
@@ -0,0 +1,94 @@
+// Copyright 2016 The Chromium 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_EDK_SYSTEM_MACH_PORT_RELAY_H_
+#define MOJO_EDK_SYSTEM_MACH_PORT_RELAY_H_
+
+#include <set>
+
+#include "base/macros.h"
+#include "base/process/port_provider_mac.h"
+#include "base/synchronization/lock.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "mojo/edk/system/channel.h"
+
+namespace mojo {
+namespace edk {
+
+// The MachPortRelay is used by a privileged process, usually the root process,
+// to manipulate Mach ports in a child process. Ports can be added to and
+// extracted from a child process that has registered itself with the
+// |base::PortProvider| used by this class.
+class MachPortRelay : public base::PortProvider::Observer {
+ public:
+ class Observer {
+ public:
+ // Called by the MachPortRelay to notify observers that a new process is
+ // ready for Mach ports to be sent/received. There are no guarantees about
+ // the thread this is called on, including the presence of a MessageLoop.
+ // Implementations must not call AddObserver() or RemoveObserver() during
+ // this function, as doing so will deadlock.
+ virtual void OnProcessReady(base::ProcessHandle process) = 0;
+ };
+
+ // Used by a child process to receive Mach ports from a sender (privileged)
+ // process. Each Mach port in |handles| is interpreted as an intermediate Mach
+ // port. It replaces each Mach port with the final Mach port received from the
+ // intermediate port. This method takes ownership of the intermediate Mach
+ // port and gives ownership of the final Mach port to the caller. Any handles
+ // that are not Mach ports will remain unchanged, and the number and ordering
+ // of handles is preserved.
+ // Returns |false| on failure and there is no guarantee about whether a Mach
+ // port is intermediate or final.
+ //
+ // See SendPortsToProcess() for the definition of intermediate and final Mach
+ // ports.
+ static bool ReceivePorts(PlatformHandleVector* handles);
+
+ explicit MachPortRelay(base::PortProvider* port_provider);
+ ~MachPortRelay() override;
+
+ // Sends the Mach ports attached to |message| to |process|.
+ // For each Mach port attached to |message|, a new Mach port, the intermediate
+ // port, is created in |process|. The message's Mach port is then sent over
+ // this intermediate port and the message is modified to refer to the name of
+ // the intermediate port. The Mach port received over the intermediate port in
+ // the child is referred to as the final Mach port.
+ // Returns |false| on failure and |message| may contain a mix of actual Mach
+ // ports and names.
+ bool SendPortsToProcess(Channel::Message* message,
+ base::ProcessHandle process);
+
+ // Extracts the Mach ports attached to |message| from |process|.
+ // Any Mach ports attached to |message| are names and not actual Mach ports
+ // that are valid in this process. For each of those Mach port names, a send
+ // right is extracted from |process| and the port name is replaced with the
+ // send right.
+ // Returns |false| on failure and |message| may contain a mix of actual Mach
+ // ports and names.
+ bool ExtractPortRights(Channel::Message* message,
+ base::ProcessHandle process);
+
+ // Observer interface.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ base::PortProvider* port_provider() const { return port_provider_; }
+
+ private:
+ // base::PortProvider::Observer implementation.
+ void OnReceivedTaskPort(base::ProcessHandle process) override;
+
+ base::PortProvider* const port_provider_;
+
+ base::Lock observers_lock_;
+ std::set<Observer*> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(MachPortRelay);
+};
+
+} // namespace edk
+} // namespace mojo
+
+#endif // MOJO_EDK_SYSTEM_MACH_PORT_RELAY_H_
diff --git a/mojo/edk/system/platform_wrapper_unittest.cc b/mojo/edk/system/platform_wrapper_unittest.cc
new file mode 100644
index 0000000..f29d62b
--- /dev/null
+++ b/mojo/edk/system/platform_wrapper_unittest.cc
@@ -0,0 +1,212 @@
+// Copyright 2016 The Chromium 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 <string.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/files/file.h"
+#include "base/files/file_util.h"
+#include "base/memory/shared_memory.h"
+#include "base/process/process_handle.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/test/mojo_test_base.h"
+#include "mojo/public/c/system/platform_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#if defined(OS_POSIX)
+#define SIMPLE_PLATFORM_HANDLE_TYPE MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR
+#elif defined(OS_WIN)
+#define SIMPLE_PLATFORM_HANDLE_TYPE MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE
+#endif
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+#define SHARED_BUFFER_PLATFORM_HANDLE_TYPE MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT
+#else
+#define SHARED_BUFFER_PLATFORM_HANDLE_TYPE SIMPLE_PLATFORM_HANDLE_TYPE
+#endif
+
+uint64_t PlatformHandleValueFromPlatformFile(base::PlatformFile file) {
+#if defined(OS_WIN)
+ return reinterpret_cast<uint64_t>(file);
+#else
+ return static_cast<uint64_t>(file);
+#endif
+}
+
+base::PlatformFile PlatformFileFromPlatformHandleValue(uint64_t value) {
+#if defined(OS_WIN)
+ return reinterpret_cast<base::PlatformFile>(value);
+#else
+ return static_cast<base::PlatformFile>(value);
+#endif
+}
+
+namespace mojo {
+namespace edk {
+namespace {
+
+using PlatformWrapperTest = test::MojoTestBase;
+
+TEST_F(PlatformWrapperTest, WrapPlatformHandle) {
+ // Create a temporary file and write a message to it.
+ base::FilePath temp_file_path;
+ ASSERT_TRUE(base::CreateTemporaryFile(&temp_file_path));
+ const std::string kMessage = "Hello, world!";
+ EXPECT_EQ(base::WriteFile(temp_file_path, kMessage.data(),
+ static_cast<int>(kMessage.size())),
+ static_cast<int>(kMessage.size()));
+
+ RUN_CHILD_ON_PIPE(ReadPlatformFile, h)
+ // Open the temporary file for reading, wrap its handle, and send it to
+ // the child along with the expected message to be read.
+ base::File file(temp_file_path,
+ base::File::FLAG_OPEN | base::File::FLAG_READ);
+ EXPECT_TRUE(file.IsValid());
+
+ MojoHandle wrapped_handle;
+ MojoPlatformHandle os_file;
+ os_file.struct_size = sizeof(MojoPlatformHandle);
+ os_file.type = SIMPLE_PLATFORM_HANDLE_TYPE;
+ os_file.value =
+ PlatformHandleValueFromPlatformFile(file.TakePlatformFile());
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWrapPlatformHandle(&os_file, &wrapped_handle));
+
+ WriteMessageWithHandles(h, kMessage, &wrapped_handle, 1);
+ END_CHILD()
+
+ base::DeleteFile(temp_file_path, false);
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReadPlatformFile, PlatformWrapperTest, h) {
+ // Read a message and a wrapped file handle; unwrap the handle.
+ MojoHandle wrapped_handle;
+ std::string message = ReadMessageWithHandles(h, &wrapped_handle, 1);
+
+ MojoPlatformHandle platform_handle;
+ platform_handle.struct_size = sizeof(MojoPlatformHandle);
+ ASSERT_EQ(MOJO_RESULT_OK,
+ MojoUnwrapPlatformHandle(wrapped_handle, &platform_handle));
+ EXPECT_EQ(SIMPLE_PLATFORM_HANDLE_TYPE, platform_handle.type);
+ base::File file(PlatformFileFromPlatformHandleValue(platform_handle.value));
+
+ // Expect to read the same message from the file.
+ std::vector<char> data(message.size());
+ EXPECT_EQ(file.ReadAtCurrentPos(data.data(), static_cast<int>(data.size())),
+ static_cast<int>(data.size()));
+ EXPECT_TRUE(std::equal(message.begin(), message.end(), data.begin()));
+}
+
+TEST_F(PlatformWrapperTest, WrapPlatformSharedBufferHandle) {
+ // Allocate a new platform shared buffer and write a message into it.
+ const std::string kMessage = "Hello, world!";
+ base::SharedMemory buffer;
+ buffer.CreateAndMapAnonymous(kMessage.size());
+ CHECK(buffer.memory());
+ memcpy(buffer.memory(), kMessage.data(), kMessage.size());
+
+ RUN_CHILD_ON_PIPE(ReadPlatformSharedBuffer, h)
+ // Wrap the shared memory handle and send it to the child along with the
+ // expected message.
+ base::SharedMemoryHandle memory_handle =
+ base::SharedMemory::DuplicateHandle(buffer.handle());
+ MojoPlatformHandle os_buffer;
+ os_buffer.struct_size = sizeof(MojoPlatformHandle);
+ os_buffer.type = SHARED_BUFFER_PLATFORM_HANDLE_TYPE;
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ os_buffer.value = static_cast<uint64_t>(memory_handle.GetMemoryObject());
+#elif defined(OS_POSIX)
+ os_buffer.value = static_cast<uint64_t>(memory_handle.fd);
+#elif defined(OS_WIN)
+ os_buffer.value = reinterpret_cast<uint64_t>(memory_handle.GetHandle());
+#endif
+
+ MojoHandle wrapped_handle;
+ ASSERT_EQ(MOJO_RESULT_OK,
+ MojoWrapPlatformSharedBufferHandle(
+ &os_buffer, kMessage.size(),
+ MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_NONE,
+ &wrapped_handle));
+ WriteMessageWithHandles(h, kMessage, &wrapped_handle, 1);
+ END_CHILD()
+}
+
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReadPlatformSharedBuffer, PlatformWrapperTest,
+ h) {
+ // Read a message and a wrapped shared buffer handle.
+ MojoHandle wrapped_handle;
+ std::string message = ReadMessageWithHandles(h, &wrapped_handle, 1);
+
+ // Check the message in the buffer
+ ExpectBufferContents(wrapped_handle, 0, message);
+
+ // Now unwrap the buffer and verify that the base::SharedMemoryHandle also
+ // works as expected.
+ MojoPlatformHandle os_buffer;
+ os_buffer.struct_size = sizeof(MojoPlatformHandle);
+ size_t size;
+ MojoPlatformSharedBufferHandleFlags flags;
+ ASSERT_EQ(MOJO_RESULT_OK,
+ MojoUnwrapPlatformSharedBufferHandle(wrapped_handle, &os_buffer,
+ &size, &flags));
+ bool read_only = flags & MOJO_PLATFORM_SHARED_BUFFER_HANDLE_FLAG_NONE;
+ EXPECT_FALSE(read_only);
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ ASSERT_EQ(MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT, os_buffer.type);
+ base::SharedMemoryHandle memory_handle(
+ static_cast<mach_port_t>(os_buffer.value), size,
+ base::GetCurrentProcId());
+#elif defined(OS_POSIX)
+ ASSERT_EQ(MOJO_PLATFORM_HANDLE_TYPE_FILE_DESCRIPTOR, os_buffer.type);
+ base::SharedMemoryHandle memory_handle(static_cast<int>(os_buffer.value),
+ false);
+#elif defined(OS_WIN)
+ ASSERT_EQ(MOJO_PLATFORM_HANDLE_TYPE_WINDOWS_HANDLE, os_buffer.type);
+ base::SharedMemoryHandle memory_handle(
+ reinterpret_cast<HANDLE>(os_buffer.value), base::GetCurrentProcId());
+#endif
+
+ base::SharedMemory memory(memory_handle, read_only);
+ memory.Map(message.size());
+ ASSERT_TRUE(memory.memory());
+
+ EXPECT_TRUE(std::equal(message.begin(), message.end(),
+ static_cast<const char*>(memory.memory())));
+}
+
+TEST_F(PlatformWrapperTest, InvalidHandle) {
+ // Wrap an invalid platform handle and expect to unwrap the same.
+
+ MojoHandle wrapped_handle;
+ MojoPlatformHandle invalid_handle;
+ invalid_handle.struct_size = sizeof(MojoPlatformHandle);
+ invalid_handle.type = MOJO_PLATFORM_HANDLE_TYPE_INVALID;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoWrapPlatformHandle(&invalid_handle, &wrapped_handle));
+ EXPECT_EQ(MOJO_RESULT_OK,
+ MojoUnwrapPlatformHandle(wrapped_handle, &invalid_handle));
+ EXPECT_EQ(MOJO_PLATFORM_HANDLE_TYPE_INVALID, invalid_handle.type);
+}
+
+TEST_F(PlatformWrapperTest, InvalidArgument) {
+ // Try to wrap an invalid MojoPlatformHandle struct and expect an error.
+ MojoHandle wrapped_handle;
+ MojoPlatformHandle platform_handle;
+ platform_handle.struct_size = 0;
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ MojoWrapPlatformHandle(&platform_handle, &wrapped_handle));
+}
+
+} // namespace
+} // namespace edk
+} // namespace mojo
diff --git a/mojo/edk/system/watch_unittest.cc b/mojo/edk/system/watch_unittest.cc
new file mode 100644
index 0000000..ec28d94
--- /dev/null
+++ b/mojo/edk/system/watch_unittest.cc
@@ -0,0 +1,480 @@
+// Copyright 2016 The Chromium 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 <functional>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/edk/system/request_context.h"
+#include "mojo/edk/test/mojo_test_base.h"
+#include "mojo/public/c/system/functions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace edk {
+namespace {
+
+void IgnoreResult(uintptr_t context,
+ MojoResult result,
+ MojoHandleSignalsState signals,
+ MojoWatchNotificationFlags flags) {
+}
+
+// A test helper class for watching a handle. The WatchHelper instance is used
+// as a watch context for a single watch callback.
+class WatchHelper {
+ public:
+ using Callback =
+ std::function<void(MojoResult result, MojoHandleSignalsState state)>;
+
+ WatchHelper() : task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
+ ~WatchHelper() {
+ CHECK(!watching_);
+ }
+
+ void Watch(MojoHandle handle,
+ MojoHandleSignals signals,
+ const Callback& callback) {
+ CHECK(!watching_);
+
+ handle_ = handle;
+ callback_ = callback;
+ watching_ = true;
+ CHECK_EQ(MOJO_RESULT_OK, MojoWatch(handle_, signals, &WatchHelper::OnNotify,
+ reinterpret_cast<uintptr_t>(this)));
+ }
+
+ bool is_watching() const { return watching_; }
+
+ void Cancel() {
+ CHECK_EQ(MOJO_RESULT_OK,
+ MojoCancelWatch(handle_, reinterpret_cast<uintptr_t>(this)));
+ CHECK(watching_);
+ watching_ = false;
+ }
+
+ private:
+ static void OnNotify(uintptr_t context,
+ MojoResult result,
+ MojoHandleSignalsState state,
+ MojoWatchNotificationFlags flags) {
+ WatchHelper* watcher = reinterpret_cast<WatchHelper*>(context);
+ watcher->task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&NotifyOnMainThread, context, result, state, flags));
+ }
+
+ static void NotifyOnMainThread(uintptr_t context,
+ MojoResult result,
+ MojoHandleSignalsState state,
+ MojoWatchNotificationFlags flags) {
+ WatchHelper* watcher = reinterpret_cast<WatchHelper*>(context);
+ CHECK(watcher->watching_);
+ if (result == MOJO_RESULT_CANCELLED)
+ watcher->watching_ = false;
+ watcher->callback_(result, state);
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ bool watching_ = false;
+ MojoHandle handle_;
+ Callback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(WatchHelper);
+};
+
+class WatchTest : public test::MojoTestBase {
+ public:
+ WatchTest() {}
+ ~WatchTest() override {}
+
+ protected:
+
+ private:
+ base::MessageLoop message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(WatchTest);
+};
+
+TEST_F(WatchTest, NotifyBasic) {
+ MojoHandle a, b;
+ CreateMessagePipe(&a, &b);
+
+ base::RunLoop loop;
+ WatchHelper b_watcher;
+ b_watcher.Watch(
+ b, MOJO_HANDLE_SIGNAL_READABLE,
+ [&] (MojoResult result, MojoHandleSignalsState state) {
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
+ state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_TRUE(b_watcher.is_watching());
+ loop.Quit();
+ });
+
+ WriteMessage(a, "Hello!");
+ loop.Run();
+
+ EXPECT_TRUE(b_watcher.is_watching());
+ b_watcher.Cancel();
+
+ CloseHandle(a);
+ CloseHandle(b);
+}
+
+TEST_F(WatchTest, NotifyUnsatisfiable) {
+ MojoHandle a, b;
+ CreateMessagePipe(&a, &b);
+
+ base::RunLoop loop;
+ WatchHelper b_watcher;
+ b_watcher.Watch(
+ b, MOJO_HANDLE_SIGNAL_READABLE,
+ [&] (MojoResult result, MojoHandleSignalsState state) {
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+ EXPECT_EQ(0u,
+ state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_EQ(0u,
+ state.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_TRUE(b_watcher.is_watching());
+ loop.Quit();
+ });
+
+ CloseHandle(a);
+ loop.Run();
+
+ b_watcher.Cancel();
+
+ CloseHandle(b);
+}
+
+TEST_F(WatchTest, NotifyCancellation) {
+ MojoHandle a, b;
+ CreateMessagePipe(&a, &b);
+
+ base::RunLoop loop;
+ WatchHelper b_watcher;
+ b_watcher.Watch(
+ b, MOJO_HANDLE_SIGNAL_READABLE,
+ [&] (MojoResult result, MojoHandleSignalsState state) {
+ EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
+ EXPECT_EQ(0u, state.satisfied_signals);
+ EXPECT_EQ(0u, state.satisfiable_signals);
+ EXPECT_FALSE(b_watcher.is_watching());
+ loop.Quit();
+ });
+
+ CloseHandle(b);
+ loop.Run();
+
+ CloseHandle(a);
+}
+
+TEST_F(WatchTest, InvalidArguemnts) {
+ MojoHandle a, b;
+ CreateMessagePipe(&a, &b);
+
+ uintptr_t context = reinterpret_cast<uintptr_t>(this);
+ EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(a, MOJO_HANDLE_SIGNAL_READABLE,
+ &IgnoreResult, context));
+
+ // Can't cancel a watch that doesn't exist.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoCancelWatch(a, ~context));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoCancelWatch(b, context));
+
+ CloseHandle(a);
+ CloseHandle(b);
+
+ // Can't watch a handle that doesn't exist.
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ MojoWatch(a, MOJO_HANDLE_SIGNAL_READABLE, &IgnoreResult, context));
+ EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
+ MojoWatch(b, MOJO_HANDLE_SIGNAL_READABLE, &IgnoreResult, context));
+}
+
+TEST_F(WatchTest, NoDuplicateContext) {
+ MojoHandle a, b;
+ CreateMessagePipe(&a, &b);
+
+ // Try to add the same watch twice; should fail.
+ uintptr_t context = reinterpret_cast<uintptr_t>(this);
+ EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(a, MOJO_HANDLE_SIGNAL_READABLE,
+ &IgnoreResult, context));
+ EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
+ MojoWatch(a, MOJO_HANDLE_SIGNAL_READABLE, &IgnoreResult, context));
+
+ // Cancel and add it again; should be OK.
+ EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(a, context));
+ EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(a, MOJO_HANDLE_SIGNAL_READABLE,
+ &IgnoreResult, context));
+
+ CloseHandle(a);
+ CloseHandle(b);
+}
+
+TEST_F(WatchTest, MultipleWatches) {
+ MojoHandle a, b;
+ CreateMessagePipe(&a, &b);
+
+ // Add multiple watchers to |b| and see that they are both notified by a
+ // single write to |a|.
+ base::RunLoop loop;
+ int expected_notifications = 2;
+ auto on_readable = [&] (MojoResult result, MojoHandleSignalsState state) {
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
+ state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_GT(expected_notifications, 0);
+ if (--expected_notifications == 0)
+ loop.Quit();
+ };
+ WatchHelper watcher1;
+ WatchHelper watcher2;
+ watcher1.Watch(b, MOJO_HANDLE_SIGNAL_READABLE, on_readable);
+ watcher2.Watch(b, MOJO_HANDLE_SIGNAL_READABLE, on_readable);
+
+ WriteMessage(a, "Ping!");
+ loop.Run();
+
+ watcher1.Cancel();
+ watcher2.Cancel();
+
+ CloseHandle(a);
+ CloseHandle(b);
+}
+
+TEST_F(WatchTest, WatchWhileSatisfied) {
+ MojoHandle a, b;
+ CreateMessagePipe(&a, &b);
+
+ // Write to |a| and then start watching |b|. The callback should be invoked
+ // synchronously.
+ WriteMessage(a, "hey");
+ bool signaled = false;
+ WatchHelper b_watcher;
+ base::RunLoop loop;
+ b_watcher.Watch(
+ b, MOJO_HANDLE_SIGNAL_READABLE,
+ [&] (MojoResult result, MojoHandleSignalsState state) {
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
+ state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
+ signaled = true;
+ loop.Quit();
+ });
+ loop.Run();
+ EXPECT_TRUE(signaled);
+ b_watcher.Cancel();
+
+ CloseHandle(a);
+ CloseHandle(b);
+}
+
+TEST_F(WatchTest, WatchWhileUnsatisfiable) {
+ MojoHandle a, b;
+ CreateMessagePipe(&a, &b);
+
+ // Close |a| and then try to watch |b|. MojoWatch() should fail.
+ CloseHandle(a);
+ uintptr_t context = reinterpret_cast<uintptr_t>(this);
+ EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
+ MojoWatch(b, MOJO_HANDLE_SIGNAL_READABLE, &IgnoreResult, context));
+
+ CloseHandle(b);
+}
+
+TEST_F(WatchTest, RespondFromCallback) {
+ MojoHandle a, b;
+ CreateMessagePipe(&a, &b);
+
+ // Watch |a| and |b|. Write to |a|, then write to |b| from within the callback
+ // which notifies it of the available message.
+ const std::string kTestMessage = "hello worlds.";
+ base::RunLoop loop;
+ WatchHelper b_watcher;
+ b_watcher.Watch(
+ b, MOJO_HANDLE_SIGNAL_READABLE,
+ [&] (MojoResult result, MojoHandleSignalsState state) {
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
+ state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_TRUE(b_watcher.is_watching());
+
+ // Echo a's message back to it.
+ WriteMessage(b, ReadMessage(b));
+ });
+
+ WatchHelper a_watcher;
+ a_watcher.Watch(
+ a, MOJO_HANDLE_SIGNAL_READABLE,
+ [&] (MojoResult result, MojoHandleSignalsState state) {
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
+ state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_TRUE(a_watcher.is_watching());
+
+ // Expect to receive back the message that was originally sent to |b|.
+ EXPECT_EQ(kTestMessage, ReadMessage(a));
+
+ loop.Quit();
+ });
+
+ WriteMessage(a, kTestMessage);
+ loop.Run();
+
+ a_watcher.Cancel();
+ b_watcher.Cancel();
+
+ CloseHandle(a);
+ CloseHandle(b);
+}
+
+TEST_F(WatchTest, WatchDataPipeConsumer) {
+ MojoHandle a, b;
+ CreateDataPipe(&a, &b, 64);
+
+ base::RunLoop loop;
+ WatchHelper b_watcher;
+ b_watcher.Watch(
+ b, MOJO_HANDLE_SIGNAL_READABLE,
+ [&] (MojoResult result, MojoHandleSignalsState state) {
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
+ state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_TRUE(b_watcher.is_watching());
+ loop.Quit();
+ });
+
+ WriteData(a, "Hello!");
+ loop.Run();
+
+ EXPECT_TRUE(b_watcher.is_watching());
+ b_watcher.Cancel();
+
+ CloseHandle(a);
+ CloseHandle(b);
+}
+
+TEST_F(WatchTest, WatchDataPipeProducer) {
+ MojoHandle a, b;
+ CreateDataPipe(&a, &b, 8);
+
+ // Fill the pipe to capacity so writes will block.
+ WriteData(a, "xxxxxxxx");
+
+ base::RunLoop loop;
+ WatchHelper a_watcher;
+ a_watcher.Watch(
+ a, MOJO_HANDLE_SIGNAL_WRITABLE,
+ [&] (MojoResult result, MojoHandleSignalsState state) {
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE,
+ state.satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE);
+ EXPECT_TRUE(a_watcher.is_watching());
+ loop.Quit();
+ });
+
+ EXPECT_EQ("xxxxxxxx", ReadData(b, 8));
+ loop.Run();
+
+ EXPECT_TRUE(a_watcher.is_watching());
+ a_watcher.Cancel();
+
+ CloseHandle(a);
+ CloseHandle(b);
+}
+
+TEST_F(WatchTest, WakeUpSelfWithinWatchCallback) {
+ MojoHandle a, b;
+ CreateMessagePipe(&a, &b);
+
+ int expected_notifications = 2;
+ base::RunLoop loop;
+ WatchHelper b_watcher;
+ b_watcher.Watch(
+ b, MOJO_HANDLE_SIGNAL_READABLE,
+ [&] (MojoResult result, MojoHandleSignalsState state) {
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
+ state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
+ EXPECT_TRUE(b_watcher.is_watching());
+ if (--expected_notifications == 0) {
+ loop.Quit();
+ } else {
+ // Trigger b's watch again from within this callback. This should be
+ // safe to do.
+ WriteMessage(a, "hey");
+ }
+ });
+
+ WriteMessage(a, "hey hey hey");
+ loop.Run();
+
+ b_watcher.Cancel();
+
+ CloseHandle(a);
+ CloseHandle(b);
+}
+
+TEST_F(WatchTest, NestedCancellation) {
+ // Verifies that cancellations in nested system request contexts preempt
+ // other notifications for the same watcher. This tests against the condition
+ // hit by http://crbug.com/622298.
+
+ MojoHandle a, b, c, d;
+ CreateMessagePipe(&a, &b);
+ CreateMessagePipe(&c, &d);
+
+ base::RunLoop loop;
+ bool a_watcher_run = false;
+ WatchHelper a_watcher;
+ a_watcher.Watch(
+ a, MOJO_HANDLE_SIGNAL_READABLE,
+ [&](MojoResult result, MojoHandleSignalsState state) {
+ a_watcher_run = true;
+ });
+
+ WatchHelper c_watcher;
+ c_watcher.Watch(
+ c, MOJO_HANDLE_SIGNAL_READABLE,
+ [&](MojoResult result, MojoHandleSignalsState state) {
+ // This will trigger a notification on |a_watcher| above to be executed
+ // once this handler finishes running...
+ CloseHandle(b);
+
+ // ...but this should prevent that notification from dispatching because
+ // |a_watcher| is now cancelled.
+ a_watcher.Cancel();
+
+ loop.Quit();
+ });
+
+ {
+ // Force "system" notifications for the synchronous behavior required to
+ // test this case.
+ mojo::edk::RequestContext request_context(
+ mojo::edk::RequestContext::Source::SYSTEM);
+
+ // Trigger the |c_watcher| callback above.
+ CloseHandle(d);
+ }
+
+ loop.Run();
+
+ EXPECT_FALSE(a_watcher.is_watching());
+ EXPECT_FALSE(a_watcher_run);
+
+ c_watcher.Cancel();
+
+ CloseHandle(a);
+ CloseHandle(c);
+}
+
+} // namespace
+} // namespace edk
+} // namespace mojo
diff --git a/mojo/public/c/README.md b/mojo/public/c/README.md
index 0b022fc..223c205 100644
--- a/mojo/public/c/README.md
+++ b/mojo/public/c/README.md
@@ -7,7 +7,7 @@ System
------
The system/ subdirectory provides definitions of the basic low-level API used by
-all Mojo applications (whether directly or indirectly). These consist primarily
+all Services (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
diff --git a/mojo/public/cpp/bindings/associated_binding_set.h b/mojo/public/cpp/bindings/associated_binding_set.h
new file mode 100644
index 0000000..59600c6
--- /dev/null
+++ b/mojo/public/cpp/bindings/associated_binding_set.h
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium 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_ASSOCIATED_BINDING_SET_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_BINDING_SET_H_
+
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "mojo/public/cpp/bindings/associated_interface_request.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+
+namespace mojo {
+
+template <typename Interface, typename ImplRefTraits>
+struct BindingSetTraits<AssociatedBinding<Interface, ImplRefTraits>> {
+ using ProxyType = AssociatedInterfacePtr<Interface>;
+ using RequestType = AssociatedInterfaceRequest<Interface>;
+ using BindingType = AssociatedBinding<Interface, ImplRefTraits>;
+ using ImplPointerType = typename BindingType::ImplPointerType;
+};
+
+template <typename Interface, typename ContextType = void>
+using AssociatedBindingSet =
+ BindingSetBase<Interface, AssociatedBinding<Interface>, ContextType>;
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_BINDING_SET_H_
diff --git a/mojo/public/cpp/bindings/lib/associated_binding.cc b/mojo/public/cpp/bindings/lib/associated_binding.cc
new file mode 100644
index 0000000..6788e68
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/associated_binding.cc
@@ -0,0 +1,62 @@
+// Copyright 2017 The Chromium 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/associated_binding.h"
+
+namespace mojo {
+
+AssociatedBindingBase::AssociatedBindingBase() {}
+
+AssociatedBindingBase::~AssociatedBindingBase() {}
+
+void AssociatedBindingBase::AddFilter(std::unique_ptr<MessageReceiver> filter) {
+ DCHECK(endpoint_client_);
+ endpoint_client_->AddFilter(std::move(filter));
+}
+
+void AssociatedBindingBase::Close() {
+ endpoint_client_.reset();
+}
+
+void AssociatedBindingBase::CloseWithReason(uint32_t custom_reason,
+ const std::string& description) {
+ if (endpoint_client_)
+ endpoint_client_->CloseWithReason(custom_reason, description);
+ Close();
+}
+
+void AssociatedBindingBase::set_connection_error_handler(
+ const base::Closure& error_handler) {
+ DCHECK(is_bound());
+ endpoint_client_->set_connection_error_handler(error_handler);
+}
+
+void AssociatedBindingBase::set_connection_error_with_reason_handler(
+ const ConnectionErrorWithReasonCallback& error_handler) {
+ DCHECK(is_bound());
+ endpoint_client_->set_connection_error_with_reason_handler(error_handler);
+}
+
+void AssociatedBindingBase::FlushForTesting() {
+ endpoint_client_->FlushForTesting();
+}
+
+void AssociatedBindingBase::BindImpl(
+ ScopedInterfaceEndpointHandle handle,
+ MessageReceiverWithResponderStatus* receiver,
+ std::unique_ptr<MessageReceiver> payload_validator,
+ bool expect_sync_requests,
+ scoped_refptr<base::SingleThreadTaskRunner> runner,
+ uint32_t interface_version) {
+ if (!handle.is_valid()) {
+ endpoint_client_.reset();
+ return;
+ }
+
+ endpoint_client_.reset(new InterfaceEndpointClient(
+ std::move(handle), receiver, std::move(payload_validator),
+ expect_sync_requests, std::move(runner), interface_version));
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/wtf_hash_util.h b/mojo/public/cpp/bindings/lib/wtf_hash_util.h
new file mode 100644
index 0000000..cc590da
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/wtf_hash_util.h
@@ -0,0 +1,132 @@
+// Copyright 2016 The Chromium 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_WTF_HASH_UTIL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_WTF_HASH_UTIL_H_
+
+#include <type_traits>
+
+#include "mojo/public/cpp/bindings/lib/hash_util.h"
+#include "mojo/public/cpp/bindings/struct_ptr.h"
+#include "third_party/WebKit/Source/wtf/HashFunctions.h"
+#include "third_party/WebKit/Source/wtf/text/StringHash.h"
+#include "third_party/WebKit/Source/wtf/text/WTFString.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename T>
+size_t WTFHashCombine(size_t seed, const T& value) {
+ // Based on proposal in:
+ // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1756.pdf
+ //
+ // TODO(tibell): We'd like to use WTF::DefaultHash instead of std::hash, but
+ // there is no general template specialization of DefaultHash for enums
+ // and there can't be an instance for bool.
+ return seed ^ (std::hash<T>()(value) + (seed << 6) + (seed >> 2));
+}
+
+template <typename T, bool has_hash_method = HasHashMethod<T>::value>
+struct WTFHashTraits;
+
+template <typename T>
+size_t WTFHash(size_t seed, const T& value);
+
+template <typename T>
+struct WTFHashTraits<T, true> {
+ static size_t Hash(size_t seed, const T& value) { return value.Hash(seed); }
+};
+
+template <typename T>
+struct WTFHashTraits<T, false> {
+ static size_t Hash(size_t seed, const T& value) {
+ return WTFHashCombine(seed, value);
+ }
+};
+
+template <>
+struct WTFHashTraits<WTF::String, false> {
+ static size_t Hash(size_t seed, const WTF::String& value) {
+ return HashCombine(seed, WTF::StringHash::hash(value));
+ }
+};
+
+template <typename T>
+size_t WTFHash(size_t seed, const T& value) {
+ return WTFHashTraits<T>::Hash(seed, value);
+}
+
+template <typename T>
+struct StructPtrHashFn {
+ static unsigned hash(const StructPtr<T>& value) {
+ return value.Hash(kHashSeed);
+ }
+ static bool equal(const StructPtr<T>& left, const StructPtr<T>& right) {
+ return left.Equals(right);
+ }
+ static const bool safeToCompareToEmptyOrDeleted = false;
+};
+
+template <typename T>
+struct InlinedStructPtrHashFn {
+ static unsigned hash(const InlinedStructPtr<T>& value) {
+ return value.Hash(kHashSeed);
+ }
+ static bool equal(const InlinedStructPtr<T>& left,
+ const InlinedStructPtr<T>& right) {
+ return left.Equals(right);
+ }
+ static const bool safeToCompareToEmptyOrDeleted = false;
+};
+
+} // namespace internal
+} // namespace mojo
+
+namespace WTF {
+
+template <typename T>
+struct DefaultHash<mojo::StructPtr<T>> {
+ using Hash = mojo::internal::StructPtrHashFn<T>;
+};
+
+template <typename T>
+struct HashTraits<mojo::StructPtr<T>>
+ : public GenericHashTraits<mojo::StructPtr<T>> {
+ static const bool hasIsEmptyValueFunction = true;
+ static bool isEmptyValue(const mojo::StructPtr<T>& value) {
+ return value.is_null();
+ }
+ static void constructDeletedValue(mojo::StructPtr<T>& slot, bool) {
+ mojo::internal::StructPtrWTFHelper<T>::ConstructDeletedValue(slot);
+ }
+ static bool isDeletedValue(const mojo::StructPtr<T>& value) {
+ return mojo::internal::StructPtrWTFHelper<T>::IsHashTableDeletedValue(
+ value);
+ }
+};
+
+template <typename T>
+struct DefaultHash<mojo::InlinedStructPtr<T>> {
+ using Hash = mojo::internal::InlinedStructPtrHashFn<T>;
+};
+
+template <typename T>
+struct HashTraits<mojo::InlinedStructPtr<T>>
+ : public GenericHashTraits<mojo::InlinedStructPtr<T>> {
+ static const bool hasIsEmptyValueFunction = true;
+ static bool isEmptyValue(const mojo::InlinedStructPtr<T>& value) {
+ return value.is_null();
+ }
+ static void constructDeletedValue(mojo::InlinedStructPtr<T>& slot, bool) {
+ mojo::internal::InlinedStructPtrWTFHelper<T>::ConstructDeletedValue(slot);
+ }
+ static bool isDeletedValue(const mojo::InlinedStructPtr<T>& value) {
+ return mojo::internal::InlinedStructPtrWTFHelper<
+ T>::IsHashTableDeletedValue(value);
+ }
+};
+
+} // namespace WTF
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_WTF_HASH_UTIL_H_
diff --git a/mojo/public/cpp/bindings/strong_associated_binding.h b/mojo/public/cpp/bindings/strong_associated_binding.h
new file mode 100644
index 0000000..a1e299b
--- /dev/null
+++ b/mojo/public/cpp/bindings/strong_associated_binding.h
@@ -0,0 +1,125 @@
+// Copyright 2016 The Chromium 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_STRONG_ASSOCIATED_BINDING_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRONG_ASSOCIATED_BINDING_H_
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
+#include "mojo/public/cpp/bindings/associated_interface_request.h"
+#include "mojo/public/cpp/bindings/connection_error_callback.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+template <typename Interface>
+class StrongAssociatedBinding;
+
+template <typename Interface>
+using StrongAssociatedBindingPtr =
+ base::WeakPtr<StrongAssociatedBinding<Interface>>;
+
+// This connects an interface implementation strongly to an associated pipe.
+// When a connection error is detected the implementation is deleted. If the
+// task runner that a StrongAssociatedBinding is bound on is stopped, the
+// connection error handler will not be invoked and the implementation will not
+// be deleted.
+//
+// To use, call StrongAssociatedBinding<T>::Create() (see below) or the helper
+// MakeStrongAssociatedBinding function:
+//
+// mojo::MakeStrongAssociatedBinding(base::MakeUnique<FooImpl>(),
+// std::move(foo_request));
+//
+template <typename Interface>
+class StrongAssociatedBinding {
+ public:
+ // Create a new StrongAssociatedBinding instance. The instance owns itself,
+ // cleaning up only in the event of a pipe connection error. Returns a WeakPtr
+ // to the new StrongAssociatedBinding instance.
+ static StrongAssociatedBindingPtr<Interface> Create(
+ std::unique_ptr<Interface> impl,
+ AssociatedInterfaceRequest<Interface> request) {
+ StrongAssociatedBinding* binding =
+ new StrongAssociatedBinding(std::move(impl), std::move(request));
+ return binding->weak_factory_.GetWeakPtr();
+ }
+
+ // Note: The error handler must not delete the interface implementation.
+ //
+ // This method may only be called after this StrongAssociatedBinding has been
+ // bound to a message pipe.
+ void set_connection_error_handler(const base::Closure& error_handler) {
+ DCHECK(binding_.is_bound());
+ connection_error_handler_ = error_handler;
+ connection_error_with_reason_handler_.Reset();
+ }
+
+ void set_connection_error_with_reason_handler(
+ const ConnectionErrorWithReasonCallback& error_handler) {
+ DCHECK(binding_.is_bound());
+ connection_error_with_reason_handler_ = error_handler;
+ connection_error_handler_.Reset();
+ }
+
+ // Forces the binding to close. This destroys the StrongBinding instance.
+ void Close() { delete this; }
+
+ Interface* impl() { return impl_.get(); }
+
+ // Sends a message on the underlying message pipe and runs the current
+ // message loop until its response is received. This can be used in tests to
+ // verify that no message was sent on a message pipe in response to some
+ // stimulus.
+ void FlushForTesting() { binding_.FlushForTesting(); }
+
+ private:
+ StrongAssociatedBinding(std::unique_ptr<Interface> impl,
+ AssociatedInterfaceRequest<Interface> request)
+ : impl_(std::move(impl)),
+ binding_(impl_.get(), std::move(request)),
+ weak_factory_(this) {
+ binding_.set_connection_error_with_reason_handler(base::Bind(
+ &StrongAssociatedBinding::OnConnectionError, base::Unretained(this)));
+ }
+
+ ~StrongAssociatedBinding() {}
+
+ void OnConnectionError(uint32_t custom_reason,
+ const std::string& description) {
+ if (!connection_error_handler_.is_null())
+ connection_error_handler_.Run();
+ else if (!connection_error_with_reason_handler_.is_null())
+ connection_error_with_reason_handler_.Run(custom_reason, description);
+ Close();
+ }
+
+ std::unique_ptr<Interface> impl_;
+ base::Closure connection_error_handler_;
+ ConnectionErrorWithReasonCallback connection_error_with_reason_handler_;
+ AssociatedBinding<Interface> binding_;
+ base::WeakPtrFactory<StrongAssociatedBinding> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(StrongAssociatedBinding);
+};
+
+template <typename Interface, typename Impl>
+StrongAssociatedBindingPtr<Interface> MakeStrongAssociatedBinding(
+ std::unique_ptr<Impl> impl,
+ AssociatedInterfaceRequest<Interface> request) {
+ return StrongAssociatedBinding<Interface>::Create(std::move(impl),
+ std::move(request));
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_STRONG_ASSOCIATED_BINDING_H_
diff --git a/mojo/public/cpp/bindings/strong_binding_set.h b/mojo/public/cpp/bindings/strong_binding_set.h
new file mode 100644
index 0000000..f6bcd52
--- /dev/null
+++ b/mojo/public/cpp/bindings/strong_binding_set.h
@@ -0,0 +1,26 @@
+// Copyright 2017 The Chromium 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_STRONG_BINDING_SET_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_SET_H_
+
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/unique_ptr_impl_ref_traits.h"
+
+namespace mojo {
+
+// This class manages a set of bindings. When the pipe a binding is bound to is
+// disconnected, the binding is automatically destroyed and removed from the
+// set, and the interface implementation is deleted. When the StrongBindingSet
+// is destructed, all outstanding bindings in the set are destroyed and all the
+// bound interface implementations are automatically deleted.
+template <typename Interface, typename ContextType = void>
+using StrongBindingSet =
+ BindingSetBase<Interface,
+ Binding<Interface, UniquePtrImplRefTraits<Interface>>,
+ ContextType>;
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_SET_H_
diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn
index 4e38f15..6244226 100644
--- a/mojo/public/cpp/bindings/tests/BUILD.gn
+++ b/mojo/public/cpp/bindings/tests/BUILD.gn
@@ -2,39 +2,35 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("../../../mojo_application.gni")
-
source_set("tests") {
testonly = true
sources = [
- "array_common_test.h",
- "array_unittest.cc",
"associated_interface_unittest.cc",
"bind_task_runner_unittest.cc",
"binding_callback_unittest.cc",
+ "binding_set_unittest.cc",
"binding_unittest.cc",
"buffer_unittest.cc",
"connector_unittest.cc",
"constant_unittest.cc",
"container_test_util.cc",
"container_test_util.h",
+ "data_view_unittest.cc",
"equals_unittest.cc",
"handle_passing_unittest.cc",
+ "hash_unittest.cc",
"interface_ptr_unittest.cc",
- "map_common_test.h",
"map_unittest.cc",
"message_queue.cc",
"message_queue.h",
"multiplex_router_unittest.cc",
+ "report_bad_message_unittest.cc",
"request_response_unittest.cc",
"router_test_util.cc",
"router_test_util.h",
- "router_unittest.cc",
"sample_service_unittest.cc",
"serialization_warning_unittest.cc",
- "stl_converters_unittest.cc",
- "string_unittest.cc",
"struct_unittest.cc",
"sync_method_unittest.cc",
"type_conversion_unittest.cc",
@@ -46,10 +42,14 @@ source_set("tests") {
deps = [
":mojo_public_bindings_test_utils",
+ "//base/test:test_support",
+ "//mojo/edk/system",
"//mojo/public/cpp/bindings",
"//mojo/public/cpp/system",
"//mojo/public/cpp/test_support:test_utils",
"//mojo/public/interfaces/bindings/tests:test_associated_interfaces",
+ "//mojo/public/interfaces/bindings/tests:test_export_component",
+ "//mojo/public/interfaces/bindings/tests:test_exported_import",
"//mojo/public/interfaces/bindings/tests:test_interfaces",
"//mojo/public/interfaces/bindings/tests:test_interfaces_experimental",
"//mojo/public/interfaces/bindings/tests:test_struct_traits_interfaces",
@@ -77,12 +77,10 @@ if (!is_ios) {
testonly = true
sources = [
- "array_common_test.h",
"container_test_util.cc",
"container_test_util.h",
- "map_common_test.h",
"variant_test_util.h",
- "wtf_array_unittest.cc",
+ "wtf_hash_unittest.cc",
"wtf_map_unittest.cc",
"wtf_types_unittest.cc",
]
@@ -90,7 +88,10 @@ if (!is_ios) {
deps = [
"//mojo/public/cpp/bindings",
"//mojo/public/cpp/system",
+ "//mojo/public/interfaces/bindings/tests:test_export_blink_component",
+ "//mojo/public/interfaces/bindings/tests:test_exported_import_blink",
"//mojo/public/interfaces/bindings/tests:test_interfaces",
+ "//mojo/public/interfaces/bindings/tests:test_interfaces_blink",
"//mojo/public/interfaces/bindings/tests:test_wtf_types",
"//mojo/public/interfaces/bindings/tests:test_wtf_types_blink",
"//testing/gtest",
@@ -123,6 +124,7 @@ source_set("perftests") {
deps = [
"//base/test:test_support",
+ "//mojo/edk/system",
"//mojo/edk/test:test_support",
"//mojo/public/cpp/bindings",
"//mojo/public/cpp/system",
diff --git a/mojo/public/cpp/bindings/tests/array_common_test.h b/mojo/public/cpp/bindings/tests/array_common_test.h
deleted file mode 100644
index 6b4bcfb..0000000
--- a/mojo/public/cpp/bindings/tests/array_common_test.h
+++ /dev/null
@@ -1,404 +0,0 @@
-// Copyright 2016 The Chromium 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 <stddef.h>
-#include <stdint.h>
-#include <utility>
-
-#include "mojo/public/cpp/bindings/lib/array_internal.h"
-#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
-#include "mojo/public/cpp/bindings/lib/serialization.h"
-#include "mojo/public/cpp/bindings/tests/container_test_util.h"
-#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace mojo {
-namespace test {
-
-// Common tests for both mojo::Array and mojo::WTFArray.
-template <template <typename...> class ArrayType>
-class ArrayCommonTest {
- public:
- // Tests null and empty arrays.
- static void NullAndEmpty() {
- ArrayType<char> array0;
- EXPECT_TRUE(array0.empty());
- EXPECT_FALSE(array0.is_null());
- array0 = nullptr;
- EXPECT_TRUE(array0.is_null());
- EXPECT_FALSE(array0.empty());
-
- ArrayType<char> array1(nullptr);
- EXPECT_TRUE(array1.is_null());
- EXPECT_FALSE(array1.empty());
- array1.SetToEmpty();
- EXPECT_TRUE(array1.empty());
- EXPECT_FALSE(array1.is_null());
- }
-
- // Tests that basic array operations work.
- static void Basic() {
- ArrayType<char> array(8);
- for (size_t i = 0; i < array.size(); ++i) {
- char val = static_cast<char>(i * 2);
- array[i] = val;
- EXPECT_EQ(val, array.at(i));
- }
- }
-
- // Tests that basic ArrayType<bool> operations work.
- static void Bool() {
- ArrayType<bool> array(64);
- for (size_t i = 0; i < array.size(); ++i) {
- bool val = i % 3 == 0;
- array[i] = val;
- EXPECT_EQ(val, array.at(i));
- }
- }
-
- // Tests that ArrayType<ScopedMessagePipeHandle> supports transferring
- // handles.
- static void Handle() {
- MessagePipe pipe;
- ArrayType<ScopedMessagePipeHandle> handles(2);
- handles[0] = std::move(pipe.handle0);
- handles[1].reset(pipe.handle1.release());
-
- EXPECT_FALSE(pipe.handle0.is_valid());
- EXPECT_FALSE(pipe.handle1.is_valid());
-
- ArrayType<ScopedMessagePipeHandle> handles2 = std::move(handles);
- EXPECT_TRUE(handles2[0].is_valid());
- EXPECT_TRUE(handles2[1].is_valid());
-
- ScopedMessagePipeHandle pipe_handle = std::move(handles2[0]);
- EXPECT_TRUE(pipe_handle.is_valid());
- EXPECT_FALSE(handles2[0].is_valid());
- }
-
- // Tests that ArrayType<ScopedMessagePipeHandle> supports closing handles.
- static void HandlesAreClosed() {
- MessagePipe pipe;
- MojoHandle pipe0_value = pipe.handle0.get().value();
- MojoHandle pipe1_value = pipe.handle0.get().value();
-
- {
- ArrayType<ScopedMessagePipeHandle> handles(2);
- handles[0] = std::move(pipe.handle0);
- handles[1].reset(pipe.handle0.release());
- }
-
- // We expect the pipes to have been closed.
- EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(pipe0_value));
- EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(pipe1_value));
- }
-
- static void Clone() {
- {
- // Test POD.
- ArrayType<int32_t> array(3);
- for (size_t i = 0; i < array.size(); ++i)
- array[i] = static_cast<int32_t>(i);
-
- ArrayType<int32_t> clone_array = array.Clone();
- EXPECT_EQ(array.size(), clone_array.size());
- for (size_t i = 0; i < array.size(); ++i)
- EXPECT_EQ(array[i], clone_array[i]);
- }
-
- {
- // Test copyable object.
- ArrayType<String> array(2);
- array[0] = "hello";
- array[1] = "world";
-
- ArrayType<String> clone_array = array.Clone();
- EXPECT_EQ(array.size(), clone_array.size());
- for (size_t i = 0; i < array.size(); ++i)
- EXPECT_EQ(array[i], clone_array[i]);
- }
-
- {
- // Test struct.
- ArrayType<RectPtr> array(2);
- array[1] = Rect::New();
- array[1]->x = 1;
- array[1]->y = 2;
- array[1]->width = 3;
- array[1]->height = 4;
-
- ArrayType<RectPtr> clone_array = array.Clone();
- EXPECT_EQ(array.size(), clone_array.size());
- EXPECT_TRUE(clone_array[0].is_null());
- EXPECT_EQ(array[1]->x, clone_array[1]->x);
- EXPECT_EQ(array[1]->y, clone_array[1]->y);
- EXPECT_EQ(array[1]->width, clone_array[1]->width);
- EXPECT_EQ(array[1]->height, clone_array[1]->height);
- }
-
- {
- // Test array of array.
- ArrayType<ArrayType<int8_t>> array(2);
- array[0] = nullptr;
- array[1] = ArrayType<int8_t>(2);
- array[1][0] = 0;
- array[1][1] = 1;
-
- ArrayType<ArrayType<int8_t>> clone_array = array.Clone();
- EXPECT_EQ(array.size(), clone_array.size());
- EXPECT_TRUE(clone_array[0].is_null());
- EXPECT_EQ(array[1].size(), clone_array[1].size());
- EXPECT_EQ(array[1][0], clone_array[1][0]);
- EXPECT_EQ(array[1][1], clone_array[1][1]);
- }
-
- {
- // Test that array of handles still works although Clone() is not
- // available.
- ArrayType<ScopedMessagePipeHandle> array(10);
- EXPECT_FALSE(array[0].is_valid());
- }
- }
-
- static void Serialization_ArrayOfPOD() {
- ArrayType<int32_t> array(4);
- for (size_t i = 0; i < array.size(); ++i)
- array[i] = static_cast<int32_t>(i);
-
- size_t size =
- mojo::internal::PrepareToSerialize<Array<int32_t>>(array, nullptr);
- EXPECT_EQ(8U + 4 * 4U, size);
-
- mojo::internal::FixedBufferForTesting buf(size);
- mojo::internal::Array_Data<int32_t>* data;
- mojo::internal::ContainerValidateParams validate_params(0, false, nullptr);
- mojo::internal::Serialize<Array<int32_t>>(array, &buf, &data,
- &validate_params, nullptr);
-
- ArrayType<int32_t> array2;
- mojo::internal::Deserialize<Array<int32_t>>(data, &array2, nullptr);
-
- EXPECT_EQ(4U, array2.size());
- for (size_t i = 0; i < array2.size(); ++i)
- EXPECT_EQ(static_cast<int32_t>(i), array2[i]);
- }
-
- static void Serialization_EmptyArrayOfPOD() {
- ArrayType<int32_t> array;
- size_t size =
- mojo::internal::PrepareToSerialize<Array<int32_t>>(array, nullptr);
- EXPECT_EQ(8U, size);
-
- mojo::internal::FixedBufferForTesting buf(size);
- mojo::internal::Array_Data<int32_t>* data;
- mojo::internal::ContainerValidateParams validate_params(0, false, nullptr);
- mojo::internal::Serialize<Array<int32_t>>(array, &buf, &data,
- &validate_params, nullptr);
-
- ArrayType<int32_t> array2;
- mojo::internal::Deserialize<Array<int32_t>>(data, &array2, nullptr);
- EXPECT_EQ(0U, array2.size());
- }
-
- static void Serialization_ArrayOfArrayOfPOD() {
- ArrayType<ArrayType<int32_t>> array(2);
- for (size_t j = 0; j < array.size(); ++j) {
- ArrayType<int32_t> inner(4);
- for (size_t i = 0; i < inner.size(); ++i)
- inner[i] = static_cast<int32_t>(i + (j * 10));
- array[j] = std::move(inner);
- }
-
- size_t size = mojo::internal::PrepareToSerialize<Array<Array<int32_t>>>(
- array, nullptr);
- EXPECT_EQ(8U + 2 * 8U + 2 * (8U + 4 * 4U), size);
-
- mojo::internal::FixedBufferForTesting buf(size);
- typename mojo::internal::MojomTypeTraits<Array<Array<int32_t>>>::Data* data;
- mojo::internal::ContainerValidateParams validate_params(
- 0, false,
- new mojo::internal::ContainerValidateParams(0, false, nullptr));
- mojo::internal::Serialize<Array<Array<int32_t>>>(array, &buf, &data,
- &validate_params, nullptr);
-
- ArrayType<ArrayType<int32_t>> array2;
- mojo::internal::Deserialize<Array<Array<int32_t>>>(data, &array2, nullptr);
-
- EXPECT_EQ(2U, array2.size());
- for (size_t j = 0; j < array2.size(); ++j) {
- const ArrayType<int32_t>& inner = array2[j];
- EXPECT_EQ(4U, inner.size());
- for (size_t i = 0; i < inner.size(); ++i)
- EXPECT_EQ(static_cast<int32_t>(i + (j * 10)), inner[i]);
- }
- }
-
- static void Serialization_ArrayOfBool() {
- ArrayType<bool> array(10);
- for (size_t i = 0; i < array.size(); ++i)
- array[i] = i % 2 ? true : false;
-
- size_t size =
- mojo::internal::PrepareToSerialize<Array<bool>>(array, nullptr);
- EXPECT_EQ(8U + 8U, size);
-
- mojo::internal::FixedBufferForTesting buf(size);
- mojo::internal::Array_Data<bool>* data;
- mojo::internal::ContainerValidateParams validate_params(0, false, nullptr);
- mojo::internal::Serialize<Array<bool>>(array, &buf, &data, &validate_params,
- nullptr);
-
- ArrayType<bool> array2;
- mojo::internal::Deserialize<Array<bool>>(data, &array2, nullptr);
-
- EXPECT_EQ(10U, array2.size());
- for (size_t i = 0; i < array2.size(); ++i)
- EXPECT_EQ(i % 2 ? true : false, array2[i]);
- }
-
- static void Serialization_ArrayOfString() {
- ArrayType<String> array(10);
- for (size_t i = 0; i < array.size(); ++i) {
- char c = 'A' + static_cast<char>(i);
- array[i] = String(&c, 1);
- }
-
- size_t size =
- mojo::internal::PrepareToSerialize<Array<String>>(array, nullptr);
- EXPECT_EQ(8U + // array header
- 10 * 8U + // array payload (10 pointers)
- 10 * (8U + // string header
- 8U), // string length of 1 padded to 8
- size);
-
- mojo::internal::FixedBufferForTesting buf(size);
- typename mojo::internal::MojomTypeTraits<Array<String>>::Data* data;
- mojo::internal::ContainerValidateParams validate_params(
- 0, false,
- new mojo::internal::ContainerValidateParams(0, false, nullptr));
- mojo::internal::Serialize<Array<String>>(array, &buf, &data,
- &validate_params, nullptr);
-
- ArrayType<String> array2;
- mojo::internal::Deserialize<Array<String>>(data, &array2, nullptr);
-
- EXPECT_EQ(10U, array2.size());
- for (size_t i = 0; i < array2.size(); ++i) {
- char c = 'A' + static_cast<char>(i);
- EXPECT_EQ(String(&c, 1), array2[i]);
- }
- }
-
- static void Resize_Copyable() {
- ASSERT_EQ(0u, CopyableType::num_instances());
- ArrayType<CopyableType> array(3);
- std::vector<CopyableType*> value_ptrs;
- value_ptrs.push_back(array[0].ptr());
- value_ptrs.push_back(array[1].ptr());
-
- for (size_t i = 0; i < array.size(); i++)
- array[i].ResetCopied();
-
- array.resize(2);
- ASSERT_EQ(2u, array.size());
- EXPECT_EQ(array.size(), CopyableType::num_instances());
- for (size_t i = 0; i < array.size(); i++) {
- EXPECT_FALSE(array[i].copied());
- EXPECT_EQ(value_ptrs[i], array[i].ptr());
- }
-
- array.resize(3);
- array[2].ResetCopied();
- ASSERT_EQ(3u, array.size());
- EXPECT_EQ(array.size(), CopyableType::num_instances());
- for (size_t i = 0; i < array.size(); i++)
- EXPECT_FALSE(array[i].copied());
- value_ptrs.push_back(array[2].ptr());
-
- size_t capacity = array.storage().capacity();
- array.resize(capacity);
- ASSERT_EQ(capacity, array.size());
- EXPECT_EQ(array.size(), CopyableType::num_instances());
- for (size_t i = 0; i < 3; i++)
- EXPECT_FALSE(array[i].copied());
- for (size_t i = 3; i < array.size(); i++) {
- array[i].ResetCopied();
- value_ptrs.push_back(array[i].ptr());
- }
-
- array.resize(capacity + 2);
- ASSERT_EQ(capacity + 2, array.size());
- EXPECT_EQ(array.size(), CopyableType::num_instances());
- for (size_t i = 0; i < capacity; i++) {
- EXPECT_TRUE(array[i].copied());
- EXPECT_EQ(value_ptrs[i], array[i].ptr());
- }
- array = nullptr;
- EXPECT_EQ(0u, CopyableType::num_instances());
- EXPECT_FALSE(array);
- array.resize(0);
- EXPECT_EQ(0u, CopyableType::num_instances());
- EXPECT_TRUE(array);
- }
-
- static void Resize_MoveOnly() {
- ASSERT_EQ(0u, MoveOnlyType::num_instances());
- ArrayType<MoveOnlyType> array(3);
- std::vector<MoveOnlyType*> value_ptrs;
- value_ptrs.push_back(array[0].ptr());
- value_ptrs.push_back(array[1].ptr());
-
- for (size_t i = 0; i < array.size(); i++)
- EXPECT_FALSE(array[i].moved());
-
- array.resize(2);
- ASSERT_EQ(2u, array.size());
- EXPECT_EQ(array.size(), MoveOnlyType::num_instances());
- for (size_t i = 0; i < array.size(); i++) {
- EXPECT_FALSE(array[i].moved());
- EXPECT_EQ(value_ptrs[i], array[i].ptr());
- }
-
- array.resize(3);
- ASSERT_EQ(3u, array.size());
- EXPECT_EQ(array.size(), MoveOnlyType::num_instances());
- for (size_t i = 0; i < array.size(); i++)
- EXPECT_FALSE(array[i].moved());
- value_ptrs.push_back(array[2].ptr());
-
- size_t capacity = array.storage().capacity();
- array.resize(capacity);
- ASSERT_EQ(capacity, array.size());
- EXPECT_EQ(array.size(), MoveOnlyType::num_instances());
- for (size_t i = 0; i < array.size(); i++)
- EXPECT_FALSE(array[i].moved());
- for (size_t i = 3; i < array.size(); i++)
- value_ptrs.push_back(array[i].ptr());
-
- array.resize(capacity + 2);
- ASSERT_EQ(capacity + 2, array.size());
- EXPECT_EQ(array.size(), MoveOnlyType::num_instances());
- for (size_t i = 0; i < capacity; i++) {
- EXPECT_TRUE(array[i].moved());
- EXPECT_EQ(value_ptrs[i], array[i].ptr());
- }
- for (size_t i = capacity; i < array.size(); i++)
- EXPECT_FALSE(array[i].moved());
-
- array = nullptr;
- EXPECT_EQ(0u, MoveOnlyType::num_instances());
- EXPECT_FALSE(array);
- array.resize(0);
- EXPECT_EQ(0u, MoveOnlyType::num_instances());
- EXPECT_TRUE(array);
- }
-};
-
-#define ARRAY_COMMON_TEST(ArrayType, test_name) \
- TEST_F(ArrayType##Test, test_name) { \
- ArrayCommonTest<ArrayType>::test_name(); \
- }
-
-} // namespace test
-} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/array_unittest.cc b/mojo/public/cpp/bindings/tests/array_unittest.cc
deleted file mode 100644
index 0700bb1..0000000
--- a/mojo/public/cpp/bindings/tests/array_unittest.cc
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2014 The Chromium 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/array.h"
-
-#include "mojo/public/cpp/bindings/lib/serialization.h"
-#include "mojo/public/cpp/bindings/tests/array_common_test.h"
-#include "mojo/public/cpp/bindings/tests/container_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace mojo {
-namespace test {
-namespace {
-
-using ArrayTest = testing::Test;
-
-ARRAY_COMMON_TEST(Array, NullAndEmpty)
-ARRAY_COMMON_TEST(Array, Basic)
-ARRAY_COMMON_TEST(Array, Bool)
-ARRAY_COMMON_TEST(Array, Handle)
-ARRAY_COMMON_TEST(Array, HandlesAreClosed)
-ARRAY_COMMON_TEST(Array, Clone)
-ARRAY_COMMON_TEST(Array, Serialization_ArrayOfPOD)
-ARRAY_COMMON_TEST(Array, Serialization_EmptyArrayOfPOD)
-ARRAY_COMMON_TEST(Array, Serialization_ArrayOfArrayOfPOD)
-ARRAY_COMMON_TEST(Array, Serialization_ArrayOfBool)
-ARRAY_COMMON_TEST(Array, Serialization_ArrayOfString)
-ARRAY_COMMON_TEST(Array, Resize_Copyable)
-ARRAY_COMMON_TEST(Array, Resize_MoveOnly)
-
-TEST_F(ArrayTest, PushBack_Copyable) {
- ASSERT_EQ(0u, CopyableType::num_instances());
- Array<CopyableType> array(2);
- array = nullptr;
- std::vector<CopyableType*> value_ptrs;
- size_t capacity = array.storage().capacity();
- for (size_t i = 0; i < capacity; i++) {
- CopyableType value;
- value_ptrs.push_back(value.ptr());
- array.push_back(value);
- ASSERT_EQ(i + 1, array.size());
- ASSERT_EQ(i + 1, value_ptrs.size());
- EXPECT_EQ(array.size() + 1, CopyableType::num_instances());
- EXPECT_TRUE(array[i].copied());
- EXPECT_EQ(value_ptrs[i], array[i].ptr());
- array[i].ResetCopied();
- EXPECT_TRUE(array);
- }
- {
- CopyableType value;
- value_ptrs.push_back(value.ptr());
- array.push_back(value);
- EXPECT_EQ(array.size() + 1, CopyableType::num_instances());
- }
- ASSERT_EQ(capacity + 1, array.size());
- EXPECT_EQ(array.size(), CopyableType::num_instances());
-
- for (size_t i = 0; i < array.size(); i++) {
- EXPECT_TRUE(array[i].copied());
- EXPECT_EQ(value_ptrs[i], array[i].ptr());
- }
- array = nullptr;
- EXPECT_EQ(0u, CopyableType::num_instances());
-}
-
-TEST_F(ArrayTest, PushBack_MoveOnly) {
- ASSERT_EQ(0u, MoveOnlyType::num_instances());
- Array<MoveOnlyType> array(2);
- array = nullptr;
- std::vector<MoveOnlyType*> value_ptrs;
- size_t capacity = array.storage().capacity();
- for (size_t i = 0; i < capacity; i++) {
- MoveOnlyType value;
- value_ptrs.push_back(value.ptr());
- array.push_back(std::move(value));
- ASSERT_EQ(i + 1, array.size());
- ASSERT_EQ(i + 1, value_ptrs.size());
- EXPECT_EQ(array.size() + 1, MoveOnlyType::num_instances());
- EXPECT_TRUE(array[i].moved());
- EXPECT_EQ(value_ptrs[i], array[i].ptr());
- array[i].ResetMoved();
- EXPECT_TRUE(array);
- }
- {
- MoveOnlyType value;
- value_ptrs.push_back(value.ptr());
- array.push_back(std::move(value));
- EXPECT_EQ(array.size() + 1, MoveOnlyType::num_instances());
- }
- ASSERT_EQ(capacity + 1, array.size());
- EXPECT_EQ(array.size(), MoveOnlyType::num_instances());
-
- for (size_t i = 0; i < array.size(); i++) {
- EXPECT_TRUE(array[i].moved());
- EXPECT_EQ(value_ptrs[i], array[i].ptr());
- }
- array = nullptr;
- EXPECT_EQ(0u, MoveOnlyType::num_instances());
-}
-
-TEST_F(ArrayTest, MoveFromAndToSTLVector_Copyable) {
- std::vector<CopyableType> vec1(1);
- Array<CopyableType> arr(std::move(vec1));
- ASSERT_EQ(1u, arr.size());
- ASSERT_FALSE(arr[0].copied());
-
- std::vector<CopyableType> vec2(arr.PassStorage());
- ASSERT_EQ(1u, vec2.size());
- ASSERT_FALSE(vec2[0].copied());
-
- ASSERT_EQ(0u, arr.size());
- ASSERT_TRUE(arr.is_null());
-}
-
-TEST_F(ArrayTest, MoveFromAndToSTLVector_MoveOnly) {
- std::vector<MoveOnlyType> vec1(1);
- Array<MoveOnlyType> arr(std::move(vec1));
-
- ASSERT_EQ(1u, arr.size());
-
- std::vector<MoveOnlyType> vec2(arr.PassStorage());
- ASSERT_EQ(1u, vec2.size());
-
- ASSERT_EQ(0u, arr.size());
- ASSERT_TRUE(arr.is_null());
-}
-
-} // namespace
-} // namespace test
-} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/associated_interface_unittest.cc b/mojo/public/cpp/bindings/tests/associated_interface_unittest.cc
index 8f2eb08..625c49c 100644
--- a/mojo/public/cpp/bindings/tests/associated_interface_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/associated_interface_unittest.cc
@@ -9,18 +9,24 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
-#include "mojo/public/cpp/bindings/associated_group.h"
#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
#include "mojo/public/cpp/bindings/associated_interface_request.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/lib/multiplex_router.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/thread_safe_interface_ptr.h"
+#include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
#include "mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -73,10 +79,8 @@ class IntegerSenderConnectionImpl : public IntegerSenderConnection {
}
void AsyncGetSender(const AsyncGetSenderCallback& callback) override {
- AssociatedInterfaceRequest<IntegerSender> request;
IntegerSenderAssociatedPtrInfo ptr_info;
- binding_.associated_group()->CreateAssociatedInterface(
- AssociatedGroup::WILL_PASS_PTR, &ptr_info, &request);
+ auto request = MakeRequest(&ptr_info);
GetSender(std::move(request));
callback.Run(std::move(ptr_info));
}
@@ -99,22 +103,42 @@ class AssociatedInterfaceTest : public testing::Test {
template <typename T>
AssociatedInterfacePtrInfo<T> EmulatePassingAssociatedPtrInfo(
AssociatedInterfacePtrInfo<T> ptr_info,
+ scoped_refptr<MultiplexRouter> source,
scoped_refptr<MultiplexRouter> target) {
ScopedInterfaceEndpointHandle handle = ptr_info.PassHandle();
- CHECK(!handle.is_local());
- return AssociatedInterfacePtrInfo<T>(
- target->CreateLocalEndpointHandle(handle.release()),
- ptr_info.version());
+ CHECK(handle.pending_association());
+ auto id = source->AssociateInterface(std::move(handle));
+ return AssociatedInterfacePtrInfo<T>(target->CreateLocalEndpointHandle(id),
+ ptr_info.version());
}
- template <typename T>
- AssociatedInterfaceRequest<T> EmulatePassingAssociatedRequest(
- AssociatedInterfaceRequest<T> request,
- scoped_refptr<MultiplexRouter> target) {
- ScopedInterfaceEndpointHandle handle = request.PassHandle();
- CHECK(!handle.is_local());
- return MakeAssociatedRequest<T>(
- target->CreateLocalEndpointHandle(handle.release()));
+ void CreateRouterPair(scoped_refptr<MultiplexRouter>* router0,
+ scoped_refptr<MultiplexRouter>* router1) {
+ MessagePipe pipe;
+ *router0 = new MultiplexRouter(std::move(pipe.handle0),
+ MultiplexRouter::MULTI_INTERFACE, true,
+ base::ThreadTaskRunnerHandle::Get());
+ *router1 = new MultiplexRouter(std::move(pipe.handle1),
+ MultiplexRouter::MULTI_INTERFACE, false,
+ base::ThreadTaskRunnerHandle::Get());
+ }
+
+ void CreateIntegerSenderWithExistingRouters(
+ scoped_refptr<MultiplexRouter> router0,
+ IntegerSenderAssociatedPtrInfo* ptr_info0,
+ scoped_refptr<MultiplexRouter> router1,
+ IntegerSenderAssociatedRequest* request1) {
+ *request1 = MakeRequest(ptr_info0);
+ *ptr_info0 = EmulatePassingAssociatedPtrInfo(std::move(*ptr_info0), router1,
+ router0);
+ }
+
+ void CreateIntegerSender(IntegerSenderAssociatedPtrInfo* ptr_info,
+ IntegerSenderAssociatedRequest* request) {
+ scoped_refptr<MultiplexRouter> router0;
+ scoped_refptr<MultiplexRouter> router1;
+ CreateRouterPair(&router0, &router1);
+ CreateIntegerSenderWithExistingRouters(router1, ptr_info, router0, request);
}
// Okay to call from any thread.
@@ -158,30 +182,27 @@ base::Callback<void(int32_t)> ExpectValueSetFlagAndRunClosure(
&DoExpectValueSetFlagAndRunClosure, expected_value, flag, closure);
}
+void Fail() {
+ FAIL() << "Unexpected connection error";
+}
+
TEST_F(AssociatedInterfaceTest, InterfacesAtBothEnds) {
// Bind to the same pipe two associated interfaces, whose implementation lives
// at different ends. Test that the two don't interfere with each other.
- MessagePipe pipe;
- scoped_refptr<MultiplexRouter> router0(new MultiplexRouter(
- true, std::move(pipe.handle0), base::ThreadTaskRunnerHandle::Get()));
- scoped_refptr<MultiplexRouter> router1(new MultiplexRouter(
- false, std::move(pipe.handle1), base::ThreadTaskRunnerHandle::Get()));
+ scoped_refptr<MultiplexRouter> router0;
+ scoped_refptr<MultiplexRouter> router1;
+ CreateRouterPair(&router0, &router1);
AssociatedInterfaceRequest<IntegerSender> request;
IntegerSenderAssociatedPtrInfo ptr_info;
-
- router0->CreateAssociatedGroup()->CreateAssociatedInterface(
- AssociatedGroup::WILL_PASS_PTR, &ptr_info, &request);
- ptr_info = EmulatePassingAssociatedPtrInfo(std::move(ptr_info), router1);
+ CreateIntegerSenderWithExistingRouters(router1, &ptr_info, router0, &request);
IntegerSenderImpl impl0(std::move(request));
AssociatedInterfacePtr<IntegerSender> ptr0;
ptr0.Bind(std::move(ptr_info));
- router0->CreateAssociatedGroup()->CreateAssociatedInterface(
- AssociatedGroup::WILL_PASS_REQUEST, &ptr_info, &request);
- request = EmulatePassingAssociatedRequest(std::move(request), router1);
+ CreateIntegerSenderWithExistingRouters(router0, &ptr_info, router1, &request);
IntegerSenderImpl impl1(std::move(request));
AssociatedInterfacePtr<IntegerSender> ptr1;
@@ -358,19 +379,15 @@ TEST_F(AssociatedInterfaceTest, MultiThreadAccess) {
const int32_t kMaxValue = 1000;
MessagePipe pipe;
- scoped_refptr<MultiplexRouter> router0(new MultiplexRouter(
- true, std::move(pipe.handle0), base::ThreadTaskRunnerHandle::Get()));
- scoped_refptr<MultiplexRouter> router1(new MultiplexRouter(
- false, std::move(pipe.handle1), base::ThreadTaskRunnerHandle::Get()));
+ scoped_refptr<MultiplexRouter> router0;
+ scoped_refptr<MultiplexRouter> router1;
+ CreateRouterPair(&router0, &router1);
AssociatedInterfaceRequest<IntegerSender> requests[4];
IntegerSenderAssociatedPtrInfo ptr_infos[4];
-
for (size_t i = 0; i < 4; ++i) {
- router0->CreateAssociatedGroup()->CreateAssociatedInterface(
- AssociatedGroup::WILL_PASS_PTR, &ptr_infos[i], &requests[i]);
- ptr_infos[i] =
- EmulatePassingAssociatedPtrInfo(std::move(ptr_infos[i]), router1);
+ CreateIntegerSenderWithExistingRouters(router1, &ptr_infos[i], router0,
+ &requests[i]);
}
TestSender senders[4];
@@ -447,19 +464,15 @@ TEST_F(AssociatedInterfaceTest, FIFO) {
const int32_t kMaxValue = 100;
MessagePipe pipe;
- scoped_refptr<MultiplexRouter> router0(new MultiplexRouter(
- true, std::move(pipe.handle0), base::ThreadTaskRunnerHandle::Get()));
- scoped_refptr<MultiplexRouter> router1(new MultiplexRouter(
- false, std::move(pipe.handle1), base::ThreadTaskRunnerHandle::Get()));
+ scoped_refptr<MultiplexRouter> router0;
+ scoped_refptr<MultiplexRouter> router1;
+ CreateRouterPair(&router0, &router1);
AssociatedInterfaceRequest<IntegerSender> requests[4];
IntegerSenderAssociatedPtrInfo ptr_infos[4];
-
for (size_t i = 0; i < 4; ++i) {
- router0->CreateAssociatedGroup()->CreateAssociatedInterface(
- AssociatedGroup::WILL_PASS_PTR, &ptr_infos[i], &requests[i]);
- ptr_infos[i] =
- EmulatePassingAssociatedPtrInfo(std::move(ptr_infos[i]), router1);
+ CreateIntegerSenderWithExistingRouters(router1, &ptr_infos[i], router0,
+ &requests[i]);
}
TestSender senders[4];
@@ -538,11 +551,10 @@ void CaptureSenderPtrInfo(IntegerSenderAssociatedPtr* storage,
TEST_F(AssociatedInterfaceTest, PassAssociatedInterfaces) {
IntegerSenderConnectionPtr connection_ptr;
- IntegerSenderConnectionImpl connection(GetProxy(&connection_ptr));
+ IntegerSenderConnectionImpl connection(MakeRequest(&connection_ptr));
IntegerSenderAssociatedPtr sender0;
- connection_ptr->GetSender(
- GetProxy(&sender0, connection_ptr.associated_group()));
+ connection_ptr->GetSender(MakeRequest(&sender0));
int32_t echoed_value = 0;
base::RunLoop run_loop;
@@ -567,11 +579,10 @@ TEST_F(AssociatedInterfaceTest, PassAssociatedInterfaces) {
TEST_F(AssociatedInterfaceTest, BindingWaitAndPauseWhenNoAssociatedInterfaces) {
IntegerSenderConnectionPtr connection_ptr;
- IntegerSenderConnectionImpl connection(GetProxy(&connection_ptr));
+ IntegerSenderConnectionImpl connection(MakeRequest(&connection_ptr));
IntegerSenderAssociatedPtr sender0;
- connection_ptr->GetSender(
- GetProxy(&sender0, connection_ptr.associated_group()));
+ connection_ptr->GetSender(MakeRequest(&sender0));
EXPECT_FALSE(connection.binding()->HasAssociatedInterfaces());
// There are no associated interfaces running on the pipe yet. It is okay to
@@ -589,6 +600,584 @@ TEST_F(AssociatedInterfaceTest, BindingWaitAndPauseWhenNoAssociatedInterfaces) {
EXPECT_TRUE(connection.binding()->HasAssociatedInterfaces());
}
+class PingServiceImpl : public PingService {
+ public:
+ explicit PingServiceImpl(PingServiceAssociatedRequest request)
+ : binding_(this, std::move(request)) {}
+ ~PingServiceImpl() override {}
+
+ AssociatedBinding<PingService>& binding() { return binding_; }
+
+ void set_ping_handler(const base::Closure& handler) {
+ ping_handler_ = handler;
+ }
+
+ // PingService:
+ void Ping(const PingCallback& callback) override {
+ if (!ping_handler_.is_null())
+ ping_handler_.Run();
+ callback.Run();
+ }
+
+ private:
+ AssociatedBinding<PingService> binding_;
+ base::Closure ping_handler_;
+};
+
+class PingProviderImpl : public AssociatedPingProvider {
+ public:
+ explicit PingProviderImpl(AssociatedPingProviderRequest request)
+ : binding_(this, std::move(request)) {}
+ ~PingProviderImpl() override {}
+
+ // AssociatedPingProvider:
+ void GetPing(PingServiceAssociatedRequest request) override {
+ ping_services_.emplace_back(new PingServiceImpl(std::move(request)));
+
+ if (expected_bindings_count_ > 0 &&
+ ping_services_.size() == expected_bindings_count_ &&
+ !quit_waiting_.is_null()) {
+ expected_bindings_count_ = 0;
+ base::ResetAndReturn(&quit_waiting_).Run();
+ }
+ }
+
+ std::vector<std::unique_ptr<PingServiceImpl>>& ping_services() {
+ return ping_services_;
+ }
+
+ void WaitForBindings(size_t count) {
+ DCHECK(quit_waiting_.is_null());
+
+ expected_bindings_count_ = count;
+ base::RunLoop loop;
+ quit_waiting_ = loop.QuitClosure();
+ loop.Run();
+ }
+
+ private:
+ Binding<AssociatedPingProvider> binding_;
+ std::vector<std::unique_ptr<PingServiceImpl>> ping_services_;
+ size_t expected_bindings_count_ = 0;
+ base::Closure quit_waiting_;
+};
+
+class CallbackFilter : public MessageReceiver {
+ public:
+ explicit CallbackFilter(const base::Closure& callback)
+ : callback_(callback) {}
+ ~CallbackFilter() override {}
+
+ static std::unique_ptr<CallbackFilter> Wrap(const base::Closure& callback) {
+ return base::MakeUnique<CallbackFilter>(callback);
+ }
+
+ // MessageReceiver:
+ bool Accept(Message* message) override {
+ callback_.Run();
+ return true;
+ }
+
+ private:
+ const base::Closure callback_;
+};
+
+// Verifies that filters work as expected on associated bindings, i.e. that
+// they're notified in order, before dispatch; and that each associated
+// binding in a group operates with its own set of filters.
+TEST_F(AssociatedInterfaceTest, BindingWithFilters) {
+ AssociatedPingProviderPtr provider;
+ PingProviderImpl provider_impl(MakeRequest(&provider));
+
+ PingServiceAssociatedPtr ping_a, ping_b;
+ provider->GetPing(MakeRequest(&ping_a));
+ provider->GetPing(MakeRequest(&ping_b));
+ provider_impl.WaitForBindings(2);
+
+ ASSERT_EQ(2u, provider_impl.ping_services().size());
+ PingServiceImpl& ping_a_impl = *provider_impl.ping_services()[0];
+ PingServiceImpl& ping_b_impl = *provider_impl.ping_services()[1];
+
+ int a_status, b_status;
+ auto handler_helper = [] (int* a_status, int* b_status, int expected_a_status,
+ int new_a_status, int expected_b_status,
+ int new_b_status) {
+ EXPECT_EQ(expected_a_status, *a_status);
+ EXPECT_EQ(expected_b_status, *b_status);
+ *a_status = new_a_status;
+ *b_status = new_b_status;
+ };
+ auto create_handler = [&] (int expected_a_status, int new_a_status,
+ int expected_b_status, int new_b_status) {
+ return base::Bind(handler_helper, &a_status, &b_status, expected_a_status,
+ new_a_status, expected_b_status, new_b_status);
+ };
+
+ ping_a_impl.binding().AddFilter(
+ CallbackFilter::Wrap(create_handler(0, 1, 0, 0)));
+ ping_a_impl.binding().AddFilter(
+ CallbackFilter::Wrap(create_handler(1, 2, 0, 0)));
+ ping_a_impl.set_ping_handler(create_handler(2, 3, 0, 0));
+
+ ping_b_impl.binding().AddFilter(
+ CallbackFilter::Wrap(create_handler(3, 3, 0, 1)));
+ ping_b_impl.binding().AddFilter(
+ CallbackFilter::Wrap(create_handler(3, 3, 1, 2)));
+ ping_b_impl.set_ping_handler(create_handler(3, 3, 2, 3));
+
+ for (int i = 0; i < 10; ++i) {
+ a_status = 0;
+ b_status = 0;
+
+ {
+ base::RunLoop loop;
+ ping_a->Ping(loop.QuitClosure());
+ loop.Run();
+ }
+
+ EXPECT_EQ(3, a_status);
+ EXPECT_EQ(0, b_status);
+
+ {
+ base::RunLoop loop;
+ ping_b->Ping(loop.QuitClosure());
+ loop.Run();
+ }
+
+ EXPECT_EQ(3, a_status);
+ EXPECT_EQ(3, b_status);
+ }
+}
+
+TEST_F(AssociatedInterfaceTest, AssociatedPtrFlushForTesting) {
+ AssociatedInterfaceRequest<IntegerSender> request;
+ IntegerSenderAssociatedPtrInfo ptr_info;
+ CreateIntegerSender(&ptr_info, &request);
+
+ IntegerSenderImpl impl0(std::move(request));
+ AssociatedInterfacePtr<IntegerSender> ptr0;
+ ptr0.Bind(std::move(ptr_info));
+ ptr0.set_connection_error_handler(base::Bind(&Fail));
+
+ bool ptr0_callback_run = false;
+ ptr0->Echo(123, ExpectValueSetFlagAndRunClosure(
+ 123, &ptr0_callback_run, base::Bind(&base::DoNothing)));
+ ptr0.FlushForTesting();
+ EXPECT_TRUE(ptr0_callback_run);
+}
+
+void SetBool(bool* value) {
+ *value = true;
+}
+
+template <typename T>
+void SetBoolWithUnusedParameter(bool* value, T unused) {
+ *value = true;
+}
+
+TEST_F(AssociatedInterfaceTest, AssociatedPtrFlushForTestingWithClosedPeer) {
+ AssociatedInterfaceRequest<IntegerSender> request;
+ IntegerSenderAssociatedPtrInfo ptr_info;
+ CreateIntegerSender(&ptr_info, &request);
+
+ AssociatedInterfacePtr<IntegerSender> ptr0;
+ ptr0.Bind(std::move(ptr_info));
+ bool called = false;
+ ptr0.set_connection_error_handler(base::Bind(&SetBool, &called));
+ request = nullptr;
+
+ ptr0.FlushForTesting();
+ EXPECT_TRUE(called);
+ ptr0.FlushForTesting();
+}
+
+TEST_F(AssociatedInterfaceTest, AssociatedBindingFlushForTesting) {
+ AssociatedInterfaceRequest<IntegerSender> request;
+ IntegerSenderAssociatedPtrInfo ptr_info;
+ CreateIntegerSender(&ptr_info, &request);
+
+ IntegerSenderImpl impl0(std::move(request));
+ impl0.set_connection_error_handler(base::Bind(&Fail));
+ AssociatedInterfacePtr<IntegerSender> ptr0;
+ ptr0.Bind(std::move(ptr_info));
+
+ bool ptr0_callback_run = false;
+ ptr0->Echo(123, ExpectValueSetFlagAndRunClosure(
+ 123, &ptr0_callback_run, base::Bind(&base::DoNothing)));
+ // Because the flush is sent from the binding, it only guarantees that the
+ // request has been received, not the response. The second flush waits for the
+ // response to be received.
+ impl0.binding()->FlushForTesting();
+ impl0.binding()->FlushForTesting();
+ EXPECT_TRUE(ptr0_callback_run);
+}
+
+TEST_F(AssociatedInterfaceTest,
+ AssociatedBindingFlushForTestingWithClosedPeer) {
+ scoped_refptr<MultiplexRouter> router0;
+ scoped_refptr<MultiplexRouter> router1;
+ CreateRouterPair(&router0, &router1);
+
+ AssociatedInterfaceRequest<IntegerSender> request;
+ {
+ IntegerSenderAssociatedPtrInfo ptr_info;
+ CreateIntegerSenderWithExistingRouters(router1, &ptr_info, router0,
+ &request);
+ }
+
+ IntegerSenderImpl impl(std::move(request));
+ bool called = false;
+ impl.set_connection_error_handler(base::Bind(&SetBool, &called));
+ impl.binding()->FlushForTesting();
+ EXPECT_TRUE(called);
+ impl.binding()->FlushForTesting();
+}
+
+TEST_F(AssociatedInterfaceTest, BindingFlushForTesting) {
+ IntegerSenderConnectionPtr ptr;
+ IntegerSenderConnectionImpl impl(MakeRequest(&ptr));
+ bool called = false;
+ ptr->AsyncGetSender(base::Bind(
+ &SetBoolWithUnusedParameter<IntegerSenderAssociatedPtrInfo>, &called));
+ EXPECT_FALSE(called);
+ impl.binding()->set_connection_error_handler(base::Bind(&Fail));
+ // Because the flush is sent from the binding, it only guarantees that the
+ // request has been received, not the response. The second flush waits for the
+ // response to be received.
+ impl.binding()->FlushForTesting();
+ impl.binding()->FlushForTesting();
+ EXPECT_TRUE(called);
+}
+
+TEST_F(AssociatedInterfaceTest, BindingFlushForTestingWithClosedPeer) {
+ IntegerSenderConnectionPtr ptr;
+ IntegerSenderConnectionImpl impl(MakeRequest(&ptr));
+ bool called = false;
+ impl.binding()->set_connection_error_handler(base::Bind(&SetBool, &called));
+ ptr.reset();
+ EXPECT_FALSE(called);
+ impl.binding()->FlushForTesting();
+ EXPECT_TRUE(called);
+ impl.binding()->FlushForTesting();
+}
+
+TEST_F(AssociatedInterfaceTest, StrongBindingFlushForTesting) {
+ IntegerSenderConnectionPtr ptr;
+ auto binding =
+ MakeStrongBinding(base::MakeUnique<IntegerSenderConnectionImpl>(
+ IntegerSenderConnectionRequest{}),
+ MakeRequest(&ptr));
+ bool called = false;
+ IntegerSenderAssociatedPtr sender_ptr;
+ ptr->GetSender(MakeRequest(&sender_ptr));
+ sender_ptr->Echo(1, base::Bind(&SetBoolWithUnusedParameter<int>, &called));
+ EXPECT_FALSE(called);
+ // Because the flush is sent from the binding, it only guarantees that the
+ // request has been received, not the response. The second flush waits for the
+ // response to be received.
+ ASSERT_TRUE(binding);
+ binding->FlushForTesting();
+ ASSERT_TRUE(binding);
+ binding->FlushForTesting();
+ EXPECT_TRUE(called);
+}
+
+TEST_F(AssociatedInterfaceTest, StrongBindingFlushForTestingWithClosedPeer) {
+ IntegerSenderConnectionPtr ptr;
+ bool called = false;
+ auto binding =
+ MakeStrongBinding(base::MakeUnique<IntegerSenderConnectionImpl>(
+ IntegerSenderConnectionRequest{}),
+ MakeRequest(&ptr));
+ binding->set_connection_error_handler(base::Bind(&SetBool, &called));
+ ptr.reset();
+ EXPECT_FALSE(called);
+ ASSERT_TRUE(binding);
+ binding->FlushForTesting();
+ EXPECT_TRUE(called);
+ ASSERT_FALSE(binding);
+}
+
+TEST_F(AssociatedInterfaceTest, PtrFlushForTesting) {
+ IntegerSenderConnectionPtr ptr;
+ IntegerSenderConnectionImpl impl(MakeRequest(&ptr));
+ bool called = false;
+ ptr.set_connection_error_handler(base::Bind(&Fail));
+ ptr->AsyncGetSender(base::Bind(
+ &SetBoolWithUnusedParameter<IntegerSenderAssociatedPtrInfo>, &called));
+ EXPECT_FALSE(called);
+ ptr.FlushForTesting();
+ EXPECT_TRUE(called);
+}
+
+TEST_F(AssociatedInterfaceTest, PtrFlushForTestingWithClosedPeer) {
+ IntegerSenderConnectionPtr ptr;
+ MakeRequest(&ptr);
+ bool called = false;
+ ptr.set_connection_error_handler(base::Bind(&SetBool, &called));
+ EXPECT_FALSE(called);
+ ptr.FlushForTesting();
+ EXPECT_TRUE(called);
+ ptr.FlushForTesting();
+}
+
+TEST_F(AssociatedInterfaceTest, AssociatedBindingConnectionErrorWithReason) {
+ AssociatedInterfaceRequest<IntegerSender> request;
+ IntegerSenderAssociatedPtrInfo ptr_info;
+ CreateIntegerSender(&ptr_info, &request);
+
+ IntegerSenderImpl impl(std::move(request));
+ AssociatedInterfacePtr<IntegerSender> ptr;
+ ptr.Bind(std::move(ptr_info));
+
+ base::RunLoop run_loop;
+ impl.binding()->set_connection_error_with_reason_handler(base::Bind(
+ [](const base::Closure& quit_closure, uint32_t custom_reason,
+ const std::string& description) {
+ EXPECT_EQ(123u, custom_reason);
+ EXPECT_EQ("farewell", description);
+ quit_closure.Run();
+ },
+ run_loop.QuitClosure()));
+
+ ptr.ResetWithReason(123u, "farewell");
+
+ run_loop.Run();
+}
+
+TEST_F(AssociatedInterfaceTest,
+ PendingAssociatedBindingConnectionErrorWithReason) {
+ // Test that AssociatedBinding is notified with connection error when the
+ // interface hasn't associated with a message pipe and the peer is closed.
+
+ IntegerSenderAssociatedPtr ptr;
+ IntegerSenderImpl impl(MakeRequest(&ptr));
+
+ base::RunLoop run_loop;
+ impl.binding()->set_connection_error_with_reason_handler(base::Bind(
+ [](const base::Closure& quit_closure, uint32_t custom_reason,
+ const std::string& description) {
+ EXPECT_EQ(123u, custom_reason);
+ EXPECT_EQ("farewell", description);
+ quit_closure.Run();
+ },
+ run_loop.QuitClosure()));
+
+ ptr.ResetWithReason(123u, "farewell");
+
+ run_loop.Run();
+}
+
+TEST_F(AssociatedInterfaceTest, AssociatedPtrConnectionErrorWithReason) {
+ AssociatedInterfaceRequest<IntegerSender> request;
+ IntegerSenderAssociatedPtrInfo ptr_info;
+ CreateIntegerSender(&ptr_info, &request);
+
+ IntegerSenderImpl impl(std::move(request));
+ AssociatedInterfacePtr<IntegerSender> ptr;
+ ptr.Bind(std::move(ptr_info));
+
+ base::RunLoop run_loop;
+ ptr.set_connection_error_with_reason_handler(base::Bind(
+ [](const base::Closure& quit_closure, uint32_t custom_reason,
+ const std::string& description) {
+ EXPECT_EQ(456u, custom_reason);
+ EXPECT_EQ("farewell", description);
+ quit_closure.Run();
+ },
+ run_loop.QuitClosure()));
+
+ impl.binding()->CloseWithReason(456u, "farewell");
+
+ run_loop.Run();
+}
+
+TEST_F(AssociatedInterfaceTest, PendingAssociatedPtrConnectionErrorWithReason) {
+ // Test that AssociatedInterfacePtr is notified with connection error when the
+ // interface hasn't associated with a message pipe and the peer is closed.
+
+ IntegerSenderAssociatedPtr ptr;
+ auto request = MakeRequest(&ptr);
+
+ base::RunLoop run_loop;
+ ptr.set_connection_error_with_reason_handler(base::Bind(
+ [](const base::Closure& quit_closure, uint32_t custom_reason,
+ const std::string& description) {
+ EXPECT_EQ(456u, custom_reason);
+ EXPECT_EQ("farewell", description);
+ quit_closure.Run();
+ },
+ run_loop.QuitClosure()));
+
+ request.ResetWithReason(456u, "farewell");
+
+ run_loop.Run();
+}
+
+TEST_F(AssociatedInterfaceTest, AssociatedRequestResetWithReason) {
+ AssociatedInterfaceRequest<IntegerSender> request;
+ IntegerSenderAssociatedPtrInfo ptr_info;
+ CreateIntegerSender(&ptr_info, &request);
+
+ AssociatedInterfacePtr<IntegerSender> ptr;
+ ptr.Bind(std::move(ptr_info));
+
+ base::RunLoop run_loop;
+ ptr.set_connection_error_with_reason_handler(base::Bind(
+ [](const base::Closure& quit_closure, uint32_t custom_reason,
+ const std::string& description) {
+ EXPECT_EQ(789u, custom_reason);
+ EXPECT_EQ("long time no see", description);
+ quit_closure.Run();
+ },
+ run_loop.QuitClosure()));
+
+ request.ResetWithReason(789u, "long time no see");
+
+ run_loop.Run();
+}
+
+TEST_F(AssociatedInterfaceTest, ThreadSafeAssociatedInterfacePtr) {
+ IntegerSenderConnectionPtr connection_ptr;
+ IntegerSenderConnectionImpl connection(MakeRequest(&connection_ptr));
+
+ IntegerSenderAssociatedPtr sender;
+ connection_ptr->GetSender(MakeRequest(&sender));
+
+ scoped_refptr<ThreadSafeIntegerSenderAssociatedPtr> thread_safe_sender =
+ ThreadSafeIntegerSenderAssociatedPtr::Create(std::move(sender));
+
+ {
+ // Test the thread safe pointer can be used from the interface ptr thread.
+ int32_t echoed_value = 0;
+ base::RunLoop run_loop;
+ (*thread_safe_sender)
+ ->Echo(123, base::Bind(&CaptureInt32, &echoed_value,
+ run_loop.QuitClosure()));
+ run_loop.Run();
+ EXPECT_EQ(123, echoed_value);
+ }
+
+ // Test the thread safe pointer can be used from another thread.
+ base::RunLoop run_loop;
+ base::Thread other_thread("service test thread");
+ other_thread.Start();
+
+ auto run_method = base::Bind(
+ [](const scoped_refptr<base::TaskRunner>& main_task_runner,
+ const base::Closure& quit_closure,
+ const scoped_refptr<ThreadSafeIntegerSenderAssociatedPtr>&
+ thread_safe_sender) {
+ auto done_callback = base::Bind(
+ [](const scoped_refptr<base::TaskRunner>& main_task_runner,
+ const base::Closure& quit_closure,
+ base::PlatformThreadId thread_id, int32_t result) {
+ EXPECT_EQ(123, result);
+ // Validate the callback is invoked on the calling thread.
+ EXPECT_EQ(thread_id, base::PlatformThread::CurrentId());
+ // Notify the run_loop to quit.
+ main_task_runner->PostTask(FROM_HERE, quit_closure);
+ });
+ (*thread_safe_sender)
+ ->Echo(123,
+ base::Bind(done_callback, main_task_runner, quit_closure,
+ base::PlatformThread::CurrentId()));
+ },
+ base::SequencedTaskRunnerHandle::Get(), run_loop.QuitClosure(),
+ thread_safe_sender);
+ other_thread.message_loop()->task_runner()->PostTask(FROM_HERE, run_method);
+
+ // Block until the method callback is called on the background thread.
+ run_loop.Run();
+}
+
+struct ForwarderTestContext {
+ IntegerSenderConnectionPtr connection_ptr;
+ std::unique_ptr<IntegerSenderConnectionImpl> interface_impl;
+ IntegerSenderAssociatedRequest sender_request;
+};
+
+TEST_F(AssociatedInterfaceTest,
+ ThreadSafeAssociatedInterfacePtrWithTaskRunner) {
+ // Start the thread from where we'll bind the interface pointer.
+ base::Thread other_thread("service test thread");
+ other_thread.Start();
+ const scoped_refptr<base::SingleThreadTaskRunner>& other_thread_task_runner =
+ other_thread.message_loop()->task_runner();
+
+ ForwarderTestContext* context = new ForwarderTestContext();
+ IntegerSenderAssociatedPtrInfo sender_info;
+ base::WaitableEvent sender_info_bound_event(
+ base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ auto setup = [](base::WaitableEvent* sender_info_bound_event,
+ IntegerSenderAssociatedPtrInfo* sender_info,
+ ForwarderTestContext* context) {
+ context->interface_impl = base::MakeUnique<IntegerSenderConnectionImpl>(
+ MakeRequest(&context->connection_ptr));
+
+ auto sender_request = MakeRequest(sender_info);
+ context->connection_ptr->GetSender(std::move(sender_request));
+
+ // Unblock the main thread as soon as |sender_info| is set.
+ sender_info_bound_event->Signal();
+ };
+ other_thread_task_runner->PostTask(
+ FROM_HERE,
+ base::Bind(setup, &sender_info_bound_event, &sender_info, context));
+ sender_info_bound_event.Wait();
+
+ // Create a ThreadSafeAssociatedPtr that binds on the background thread and is
+ // associated with |connection_ptr| there.
+ scoped_refptr<ThreadSafeIntegerSenderAssociatedPtr> thread_safe_ptr =
+ ThreadSafeIntegerSenderAssociatedPtr::Create(std::move(sender_info),
+ other_thread_task_runner);
+
+ // Issue a call on the thread-safe ptr immediately. Note that this may happen
+ // before the interface is bound on the background thread, and that must be
+ // OK.
+ {
+ auto echo_callback =
+ base::Bind([](const base::Closure& quit_closure, int32_t result) {
+ EXPECT_EQ(123, result);
+ quit_closure.Run();
+ });
+ base::RunLoop run_loop;
+ (*thread_safe_ptr)
+ ->Echo(123, base::Bind(echo_callback, run_loop.QuitClosure()));
+
+ // Block until the method callback is called.
+ run_loop.Run();
+ }
+
+ other_thread_task_runner->DeleteSoon(FROM_HERE, context);
+
+ // Reset the pointer now so the InterfacePtr associated resources can be
+ // deleted before the background thread's message loop is invalidated.
+ thread_safe_ptr = nullptr;
+}
+
+class DiscardingAssociatedPingProviderProvider
+ : public AssociatedPingProviderProvider {
+ public:
+ void GetPingProvider(
+ AssociatedPingProviderAssociatedRequest request) override {}
+};
+
+TEST_F(AssociatedInterfaceTest, CloseWithoutBindingAssociatedRequest) {
+ DiscardingAssociatedPingProviderProvider ping_provider_provider;
+ mojo::Binding<AssociatedPingProviderProvider> binding(
+ &ping_provider_provider);
+ auto provider_provider = binding.CreateInterfacePtrAndBind();
+ AssociatedPingProviderAssociatedPtr provider;
+ provider_provider->GetPingProvider(mojo::MakeRequest(&provider));
+ PingServiceAssociatedPtr ping;
+ provider->GetPing(mojo::MakeRequest(&ping));
+ base::RunLoop run_loop;
+ ping.set_connection_error_handler(run_loop.QuitClosure());
+ run_loop.Run();
+}
+
} // namespace
} // namespace test
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/bind_task_runner_unittest.cc b/mojo/public/cpp/bindings/tests/bind_task_runner_unittest.cc
index dab64f0..0c777ec 100644
--- a/mojo/public/cpp/bindings/tests/bind_task_runner_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/bind_task_runner_unittest.cc
@@ -10,7 +10,6 @@
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
-#include "mojo/public/cpp/bindings/associated_group.h"
#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
#include "mojo/public/cpp/bindings/associated_interface_request.h"
@@ -187,7 +186,7 @@ class BindTaskRunnerTest : public testing::Test {
binding_task_runner_ = scoped_refptr<TestTaskRunner>(new TestTaskRunner);
ptr_task_runner_ = scoped_refptr<TestTaskRunner>(new TestTaskRunner);
- auto request = GetProxy(&ptr_, ptr_task_runner_);
+ auto request = MakeRequest(&ptr_, ptr_task_runner_);
impl_.reset(new ImplType(std::move(request), binding_task_runner_));
}
@@ -213,7 +212,7 @@ class AssociatedBindTaskRunnerTest : public testing::Test {
sender_ptr_task_runner_ = scoped_refptr<TestTaskRunner>(new TestTaskRunner);
auto connection_request =
- GetProxy(&connection_ptr_, connection_ptr_task_runner_);
+ MakeRequest(&connection_ptr_, connection_ptr_task_runner_);
connection_impl_.reset(new IntegerSenderConnectionImpl(
std::move(connection_request), connection_binding_task_runner_,
sender_binding_task_runner_));
@@ -222,9 +221,8 @@ class AssociatedBindTaskRunnerTest : public testing::Test {
base::Bind(&AssociatedBindTaskRunnerTest::QuitTaskRunner,
base::Unretained(this)));
- connection_ptr_->GetSender(GetProxy(&sender_ptr_,
- connection_ptr_.associated_group(),
- sender_ptr_task_runner_));
+ connection_ptr_->GetSender(
+ MakeRequest(&sender_ptr_, sender_ptr_task_runner_));
connection_binding_task_runner_->Run();
}
diff --git a/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc b/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc
index 02b082a..43122ce 100644
--- a/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc
@@ -9,10 +9,10 @@
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
+#include "base/test/gtest_util.h"
#include "build/build_config.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/interface_ptr.h"
-#include "mojo/public/cpp/bindings/string.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "mojo/public/cpp/test_support/test_support.h"
#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h"
@@ -140,7 +140,7 @@ class BindingCallbackTest : public testing::Test {
TEST_F(BindingCallbackTest, Basic) {
// Create the ServerImpl and the Binding.
InterfaceImpl server_impl;
- Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_));
+ Binding<sample::Provider> binding(&server_impl, MakeRequest(&interface_ptr_));
// Initialize the test values.
server_impl.resetLastServerValueSeen();
@@ -199,7 +199,8 @@ TEST_F(BindingCallbackTest, DeleteBindingThenRunCallback) {
base::RunLoop run_loop;
{
// Create the binding in an inner scope so it can be deleted first.
- Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_));
+ Binding<sample::Provider> binding(&server_impl,
+ MakeRequest(&interface_ptr_));
interface_ptr_.set_connection_error_handler(run_loop.QuitClosure());
// Initialize the test values.
@@ -244,7 +245,8 @@ TEST_F(BindingCallbackTest, DeleteBindingThenDeleteCallback) {
InterfaceImpl server_impl;
{
// Create the binding in an inner scope so it can be deleted first.
- Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_));
+ Binding<sample::Provider> binding(&server_impl,
+ MakeRequest(&interface_ptr_));
// Initialize the test values.
server_impl.resetLastServerValueSeen();
@@ -275,7 +277,7 @@ TEST_F(BindingCallbackTest, DeleteBindingThenDeleteCallback) {
TEST_F(BindingCallbackTest, CloseBindingBeforeDeletingCallback) {
// Create the ServerImpl and the Binding.
InterfaceImpl server_impl;
- Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_));
+ Binding<sample::Provider> binding(&server_impl, MakeRequest(&interface_ptr_));
// Initialize the test values.
server_impl.resetLastServerValueSeen();
@@ -310,7 +312,7 @@ TEST_F(BindingCallbackTest, CloseBindingBeforeDeletingCallback) {
TEST_F(BindingCallbackTest, DeleteCallbackBeforeBindingDeathTest) {
// Create the ServerImpl and the Binding.
InterfaceImpl server_impl;
- Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_));
+ Binding<sample::Provider> binding(&server_impl, MakeRequest(&interface_ptr_));
// Initialize the test values.
server_impl.resetLastServerValueSeen();
@@ -328,17 +330,7 @@ TEST_F(BindingCallbackTest, DeleteCallbackBeforeBindingDeathTest) {
EXPECT_EQ(7, server_impl.last_server_value_seen());
EXPECT_EQ(0, last_client_callback_value_seen_);
-#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && !defined(OS_ANDROID)
- // Delete the callback without running it. This should cause a crash in debug
- // builds due to a DCHECK.
- std::string regex("Check failed: !is_valid");
-#if defined(OS_WIN)
- // TODO(msw): Fix MOJO_DCHECK logs and EXPECT_DEATH* on Win: crbug.com/535014
- regex.clear();
-#endif // OS_WIN
- EXPECT_DEATH_IF_SUPPORTED(server_impl.DeleteCallback(), regex.c_str());
-#endif // (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) &&
- // !defined(OS_ANDROID)
+ EXPECT_DCHECK_DEATH(server_impl.DeleteCallback());
}
} // namespace
diff --git a/mojo/public/cpp/bindings/tests/binding_set_unittest.cc b/mojo/public/cpp/bindings/tests/binding_set_unittest.cc
new file mode 100644
index 0000000..07acfbe
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/binding_set_unittest.cc
@@ -0,0 +1,416 @@
+// Copyright 2016 The Chromium 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 <memory>
+#include <utility>
+
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/bindings/associated_binding_set.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/strong_binding_set.h"
+#include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+class BindingSetTest : public testing::Test {
+ public:
+ BindingSetTest() {}
+ ~BindingSetTest() override {}
+
+ base::MessageLoop& loop() { return loop_; }
+
+ private:
+ base::MessageLoop loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(BindingSetTest);
+};
+
+template <typename BindingSetType, typename ContextType>
+void ExpectContextHelper(BindingSetType* binding_set,
+ ContextType expected_context) {
+ EXPECT_EQ(expected_context, binding_set->dispatch_context());
+}
+
+template <typename BindingSetType, typename ContextType>
+base::Closure ExpectContext(BindingSetType* binding_set,
+ ContextType expected_context) {
+ return base::Bind(
+ &ExpectContextHelper<BindingSetType, ContextType>, binding_set,
+ expected_context);
+}
+
+base::Closure Sequence(const base::Closure& first,
+ const base::Closure& second) {
+ return base::Bind(
+ [] (const base::Closure& first, const base::Closure& second) {
+ first.Run();
+ second.Run();
+ }, first, second);
+}
+
+class PingImpl : public PingService {
+ public:
+ PingImpl() {}
+ ~PingImpl() override {}
+
+ void set_ping_handler(const base::Closure& handler) {
+ ping_handler_ = handler;
+ }
+
+ private:
+ // PingService:
+ void Ping(const PingCallback& callback) override {
+ if (!ping_handler_.is_null())
+ ping_handler_.Run();
+ callback.Run();
+ }
+
+ base::Closure ping_handler_;
+};
+
+TEST_F(BindingSetTest, BindingSetContext) {
+ PingImpl impl;
+
+ BindingSet<PingService, int> bindings;
+ PingServicePtr ping_a, ping_b;
+ bindings.AddBinding(&impl, MakeRequest(&ping_a), 1);
+ bindings.AddBinding(&impl, MakeRequest(&ping_b), 2);
+
+ {
+ impl.set_ping_handler(ExpectContext(&bindings, 1));
+ base::RunLoop loop;
+ ping_a->Ping(loop.QuitClosure());
+ loop.Run();
+ }
+
+ {
+ impl.set_ping_handler(ExpectContext(&bindings, 2));
+ base::RunLoop loop;
+ ping_b->Ping(loop.QuitClosure());
+ loop.Run();
+ }
+
+ {
+ base::RunLoop loop;
+ bindings.set_connection_error_handler(
+ Sequence(ExpectContext(&bindings, 1), loop.QuitClosure()));
+ ping_a.reset();
+ loop.Run();
+ }
+
+ {
+ base::RunLoop loop;
+ bindings.set_connection_error_handler(
+ Sequence(ExpectContext(&bindings, 2), loop.QuitClosure()));
+ ping_b.reset();
+ loop.Run();
+ }
+
+ EXPECT_TRUE(bindings.empty());
+}
+
+TEST_F(BindingSetTest, BindingSetConnectionErrorWithReason) {
+ PingImpl impl;
+ PingServicePtr ptr;
+ BindingSet<PingService> bindings;
+ bindings.AddBinding(&impl, MakeRequest(&ptr));
+
+ base::RunLoop run_loop;
+ bindings.set_connection_error_with_reason_handler(base::Bind(
+ [](const base::Closure& quit_closure, uint32_t custom_reason,
+ const std::string& description) {
+ EXPECT_EQ(1024u, custom_reason);
+ EXPECT_EQ("bye", description);
+ quit_closure.Run();
+ },
+ run_loop.QuitClosure()));
+
+ ptr.ResetWithReason(1024u, "bye");
+}
+
+class PingProviderImpl : public AssociatedPingProvider, public PingService {
+ public:
+ PingProviderImpl() {}
+ ~PingProviderImpl() override {}
+
+ void set_new_ping_context(int context) { new_ping_context_ = context; }
+
+ void set_new_ping_handler(const base::Closure& handler) {
+ new_ping_handler_ = handler;
+ }
+
+ void set_ping_handler(const base::Closure& handler) {
+ ping_handler_ = handler;
+ }
+
+ AssociatedBindingSet<PingService, int>& ping_bindings() {
+ return ping_bindings_;
+ }
+
+ private:
+ // AssociatedPingProvider:
+ void GetPing(PingServiceAssociatedRequest request) override {
+ ping_bindings_.AddBinding(this, std::move(request), new_ping_context_);
+ if (!new_ping_handler_.is_null())
+ new_ping_handler_.Run();
+ }
+
+ // PingService:
+ void Ping(const PingCallback& callback) override {
+ if (!ping_handler_.is_null())
+ ping_handler_.Run();
+ callback.Run();
+ }
+
+ AssociatedBindingSet<PingService, int> ping_bindings_;
+ int new_ping_context_ = -1;
+ base::Closure ping_handler_;
+ base::Closure new_ping_handler_;
+};
+
+TEST_F(BindingSetTest, AssociatedBindingSetContext) {
+ AssociatedPingProviderPtr provider;
+ PingProviderImpl impl;
+ Binding<AssociatedPingProvider> binding(&impl, MakeRequest(&provider));
+
+ PingServiceAssociatedPtr ping_a;
+ {
+ base::RunLoop loop;
+ impl.set_new_ping_context(1);
+ impl.set_new_ping_handler(loop.QuitClosure());
+ provider->GetPing(MakeRequest(&ping_a));
+ loop.Run();
+ }
+
+ PingServiceAssociatedPtr ping_b;
+ {
+ base::RunLoop loop;
+ impl.set_new_ping_context(2);
+ impl.set_new_ping_handler(loop.QuitClosure());
+ provider->GetPing(MakeRequest(&ping_b));
+ loop.Run();
+ }
+
+ {
+ impl.set_ping_handler(ExpectContext(&impl.ping_bindings(), 1));
+ base::RunLoop loop;
+ ping_a->Ping(loop.QuitClosure());
+ loop.Run();
+ }
+
+ {
+ impl.set_ping_handler(ExpectContext(&impl.ping_bindings(), 2));
+ base::RunLoop loop;
+ ping_b->Ping(loop.QuitClosure());
+ loop.Run();
+ }
+
+ {
+ base::RunLoop loop;
+ impl.ping_bindings().set_connection_error_handler(
+ Sequence(ExpectContext(&impl.ping_bindings(), 1), loop.QuitClosure()));
+ ping_a.reset();
+ loop.Run();
+ }
+
+ {
+ base::RunLoop loop;
+ impl.ping_bindings().set_connection_error_handler(
+ Sequence(ExpectContext(&impl.ping_bindings(), 2), loop.QuitClosure()));
+ ping_b.reset();
+ loop.Run();
+ }
+
+ EXPECT_TRUE(impl.ping_bindings().empty());
+}
+
+TEST_F(BindingSetTest, MasterInterfaceBindingSetContext) {
+ AssociatedPingProviderPtr provider_a, provider_b;
+ PingProviderImpl impl;
+ BindingSet<AssociatedPingProvider, int> bindings;
+
+ bindings.AddBinding(&impl, MakeRequest(&provider_a), 1);
+ bindings.AddBinding(&impl, MakeRequest(&provider_b), 2);
+
+ {
+ PingServiceAssociatedPtr ping;
+ base::RunLoop loop;
+ impl.set_new_ping_handler(
+ Sequence(ExpectContext(&bindings, 1), loop.QuitClosure()));
+ provider_a->GetPing(MakeRequest(&ping));
+ loop.Run();
+ }
+
+ {
+ PingServiceAssociatedPtr ping;
+ base::RunLoop loop;
+ impl.set_new_ping_handler(
+ Sequence(ExpectContext(&bindings, 2), loop.QuitClosure()));
+ provider_b->GetPing(MakeRequest(&ping));
+ loop.Run();
+ }
+
+ {
+ base::RunLoop loop;
+ bindings.set_connection_error_handler(
+ Sequence(ExpectContext(&bindings, 1), loop.QuitClosure()));
+ provider_a.reset();
+ loop.Run();
+ }
+
+ {
+ base::RunLoop loop;
+ bindings.set_connection_error_handler(
+ Sequence(ExpectContext(&bindings, 2), loop.QuitClosure()));
+ provider_b.reset();
+ loop.Run();
+ }
+
+ EXPECT_TRUE(bindings.empty());
+}
+
+TEST_F(BindingSetTest, PreDispatchHandler) {
+ PingImpl impl;
+
+ BindingSet<PingService, int> bindings;
+ PingServicePtr ping_a, ping_b;
+ bindings.AddBinding(&impl, MakeRequest(&ping_a), 1);
+ bindings.AddBinding(&impl, MakeRequest(&ping_b), 2);
+
+ {
+ bindings.set_pre_dispatch_handler(base::Bind([] (const int& context) {
+ EXPECT_EQ(1, context);
+ }));
+ base::RunLoop loop;
+ ping_a->Ping(loop.QuitClosure());
+ loop.Run();
+ }
+
+ {
+ bindings.set_pre_dispatch_handler(base::Bind([] (const int& context) {
+ EXPECT_EQ(2, context);
+ }));
+ base::RunLoop loop;
+ ping_b->Ping(loop.QuitClosure());
+ loop.Run();
+ }
+
+ {
+ base::RunLoop loop;
+ bindings.set_pre_dispatch_handler(
+ base::Bind([](base::RunLoop* loop, const int& context) {
+ EXPECT_EQ(1, context);
+ loop->Quit();
+ }, &loop));
+ ping_a.reset();
+ loop.Run();
+ }
+
+ {
+ base::RunLoop loop;
+ bindings.set_pre_dispatch_handler(
+ base::Bind([](base::RunLoop* loop, const int& context) {
+ EXPECT_EQ(2, context);
+ loop->Quit();
+ }, &loop));
+ ping_b.reset();
+ loop.Run();
+ }
+
+ EXPECT_TRUE(bindings.empty());
+}
+
+TEST_F(BindingSetTest, AssociatedBindingSetConnectionErrorWithReason) {
+ AssociatedPingProviderPtr master_ptr;
+ PingProviderImpl master_impl;
+ Binding<AssociatedPingProvider> master_binding(&master_impl, &master_ptr);
+
+ base::RunLoop run_loop;
+ master_impl.ping_bindings().set_connection_error_with_reason_handler(
+ base::Bind(
+ [](const base::Closure& quit_closure, uint32_t custom_reason,
+ const std::string& description) {
+ EXPECT_EQ(2048u, custom_reason);
+ EXPECT_EQ("bye", description);
+ quit_closure.Run();
+ },
+ run_loop.QuitClosure()));
+
+ PingServiceAssociatedPtr ptr;
+ master_ptr->GetPing(MakeRequest(&ptr));
+
+ ptr.ResetWithReason(2048u, "bye");
+
+ run_loop.Run();
+}
+
+class PingInstanceCounter : public PingService {
+ public:
+ PingInstanceCounter() { ++instance_count; }
+ ~PingInstanceCounter() override { --instance_count; }
+
+ void Ping(const PingCallback& callback) override {}
+
+ static int instance_count;
+};
+int PingInstanceCounter::instance_count = 0;
+
+TEST_F(BindingSetTest, StrongBinding_Destructor) {
+ PingServicePtr ping_a, ping_b;
+ auto bindings = base::MakeUnique<StrongBindingSet<PingService>>();
+
+ bindings->AddBinding(base::MakeUnique<PingInstanceCounter>(),
+ mojo::MakeRequest(&ping_a));
+ EXPECT_EQ(1, PingInstanceCounter::instance_count);
+
+ bindings->AddBinding(base::MakeUnique<PingInstanceCounter>(),
+ mojo::MakeRequest(&ping_b));
+ EXPECT_EQ(2, PingInstanceCounter::instance_count);
+
+ bindings.reset();
+ EXPECT_EQ(0, PingInstanceCounter::instance_count);
+}
+
+TEST_F(BindingSetTest, StrongBinding_ConnectionError) {
+ PingServicePtr ping_a, ping_b;
+ StrongBindingSet<PingService> bindings;
+ bindings.AddBinding(base::MakeUnique<PingInstanceCounter>(),
+ mojo::MakeRequest(&ping_a));
+ bindings.AddBinding(base::MakeUnique<PingInstanceCounter>(),
+ mojo::MakeRequest(&ping_b));
+ EXPECT_EQ(2, PingInstanceCounter::instance_count);
+
+ ping_a.reset();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(1, PingInstanceCounter::instance_count);
+
+ ping_b.reset();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(0, PingInstanceCounter::instance_count);
+}
+
+TEST_F(BindingSetTest, StrongBinding_RemoveBinding) {
+ PingServicePtr ping_a, ping_b;
+ StrongBindingSet<PingService> bindings;
+ BindingId binding_id_a = bindings.AddBinding(
+ base::MakeUnique<PingInstanceCounter>(), mojo::MakeRequest(&ping_a));
+ BindingId binding_id_b = bindings.AddBinding(
+ base::MakeUnique<PingInstanceCounter>(), mojo::MakeRequest(&ping_b));
+ EXPECT_EQ(2, PingInstanceCounter::instance_count);
+
+ EXPECT_TRUE(bindings.RemoveBinding(binding_id_a));
+ EXPECT_EQ(1, PingInstanceCounter::instance_count);
+
+ EXPECT_TRUE(bindings.RemoveBinding(binding_id_b));
+ EXPECT_EQ(0, PingInstanceCounter::instance_count);
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/binding_unittest.cc b/mojo/public/cpp/bindings/tests/binding_unittest.cc
index 4838ee8..e76993b 100644
--- a/mojo/public/cpp/bindings/tests/binding_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/binding_unittest.cc
@@ -11,9 +11,12 @@
#include <utility>
#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h"
#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -63,7 +66,8 @@ void DoSetFlagAndRunClosure(bool* flag,
const base::Closure& closure,
Args... args) {
*flag = true;
- closure.Run();
+ if (!closure.is_null())
+ closure.Run();
}
template <typename... Args>
@@ -80,7 +84,7 @@ using BindingTest = BindingTestBase;
TEST_F(BindingTest, Close) {
bool called = false;
sample::ServicePtr ptr;
- auto request = GetProxy(&ptr);
+ auto request = MakeRequest(&ptr);
base::RunLoop run_loop;
ptr.set_connection_error_handler(
SetFlagAndRunClosure(&called, run_loop.QuitClosure()));
@@ -98,7 +102,7 @@ TEST_F(BindingTest, DestroyClosesMessagePipe) {
bool encountered_error = false;
ServiceImpl impl;
sample::ServicePtr ptr;
- auto request = GetProxy(&ptr);
+ auto request = MakeRequest(&ptr);
base::RunLoop run_loop;
ptr.set_connection_error_handler(
SetFlagAndRunClosure(&encountered_error, run_loop.QuitClosure()));
@@ -134,7 +138,7 @@ TEST_F(BindingTest, ConnectionError) {
{
ServiceImpl impl;
sample::ServicePtr ptr;
- Binding<sample::Service> binding(&impl, GetProxy(&ptr));
+ Binding<sample::Service> binding(&impl, MakeRequest(&ptr));
base::RunLoop run_loop;
binding.set_connection_error_handler(
SetFlagAndRunClosure(&called, run_loop.QuitClosure()));
@@ -153,7 +157,7 @@ TEST_F(BindingTest, ConnectionError) {
TEST_F(BindingTest, CloseDoesntCallConnectionErrorHandler) {
ServiceImpl impl;
sample::ServicePtr ptr;
- Binding<sample::Service> binding(&impl, GetProxy(&ptr));
+ Binding<sample::Service> binding(&impl, MakeRequest(&ptr));
bool called = false;
binding.set_connection_error_handler(SetFlagAndRunClosure(&called));
binding.Close();
@@ -200,7 +204,7 @@ TEST_F(BindingTest, SelfDeleteOnConnectionError) {
// This should delete itself on connection error.
base::RunLoop run_loop;
new ServiceImplWithBinding(&was_deleted, run_loop.QuitClosure(),
- GetProxy(&ptr));
+ MakeRequest(&ptr));
ptr.reset();
EXPECT_FALSE(was_deleted);
run_loop.Run();
@@ -211,7 +215,7 @@ TEST_F(BindingTest, SelfDeleteOnConnectionError) {
TEST_F(BindingTest, Unbind) {
ServiceImpl impl;
sample::ServicePtr ptr;
- Binding<sample::Service> binding(&impl, GetProxy(&ptr));
+ Binding<sample::Service> binding(&impl, MakeRequest(&ptr));
bool called = false;
base::RunLoop run_loop;
@@ -269,7 +273,7 @@ TEST_F(BindingTest, PauseResume) {
bool called = false;
base::RunLoop run_loop;
sample::ServicePtr ptr;
- auto request = GetProxy(&ptr);
+ auto request = MakeRequest(&ptr);
ServiceImpl impl;
Binding<sample::Service> binding(&impl, std::move(request));
binding.PauseIncomingMethodCallProcessing();
@@ -292,7 +296,7 @@ TEST_F(BindingTest, ErrorHandleNotRunWhilePaused) {
bool called = false;
base::RunLoop run_loop;
sample::ServicePtr ptr;
- auto request = GetProxy(&ptr);
+ auto request = MakeRequest(&ptr);
ServiceImpl impl;
Binding<sample::Service> binding(&impl, std::move(request));
binding.set_connection_error_handler(
@@ -310,6 +314,177 @@ TEST_F(BindingTest, ErrorHandleNotRunWhilePaused) {
EXPECT_TRUE(called);
}
+class PingServiceImpl : public test::PingService {
+ public:
+ PingServiceImpl() {}
+ ~PingServiceImpl() override {}
+
+ // test::PingService:
+ void Ping(const PingCallback& callback) override {
+ if (!ping_handler_.is_null())
+ ping_handler_.Run();
+ callback.Run();
+ }
+
+ void set_ping_handler(const base::Closure& handler) {
+ ping_handler_ = handler;
+ }
+
+ private:
+ base::Closure ping_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(PingServiceImpl);
+};
+
+class CallbackFilter : public MessageReceiver {
+ public:
+ explicit CallbackFilter(const base::Closure& callback)
+ : callback_(callback) {}
+ ~CallbackFilter() override {}
+
+ static std::unique_ptr<CallbackFilter> Wrap(const base::Closure& callback) {
+ return base::MakeUnique<CallbackFilter>(callback);
+ }
+
+ // MessageReceiver:
+ bool Accept(Message* message) override {
+ callback_.Run();
+ return true;
+ }
+
+ private:
+ const base::Closure callback_;
+};
+
+// Verifies that message filters are notified in the order they were added and
+// are always notified before a message is dispatched.
+TEST_F(BindingTest, MessageFilter) {
+ test::PingServicePtr ptr;
+ PingServiceImpl impl;
+ mojo::Binding<test::PingService> binding(&impl, MakeRequest(&ptr));
+
+ int status = 0;
+ auto handler_helper = [] (int* status, int expected_status, int new_status) {
+ EXPECT_EQ(expected_status, *status);
+ *status = new_status;
+ };
+ auto create_handler = [&] (int expected_status, int new_status) {
+ return base::Bind(handler_helper, &status, expected_status, new_status);
+ };
+
+ binding.AddFilter(CallbackFilter::Wrap(create_handler(0, 1)));
+ binding.AddFilter(CallbackFilter::Wrap(create_handler(1, 2)));
+ impl.set_ping_handler(create_handler(2, 3));
+
+ for (int i = 0; i < 10; ++i) {
+ status = 0;
+ base::RunLoop loop;
+ ptr->Ping(loop.QuitClosure());
+ loop.Run();
+ EXPECT_EQ(3, status);
+ }
+}
+
+void Fail() {
+ FAIL() << "Unexpected connection error";
+}
+
+TEST_F(BindingTest, FlushForTesting) {
+ bool called = false;
+ sample::ServicePtr ptr;
+ auto request = MakeRequest(&ptr);
+ ServiceImpl impl;
+ Binding<sample::Service> binding(&impl, std::move(request));
+ binding.set_connection_error_handler(base::Bind(&Fail));
+
+ ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
+ SetFlagAndRunClosure<int32_t>(&called));
+ EXPECT_FALSE(called);
+ // Because the flush is sent from the binding, it only guarantees that the
+ // request has been received, not the response. The second flush waits for the
+ // response to be received.
+ binding.FlushForTesting();
+ binding.FlushForTesting();
+ EXPECT_TRUE(called);
+}
+
+TEST_F(BindingTest, FlushForTestingWithClosedPeer) {
+ bool called = false;
+ sample::ServicePtr ptr;
+ auto request = MakeRequest(&ptr);
+ ServiceImpl impl;
+ Binding<sample::Service> binding(&impl, std::move(request));
+ binding.set_connection_error_handler(SetFlagAndRunClosure(&called));
+ ptr.reset();
+
+ EXPECT_FALSE(called);
+ binding.FlushForTesting();
+ EXPECT_TRUE(called);
+ binding.FlushForTesting();
+}
+
+TEST_F(BindingTest, ConnectionErrorWithReason) {
+ sample::ServicePtr ptr;
+ auto request = MakeRequest(&ptr);
+ ServiceImpl impl;
+ Binding<sample::Service> binding(&impl, std::move(request));
+
+ base::RunLoop run_loop;
+ binding.set_connection_error_with_reason_handler(base::Bind(
+ [](const base::Closure& quit_closure, uint32_t custom_reason,
+ const std::string& description) {
+ EXPECT_EQ(1234u, custom_reason);
+ EXPECT_EQ("hello", description);
+ quit_closure.Run();
+ },
+ run_loop.QuitClosure()));
+
+ ptr.ResetWithReason(1234u, "hello");
+
+ run_loop.Run();
+}
+
+template <typename T>
+struct WeakPtrImplRefTraits {
+ using PointerType = base::WeakPtr<T>;
+
+ static bool IsNull(const base::WeakPtr<T>& ptr) { return !ptr; }
+ static T* GetRawPointer(base::WeakPtr<T>* ptr) { return ptr->get(); }
+};
+
+template <typename T>
+using WeakBinding = Binding<T, WeakPtrImplRefTraits<T>>;
+
+TEST_F(BindingTest, CustomImplPointerType) {
+ PingServiceImpl impl;
+ base::WeakPtrFactory<test::PingService> weak_factory(&impl);
+
+ test::PingServicePtr proxy;
+ WeakBinding<test::PingService> binding(weak_factory.GetWeakPtr(),
+ MakeRequest(&proxy));
+
+ {
+ // Ensure the binding is functioning.
+ base::RunLoop run_loop;
+ proxy->Ping(run_loop.QuitClosure());
+ run_loop.Run();
+ }
+
+ {
+ // Attempt to dispatch another message after the WeakPtr is invalidated.
+ base::Closure assert_not_reached = base::Bind([] { NOTREACHED(); });
+ impl.set_ping_handler(assert_not_reached);
+ proxy->Ping(assert_not_reached);
+
+ // The binding will close its end of the pipe which will trigger a
+ // connection error on |proxy|.
+ base::RunLoop run_loop;
+ proxy.set_connection_error_handler(run_loop.QuitClosure());
+ weak_factory.InvalidateWeakPtrs();
+ run_loop.Run();
+ }
+}
+
// StrongBindingTest -----------------------------------------------------------
using StrongBindingTest = BindingTestBase;
@@ -320,43 +495,31 @@ TEST_F(StrongBindingTest, DestroyClosesMessagePipe) {
base::RunLoop run_loop;
bool encountered_error = false;
bool was_deleted = false;
- ServiceImpl impl(&was_deleted);
sample::ServicePtr ptr;
- auto request = GetProxy(&ptr);
+ auto request = MakeRequest(&ptr);
ptr.set_connection_error_handler(
SetFlagAndRunClosure(&encountered_error, run_loop.QuitClosure()));
bool called = false;
base::RunLoop run_loop2;
- {
- StrongBinding<sample::Service> binding(&impl, std::move(request));
- ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
- SetFlagAndRunClosure<int32_t>(&called,
- run_loop2.QuitClosure()));
- run_loop2.Run();
- EXPECT_TRUE(called);
- EXPECT_FALSE(encountered_error);
- }
- // Now that the StrongBinding is out of scope we should detect an error on the
- // other end of the pipe.
- run_loop.Run();
- EXPECT_TRUE(encountered_error);
- // But destroying the StrongBinding doesn't destroy the object.
- ASSERT_FALSE(was_deleted);
-}
-
-class ServiceImplWithStrongBinding : public ServiceImpl {
- public:
- ServiceImplWithStrongBinding(bool* was_deleted,
- InterfaceRequest<sample::Service> request)
- : ServiceImpl(was_deleted), binding_(this, std::move(request)) {}
- StrongBinding<sample::Service>& binding() { return binding_; }
+ auto binding = MakeStrongBinding(base::MakeUnique<ServiceImpl>(&was_deleted),
+ std::move(request));
+ ptr->Frobinate(
+ nullptr, sample::Service::BazOptions::REGULAR, nullptr,
+ SetFlagAndRunClosure<int32_t>(&called, run_loop2.QuitClosure()));
+ run_loop2.Run();
+ EXPECT_TRUE(called);
+ EXPECT_FALSE(encountered_error);
+ binding->Close();
- private:
- StrongBinding<sample::Service> binding_;
+ // Now that the StrongBinding is closed we should detect an error on the other
+ // end of the pipe.
+ run_loop.Run();
+ EXPECT_TRUE(encountered_error);
- DISALLOW_COPY_AND_ASSIGN(ServiceImplWithStrongBinding);
-};
+ // Destroying the StrongBinding also destroys the impl.
+ ASSERT_TRUE(was_deleted);
+}
// Tests the typical case, where the implementation object owns the
// StrongBinding (and should be destroyed on connection error).
@@ -366,7 +529,7 @@ TEST_F(StrongBindingTest, ConnectionErrorDestroysImpl) {
// Will delete itself.
base::RunLoop run_loop;
new ServiceImplWithBinding(&was_deleted, run_loop.QuitClosure(),
- GetProxy(&ptr));
+ MakeRequest(&ptr));
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(was_deleted);
@@ -377,37 +540,71 @@ TEST_F(StrongBindingTest, ConnectionErrorDestroysImpl) {
EXPECT_TRUE(was_deleted);
}
-// Tests that even when the implementation object owns the StrongBinding, that
-// the implementation can still be deleted (which should result in the message
-// pipe being closed). Also checks that the connection error handler doesn't get
-// called.
-TEST_F(StrongBindingTest, ExplicitDeleteImpl) {
- bool ptr_error_handler_called = false;
- sample::ServicePtr ptr;
- auto request = GetProxy(&ptr);
- base::RunLoop run_loop;
- ptr.set_connection_error_handler(
- SetFlagAndRunClosure(&ptr_error_handler_called, run_loop.QuitClosure()));
+TEST_F(StrongBindingTest, FlushForTesting) {
+ bool called = false;
bool was_deleted = false;
- ServiceImplWithStrongBinding* impl =
- new ServiceImplWithStrongBinding(&was_deleted, std::move(request));
- bool binding_error_handler_called = false;
- impl->binding().set_connection_error_handler(
- SetFlagAndRunClosure(&binding_error_handler_called));
+ sample::ServicePtr ptr;
+ auto request = MakeRequest(&ptr);
+ auto binding = MakeStrongBinding(base::MakeUnique<ServiceImpl>(&was_deleted),
+ std::move(request));
+ binding->set_connection_error_handler(base::Bind(&Fail));
- base::RunLoop().RunUntilIdle();
- EXPECT_FALSE(ptr_error_handler_called);
+ ptr->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
+ SetFlagAndRunClosure<int32_t>(&called));
+ EXPECT_FALSE(called);
+ // Because the flush is sent from the binding, it only guarantees that the
+ // request has been received, not the response. The second flush waits for the
+ // response to be received.
+ ASSERT_TRUE(binding);
+ binding->FlushForTesting();
+ ASSERT_TRUE(binding);
+ binding->FlushForTesting();
+ EXPECT_TRUE(called);
EXPECT_FALSE(was_deleted);
-
- delete impl;
- EXPECT_FALSE(ptr_error_handler_called);
+ ptr.reset();
+ ASSERT_TRUE(binding);
+ binding->set_connection_error_handler(base::Closure());
+ binding->FlushForTesting();
EXPECT_TRUE(was_deleted);
- was_deleted = false; // It shouldn't be double-deleted!
- run_loop.Run();
- EXPECT_TRUE(ptr_error_handler_called);
+}
+
+TEST_F(StrongBindingTest, FlushForTestingWithClosedPeer) {
+ bool called = false;
+ bool was_deleted = false;
+ sample::ServicePtr ptr;
+ auto request = MakeRequest(&ptr);
+ auto binding = MakeStrongBinding(base::MakeUnique<ServiceImpl>(&was_deleted),
+ std::move(request));
+ binding->set_connection_error_handler(SetFlagAndRunClosure(&called));
+ ptr.reset();
+
+ EXPECT_FALSE(called);
EXPECT_FALSE(was_deleted);
+ ASSERT_TRUE(binding);
+ binding->FlushForTesting();
+ EXPECT_TRUE(called);
+ EXPECT_TRUE(was_deleted);
+ ASSERT_FALSE(binding);
+}
- EXPECT_FALSE(binding_error_handler_called);
+TEST_F(StrongBindingTest, ConnectionErrorWithReason) {
+ sample::ServicePtr ptr;
+ auto request = MakeRequest(&ptr);
+ auto binding =
+ MakeStrongBinding(base::MakeUnique<ServiceImpl>(), std::move(request));
+ base::RunLoop run_loop;
+ binding->set_connection_error_with_reason_handler(base::Bind(
+ [](const base::Closure& quit_closure, uint32_t custom_reason,
+ const std::string& description) {
+ EXPECT_EQ(5678u, custom_reason);
+ EXPECT_EQ("hello", description);
+ quit_closure.Run();
+ },
+ run_loop.QuitClosure()));
+
+ ptr.ResetWithReason(5678u, "hello");
+
+ run_loop.Run();
}
} // namespace
diff --git a/mojo/public/cpp/bindings/tests/bindings_perftest.cc b/mojo/public/cpp/bindings/tests/bindings_perftest.cc
index db0dd05..6a50de4 100644
--- a/mojo/public/cpp/bindings/tests/bindings_perftest.cc
+++ b/mojo/public/cpp/bindings/tests/bindings_perftest.cc
@@ -8,7 +8,13 @@
#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
+#include "mojo/public/cpp/bindings/lib/message_builder.h"
+#include "mojo/public/cpp/bindings/lib/multiplex_router.h"
+#include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/cpp/test_support/test_support.h"
#include "mojo/public/cpp/test_support/test_utils.h"
#include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
@@ -81,9 +87,7 @@ void PingPongTest::OnPingDone() {
}
struct BoundPingService {
- BoundPingService() : binding(&impl) {
- binding.Bind(GetProxy(&service));
- }
+ BoundPingService() : binding(&impl) { binding.Bind(MakeRequest(&service)); }
PingServiceImpl impl;
test::PingServicePtr service;
@@ -101,7 +105,7 @@ class MojoBindingsPerftest : public testing::Test {
TEST_F(MojoBindingsPerftest, InProcessPingPong) {
test::PingServicePtr service;
PingServiceImpl impl;
- Binding<test::PingService> binding(&impl, GetProxy(&service));
+ Binding<test::PingService> binding(&impl, MakeRequest(&service));
PingPongTest test(std::move(service));
{
@@ -133,5 +137,148 @@ TEST_F(MojoBindingsPerftest, InProcessPingPong) {
}
}
+class PingPongPaddle : public MessageReceiverWithResponderStatus {
+ public:
+ PingPongPaddle(MessageReceiver* sender) : sender_(sender) {}
+
+ void set_sender(MessageReceiver* sender) { sender_ = sender; }
+
+ bool Accept(Message* message) override {
+ uint32_t count = message->header()->name;
+ if (!quit_closure_.is_null()) {
+ count++;
+ if (count >= expected_count_) {
+ end_time_ = base::TimeTicks::Now();
+ quit_closure_.Run();
+ return true;
+ }
+ }
+
+ internal::MessageBuilder builder(count, 0, 8, 0);
+ bool result = sender_->Accept(builder.message());
+ DCHECK(result);
+ return true;
+ }
+
+ bool AcceptWithResponder(Message* message,
+ MessageReceiverWithStatus* responder) override {
+ NOTREACHED();
+ return true;
+ }
+
+ base::TimeDelta Serve(uint32_t expected_count) {
+ base::RunLoop run_loop;
+
+ expected_count_ = expected_count;
+ quit_closure_ = run_loop.QuitClosure();
+
+ start_time_ = base::TimeTicks::Now();
+ internal::MessageBuilder builder(0, 0, 8, 0);
+ bool result = sender_->Accept(builder.message());
+ DCHECK(result);
+
+ run_loop.Run();
+
+ return end_time_ - start_time_;
+ }
+
+ private:
+ base::TimeTicks start_time_;
+ base::TimeTicks end_time_;
+ uint32_t expected_count_ = 0;
+ MessageReceiver* sender_;
+ base::Closure quit_closure_;
+};
+
+TEST_F(MojoBindingsPerftest, MultiplexRouterPingPong) {
+ MessagePipe pipe;
+ scoped_refptr<internal::MultiplexRouter> router0(
+ new internal::MultiplexRouter(std::move(pipe.handle0),
+ internal::MultiplexRouter::SINGLE_INTERFACE,
+ true, base::ThreadTaskRunnerHandle::Get()));
+ scoped_refptr<internal::MultiplexRouter> router1(
+ new internal::MultiplexRouter(
+ std::move(pipe.handle1), internal::MultiplexRouter::SINGLE_INTERFACE,
+ false, base::ThreadTaskRunnerHandle::Get()));
+
+ PingPongPaddle paddle0(nullptr);
+ PingPongPaddle paddle1(nullptr);
+
+ InterfaceEndpointClient client0(
+ router0->CreateLocalEndpointHandle(kMasterInterfaceId), &paddle0, nullptr,
+ false, base::ThreadTaskRunnerHandle::Get(), 0u);
+ InterfaceEndpointClient client1(
+ router1->CreateLocalEndpointHandle(kMasterInterfaceId), &paddle1, nullptr,
+ false, base::ThreadTaskRunnerHandle::Get(), 0u);
+
+ paddle0.set_sender(&client0);
+ paddle1.set_sender(&client1);
+
+ static const uint32_t kWarmUpIterations = 1000;
+ static const uint32_t kTestIterations = 1000000;
+
+ paddle0.Serve(kWarmUpIterations);
+
+ base::TimeDelta duration = paddle0.Serve(kTestIterations);
+
+ test::LogPerfResult("MultiplexRouterPingPong", nullptr,
+ kTestIterations / duration.InSecondsF(), "pings/second");
+}
+
+class CounterReceiver : public MessageReceiverWithResponderStatus {
+ public:
+ bool Accept(Message* message) override {
+ counter_++;
+ return true;
+ }
+
+ bool AcceptWithResponder(Message* message,
+ MessageReceiverWithStatus* responder) override {
+ NOTREACHED();
+ return true;
+ }
+
+ uint32_t counter() const { return counter_; }
+
+ void Reset() { counter_ = 0; }
+
+ private:
+ uint32_t counter_ = 0;
+};
+
+TEST_F(MojoBindingsPerftest, MultiplexRouterDispatchCost) {
+ MessagePipe pipe;
+ scoped_refptr<internal::MultiplexRouter> router(new internal::MultiplexRouter(
+ std::move(pipe.handle0), internal::MultiplexRouter::SINGLE_INTERFACE,
+ true, base::ThreadTaskRunnerHandle::Get()));
+ CounterReceiver receiver;
+ InterfaceEndpointClient client(
+ router->CreateLocalEndpointHandle(kMasterInterfaceId), &receiver, nullptr,
+ false, base::ThreadTaskRunnerHandle::Get(), 0u);
+
+ static const uint32_t kIterations[] = {1000, 3000000};
+
+ for (size_t i = 0; i < 2; ++i) {
+ receiver.Reset();
+ base::TimeTicks start_time = base::TimeTicks::Now();
+ for (size_t j = 0; j < kIterations[i]; ++j) {
+ internal::MessageBuilder builder(0, 0, 8, 0);
+ bool result =
+ router->SimulateReceivingMessageForTesting(builder.message());
+ DCHECK(result);
+ }
+
+ base::TimeTicks end_time = base::TimeTicks::Now();
+ base::TimeDelta duration = end_time - start_time;
+ CHECK_EQ(kIterations[i], receiver.counter());
+
+ if (i == 1) {
+ test::LogPerfResult("MultiplexRouterDispatchCost", nullptr,
+ kIterations[i] / duration.InSecondsF(),
+ "times/second");
+ }
+ }
+}
+
} // namespace
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/connector_unittest.cc b/mojo/public/cpp/bindings/tests/connector_unittest.cc
index 89cc51d..74ecb7a 100644
--- a/mojo/public/cpp/bindings/tests/connector_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/connector_unittest.cc
@@ -14,6 +14,7 @@
#include "base/callback_helpers.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
+#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "mojo/public/cpp/bindings/lib/message_builder.h"
#include "mojo/public/cpp/bindings/tests/message_queue.h"
@@ -98,10 +99,10 @@ class ConnectorTest : public testing::Test {
void AllocMessage(const char* text, Message* message) {
size_t payload_size = strlen(text) + 1; // Plus null terminator.
- internal::MessageBuilder builder(1, payload_size);
+ internal::MessageBuilder builder(1, 0, payload_size, 0);
memcpy(builder.buffer()->Allocate(payload_size), text, payload_size);
- builder.message()->MoveTo(message);
+ *message = std::move(*builder.message());
}
protected:
@@ -572,6 +573,27 @@ TEST_F(ConnectorTest, ProcessWhenNested) {
ASSERT_EQ(2u, accumulator.size());
}
+TEST_F(ConnectorTest, DestroyOnDifferentThreadAfterClose) {
+ std::unique_ptr<Connector> connector(
+ new Connector(std::move(handle0_), Connector::SINGLE_THREADED_SEND,
+ base::ThreadTaskRunnerHandle::Get()));
+
+ connector->CloseMessagePipe();
+
+ base::Thread another_thread("ThreadForDestroyingConnector");
+ another_thread.Start();
+
+ base::RunLoop run_loop;
+ another_thread.task_runner()->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(
+ [](std::unique_ptr<Connector> connector) { connector.reset(); },
+ base::Passed(std::move(connector))),
+ run_loop.QuitClosure());
+
+ run_loop.Run();
+}
+
} // namespace
} // namespace test
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/constant_unittest.cc b/mojo/public/cpp/bindings/tests/constant_unittest.cc
index f6394f3..caa6464 100644
--- a/mojo/public/cpp/bindings/tests/constant_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/constant_unittest.cc
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <cmath>
+
+#include "base/strings/string_piece.h"
#include "mojo/public/interfaces/bindings/tests/test_constants.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -19,23 +22,38 @@ TEST(ConstantTest, GlobalConstants) {
static_assert(kUint32Value == 4294967295U, "");
static_assert(kInt64Value == -9223372036854775807, "");
static_assert(kUint64Value == 9999999999999999999ULL, "");
-
- EXPECT_DOUBLE_EQ(kDoubleValue, 3.14159);
- EXPECT_FLOAT_EQ(kFloatValue, 2.71828f);
+ static_assert(kDoubleValue == 3.14159, "");
+ static_assert(kFloatValue == 2.71828f, "");
+
+ EXPECT_EQ(base::StringPiece(kStringValue), "test string contents");
+ EXPECT_TRUE(std::isnan(kDoubleNaN));
+ EXPECT_TRUE(std::isinf(kDoubleInfinity));
+ EXPECT_TRUE(std::isinf(kDoubleNegativeInfinity));
+ EXPECT_NE(kDoubleInfinity, kDoubleNegativeInfinity);
+ EXPECT_TRUE(std::isnan(kFloatNaN));
+ EXPECT_TRUE(std::isinf(kFloatInfinity));
+ EXPECT_TRUE(std::isinf(kFloatNegativeInfinity));
+ EXPECT_NE(kFloatInfinity, kFloatNegativeInfinity);
}
TEST(ConstantTest, StructConstants) {
// Compile-time constants.
static_assert(StructWithConstants::kInt8Value == 5U, "");
+ static_assert(StructWithConstants::kFloatValue == 765.432f, "");
- EXPECT_FLOAT_EQ(StructWithConstants::kFloatValue, 765.432f);
+ EXPECT_EQ(base::StringPiece(StructWithConstants::kStringValue),
+ "struct test string contents");
}
TEST(ConstantTest, InterfaceConstants) {
// Compile-time constants.
static_assert(InterfaceWithConstants::kUint32Value == 20100722, "");
+ static_assert(InterfaceWithConstants::kDoubleValue == 12.34567, "");
- EXPECT_DOUBLE_EQ(InterfaceWithConstants::kDoubleValue, 12.34567);
+ EXPECT_EQ(base::StringPiece(InterfaceWithConstants::kStringValue),
+ "interface test string contents");
+ EXPECT_EQ(base::StringPiece(InterfaceWithConstants::Name_),
+ "mojo::test::InterfaceWithConstants");
}
} // namespace test
diff --git a/mojo/public/cpp/bindings/tests/data_view_unittest.cc b/mojo/public/cpp/bindings/tests/data_view_unittest.cc
new file mode 100644
index 0000000..0ebfda5
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/data_view_unittest.cc
@@ -0,0 +1,303 @@
+// Copyright 2016 The Chromium 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 <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "base/message_loop/message_loop.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/interfaces/bindings/tests/test_data_view.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace data_view {
+namespace {
+
+class DataViewTest : public testing::Test {
+ private:
+ base::MessageLoop message_loop_;
+};
+
+struct DataViewHolder {
+ std::unique_ptr<TestStructDataView> data_view;
+ std::unique_ptr<mojo::internal::FixedBufferForTesting> buf;
+ mojo::internal::SerializationContext context;
+};
+
+std::unique_ptr<DataViewHolder> SerializeTestStruct(TestStructPtr input) {
+ std::unique_ptr<DataViewHolder> result(new DataViewHolder);
+
+ size_t size = mojo::internal::PrepareToSerialize<TestStructDataView>(
+ input, &result->context);
+
+ result->buf.reset(new mojo::internal::FixedBufferForTesting(size));
+ internal::TestStruct_Data* data = nullptr;
+ mojo::internal::Serialize<TestStructDataView>(input, result->buf.get(), &data,
+ &result->context);
+
+ result->data_view.reset(new TestStructDataView(data, &result->context));
+ return result;
+}
+
+class TestInterfaceImpl : public TestInterface {
+ public:
+ explicit TestInterfaceImpl(TestInterfaceRequest request)
+ : binding_(this, std::move(request)) {}
+ ~TestInterfaceImpl() override {}
+
+ // TestInterface implementation:
+ void Echo(int32_t value, const EchoCallback& callback) override {
+ callback.Run(value);
+ }
+
+ private:
+ Binding<TestInterface> binding_;
+};
+
+} // namespace
+
+TEST_F(DataViewTest, String) {
+ TestStructPtr obj(TestStruct::New());
+ obj->f_string = "hello";
+
+ auto data_view_holder = SerializeTestStruct(std::move(obj));
+ auto& data_view = *data_view_holder->data_view;
+
+ StringDataView string_data_view;
+ data_view.GetFStringDataView(&string_data_view);
+
+ ASSERT_FALSE(string_data_view.is_null());
+ EXPECT_EQ(std::string("hello"),
+ std::string(string_data_view.storage(), string_data_view.size()));
+}
+
+TEST_F(DataViewTest, NestedStruct) {
+ TestStructPtr obj(TestStruct::New());
+ obj->f_struct = NestedStruct::New();
+ obj->f_struct->f_int32 = 42;
+
+ auto data_view_holder = SerializeTestStruct(std::move(obj));
+ auto& data_view = *data_view_holder->data_view;
+
+ NestedStructDataView struct_data_view;
+ data_view.GetFStructDataView(&struct_data_view);
+
+ ASSERT_FALSE(struct_data_view.is_null());
+ EXPECT_EQ(42, struct_data_view.f_int32());
+}
+
+TEST_F(DataViewTest, NativeStruct) {
+ TestStructPtr obj(TestStruct::New());
+ obj->f_native_struct = NativeStruct::New();
+ obj->f_native_struct->data = std::vector<uint8_t>({3, 2, 1});
+
+ auto data_view_holder = SerializeTestStruct(std::move(obj));
+ auto& data_view = *data_view_holder->data_view;
+
+ NativeStructDataView struct_data_view;
+ data_view.GetFNativeStructDataView(&struct_data_view);
+
+ ASSERT_FALSE(struct_data_view.is_null());
+ ASSERT_EQ(3u, struct_data_view.size());
+ EXPECT_EQ(3, struct_data_view[0]);
+ EXPECT_EQ(2, struct_data_view[1]);
+ EXPECT_EQ(1, struct_data_view[2]);
+ EXPECT_EQ(3, *struct_data_view.data());
+}
+
+TEST_F(DataViewTest, BoolArray) {
+ TestStructPtr obj(TestStruct::New());
+ obj->f_bool_array = {true, false};
+
+ auto data_view_holder = SerializeTestStruct(std::move(obj));
+ auto& data_view = *data_view_holder->data_view;
+
+ ArrayDataView<bool> array_data_view;
+ data_view.GetFBoolArrayDataView(&array_data_view);
+
+ ASSERT_FALSE(array_data_view.is_null());
+ ASSERT_EQ(2u, array_data_view.size());
+ EXPECT_TRUE(array_data_view[0]);
+ EXPECT_FALSE(array_data_view[1]);
+}
+
+TEST_F(DataViewTest, IntegerArray) {
+ TestStructPtr obj(TestStruct::New());
+ obj->f_int32_array = {1024, 128};
+
+ auto data_view_holder = SerializeTestStruct(std::move(obj));
+ auto& data_view = *data_view_holder->data_view;
+
+ ArrayDataView<int32_t> array_data_view;
+ data_view.GetFInt32ArrayDataView(&array_data_view);
+
+ ASSERT_FALSE(array_data_view.is_null());
+ ASSERT_EQ(2u, array_data_view.size());
+ EXPECT_EQ(1024, array_data_view[0]);
+ EXPECT_EQ(128, array_data_view[1]);
+ EXPECT_EQ(1024, *array_data_view.data());
+}
+
+TEST_F(DataViewTest, EnumArray) {
+ TestStructPtr obj(TestStruct::New());
+ obj->f_enum_array = {TestEnum::VALUE_1, TestEnum::VALUE_0};
+
+ auto data_view_holder = SerializeTestStruct(std::move(obj));
+ auto& data_view = *data_view_holder->data_view;
+
+ ArrayDataView<TestEnum> array_data_view;
+ data_view.GetFEnumArrayDataView(&array_data_view);
+
+ ASSERT_FALSE(array_data_view.is_null());
+ ASSERT_EQ(2u, array_data_view.size());
+ EXPECT_EQ(TestEnum::VALUE_1, array_data_view[0]);
+ EXPECT_EQ(TestEnum::VALUE_0, array_data_view[1]);
+ EXPECT_EQ(TestEnum::VALUE_0, *(array_data_view.data() + 1));
+
+ TestEnum output;
+ ASSERT_TRUE(array_data_view.Read(0, &output));
+ EXPECT_EQ(TestEnum::VALUE_1, output);
+}
+
+TEST_F(DataViewTest, InterfaceArray) {
+ TestInterfacePtr ptr;
+ TestInterfaceImpl impl(MakeRequest(&ptr));
+
+ TestStructPtr obj(TestStruct::New());
+ obj->f_interface_array.push_back(std::move(ptr));
+
+ auto data_view_holder = SerializeTestStruct(std::move(obj));
+ auto& data_view = *data_view_holder->data_view;
+
+ ArrayDataView<TestInterfacePtrDataView> array_data_view;
+ data_view.GetFInterfaceArrayDataView(&array_data_view);
+
+ ASSERT_FALSE(array_data_view.is_null());
+ ASSERT_EQ(1u, array_data_view.size());
+
+ TestInterfacePtr ptr2 = array_data_view.Take<TestInterfacePtr>(0);
+ ASSERT_TRUE(ptr2);
+ int32_t result = 0;
+ ASSERT_TRUE(ptr2->Echo(42, &result));
+ EXPECT_EQ(42, result);
+}
+
+TEST_F(DataViewTest, NestedArray) {
+ TestStructPtr obj(TestStruct::New());
+ obj->f_nested_array = {{3, 4}, {2}};
+
+ auto data_view_holder = SerializeTestStruct(std::move(obj));
+ auto& data_view = *data_view_holder->data_view;
+
+ ArrayDataView<ArrayDataView<int32_t>> array_data_view;
+ data_view.GetFNestedArrayDataView(&array_data_view);
+
+ ASSERT_FALSE(array_data_view.is_null());
+ ASSERT_EQ(2u, array_data_view.size());
+
+ ArrayDataView<int32_t> nested_array_data_view;
+ array_data_view.GetDataView(0, &nested_array_data_view);
+ ASSERT_FALSE(nested_array_data_view.is_null());
+ ASSERT_EQ(2u, nested_array_data_view.size());
+ EXPECT_EQ(4, nested_array_data_view[1]);
+
+ std::vector<int32_t> vec;
+ ASSERT_TRUE(array_data_view.Read(1, &vec));
+ ASSERT_EQ(1u, vec.size());
+ EXPECT_EQ(2, vec[0]);
+}
+
+TEST_F(DataViewTest, StructArray) {
+ NestedStructPtr nested_struct(NestedStruct::New());
+ nested_struct->f_int32 = 42;
+
+ TestStructPtr obj(TestStruct::New());
+ obj->f_struct_array.push_back(std::move(nested_struct));
+
+ auto data_view_holder = SerializeTestStruct(std::move(obj));
+ auto& data_view = *data_view_holder->data_view;
+
+ ArrayDataView<NestedStructDataView> array_data_view;
+ data_view.GetFStructArrayDataView(&array_data_view);
+
+ ASSERT_FALSE(array_data_view.is_null());
+ ASSERT_EQ(1u, array_data_view.size());
+
+ NestedStructDataView struct_data_view;
+ array_data_view.GetDataView(0, &struct_data_view);
+ ASSERT_FALSE(struct_data_view.is_null());
+ EXPECT_EQ(42, struct_data_view.f_int32());
+
+ NestedStructPtr nested_struct2;
+ ASSERT_TRUE(array_data_view.Read(0, &nested_struct2));
+ ASSERT_TRUE(nested_struct2);
+ EXPECT_EQ(42, nested_struct2->f_int32);
+}
+
+TEST_F(DataViewTest, Map) {
+ TestStructPtr obj(TestStruct::New());
+ obj->f_map["1"] = 1;
+ obj->f_map["2"] = 2;
+
+ auto data_view_holder = SerializeTestStruct(std::move(obj));
+ auto& data_view = *data_view_holder->data_view;
+
+ MapDataView<StringDataView, int32_t> map_data_view;
+ data_view.GetFMapDataView(&map_data_view);
+
+ ASSERT_FALSE(map_data_view.is_null());
+ ASSERT_EQ(2u, map_data_view.size());
+
+ ASSERT_FALSE(map_data_view.keys().is_null());
+ ASSERT_EQ(2u, map_data_view.keys().size());
+
+ ASSERT_FALSE(map_data_view.values().is_null());
+ ASSERT_EQ(2u, map_data_view.values().size());
+
+ std::vector<std::string> keys;
+ ASSERT_TRUE(map_data_view.ReadKeys(&keys));
+ std::vector<int32_t> values;
+ ASSERT_TRUE(map_data_view.ReadValues(&values));
+
+ std::unordered_map<std::string, int32_t> map;
+ for (size_t i = 0; i < 2; ++i)
+ map[keys[i]] = values[i];
+
+ EXPECT_EQ(1, map["1"]);
+ EXPECT_EQ(2, map["2"]);
+}
+
+TEST_F(DataViewTest, UnionArray) {
+ TestUnionPtr union_ptr(TestUnion::New());
+ union_ptr->set_f_int32(1024);
+
+ TestStructPtr obj(TestStruct::New());
+ obj->f_union_array.push_back(std::move(union_ptr));
+
+ auto data_view_holder = SerializeTestStruct(std::move(obj));
+ auto& data_view = *data_view_holder->data_view;
+
+ ArrayDataView<TestUnionDataView> array_data_view;
+ data_view.GetFUnionArrayDataView(&array_data_view);
+ ASSERT_FALSE(array_data_view.is_null());
+ ASSERT_EQ(1u, array_data_view.size());
+
+ TestUnionDataView union_data_view;
+ array_data_view.GetDataView(0, &union_data_view);
+ ASSERT_FALSE(union_data_view.is_null());
+
+ TestUnionPtr union_ptr2;
+ ASSERT_TRUE(array_data_view.Read(0, &union_ptr2));
+ ASSERT_TRUE(union_ptr2->is_f_int32());
+ EXPECT_EQ(1024, union_ptr2->get_f_int32());
+}
+
+} // namespace data_view
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/e2e_perftest.cc b/mojo/public/cpp/bindings/tests/e2e_perftest.cc
index 66ac6dc..bc69e0f 100644
--- a/mojo/public/cpp/bindings/tests/e2e_perftest.cc
+++ b/mojo/public/cpp/bindings/tests/e2e_perftest.cc
@@ -7,13 +7,14 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/perf_time_logger.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/edk/embedder/embedder.h"
#include "mojo/edk/test/mojo_test_base.h"
-#include "mojo/edk/test/scoped_ipc_support.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/interfaces/bindings/tests/ping_service.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -23,8 +24,7 @@ namespace {
class EchoServiceImpl : public test::EchoService {
public:
- EchoServiceImpl(InterfaceRequest<EchoService> request,
- const base::Closure& quit_closure);
+ explicit EchoServiceImpl(const base::Closure& quit_closure);
~EchoServiceImpl() override;
// |EchoService| methods:
@@ -32,13 +32,11 @@ class EchoServiceImpl : public test::EchoService {
const EchoCallback& callback) override;
private:
- const StrongBinding<EchoService> binding_;
const base::Closure quit_closure_;
};
-EchoServiceImpl::EchoServiceImpl(InterfaceRequest<EchoService> request,
- const base::Closure& quit_closure)
- : binding_(this, std::move(request)), quit_closure_(quit_closure) {}
+EchoServiceImpl::EchoServiceImpl(const base::Closure& quit_closure)
+ : quit_closure_(quit_closure) {}
EchoServiceImpl::~EchoServiceImpl() {
quit_closure_.Run();
@@ -163,7 +161,7 @@ class MojoE2EPerftest : public edk::test::MojoTestBase {
void CreateAndRunService(InterfaceRequest<test::EchoService> request,
const base::Closure& cb) {
- new EchoServiceImpl(std::move(request), cb);
+ MakeStrongBinding(base::MakeUnique<EchoServiceImpl>(cb), std::move(request));
}
DEFINE_TEST_CLIENT_TEST_WITH_PIPE(PingService, MojoE2EPerftest, mp) {
@@ -173,7 +171,7 @@ DEFINE_TEST_CLIENT_TEST_WITH_PIPE(PingService, MojoE2EPerftest, mp) {
InterfaceRequest<test::EchoService> request;
request.Bind(ScopedMessagePipeHandle(MessagePipeHandle(service_mp)));
base::RunLoop run_loop;
- edk::test::GetIoTaskRunner()->PostTask(
+ edk::GetIOTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&CreateAndRunService, base::Passed(&request),
base::Bind(base::IgnoreResult(&base::TaskRunner::PostTask),
@@ -197,7 +195,7 @@ TEST_F(MojoE2EPerftest, MultiProcessEchoIoThread) {
MojoHandle client_mp, service_mp;
CreateMessagePipe(&client_mp, &service_mp);
WriteMessageWithHandles(mp, "hello", &service_mp, 1);
- RunTestOnTaskRunner(edk::test::GetIoTaskRunner(), client_mp,
+ RunTestOnTaskRunner(edk::GetIOTaskRunner().get(), client_mp,
"MultiProcessEchoIoThread");
END_CHILD()
}
diff --git a/mojo/public/cpp/bindings/tests/equals_unittest.cc b/mojo/public/cpp/bindings/tests/equals_unittest.cc
index 376c2bd..6483baf 100644
--- a/mojo/public/cpp/bindings/tests/equals_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/equals_unittest.cc
@@ -14,12 +14,7 @@ namespace test {
namespace {
RectPtr CreateRect() {
- RectPtr r = Rect::New();
- r->x = 1;
- r->y = 2;
- r->width = 3;
- r->height = 4;
- return r;
+ return Rect::New(1, 2, 3, 4);
}
using EqualsTest = testing::Test;
@@ -48,9 +43,7 @@ TEST_F(EqualsTest, Struct) {
}
TEST_F(EqualsTest, StructNested) {
- RectPairPtr p1(RectPair::New());
- p1->first = CreateRect();
- p1->second = CreateRect();
+ RectPairPtr p1(RectPair::New(CreateRect(), CreateRect()));
RectPairPtr p2(p1.Clone());
EXPECT_TRUE(p1.Equals(p2));
p2->second->width = 0;
@@ -60,10 +53,9 @@ TEST_F(EqualsTest, StructNested) {
}
TEST_F(EqualsTest, Array) {
- NamedRegionPtr n1(NamedRegion::New());
- n1->name.emplace("n1");
- n1->rects.emplace();
- n1->rects->push_back(CreateRect());
+ std::vector<RectPtr> rects;
+ rects.push_back(CreateRect());
+ NamedRegionPtr n1(NamedRegion::New(std::string("n1"), std::move(rects)));
NamedRegionPtr n2(n1.Clone());
EXPECT_TRUE(n1.Equals(n2));
@@ -84,37 +76,6 @@ TEST_F(EqualsTest, Array) {
EXPECT_TRUE(n1.Equals(n2));
}
-TEST_F(EqualsTest, Map) {
- auto n1(NamedRegion::New());
- n1->name.emplace("foo");
- n1->rects.emplace();
- n1->rects->push_back(CreateRect());
-
- Map<std::string, NamedRegionPtr> m1;
- m1.insert("foo", std::move(n1));
-
- decltype(m1) m2;
- EXPECT_FALSE(m1.Equals(m2));
-
- m2.insert("bar", m1.at("foo").Clone());
- EXPECT_FALSE(m1.Equals(m2));
-
- m2 = m1.Clone();
- m2.at("foo")->name.emplace("monkey");
- EXPECT_FALSE(m1.Equals(m2));
-
- m2 = m1.Clone();
- m2.at("foo")->rects->push_back(Rect::New());
- EXPECT_FALSE(m1.Equals(m2));
-
- m2.at("foo")->rects->resize(1);
- (*m2.at("foo")->rects)[0]->width = 1;
- EXPECT_FALSE(m1.Equals(m2));
-
- m2 = m1.Clone();
- EXPECT_TRUE(m1.Equals(m2));
-}
-
TEST_F(EqualsTest, InterfacePtr) {
base::MessageLoop message_loop;
@@ -124,13 +85,13 @@ TEST_F(EqualsTest, InterfacePtr) {
EXPECT_TRUE(inf1.Equals(inf1));
EXPECT_TRUE(inf1.Equals(inf2));
- auto inf1_request = GetProxy(&inf1);
+ auto inf1_request = MakeRequest(&inf1);
ALLOW_UNUSED_LOCAL(inf1_request);
EXPECT_TRUE(inf1.Equals(inf1));
EXPECT_FALSE(inf1.Equals(inf2));
- auto inf2_request = GetProxy(&inf2);
+ auto inf2_request = MakeRequest(&inf2);
ALLOW_UNUSED_LOCAL(inf2_request);
EXPECT_FALSE(inf1.Equals(inf2));
@@ -146,13 +107,13 @@ TEST_F(EqualsTest, InterfaceRequest) {
EXPECT_TRUE(req1.Equals(req2));
SomeInterfacePtr inf1;
- req1 = GetProxy(&inf1);
+ req1 = MakeRequest(&inf1);
EXPECT_TRUE(req1.Equals(req1));
EXPECT_FALSE(req1.Equals(req2));
SomeInterfacePtr inf2;
- req2 = GetProxy(&inf2);
+ req2 = MakeRequest(&inf2);
EXPECT_FALSE(req1.Equals(req2));
}
diff --git a/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc b/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc
index 0bd9b28..6797fe4 100644
--- a/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/handle_passing_unittest.cc
@@ -5,6 +5,7 @@
#include <stdint.h>
#include <utility>
+#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "mojo/public/cpp/bindings/binding.h"
@@ -56,8 +57,8 @@ int ImportedInterfaceImpl::do_something_count_ = 0;
class SampleNamedObjectImpl : public sample::NamedObject {
public:
- explicit SampleNamedObjectImpl(InterfaceRequest<sample::NamedObject> request)
- : binding_(this, std::move(request)) {}
+ SampleNamedObjectImpl() {}
+
void SetName(const std::string& name) override { name_ = name; }
void GetName(const GetNameCallback& callback) override {
@@ -66,7 +67,6 @@ class SampleNamedObjectImpl : public sample::NamedObject {
private:
std::string name_;
- StrongBinding<sample::NamedObject> binding_;
};
class SampleFactoryImpl : public sample::Factory {
@@ -95,9 +95,7 @@ class SampleFactoryImpl : public sample::Factory {
EXPECT_TRUE(WriteTextMessage(pipe1_.get(), text2));
}
- sample::ResponsePtr response(sample::Response::New());
- response->x = 2;
- response->pipe = std::move(pipe0);
+ sample::ResponsePtr response(sample::Response::New(2, std::move(pipe0)));
callback.Run(std::move(response), text1);
if (request->obj)
@@ -115,7 +113,7 @@ class SampleFactoryImpl : public sample::Factory {
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(pipe.get().value(), MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &state));
- ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, state.satisfied_signals);
+ ASSERT_TRUE(state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
ASSERT_EQ(MOJO_RESULT_OK,
ReadDataRaw(
pipe.get(), nullptr, &data_size, MOJO_READ_DATA_FLAG_QUERY));
@@ -133,7 +131,8 @@ class SampleFactoryImpl : public sample::Factory {
void CreateNamedObject(
InterfaceRequest<sample::NamedObject> object_request) override {
EXPECT_TRUE(object_request.is_pending());
- new SampleNamedObjectImpl(std::move(object_request));
+ MakeStrongBinding(base::MakeUnique<SampleNamedObjectImpl>(),
+ std::move(object_request));
}
// These aren't called or implemented, but exist here to test that the
@@ -200,7 +199,7 @@ void DoStuff2(bool* got_response,
TEST_F(HandlePassingTest, Basic) {
sample::FactoryPtr factory;
- SampleFactoryImpl factory_impl(GetProxy(&factory));
+ SampleFactoryImpl factory_impl(MakeRequest(&factory));
MessagePipe pipe0;
EXPECT_TRUE(WriteTextMessage(pipe0.handle1.get(), kText1));
@@ -210,13 +209,11 @@ TEST_F(HandlePassingTest, Basic) {
imported::ImportedInterfacePtr imported;
base::RunLoop run_loop;
- ImportedInterfaceImpl imported_impl(GetProxy(&imported),
+ ImportedInterfaceImpl imported_impl(MakeRequest(&imported),
run_loop.QuitClosure());
- sample::RequestPtr request(sample::Request::New());
- request->x = 1;
- request->pipe = std::move(pipe1.handle0);
- request->obj = std::move(imported);
+ sample::RequestPtr request(sample::Request::New(
+ 1, std::move(pipe1.handle0), base::nullopt, std::move(imported)));
bool got_response = false;
std::string got_text_reply;
base::RunLoop run_loop2;
@@ -237,10 +234,11 @@ TEST_F(HandlePassingTest, Basic) {
TEST_F(HandlePassingTest, PassInvalid) {
sample::FactoryPtr factory;
- SampleFactoryImpl factory_impl(GetProxy(&factory));
+ SampleFactoryImpl factory_impl(MakeRequest(&factory));
- sample::RequestPtr request(sample::Request::New());
- request->x = 1;
+ sample::RequestPtr request(
+ sample::Request::New(1, ScopedMessagePipeHandle(), base::nullopt,
+ imported::ImportedInterfacePtr()));
bool got_response = false;
std::string got_text_reply;
base::RunLoop run_loop;
@@ -258,7 +256,7 @@ TEST_F(HandlePassingTest, PassInvalid) {
// Verifies DataPipeConsumer can be passed and read from.
TEST_F(HandlePassingTest, DataPipe) {
sample::FactoryPtr factory;
- SampleFactoryImpl factory_impl(GetProxy(&factory));
+ SampleFactoryImpl factory_impl(MakeRequest(&factory));
// Writes a string to a data pipe and passes the data pipe (consumer) to the
// factory.
@@ -296,7 +294,7 @@ TEST_F(HandlePassingTest, DataPipe) {
TEST_F(HandlePassingTest, PipesAreClosed) {
sample::FactoryPtr factory;
- SampleFactoryImpl factory_impl(GetProxy(&factory));
+ SampleFactoryImpl factory_impl(MakeRequest(&factory));
MessagePipe extra_pipe;
@@ -322,12 +320,12 @@ TEST_F(HandlePassingTest, PipesAreClosed) {
TEST_F(HandlePassingTest, CreateNamedObject) {
sample::FactoryPtr factory;
- SampleFactoryImpl factory_impl(GetProxy(&factory));
+ SampleFactoryImpl factory_impl(MakeRequest(&factory));
sample::NamedObjectPtr object1;
EXPECT_FALSE(object1);
- InterfaceRequest<sample::NamedObject> object1_request = GetProxy(&object1);
+ InterfaceRequest<sample::NamedObject> object1_request(&object1);
EXPECT_TRUE(object1_request.is_pending());
factory->CreateNamedObject(std::move(object1_request));
EXPECT_FALSE(object1_request.is_pending()); // We've passed the request.
@@ -336,7 +334,7 @@ TEST_F(HandlePassingTest, CreateNamedObject) {
object1->SetName("object1");
sample::NamedObjectPtr object2;
- factory->CreateNamedObject(GetProxy(&object2));
+ factory->CreateNamedObject(MakeRequest(&object2));
object2->SetName("object2");
base::RunLoop run_loop, run_loop2;
diff --git a/mojo/public/cpp/bindings/tests/hash_unittest.cc b/mojo/public/cpp/bindings/tests/hash_unittest.cc
new file mode 100644
index 0000000..9ce1f5b
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/hash_unittest.cc
@@ -0,0 +1,35 @@
+// Copyright 2016 The Chromium 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/hash_util.h"
+
+#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+using HashTest = testing::Test;
+
+TEST_F(HashTest, NestedStruct) {
+ // Just check that this template instantiation compiles.
+ ASSERT_EQ(
+ ::mojo::internal::Hash(::mojo::internal::kHashSeed,
+ SimpleNestedStruct::New(ContainsOther::New(1))),
+ ::mojo::internal::Hash(::mojo::internal::kHashSeed,
+ SimpleNestedStruct::New(ContainsOther::New(1))));
+}
+
+TEST_F(HashTest, UnmappedNativeStruct) {
+ // Just check that this template instantiation compiles.
+ ASSERT_EQ(::mojo::internal::Hash(::mojo::internal::kHashSeed,
+ UnmappedNativeStruct::New()),
+ ::mojo::internal::Hash(::mojo::internal::kHashSeed,
+ UnmappedNativeStruct::New()));
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc b/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc
index aff6251..431a844 100644
--- a/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/interface_ptr_unittest.cc
@@ -7,10 +7,14 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/bindings/thread_safe_interface_ptr.h"
#include "mojo/public/interfaces/bindings/tests/math_calculator.mojom.h"
#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h"
#include "mojo/public/interfaces/bindings/tests/sample_service.mojom.h"
@@ -29,10 +33,6 @@ class MathCalculatorImpl : public math::Calculator {
: total_(0.0), binding_(this, std::move(request)) {}
~MathCalculatorImpl() override {}
- void CloseMessagePipe() { binding_.Close(); }
-
- void WaitForIncomingMethodCall() { binding_.WaitForIncomingMethodCall(); }
-
void Clear(const CalcCallback& callback) override {
total_ = 0.0;
callback.Run(total_);
@@ -48,6 +48,8 @@ class MathCalculatorImpl : public math::Calculator {
callback.Run(total_);
}
+ Binding<math::Calculator>* binding() { return &binding_; }
+
private:
double total_;
Binding<math::Calculator> binding_;
@@ -78,6 +80,8 @@ class MathCalculatorUI {
double GetOutput() const { return output_; }
+ math::CalculatorPtr& GetInterfacePtr() { return calculator_; }
+
private:
void Output(const base::Closure& closure, double output) {
output_ = output;
@@ -218,13 +222,13 @@ void ExpectValueAndRunClosure(uint32_t expected_value,
TEST_F(InterfacePtrTest, IsBound) {
math::CalculatorPtr calc;
EXPECT_FALSE(calc.is_bound());
- MathCalculatorImpl calc_impl(GetProxy(&calc));
+ MathCalculatorImpl calc_impl(MakeRequest(&calc));
EXPECT_TRUE(calc.is_bound());
}
TEST_F(InterfacePtrTest, EndToEnd) {
math::CalculatorPtr calc;
- MathCalculatorImpl calc_impl(GetProxy(&calc));
+ MathCalculatorImpl calc_impl(MakeRequest(&calc));
// Suppose this is instantiated in a process that has pipe1_.
MathCalculatorUI calculator_ui(std::move(calc));
@@ -240,7 +244,7 @@ TEST_F(InterfacePtrTest, EndToEnd) {
TEST_F(InterfacePtrTest, EndToEnd_Synchronous) {
math::CalculatorPtr calc;
- MathCalculatorImpl calc_impl(GetProxy(&calc));
+ MathCalculatorImpl calc_impl(MakeRequest(&calc));
// Suppose this is instantiated in a process that has pipe1_.
MathCalculatorUI calculator_ui(std::move(calc));
@@ -250,14 +254,14 @@ TEST_F(InterfacePtrTest, EndToEnd_Synchronous) {
base::RunLoop run_loop;
calculator_ui.Add(2.0, run_loop.QuitClosure());
EXPECT_EQ(0.0, calculator_ui.GetOutput());
- calc_impl.WaitForIncomingMethodCall();
+ calc_impl.binding()->WaitForIncomingMethodCall();
run_loop.Run();
EXPECT_EQ(2.0, calculator_ui.GetOutput());
base::RunLoop run_loop2;
calculator_ui.Multiply(5.0, run_loop2.QuitClosure());
EXPECT_EQ(2.0, calculator_ui.GetOutput());
- calc_impl.WaitForIncomingMethodCall();
+ calc_impl.binding()->WaitForIncomingMethodCall();
run_loop2.Run();
EXPECT_EQ(10.0, calculator_ui.GetOutput());
}
@@ -265,7 +269,7 @@ TEST_F(InterfacePtrTest, EndToEnd_Synchronous) {
TEST_F(InterfacePtrTest, Movable) {
math::CalculatorPtr a;
math::CalculatorPtr b;
- MathCalculatorImpl calc_impl(GetProxy(&b));
+ MathCalculatorImpl calc_impl(MakeRequest(&b));
EXPECT_TRUE(!a);
EXPECT_FALSE(!b);
@@ -312,7 +316,7 @@ TEST_F(InterfacePtrTest, BindInvalidHandle) {
TEST_F(InterfacePtrTest, EncounteredError) {
math::CalculatorPtr proxy;
- MathCalculatorImpl calc_impl(GetProxy(&proxy));
+ MathCalculatorImpl calc_impl(MakeRequest(&proxy));
MathCalculatorUI calculator_ui(std::move(proxy));
@@ -326,7 +330,7 @@ TEST_F(InterfacePtrTest, EncounteredError) {
EXPECT_FALSE(calculator_ui.encountered_error());
// Close the server.
- calc_impl.CloseMessagePipe();
+ calc_impl.binding()->Close();
// The state change isn't picked up locally yet.
base::RunLoop run_loop2;
@@ -341,7 +345,7 @@ TEST_F(InterfacePtrTest, EncounteredError) {
TEST_F(InterfacePtrTest, EncounteredErrorCallback) {
math::CalculatorPtr proxy;
- MathCalculatorImpl calc_impl(GetProxy(&proxy));
+ MathCalculatorImpl calc_impl(MakeRequest(&proxy));
bool encountered_error = false;
base::RunLoop run_loop;
@@ -361,7 +365,7 @@ TEST_F(InterfacePtrTest, EncounteredErrorCallback) {
EXPECT_FALSE(calculator_ui.encountered_error());
// Close the server.
- calc_impl.CloseMessagePipe();
+ calc_impl.binding()->Close();
// The state change isn't picked up locally yet.
EXPECT_FALSE(calculator_ui.encountered_error());
@@ -378,7 +382,7 @@ TEST_F(InterfacePtrTest, EncounteredErrorCallback) {
TEST_F(InterfacePtrTest, DestroyInterfacePtrOnMethodResponse) {
math::CalculatorPtr proxy;
- MathCalculatorImpl calc_impl(GetProxy(&proxy));
+ MathCalculatorImpl calc_impl(MakeRequest(&proxy));
EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
@@ -393,7 +397,7 @@ TEST_F(InterfacePtrTest, DestroyInterfacePtrOnMethodResponse) {
TEST_F(InterfacePtrTest, NestedDestroyInterfacePtrOnMethodResponse) {
math::CalculatorPtr proxy;
- MathCalculatorImpl calc_impl(GetProxy(&proxy));
+ MathCalculatorImpl calc_impl(MakeRequest(&proxy));
EXPECT_EQ(0, SelfDestructingMathCalculatorUI::num_instances());
@@ -408,7 +412,7 @@ TEST_F(InterfacePtrTest, NestedDestroyInterfacePtrOnMethodResponse) {
TEST_F(InterfacePtrTest, ReentrantWaitForIncomingMethodCall) {
sample::ServicePtr proxy;
- ReentrantServiceImpl impl(GetProxy(&proxy));
+ ReentrantServiceImpl impl(MakeRequest(&proxy));
base::RunLoop run_loop, run_loop2;
proxy->Frobinate(nullptr, sample::Service::BazOptions::REGULAR, nullptr,
@@ -427,7 +431,7 @@ TEST_F(InterfacePtrTest, ReentrantWaitForIncomingMethodCall) {
TEST_F(InterfacePtrTest, QueryVersion) {
IntegerAccessorImpl impl;
sample::IntegerAccessorPtr ptr;
- Binding<sample::IntegerAccessor> binding(&impl, GetProxy(&ptr));
+ Binding<sample::IntegerAccessor> binding(&impl, MakeRequest(&ptr));
EXPECT_EQ(0u, ptr.version());
@@ -442,7 +446,7 @@ TEST_F(InterfacePtrTest, QueryVersion) {
TEST_F(InterfacePtrTest, RequireVersion) {
IntegerAccessorImpl impl;
sample::IntegerAccessorPtr ptr;
- Binding<sample::IntegerAccessor> binding(&impl, GetProxy(&ptr));
+ Binding<sample::IntegerAccessor> binding(&impl, MakeRequest(&ptr));
EXPECT_EQ(0u, ptr.version());
@@ -479,17 +483,7 @@ TEST_F(InterfacePtrTest, RequireVersion) {
class StrongMathCalculatorImpl : public math::Calculator {
public:
- StrongMathCalculatorImpl(ScopedMessagePipeHandle handle,
- bool* error_received,
- bool* destroyed,
- const base::Closure& closure)
- : error_received_(error_received),
- destroyed_(destroyed),
- closure_(closure),
- binding_(this, std::move(handle)) {
- binding_.set_connection_error_handler(
- base::Bind(&SetFlagAndRunClosure, error_received_, closure_));
- }
+ StrongMathCalculatorImpl(bool* destroyed) : destroyed_(destroyed) {}
~StrongMathCalculatorImpl() override { *destroyed_ = true; }
// math::Calculator implementation.
@@ -507,11 +501,7 @@ class StrongMathCalculatorImpl : public math::Calculator {
private:
double total_ = 0.0;
- bool* error_received_;
bool* destroyed_;
- base::Closure closure_;
-
- StrongBinding<math::Calculator> binding_;
};
TEST(StrongConnectorTest, Math) {
@@ -519,13 +509,14 @@ TEST(StrongConnectorTest, Math) {
bool error_received = false;
bool destroyed = false;
- MessagePipe pipe;
+ math::CalculatorPtr calc;
base::RunLoop run_loop;
- new StrongMathCalculatorImpl(std::move(pipe.handle0), &error_received,
- &destroyed, run_loop.QuitClosure());
- math::CalculatorPtr calc;
- calc.Bind(InterfacePtrInfo<math::Calculator>(std::move(pipe.handle1), 0u));
+ auto binding =
+ MakeStrongBinding(base::MakeUnique<StrongMathCalculatorImpl>(&destroyed),
+ MakeRequest(&calc));
+ binding->set_connection_error_handler(base::Bind(
+ &SetFlagAndRunClosure, &error_received, run_loop.QuitClosure()));
{
// Suppose this is instantiated in a process that has the other end of the
@@ -626,10 +617,8 @@ TEST(WeakConnectorTest, Math) {
class CImpl : public C {
public:
- CImpl(bool* d_called, InterfaceRequest<C> request,
- const base::Closure& closure)
- : d_called_(d_called), binding_(this, std::move(request)),
- closure_(closure) {}
+ CImpl(bool* d_called, const base::Closure& closure)
+ : d_called_(d_called), closure_(closure) {}
~CImpl() override {}
private:
@@ -639,25 +628,22 @@ class CImpl : public C {
}
bool* d_called_;
- StrongBinding<C> binding_;
base::Closure closure_;
};
class BImpl : public B {
public:
- BImpl(bool* d_called, InterfaceRequest<B> request,
- const base::Closure& closure)
- : d_called_(d_called), binding_(this, std::move(request)),
- closure_(closure) {}
+ BImpl(bool* d_called, const base::Closure& closure)
+ : d_called_(d_called), closure_(closure) {}
~BImpl() override {}
private:
void GetC(InterfaceRequest<C> c) override {
- new CImpl(d_called_, std::move(c), closure_);
+ MakeStrongBinding(base::MakeUnique<CImpl>(d_called_, closure_),
+ std::move(c));
}
bool* d_called_;
- StrongBinding<B> binding_;
base::Closure closure_;
};
@@ -672,7 +658,8 @@ class AImpl : public A {
private:
void GetB(InterfaceRequest<B> b) override {
- new BImpl(&d_called_, std::move(b), closure_);
+ MakeStrongBinding(base::MakeUnique<BImpl>(&d_called_, closure_),
+ std::move(b));
}
bool d_called_;
@@ -683,15 +670,15 @@ class AImpl : public A {
TEST_F(InterfacePtrTest, Scoping) {
APtr a;
base::RunLoop run_loop;
- AImpl a_impl(GetProxy(&a), run_loop.QuitClosure());
+ AImpl a_impl(MakeRequest(&a), run_loop.QuitClosure());
EXPECT_FALSE(a_impl.d_called());
{
BPtr b;
- a->GetB(GetProxy(&b));
+ a->GetB(MakeRequest(&b));
CPtr c;
- b->GetC(GetProxy(&c));
+ b->GetC(MakeRequest(&c));
c->D();
}
@@ -718,11 +705,11 @@ class PingTestImpl : public sample::PingTest {
// Tests that FuseProxy does what it's supposed to do.
TEST_F(InterfacePtrTest, Fusion) {
sample::PingTestPtr proxy;
- PingTestImpl impl(GetProxy(&proxy));
+ PingTestImpl impl(MakeRequest(&proxy));
// Create another PingTest pipe.
sample::PingTestPtr ptr;
- sample::PingTestRequest request = GetProxy(&ptr);
+ sample::PingTestRequest request(&ptr);
// Fuse the new pipe to the one hanging off |impl|.
EXPECT_TRUE(FuseInterface(std::move(request), proxy.PassInterface()));
@@ -735,6 +722,216 @@ TEST_F(InterfacePtrTest, Fusion) {
EXPECT_TRUE(called);
}
+void Fail() {
+ FAIL() << "Unexpected connection error";
+}
+
+TEST_F(InterfacePtrTest, FlushForTesting) {
+ math::CalculatorPtr calc;
+ MathCalculatorImpl calc_impl(MakeRequest(&calc));
+ calc.set_connection_error_handler(base::Bind(&Fail));
+
+ MathCalculatorUI calculator_ui(std::move(calc));
+
+ calculator_ui.Add(2.0, base::Bind(&base::DoNothing));
+ calculator_ui.GetInterfacePtr().FlushForTesting();
+ EXPECT_EQ(2.0, calculator_ui.GetOutput());
+
+ calculator_ui.Multiply(5.0, base::Bind(&base::DoNothing));
+ calculator_ui.GetInterfacePtr().FlushForTesting();
+
+ EXPECT_EQ(10.0, calculator_ui.GetOutput());
+}
+
+void SetBool(bool* value) {
+ *value = true;
+}
+
+TEST_F(InterfacePtrTest, FlushForTestingWithClosedPeer) {
+ math::CalculatorPtr calc;
+ MakeRequest(&calc);
+ bool called = false;
+ calc.set_connection_error_handler(base::Bind(&SetBool, &called));
+ calc.FlushForTesting();
+ EXPECT_TRUE(called);
+ calc.FlushForTesting();
+}
+
+TEST_F(InterfacePtrTest, ConnectionErrorWithReason) {
+ math::CalculatorPtr calc;
+ MathCalculatorImpl calc_impl(MakeRequest(&calc));
+
+ base::RunLoop run_loop;
+ calc.set_connection_error_with_reason_handler(base::Bind(
+ [](const base::Closure& quit_closure, uint32_t custom_reason,
+ const std::string& description) {
+ EXPECT_EQ(42u, custom_reason);
+ EXPECT_EQ("hey", description);
+ quit_closure.Run();
+ },
+ run_loop.QuitClosure()));
+
+ calc_impl.binding()->CloseWithReason(42u, "hey");
+
+ run_loop.Run();
+}
+
+TEST_F(InterfacePtrTest, InterfaceRequestResetWithReason) {
+ math::CalculatorPtr calc;
+ auto request = MakeRequest(&calc);
+
+ base::RunLoop run_loop;
+ calc.set_connection_error_with_reason_handler(base::Bind(
+ [](const base::Closure& quit_closure, uint32_t custom_reason,
+ const std::string& description) {
+ EXPECT_EQ(88u, custom_reason);
+ EXPECT_EQ("greetings", description);
+ quit_closure.Run();
+ },
+ run_loop.QuitClosure()));
+
+ request.ResetWithReason(88u, "greetings");
+
+ run_loop.Run();
+}
+
+TEST_F(InterfacePtrTest, CallbackIsPassedInterfacePtr) {
+ sample::PingTestPtr ptr;
+ sample::PingTestRequest request(&ptr);
+
+ base::RunLoop run_loop;
+
+ // Make a call with the proxy's lifetime bound to the response callback.
+ sample::PingTest* raw_proxy = ptr.get();
+ ptr.set_connection_error_handler(run_loop.QuitClosure());
+ raw_proxy->Ping(
+ base::Bind([](sample::PingTestPtr ptr) {}, base::Passed(&ptr)));
+
+ // Trigger an error on |ptr|. This will ultimately lead to the proxy's
+ // response callbacks being destroyed, which will in turn lead to the proxy
+ // being destroyed. This should not crash.
+ request.PassMessagePipe();
+ run_loop.Run();
+}
+
+TEST_F(InterfacePtrTest, ConnectionErrorHandlerOwnsInterfacePtr) {
+ sample::PingTestPtr* ptr = new sample::PingTestPtr;
+ sample::PingTestRequest request(ptr);
+
+ base::RunLoop run_loop;
+
+ // Make a call with |ptr|'s lifetime bound to the connection error handler
+ // callback.
+ ptr->set_connection_error_handler(base::Bind(
+ [](const base::Closure& quit, sample::PingTestPtr* ptr) {
+ ptr->reset();
+ quit.Run();
+ },
+ run_loop.QuitClosure(), base::Owned(ptr)));
+
+ // Trigger an error on |ptr|. In the error handler |ptr| is reset. This
+ // shouldn't immediately destroy the callback (and |ptr| that it owns), before
+ // the callback is completed.
+ request.PassMessagePipe();
+ run_loop.Run();
+}
+
+TEST_F(InterfacePtrTest, ThreadSafeInterfacePointer) {
+ math::CalculatorPtr ptr;
+ MathCalculatorImpl calc_impl(MakeRequest(&ptr));
+ scoped_refptr<math::ThreadSafeCalculatorPtr> thread_safe_ptr =
+ math::ThreadSafeCalculatorPtr::Create(std::move(ptr));
+
+ base::RunLoop run_loop;
+
+ // Create and start the thread from where we'll call the interface pointer.
+ base::Thread other_thread("service test thread");
+ other_thread.Start();
+
+ auto run_method = base::Bind(
+ [](const scoped_refptr<base::TaskRunner>& main_task_runner,
+ const base::Closure& quit_closure,
+ const scoped_refptr<math::ThreadSafeCalculatorPtr>& thread_safe_ptr) {
+ auto calc_callback = base::Bind(
+ [](const scoped_refptr<base::TaskRunner>& main_task_runner,
+ const base::Closure& quit_closure,
+ base::PlatformThreadId thread_id,
+ double result) {
+ EXPECT_EQ(123, result);
+ // Validate the callback is invoked on the calling thread.
+ EXPECT_EQ(thread_id, base::PlatformThread::CurrentId());
+ // Notify the run_loop to quit.
+ main_task_runner->PostTask(FROM_HERE, quit_closure);
+ });
+ (*thread_safe_ptr)->Add(
+ 123, base::Bind(calc_callback, main_task_runner, quit_closure,
+ base::PlatformThread::CurrentId()));
+ },
+ base::SequencedTaskRunnerHandle::Get(), run_loop.QuitClosure(),
+ thread_safe_ptr);
+ other_thread.message_loop()->task_runner()->PostTask(FROM_HERE, run_method);
+
+ // Block until the method callback is called on the background thread.
+ run_loop.Run();
+}
+
+TEST_F(InterfacePtrTest, ThreadSafeInterfacePointerWithTaskRunner) {
+ // Create and start the thread from where we'll bind the interface pointer.
+ base::Thread other_thread("service test thread");
+ other_thread.Start();
+ const scoped_refptr<base::SingleThreadTaskRunner>& other_thread_task_runner =
+ other_thread.message_loop()->task_runner();
+
+ math::CalculatorPtr ptr;
+ math::CalculatorRequest request(&ptr);
+
+ // Create a ThreadSafeInterfacePtr that we'll bind from a different thread.
+ scoped_refptr<math::ThreadSafeCalculatorPtr> thread_safe_ptr =
+ math::ThreadSafeCalculatorPtr::Create(ptr.PassInterface(),
+ other_thread_task_runner);
+ ASSERT_TRUE(thread_safe_ptr);
+
+ MathCalculatorImpl* math_calc_impl = nullptr;
+ {
+ base::RunLoop run_loop;
+ auto run_method = base::Bind(
+ [](const scoped_refptr<base::TaskRunner>& main_task_runner,
+ const base::Closure& quit_closure,
+ const scoped_refptr<math::ThreadSafeCalculatorPtr>& thread_safe_ptr,
+ math::CalculatorRequest request,
+ MathCalculatorImpl** math_calc_impl) {
+ math::CalculatorPtr ptr;
+ // In real life, the implementation would have a legitimate owner.
+ *math_calc_impl = new MathCalculatorImpl(std::move(request));
+ main_task_runner->PostTask(FROM_HERE, quit_closure);
+ },
+ base::SequencedTaskRunnerHandle::Get(), run_loop.QuitClosure(),
+ thread_safe_ptr, base::Passed(&request), &math_calc_impl);
+ other_thread.message_loop()->task_runner()->PostTask(FROM_HERE, run_method);
+ run_loop.Run();
+ }
+
+ {
+ // The interface ptr is bound, we can call methods on it.
+ auto calc_callback =
+ base::Bind([](const base::Closure& quit_closure, double result) {
+ EXPECT_EQ(123, result);
+ quit_closure.Run();
+ });
+ base::RunLoop run_loop;
+ (*thread_safe_ptr)
+ ->Add(123, base::Bind(calc_callback, run_loop.QuitClosure()));
+ // Block until the method callback is called.
+ run_loop.Run();
+ }
+
+ other_thread_task_runner->DeleteSoon(FROM_HERE, math_calc_impl);
+
+ // Reset the pointer now so the InterfacePtr associated resources can be
+ // deleted before the background thread's message loop is invalidated.
+ thread_safe_ptr = nullptr;
+}
+
} // namespace
} // namespace test
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/map_common_test.h b/mojo/public/cpp/bindings/tests/map_common_test.h
deleted file mode 100644
index c8654ce..0000000
--- a/mojo/public/cpp/bindings/tests/map_common_test.h
+++ /dev/null
@@ -1,230 +0,0 @@
-// Copyright 2016 The Chromium 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/array.h"
-#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
-#include "mojo/public/cpp/bindings/lib/serialization.h"
-#include "mojo/public/cpp/bindings/lib/validate_params.h"
-#include "mojo/public/cpp/bindings/map.h"
-#include "mojo/public/cpp/bindings/string.h"
-#include "mojo/public/cpp/bindings/tests/container_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace WTF {
-class String;
-}
-
-namespace mojo {
-
-template <typename T>
-class WTFArray;
-
-template <typename K, typename V>
-class WTFMap;
-
-namespace test {
-namespace {
-
-struct StringIntData {
- const char* string_data;
- int int_data;
-} kStringIntData[] = {
- {"one", 1},
- {"two", 2},
- {"three", 3},
- {"four", 4},
-};
-
-const size_t kStringIntDataSize = 4;
-
-} // namespace
-
-template <template <typename...> class MapType>
-struct TypeTraits;
-
-template <>
-struct TypeTraits<Map> {
- using StringType = mojo::String;
- template <typename T>
- using ArrayType = Array<T>;
-};
-
-template <>
-struct TypeTraits<WTFMap> {
- using StringType = WTF::String;
- template <typename T>
- using ArrayType = WTFArray<T>;
-};
-
-// Common tests for both mojo::Map and mojo::WTFMap.
-template <template <typename...> class MapType>
-class MapCommonTest {
- public:
- using StringType = typename TypeTraits<MapType>::StringType;
- template <typename T>
- using ArrayType = typename TypeTraits<MapType>::template ArrayType<T>;
-
- // Tests null and empty maps.
- static void NullAndEmpty() {
- MapType<char, char> map0;
- EXPECT_TRUE(map0.empty());
- EXPECT_FALSE(map0.is_null());
- map0 = nullptr;
- EXPECT_TRUE(map0.is_null());
- EXPECT_FALSE(map0.empty());
-
- MapType<char, char> map1(nullptr);
- EXPECT_TRUE(map1.is_null());
- EXPECT_FALSE(map1.empty());
- map1.SetToEmpty();
- EXPECT_TRUE(map1.empty());
- EXPECT_FALSE(map1.is_null());
- }
-
- // Tests that basic Map operations work.
- static void InsertWorks() {
- MapType<StringType, int> map;
- for (size_t i = 0; i < kStringIntDataSize; ++i)
- map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data);
-
- for (size_t i = 0; i < kStringIntDataSize; ++i) {
- EXPECT_EQ(kStringIntData[i].int_data,
- map.at(kStringIntData[i].string_data));
- }
- }
-
- static void TestIndexOperator() {
- MapType<StringType, int> map;
- for (size_t i = 0; i < kStringIntDataSize; ++i)
- map[kStringIntData[i].string_data] = kStringIntData[i].int_data;
-
- for (size_t i = 0; i < kStringIntDataSize; ++i) {
- EXPECT_EQ(kStringIntData[i].int_data,
- map.at(kStringIntData[i].string_data));
- }
- }
-
- static void TestIndexOperatorAsRValue() {
- MapType<StringType, int> map;
- for (size_t i = 0; i < kStringIntDataSize; ++i)
- map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data);
-
- for (size_t i = 0; i < kStringIntDataSize; ++i) {
- EXPECT_EQ(kStringIntData[i].int_data, map[kStringIntData[i].string_data]);
- }
- }
-
- static void TestIndexOperatorMoveOnly() {
- ASSERT_EQ(0u, MoveOnlyType::num_instances());
- MapType<StringType, ArrayType<int32_t>> map;
-
- for (size_t i = 0; i < kStringIntDataSize; ++i) {
- const char* key = kStringIntData[i].string_data;
- ArrayType<int32_t> array(1);
- array[0] = kStringIntData[i].int_data;
- map[key] = std::move(array);
- EXPECT_TRUE(map);
- }
-
- // We now read back that data, to test the behavior of operator[].
- for (size_t i = 0; i < kStringIntDataSize; ++i) {
- auto& value = map[kStringIntData[i].string_data];
- ASSERT_EQ(1u, value.size());
- EXPECT_EQ(kStringIntData[i].int_data, value[0]);
- }
- }
-
- static void MapArrayClone() {
- MapType<StringType, ArrayType<StringType>> m;
- for (size_t i = 0; i < kStringIntDataSize; ++i) {
- ArrayType<StringType> s(1);
- s[0] = StringType(kStringIntData[i].string_data);
- m.insert(kStringIntData[i].string_data, std::move(s));
- }
-
- MapType<StringType, ArrayType<StringType>> m2 = m.Clone();
-
- for (size_t i = 0; i < kStringIntDataSize; ++i) {
- ASSERT_NE(m2.end(), m2.find(kStringIntData[i].string_data));
- ASSERT_EQ(1u, m2[kStringIntData[i].string_data].size());
- EXPECT_EQ(StringType(kStringIntData[i].string_data),
- m2[kStringIntData[i].string_data][0]);
- }
- }
-
- static void ArrayOfMap() {
- {
- using MojomType = Array<Map<int32_t, int8_t>>;
- using UserType = ArrayType<MapType<int32_t, int8_t>>;
-
- UserType array(1);
- array[0].insert(1, 42);
-
- mojo::internal::SerializationContext context;
- size_t size =
- mojo::internal::PrepareToSerialize<MojomType>(array, &context);
- mojo::internal::FixedBufferForTesting buf(size);
- typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
- mojo::internal::ContainerValidateParams validate_params(
- 0, false,
- new mojo::internal::ContainerValidateParams(
- new mojo::internal::ContainerValidateParams(0, false, nullptr),
- new mojo::internal::ContainerValidateParams(0, false, nullptr)));
-
- mojo::internal::Serialize<MojomType>(array, &buf, &data, &validate_params,
- &context);
-
- UserType deserialized_array;
- mojo::internal::Deserialize<MojomType>(data, &deserialized_array,
- &context);
-
- ASSERT_EQ(1u, deserialized_array.size());
- ASSERT_EQ(1u, deserialized_array[0].size());
- ASSERT_EQ(42, deserialized_array[0].at(1));
- }
-
- {
- using MojomType = Array<Map<String, Array<bool>>>;
- using UserType = ArrayType<MapType<StringType, ArrayType<bool>>>;
-
- UserType array(1);
- ArrayType<bool> map_value(2);
- map_value[0] = false;
- map_value[1] = true;
- array[0].insert("hello world", std::move(map_value));
-
- mojo::internal::SerializationContext context;
- size_t size =
- mojo::internal::PrepareToSerialize<MojomType>(array, &context);
- mojo::internal::FixedBufferForTesting buf(size);
- typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
- mojo::internal::ContainerValidateParams validate_params(
- 0, false,
- new mojo::internal::ContainerValidateParams(
- new mojo::internal::ContainerValidateParams(
- 0, false, new mojo::internal::ContainerValidateParams(
- 0, false, nullptr)),
- new mojo::internal::ContainerValidateParams(
- 0, false, new mojo::internal::ContainerValidateParams(
- 0, false, nullptr))));
- mojo::internal::Serialize<MojomType>(array, &buf, &data, &validate_params,
- &context);
-
- UserType deserialized_array;
- mojo::internal::Deserialize<MojomType>(data, &deserialized_array,
- &context);
-
- ASSERT_EQ(1u, deserialized_array.size());
- ASSERT_EQ(1u, deserialized_array[0].size());
- ASSERT_FALSE(deserialized_array[0].at("hello world")[0]);
- ASSERT_TRUE(deserialized_array[0].at("hello world")[1]);
- }
- }
-};
-
-#define MAP_COMMON_TEST(MapType, test_name) \
- TEST_F(MapType##Test, test_name) { MapCommonTest<MapType>::test_name(); }
-
-} // namespace test
-} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/map_unittest.cc b/mojo/public/cpp/bindings/tests/map_unittest.cc
index c74a15d..8d630a5 100644
--- a/mojo/public/cpp/bindings/tests/map_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/map_unittest.cc
@@ -1,225 +1,44 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium 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/map.h"
-
#include <stddef.h>
#include <stdint.h>
+#include <unordered_map>
#include <utility>
-#include "mojo/public/cpp/bindings/array.h"
-#include "mojo/public/cpp/bindings/string.h"
-#include "mojo/public/cpp/bindings/tests/container_test_util.h"
-#include "mojo/public/cpp/bindings/tests/map_common_test.h"
+#include "mojo/public/cpp/bindings/tests/rect_chromium.h"
+#include "mojo/public/interfaces/bindings/tests/rect.mojom.h"
+#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace test {
-
namespace {
-using MapTest = testing::Test;
-
-MAP_COMMON_TEST(Map, NullAndEmpty)
-MAP_COMMON_TEST(Map, InsertWorks)
-MAP_COMMON_TEST(Map, TestIndexOperator)
-MAP_COMMON_TEST(Map, TestIndexOperatorAsRValue)
-MAP_COMMON_TEST(Map, TestIndexOperatorMoveOnly)
-MAP_COMMON_TEST(Map, MapArrayClone)
-MAP_COMMON_TEST(Map, ArrayOfMap)
-
-TEST_F(MapTest, ConstructedFromArray) {
- Array<String> keys(kStringIntDataSize);
- Array<int> values(kStringIntDataSize);
- for (size_t i = 0; i < kStringIntDataSize; ++i) {
- keys[i] = kStringIntData[i].string_data;
- values[i] = kStringIntData[i].int_data;
- }
-
- Map<String, int> map(std::move(keys), std::move(values));
-
- for (size_t i = 0; i < kStringIntDataSize; ++i) {
- EXPECT_EQ(kStringIntData[i].int_data,
- map.at(mojo::String(kStringIntData[i].string_data)));
- }
-}
-
-TEST_F(MapTest, DecomposeMapTo) {
- Array<String> keys(kStringIntDataSize);
- Array<int> values(kStringIntDataSize);
- for (size_t i = 0; i < kStringIntDataSize; ++i) {
- keys[i] = kStringIntData[i].string_data;
- values[i] = kStringIntData[i].int_data;
- }
-
- Map<String, int> map(std::move(keys), std::move(values));
- EXPECT_EQ(kStringIntDataSize, map.size());
-
- Array<String> keys2;
- Array<int> values2;
- map.DecomposeMapTo(&keys2, &values2);
- EXPECT_EQ(0u, map.size());
+TEST(MapTest, StructKey) {
+ std::unordered_map<RectPtr, int32_t> map;
+ map.insert(std::make_pair(Rect::New(1, 2, 3, 4), 123));
- EXPECT_EQ(kStringIntDataSize, keys2.size());
- EXPECT_EQ(kStringIntDataSize, values2.size());
+ RectPtr key = Rect::New(1, 2, 3, 4);
+ ASSERT_NE(map.end(), map.find(key));
+ ASSERT_EQ(123, map.find(key)->second);
- for (size_t i = 0; i < kStringIntDataSize; ++i) {
- // We are not guaranteed that the copies have the same sorting as the
- // originals.
- String key = kStringIntData[i].string_data;
- int value = kStringIntData[i].int_data;
-
- bool found = false;
- for (size_t j = 0; j < keys2.size(); ++j) {
- if (keys2[j] == key) {
- EXPECT_EQ(value, values2[j]);
- found = true;
- break;
- }
- }
-
- EXPECT_TRUE(found);
- }
-}
-
-TEST_F(MapTest, Insert_Copyable) {
- ASSERT_EQ(0u, CopyableType::num_instances());
- mojo::Map<mojo::String, CopyableType> map;
- std::vector<CopyableType*> value_ptrs;
-
- for (size_t i = 0; i < kStringIntDataSize; ++i) {
- const char* key = kStringIntData[i].string_data;
- CopyableType value;
- value_ptrs.push_back(value.ptr());
- map.insert(key, value);
- ASSERT_EQ(i + 1, map.size());
- ASSERT_EQ(i + 1, value_ptrs.size());
- EXPECT_EQ(map.size() + 1, CopyableType::num_instances());
- EXPECT_TRUE(map.at(key).copied());
- EXPECT_EQ(value_ptrs[i], map.at(key).ptr());
- map.at(key).ResetCopied();
- EXPECT_TRUE(map);
- }
-
- // std::map doesn't have a capacity() method like std::vector so this test is
- // a lot more boring.
-
- map = nullptr;
- EXPECT_EQ(0u, CopyableType::num_instances());
+ map.erase(key);
+ ASSERT_EQ(0u, map.size());
}
-TEST_F(MapTest, Insert_MoveOnly) {
- ASSERT_EQ(0u, MoveOnlyType::num_instances());
- mojo::Map<mojo::String, MoveOnlyType> map;
- std::vector<MoveOnlyType*> value_ptrs;
-
- for (size_t i = 0; i < kStringIntDataSize; ++i) {
- const char* key = kStringIntData[i].string_data;
- MoveOnlyType value;
- value_ptrs.push_back(value.ptr());
- map.insert(key, std::move(value));
- ASSERT_EQ(i + 1, map.size());
- ASSERT_EQ(i + 1, value_ptrs.size());
- EXPECT_EQ(map.size() + 1, MoveOnlyType::num_instances());
- EXPECT_TRUE(map.at(key).moved());
- EXPECT_EQ(value_ptrs[i], map.at(key).ptr());
- map.at(key).ResetMoved();
- EXPECT_TRUE(map);
- }
-
- // std::map doesn't have a capacity() method like std::vector so this test is
- // a lot more boring.
-
- map = nullptr;
- EXPECT_EQ(0u, MoveOnlyType::num_instances());
-}
-
-TEST_F(MapTest, IndexOperator_MoveOnly) {
- ASSERT_EQ(0u, MoveOnlyType::num_instances());
- mojo::Map<mojo::String, MoveOnlyType> map;
- std::vector<MoveOnlyType*> value_ptrs;
-
- for (size_t i = 0; i < kStringIntDataSize; ++i) {
- const char* key = kStringIntData[i].string_data;
- MoveOnlyType value;
- value_ptrs.push_back(value.ptr());
- map[key] = std::move(value);
- ASSERT_EQ(i + 1, map.size());
- ASSERT_EQ(i + 1, value_ptrs.size());
- EXPECT_EQ(map.size() + 1, MoveOnlyType::num_instances());
- EXPECT_TRUE(map.at(key).moved());
- EXPECT_EQ(value_ptrs[i], map.at(key).ptr());
- map.at(key).ResetMoved();
- EXPECT_TRUE(map);
- }
-
- // std::map doesn't have a capacity() method like std::vector so this test is
- // a lot more boring.
-
- map = nullptr;
- EXPECT_EQ(0u, MoveOnlyType::num_instances());
-}
-
-TEST_F(MapTest, STLToMojo) {
- std::map<std::string, int> stl_data;
- for (size_t i = 0; i < kStringIntDataSize; ++i)
- stl_data[kStringIntData[i].string_data] = kStringIntData[i].int_data;
-
- Map<String, int32_t> mojo_data = Map<String, int32_t>::From(stl_data);
- for (size_t i = 0; i < kStringIntDataSize; ++i) {
- EXPECT_EQ(kStringIntData[i].int_data,
- mojo_data.at(kStringIntData[i].string_data));
- }
-}
-
-TEST_F(MapTest, MojoToSTL) {
- Map<String, int32_t> mojo_map;
- for (size_t i = 0; i < kStringIntDataSize; ++i)
- mojo_map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data);
-
- std::map<std::string, int> stl_map =
- mojo_map.To<std::map<std::string, int>>();
- for (size_t i = 0; i < kStringIntDataSize; ++i) {
- auto it = stl_map.find(kStringIntData[i].string_data);
- ASSERT_TRUE(it != stl_map.end());
- EXPECT_EQ(kStringIntData[i].int_data, it->second);
- }
-}
-
-TEST_F(MapTest, MoveFromAndToSTLMap_Copyable) {
- std::map<int32_t, CopyableType> map1;
- map1.insert(std::make_pair(123, CopyableType()));
- map1[123].ResetCopied();
-
- Map<int32_t, CopyableType> mojo_map(std::move(map1));
- ASSERT_EQ(1u, mojo_map.size());
- ASSERT_NE(mojo_map.end(), mojo_map.find(123));
- ASSERT_FALSE(mojo_map[123].copied());
-
- std::map<int32_t, CopyableType> map2(mojo_map.PassStorage());
- ASSERT_EQ(1u, map2.size());
- ASSERT_NE(map2.end(), map2.find(123));
- ASSERT_FALSE(map2[123].copied());
-
- ASSERT_EQ(0u, mojo_map.size());
- ASSERT_TRUE(mojo_map.is_null());
-}
-
-TEST_F(MapTest, MoveFromAndToSTLMap_MoveOnly) {
- std::map<int32_t, MoveOnlyType> map1;
- map1.insert(std::make_pair(123, MoveOnlyType()));
-
- Map<int32_t, MoveOnlyType> mojo_map(std::move(map1));
- ASSERT_EQ(1u, mojo_map.size());
- ASSERT_NE(mojo_map.end(), mojo_map.find(123));
+TEST(MapTest, TypemappedStructKey) {
+ std::unordered_map<ContainsHashablePtr, int32_t> map;
+ map.insert(
+ std::make_pair(ContainsHashable::New(RectChromium(1, 2, 3, 4)), 123));
- std::map<int32_t, MoveOnlyType> map2(mojo_map.PassStorage());
- ASSERT_EQ(1u, map2.size());
- ASSERT_NE(map2.end(), map2.find(123));
+ ContainsHashablePtr key = ContainsHashable::New(RectChromium(1, 2, 3, 4));
+ ASSERT_NE(map.end(), map.find(key));
+ ASSERT_EQ(123, map.find(key)->second);
- ASSERT_EQ(0u, mojo_map.size());
- ASSERT_TRUE(mojo_map.is_null());
+ map.erase(key);
+ ASSERT_EQ(0u, map.size());
}
} // namespace
diff --git a/mojo/public/cpp/bindings/tests/message_queue.cc b/mojo/public/cpp/bindings/tests/message_queue.cc
index 96a4829..32ed763 100644
--- a/mojo/public/cpp/bindings/tests/message_queue.cc
+++ b/mojo/public/cpp/bindings/tests/message_queue.cc
@@ -14,8 +14,6 @@ MessageQueue::MessageQueue() {
}
MessageQueue::~MessageQueue() {
- while (!queue_.empty())
- Pop();
}
bool MessageQueue::IsEmpty() const {
@@ -23,19 +21,17 @@ bool MessageQueue::IsEmpty() const {
}
void MessageQueue::Push(Message* message) {
- queue_.push(new Message());
- message->MoveTo(queue_.back());
+ queue_.emplace(std::move(*message));
}
void MessageQueue::Pop(Message* message) {
DCHECK(!queue_.empty());
- queue_.front()->MoveTo(message);
+ *message = std::move(queue_.front());
Pop();
}
void MessageQueue::Pop() {
DCHECK(!queue_.empty());
- delete queue_.front();
queue_.pop();
}
diff --git a/mojo/public/cpp/bindings/tests/message_queue.h b/mojo/public/cpp/bindings/tests/message_queue.h
index 8859869..8f13f7a 100644
--- a/mojo/public/cpp/bindings/tests/message_queue.h
+++ b/mojo/public/cpp/bindings/tests/message_queue.h
@@ -8,10 +8,9 @@
#include <queue>
#include "base/macros.h"
+#include "mojo/public/cpp/bindings/message.h"
namespace mojo {
-class Message;
-
namespace test {
// A queue for Message objects.
@@ -34,7 +33,7 @@ class MessageQueue {
private:
void Pop();
- std::queue<Message*> queue_;
+ std::queue<Message> queue_;
DISALLOW_COPY_AND_ASSIGN(MessageQueue);
};
diff --git a/mojo/public/cpp/bindings/tests/mojo_test_blink_export.h b/mojo/public/cpp/bindings/tests/mojo_test_blink_export.h
new file mode 100644
index 0000000..b3bbe27
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/mojo_test_blink_export.h
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium 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_TESTS_MOJO_TEST_BLINK_EXPORT_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_MOJO_TEST_BLINK_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(MOJO_TEST_BLINK_IMPLEMENTATION)
+#define MOJO_TEST_BLINK_EXPORT __declspec(dllexport)
+#else
+#define MOJO_TEST_BLINK_EXPORT __declspec(dllimport)
+#endif // defined(MOJO_TEST_BLINK_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(MOJO_TEST_BLINK_IMPLEMENTATION)
+#define MOJO_TEST_BLINK_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_TEST_BLINK_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define MOJO_TEST_BLINK_EXPORT
+#endif
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_TESTS_MOJO_TEST_BLINK_EXPORT_H_
diff --git a/mojo/public/cpp/bindings/tests/mojo_test_export.h b/mojo/public/cpp/bindings/tests/mojo_test_export.h
new file mode 100644
index 0000000..a48a1ba
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/mojo_test_export.h
@@ -0,0 +1,29 @@
+// Copyright 2016 The Chromium 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_TESTS_MOJO_TEST_EXPORT_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_MOJO_TEST_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(MOJO_TEST_IMPLEMENTATION)
+#define MOJO_TEST_EXPORT __declspec(dllexport)
+#else
+#define MOJO_TEST_EXPORT __declspec(dllimport)
+#endif // defined(MOJO_TEST_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(MOJO_TEST_IMPLEMENTATION)
+#define MOJO_TEST_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_TEST_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define MOJO_TEST_EXPORT
+#endif
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_TESTS_MOJO_TEST_EXPORT_H_
diff --git a/mojo/public/cpp/bindings/tests/multiplex_router_unittest.cc b/mojo/public/cpp/bindings/tests/multiplex_router_unittest.cc
index 9bfcf2c..31963e0 100644
--- a/mojo/public/cpp/bindings/tests/multiplex_router_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/multiplex_router_unittest.cc
@@ -14,7 +14,6 @@
#include "base/threading/thread_task_runner_handle.h"
#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
#include "mojo/public/cpp/bindings/message.h"
-#include "mojo/public/cpp/bindings/message_filter.h"
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
#include "mojo/public/cpp/bindings/tests/message_queue.h"
#include "mojo/public/cpp/bindings/tests/router_test_util.h"
@@ -32,27 +31,22 @@ class MultiplexRouterTest : public testing::Test {
void SetUp() override {
MessagePipe pipe;
- router0_ = new MultiplexRouter(true, std::move(pipe.handle0),
+ router0_ = new MultiplexRouter(std::move(pipe.handle0),
+ MultiplexRouter::MULTI_INTERFACE, false,
base::ThreadTaskRunnerHandle::Get());
- router1_ = new MultiplexRouter(true, std::move(pipe.handle1),
+ router1_ = new MultiplexRouter(std::move(pipe.handle1),
+ MultiplexRouter::MULTI_INTERFACE, true,
base::ThreadTaskRunnerHandle::Get());
- router0_->CreateEndpointHandlePair(&endpoint0_, &endpoint1_);
- endpoint1_ =
- EmulatePassingEndpointHandle(std::move(endpoint1_), router1_.get());
+ ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(&endpoint0_,
+ &endpoint1_);
+ auto id = router0_->AssociateInterface(std::move(endpoint1_));
+ endpoint1_ = router1_->CreateLocalEndpointHandle(id);
}
void TearDown() override {}
void PumpMessages() { base::RunLoop().RunUntilIdle(); }
- ScopedInterfaceEndpointHandle EmulatePassingEndpointHandle(
- ScopedInterfaceEndpointHandle handle,
- MultiplexRouter* target) {
- CHECK(!handle.is_local());
-
- return target->CreateLocalEndpointHandle(handle.release());
- }
-
protected:
scoped_refptr<MultiplexRouter> router0_;
scoped_refptr<MultiplexRouter> router1_;
@@ -65,12 +59,12 @@ class MultiplexRouterTest : public testing::Test {
TEST_F(MultiplexRouterTest, BasicRequestResponse) {
InterfaceEndpointClient client0(std::move(endpoint0_), nullptr,
- base::WrapUnique(new PassThroughFilter()),
- false, base::ThreadTaskRunnerHandle::Get());
+ base::MakeUnique<PassThroughFilter>(), false,
+ base::ThreadTaskRunnerHandle::Get(), 0u);
ResponseGenerator generator;
InterfaceEndpointClient client1(std::move(endpoint1_), &generator,
- base::WrapUnique(new PassThroughFilter()),
- false, base::ThreadTaskRunnerHandle::Get());
+ base::MakeUnique<PassThroughFilter>(), false,
+ base::ThreadTaskRunnerHandle::Get(), 0u);
Message request;
AllocRequestMessage(1, "hello", &request);
@@ -112,12 +106,12 @@ TEST_F(MultiplexRouterTest, BasicRequestResponse) {
TEST_F(MultiplexRouterTest, BasicRequestResponse_Synchronous) {
InterfaceEndpointClient client0(std::move(endpoint0_), nullptr,
- base::WrapUnique(new PassThroughFilter()),
- false, base::ThreadTaskRunnerHandle::Get());
+ base::MakeUnique<PassThroughFilter>(), false,
+ base::ThreadTaskRunnerHandle::Get(), 0u);
ResponseGenerator generator;
InterfaceEndpointClient client1(std::move(endpoint1_), &generator,
- base::WrapUnique(new PassThroughFilter()),
- false, base::ThreadTaskRunnerHandle::Get());
+ base::MakeUnique<PassThroughFilter>(), false,
+ base::ThreadTaskRunnerHandle::Get(), 0u);
Message request;
AllocRequestMessage(1, "hello", &request);
@@ -154,46 +148,18 @@ TEST_F(MultiplexRouterTest, BasicRequestResponse_Synchronous) {
std::string(reinterpret_cast<const char*>(response.payload())));
}
-TEST_F(MultiplexRouterTest, RequestWithNoReceiver) {
- InterfaceEndpointClient client0(std::move(endpoint0_), nullptr,
- base::WrapUnique(new PassThroughFilter()),
- false, base::ThreadTaskRunnerHandle::Get());
- InterfaceEndpointClient client1(std::move(endpoint1_), nullptr,
- base::WrapUnique(new PassThroughFilter()),
- false, base::ThreadTaskRunnerHandle::Get());
-
- // Without an incoming receiver set on client1, we expect client0 to observe
- // an error as a result of sending a message.
-
- Message request;
- AllocRequestMessage(1, "hello", &request);
-
- MessageQueue message_queue;
- base::RunLoop run_loop, run_loop2;
- client0.set_connection_error_handler(run_loop.QuitClosure());
- client1.set_connection_error_handler(run_loop2.QuitClosure());
- client0.AcceptWithResponder(
- &request, new MessageAccumulator(&message_queue, run_loop.QuitClosure()));
-
- run_loop.Run();
- run_loop2.Run();
-
- EXPECT_TRUE(client0.encountered_error());
- EXPECT_TRUE(client1.encountered_error());
- EXPECT_TRUE(message_queue.IsEmpty());
-}
-
// Tests MultiplexRouter using the LazyResponseGenerator. The responses will not
// be sent until after the requests have been accepted.
TEST_F(MultiplexRouterTest, LazyResponses) {
- InterfaceEndpointClient client0(std::move(endpoint0_), nullptr,
- base::WrapUnique(new PassThroughFilter()),
- false, base::ThreadTaskRunnerHandle::Get());
+ InterfaceEndpointClient client0(
+ std::move(endpoint0_), nullptr, base::WrapUnique(new PassThroughFilter()),
+ false, base::ThreadTaskRunnerHandle::Get(), 0u);
base::RunLoop run_loop;
LazyResponseGenerator generator(run_loop.QuitClosure());
InterfaceEndpointClient client1(std::move(endpoint1_), &generator,
base::WrapUnique(new PassThroughFilter()),
- false, base::ThreadTaskRunnerHandle::Get());
+ false, base::ThreadTaskRunnerHandle::Get(),
+ 0u);
Message request;
AllocRequestMessage(1, "hello", &request);
@@ -257,9 +223,9 @@ void ForwardErrorHandler(bool* called, const base::Closure& callback) {
// both sides still appear to have a valid message pipe handle bound.
TEST_F(MultiplexRouterTest, MissingResponses) {
base::RunLoop run_loop0, run_loop1;
- InterfaceEndpointClient client0(std::move(endpoint0_), nullptr,
- base::WrapUnique(new PassThroughFilter()),
- false, base::ThreadTaskRunnerHandle::Get());
+ InterfaceEndpointClient client0(
+ std::move(endpoint0_), nullptr, base::WrapUnique(new PassThroughFilter()),
+ false, base::ThreadTaskRunnerHandle::Get(), 0u);
bool error_handler_called0 = false;
client0.set_connection_error_handler(
base::Bind(&ForwardErrorHandler, &error_handler_called0,
@@ -269,7 +235,8 @@ TEST_F(MultiplexRouterTest, MissingResponses) {
LazyResponseGenerator generator(run_loop3.QuitClosure());
InterfaceEndpointClient client1(std::move(endpoint1_), &generator,
base::WrapUnique(new PassThroughFilter()),
- false, base::ThreadTaskRunnerHandle::Get());
+ false, base::ThreadTaskRunnerHandle::Get(),
+ 0u);
bool error_handler_called1 = false;
client1.set_connection_error_handler(
base::Bind(&ForwardErrorHandler, &error_handler_called1,
@@ -314,12 +281,13 @@ TEST_F(MultiplexRouterTest, LateResponse) {
base::RunLoop run_loop;
LazyResponseGenerator generator(run_loop.QuitClosure());
{
- InterfaceEndpointClient client0(std::move(endpoint0_), nullptr,
- base::WrapUnique(new PassThroughFilter()),
- false, base::ThreadTaskRunnerHandle::Get());
+ InterfaceEndpointClient client0(
+ std::move(endpoint0_), nullptr, base::MakeUnique<PassThroughFilter>(),
+ false, base::ThreadTaskRunnerHandle::Get(), 0u);
InterfaceEndpointClient client1(std::move(endpoint1_), &generator,
- base::WrapUnique(new PassThroughFilter()),
- false, base::ThreadTaskRunnerHandle::Get());
+ base::MakeUnique<PassThroughFilter>(),
+ false, base::ThreadTaskRunnerHandle::Get(),
+ 0u);
Message request;
AllocRequestMessage(1, "hello", &request);
diff --git a/mojo/public/cpp/bindings/tests/pickle_unittest.cc b/mojo/public/cpp/bindings/tests/pickle_unittest.cc
index bd716cc..a5947ce 100644
--- a/mojo/public/cpp/bindings/tests/pickle_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/pickle_unittest.cc
@@ -24,28 +24,25 @@ namespace test {
namespace {
template <typename T>
-void DoExpectResult(int foo,
- int bar,
- const base::Closure& callback,
- const T& actual) {
+void DoExpectResult(int foo, int bar, const base::Closure& callback, T actual) {
EXPECT_EQ(foo, actual.foo());
EXPECT_EQ(bar, actual.bar());
callback.Run();
}
template <typename T>
-base::Callback<void(const T&)> ExpectResult(const T& t,
- const base::Closure& callback) {
+base::Callback<void(T)> ExpectResult(const T& t,
+ const base::Closure& callback) {
return base::Bind(&DoExpectResult<T>, t.foo(), t.bar(), callback);
}
template <typename T>
-void DoFail(const std::string& reason, const T&) {
+void DoFail(const std::string& reason, T) {
EXPECT_TRUE(false) << reason;
}
template <typename T>
-base::Callback<void(const T&)> Fail(const std::string& reason) {
+base::Callback<void(T)> Fail(const std::string& reason) {
return base::Bind(&DoFail<T>, reason);
}
@@ -89,9 +86,9 @@ class ChromiumPicklePasserImpl : public PicklePasser {
ChromiumPicklePasserImpl() {}
// mojo::test::PicklePasser:
- void PassPickledStruct(const PickledStructChromium& pickle,
+ void PassPickledStruct(PickledStructChromium pickle,
const PassPickledStructCallback& callback) override {
- callback.Run(pickle);
+ callback.Run(std::move(pickle));
}
void PassPickledEnum(PickledEnumChromium pickle,
@@ -105,15 +102,15 @@ class ChromiumPicklePasserImpl : public PicklePasser {
callback.Run(std::move(container));
}
- void PassPickles(const std::vector<PickledStructChromium>& pickles,
+ void PassPickles(std::vector<PickledStructChromium> pickles,
const PassPicklesCallback& callback) override {
- callback.Run(pickles);
+ callback.Run(std::move(pickles));
}
void PassPickleArrays(
- const std::vector<std::vector<PickledStructChromium>>& pickle_arrays,
+ std::vector<std::vector<PickledStructChromium>> pickle_arrays,
const PassPickleArraysCallback& callback) override {
- callback.Run(pickle_arrays);
+ callback.Run(std::move(pickle_arrays));
}
};
@@ -123,9 +120,9 @@ class BlinkPicklePasserImpl : public blink::PicklePasser {
BlinkPicklePasserImpl() {}
// mojo::test::blink::PicklePasser:
- void PassPickledStruct(const PickledStructBlink& pickle,
+ void PassPickledStruct(PickledStructBlink pickle,
const PassPickledStructCallback& callback) override {
- callback.Run(pickle);
+ callback.Run(std::move(pickle));
}
void PassPickledEnum(PickledEnumBlink pickle,
@@ -139,15 +136,15 @@ class BlinkPicklePasserImpl : public blink::PicklePasser {
callback.Run(std::move(container));
}
- void PassPickles(const WTF::Vector<PickledStructBlink>& pickles,
+ void PassPickles(WTF::Vector<PickledStructBlink> pickles,
const PassPicklesCallback& callback) override {
- callback.Run(pickles);
+ callback.Run(std::move(pickles));
}
void PassPickleArrays(
- const WTF::Vector<WTF::Vector<PickledStructBlink>>& pickle_arrays,
+ WTF::Vector<WTF::Vector<PickledStructBlink>> pickle_arrays,
const PassPickleArraysCallback& callback) override {
- callback.Run(pickle_arrays);
+ callback.Run(std::move(pickle_arrays));
}
};
@@ -160,7 +157,7 @@ class PickleTest : public testing::Test {
template <typename ProxyType = PicklePasser>
InterfacePtr<ProxyType> ConnectToChromiumService() {
InterfacePtr<ProxyType> proxy;
- InterfaceRequest<ProxyType> request = GetProxy(&proxy);
+ InterfaceRequest<ProxyType> request(&proxy);
chromium_bindings_.AddBinding(
&chromium_service_,
ConvertInterfaceRequest<PicklePasser>(std::move(request)));
@@ -170,7 +167,7 @@ class PickleTest : public testing::Test {
template <typename ProxyType = blink::PicklePasser>
InterfacePtr<ProxyType> ConnectToBlinkService() {
InterfacePtr<ProxyType> proxy;
- InterfaceRequest<ProxyType> request = GetProxy(&proxy);
+ InterfaceRequest<ProxyType> request(&proxy);
blink_bindings_.AddBinding(
&blink_service_,
ConvertInterfaceRequest<blink::PicklePasser>(std::move(request)));
@@ -301,7 +298,7 @@ TEST_F(PickleTest, BlinkProxyToChromiumService) {
TEST_F(PickleTest, PickleArray) {
auto proxy = ConnectToChromiumService();
- auto pickles = Array<PickledStructChromium>::New(2);
+ auto pickles = std::vector<PickledStructChromium>(2);
pickles[0].set_foo(1);
pickles[0].set_bar(2);
pickles[0].set_baz(100);
@@ -314,19 +311,18 @@ TEST_F(PickleTest, PickleArray) {
// deserialized intact. This ensures that the ParamTraits are actually used
// rather than doing a byte-for-byte copy of the element data, beacuse the
// |baz| field should never be serialized.
- proxy->PassPickles(
- std::move(pickles),
- BindSimpleLambda<const std::vector<PickledStructChromium>&>(
- [&](const std::vector<PickledStructChromium>& passed) {
- ASSERT_EQ(2u, passed.size());
- EXPECT_EQ(1, passed[0].foo());
- EXPECT_EQ(2, passed[0].bar());
- EXPECT_EQ(0, passed[0].baz());
- EXPECT_EQ(3, passed[1].foo());
- EXPECT_EQ(4, passed[1].bar());
- EXPECT_EQ(0, passed[1].baz());
- run_loop.Quit();
- }));
+ proxy->PassPickles(std::move(pickles),
+ BindSimpleLambda<std::vector<PickledStructChromium>>(
+ [&](std::vector<PickledStructChromium> passed) {
+ ASSERT_EQ(2u, passed.size());
+ EXPECT_EQ(1, passed[0].foo());
+ EXPECT_EQ(2, passed[0].bar());
+ EXPECT_EQ(0, passed[0].baz());
+ EXPECT_EQ(3, passed[1].foo());
+ EXPECT_EQ(4, passed[1].bar());
+ EXPECT_EQ(0, passed[1].baz());
+ run_loop.Quit();
+ }));
run_loop.Run();
}
}
@@ -353,10 +349,9 @@ TEST_F(PickleTest, PickleArrayArray) {
base::RunLoop run_loop;
// Verify that the array-of-arrays serializes and deserializes properly.
proxy->PassPickleArrays(
- pickle_arrays,
- BindSimpleLambda<
- const std::vector<std::vector<PickledStructChromium>>&>(
- [&](const std::vector<std::vector<PickledStructChromium>>& passed) {
+ std::move(pickle_arrays),
+ BindSimpleLambda<std::vector<std::vector<PickledStructChromium>>>(
+ [&](std::vector<std::vector<PickledStructChromium>> passed) {
ASSERT_EQ(2u, passed.size());
ASSERT_EQ(2u, passed[0].size());
ASSERT_EQ(2u, passed[1].size());
diff --git a/mojo/public/cpp/bindings/tests/pickled_types_blink.h b/mojo/public/cpp/bindings/tests/pickled_types_blink.h
index 0b7bd20..37e9e70 100644
--- a/mojo/public/cpp/bindings/tests/pickled_types_blink.h
+++ b/mojo/public/cpp/bindings/tests/pickled_types_blink.h
@@ -51,9 +51,14 @@ class PickledStructBlink {
bar_ = bar;
}
+ // The |baz| field should never be serialized.
+ int baz() const { return baz_; }
+ void set_baz(int baz) { baz_ = baz; }
+
private:
int foo_ = 0;
int bar_ = 0;
+ int baz_ = 0;
DISALLOW_COPY_AND_ASSIGN(PickledStructBlink);
};
diff --git a/mojo/public/cpp/bindings/tests/rect_blink.h b/mojo/public/cpp/bindings/tests/rect_blink.h
index de7a792..7335989 100644
--- a/mojo/public/cpp/bindings/tests/rect_blink.h
+++ b/mojo/public/cpp/bindings/tests/rect_blink.h
@@ -51,6 +51,12 @@ class RectBlink {
int computeArea() const { return width_ * height_; }
+ bool operator==(const RectBlink& other) const {
+ return (x() == other.x() && y() == other.y() && width() == other.width() &&
+ height() == other.height());
+ }
+ bool operator!=(const RectBlink& other) const { return !(*this == other); }
+
private:
int x_ = 0;
int y_ = 0;
@@ -61,4 +67,17 @@ class RectBlink {
} // namespace test
} // namespace mojo
+namespace std {
+
+template <>
+struct hash<mojo::test::RectBlink> {
+ size_t operator()(const mojo::test::RectBlink& value) {
+ // Terrible hash function:
+ return (std::hash<int>()(value.x()) ^ std::hash<int>()(value.y()) ^
+ std::hash<int>()(value.width()) ^ std::hash<int>()(value.height()));
+ }
+};
+
+} // namespace std
+
#endif // MOJO_PUBLIC_CPP_BINDINGS_TESTS_RECT_BLINK_H_
diff --git a/mojo/public/cpp/bindings/tests/rect_blink.typemap b/mojo/public/cpp/bindings/tests/rect_blink.typemap
index 37ee409..657ea1a 100644
--- a/mojo/public/cpp/bindings/tests/rect_blink.typemap
+++ b/mojo/public/cpp/bindings/tests/rect_blink.typemap
@@ -3,7 +3,16 @@
# found in the LICENSE file.
mojom = "//mojo/public/interfaces/bindings/tests/rect.mojom"
-public_headers = [ "//mojo/public/cpp/bindings/tests/rect_blink.h" ]
-traits_headers = [ "//mojo/public/cpp/bindings/tests/rect_blink_traits.h" ]
+public_headers = [
+ "//mojo/public/cpp/bindings/tests/rect_blink.h",
+ "//mojo/public/cpp/bindings/tests/shared_rect.h",
+]
+traits_headers = [
+ "//mojo/public/cpp/bindings/tests/rect_blink_traits.h",
+ "//mojo/public/cpp/bindings/tests/shared_rect_traits.h",
+]
-type_mappings = [ "mojo.test.TypemappedRect=mojo::test::RectBlink" ]
+type_mappings = [
+ "mojo.test.TypemappedRect=mojo::test::RectBlink[hashable]",
+ "mojo.test.SharedTypemappedRect=mojo::test::SharedRect",
+]
diff --git a/mojo/public/cpp/bindings/tests/rect_blink_traits.h b/mojo/public/cpp/bindings/tests/rect_blink_traits.h
index c5f6068..7258739 100644
--- a/mojo/public/cpp/bindings/tests/rect_blink_traits.h
+++ b/mojo/public/cpp/bindings/tests/rect_blink_traits.h
@@ -12,14 +12,13 @@
namespace mojo {
template <>
-struct StructTraits<test::blink::TypemappedRect, test::RectBlink> {
+struct StructTraits<test::TypemappedRectDataView, test::RectBlink> {
static int x(const test::RectBlink& r) { return r.x(); }
static int y(const test::RectBlink& r) { return r.y(); }
static int width(const test::RectBlink& r) { return r.width(); }
static int height(const test::RectBlink& r) { return r.height(); }
- static bool Read(test::blink::TypemappedRectDataView r,
- test::RectBlink* out) {
+ static bool Read(test::TypemappedRectDataView r, test::RectBlink* out) {
if (r.x() < 0 || r.y() < 0 || r.width() < 0 || r.height() < 0) {
return false;
}
diff --git a/mojo/public/cpp/bindings/tests/rect_chromium.h b/mojo/public/cpp/bindings/tests/rect_chromium.h
index 20c4362..d2e0a3e 100644
--- a/mojo/public/cpp/bindings/tests/rect_chromium.h
+++ b/mojo/public/cpp/bindings/tests/rect_chromium.h
@@ -55,6 +55,12 @@ class RectChromium {
int GetArea() const { return width_ * height_; }
+ bool operator==(const RectChromium& other) const {
+ return (x() == other.x() && y() == other.y() && width() == other.width() &&
+ height() == other.height());
+ }
+ bool operator!=(const RectChromium& other) const { return !(*this == other); }
+
private:
int x_ = 0;
int y_ = 0;
@@ -65,4 +71,17 @@ class RectChromium {
} // namespace test
} // namespace mojo
+namespace std {
+
+template <>
+struct hash<mojo::test::RectChromium> {
+ size_t operator()(const mojo::test::RectChromium& value) {
+ // Terrible hash function:
+ return (std::hash<int>()(value.x()) ^ std::hash<int>()(value.y()) ^
+ std::hash<int>()(value.width()) ^ std::hash<int>()(value.height()));
+ }
+};
+
+} // namespace std
+
#endif // MOJO_PUBLIC_CPP_BINDINGS_TESTS_RECT_CHROMIUM_H_
diff --git a/mojo/public/cpp/bindings/tests/rect_chromium.typemap b/mojo/public/cpp/bindings/tests/rect_chromium.typemap
index 0da4021..7e5df84 100644
--- a/mojo/public/cpp/bindings/tests/rect_chromium.typemap
+++ b/mojo/public/cpp/bindings/tests/rect_chromium.typemap
@@ -3,7 +3,16 @@
# found in the LICENSE file.
mojom = "//mojo/public/interfaces/bindings/tests/rect.mojom"
-public_headers = [ "//mojo/public/cpp/bindings/tests/rect_chromium.h" ]
-traits_headers = [ "//mojo/public/cpp/bindings/tests/rect_chromium_traits.h" ]
+public_headers = [
+ "//mojo/public/cpp/bindings/tests/rect_chromium.h",
+ "//mojo/public/cpp/bindings/tests/shared_rect.h",
+]
+traits_headers = [
+ "//mojo/public/cpp/bindings/tests/rect_chromium_traits.h",
+ "//mojo/public/cpp/bindings/tests/shared_rect_traits.h",
+]
-type_mappings = [ "mojo.test.TypemappedRect=mojo::test::RectChromium" ]
+type_mappings = [
+ "mojo.test.TypemappedRect=mojo::test::RectChromium[hashable]",
+ "mojo.test.SharedTypemappedRect=mojo::test::SharedRect",
+]
diff --git a/mojo/public/cpp/bindings/tests/rect_chromium_traits.h b/mojo/public/cpp/bindings/tests/rect_chromium_traits.h
index ab65371..b446d7d 100644
--- a/mojo/public/cpp/bindings/tests/rect_chromium_traits.h
+++ b/mojo/public/cpp/bindings/tests/rect_chromium_traits.h
@@ -12,7 +12,7 @@
namespace mojo {
template <>
-struct StructTraits<test::TypemappedRect, test::RectChromium> {
+struct StructTraits<test::TypemappedRectDataView, test::RectChromium> {
static int x(const test::RectChromium& r) { return r.x(); }
static int y(const test::RectChromium& r) { return r.y(); }
static int width(const test::RectChromium& r) { return r.width(); }
diff --git a/mojo/public/cpp/bindings/tests/report_bad_message_unittest.cc b/mojo/public/cpp/bindings/tests/report_bad_message_unittest.cc
new file mode 100644
index 0000000..1bf3f7a
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/report_bad_message_unittest.cc
@@ -0,0 +1,194 @@
+// Copyright 2016 The Chromium 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/callback.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/interfaces/bindings/tests/test_bad_messages.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+class TestBadMessagesImpl : public TestBadMessages {
+ public:
+ TestBadMessagesImpl() : binding_(this) {}
+ ~TestBadMessagesImpl() override {}
+
+ void BindImpl(TestBadMessagesRequest request) {
+ binding_.Bind(std::move(request));
+ }
+
+ const ReportBadMessageCallback& bad_message_callback() {
+ return bad_message_callback_;
+ }
+
+ private:
+ // TestBadMessages:
+ void RejectEventually(const RejectEventuallyCallback& callback) override {
+ bad_message_callback_ = GetBadMessageCallback();
+ callback.Run();
+ }
+
+ void RequestResponse(const RequestResponseCallback& callback) override {
+ callback.Run();
+ }
+
+ void RejectSync(const RejectSyncCallback& callback) override {
+ callback.Run();
+ ReportBadMessage("go away");
+ }
+
+ void RequestResponseSync(
+ const RequestResponseSyncCallback& callback) override {
+ callback.Run();
+ }
+
+ ReportBadMessageCallback bad_message_callback_;
+ mojo::Binding<TestBadMessages> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestBadMessagesImpl);
+};
+
+class ReportBadMessageTest : public testing::Test {
+ public:
+ ReportBadMessageTest() {}
+
+ void SetUp() override {
+ mojo::edk::SetDefaultProcessErrorCallback(
+ base::Bind(&ReportBadMessageTest::OnProcessError,
+ base::Unretained(this)));
+
+ impl_.BindImpl(MakeRequest(&proxy_));
+ }
+
+ void TearDown() override {
+ mojo::edk::SetDefaultProcessErrorCallback(
+ mojo::edk::ProcessErrorCallback());
+ }
+
+ TestBadMessages* proxy() { return proxy_.get(); }
+
+ TestBadMessagesImpl* impl() { return &impl_; }
+
+ void SetErrorHandler(const base::Closure& handler) {
+ error_handler_ = handler;
+ }
+
+ private:
+ void OnProcessError(const std::string& error) {
+ if (!error_handler_.is_null())
+ error_handler_.Run();
+ }
+
+ TestBadMessagesPtr proxy_;
+ TestBadMessagesImpl impl_;
+ base::Closure error_handler_;
+ base::MessageLoop message_loop;
+};
+
+TEST_F(ReportBadMessageTest, Request) {
+ // Verify that basic immediate error reporting works.
+ bool error = false;
+ SetErrorHandler(base::Bind([] (bool* flag) { *flag = true; }, &error));
+ EXPECT_TRUE(proxy()->RejectSync());
+ EXPECT_TRUE(error);
+}
+
+TEST_F(ReportBadMessageTest, RequestAsync) {
+ bool error = false;
+ SetErrorHandler(base::Bind([] (bool* flag) { *flag = true; }, &error));
+
+ // This should capture a bad message reporting callback in the impl.
+ base::RunLoop loop;
+ proxy()->RejectEventually(loop.QuitClosure());
+ loop.Run();
+
+ EXPECT_FALSE(error);
+
+ // Now we can run the callback and it should trigger a bad message report.
+ DCHECK(!impl()->bad_message_callback().is_null());
+ impl()->bad_message_callback().Run("bad!");
+ EXPECT_TRUE(error);
+}
+
+TEST_F(ReportBadMessageTest, Response) {
+ bool error = false;
+ SetErrorHandler(base::Bind([] (bool* flag) { *flag = true; }, &error));
+
+ base::RunLoop loop;
+ proxy()->RequestResponse(
+ base::Bind([] (const base::Closure& quit) {
+ // Report a bad message inside the response callback. This should
+ // trigger the error handler.
+ ReportBadMessage("no way!");
+ quit.Run();
+ },
+ loop.QuitClosure()));
+ loop.Run();
+
+ EXPECT_TRUE(error);
+}
+
+TEST_F(ReportBadMessageTest, ResponseAsync) {
+ bool error = false;
+ SetErrorHandler(base::Bind([] (bool* flag) { *flag = true; }, &error));
+
+ ReportBadMessageCallback bad_message_callback;
+ base::RunLoop loop;
+ proxy()->RequestResponse(
+ base::Bind([] (const base::Closure& quit,
+ ReportBadMessageCallback* callback) {
+ // Capture the bad message callback inside the response callback.
+ *callback = GetBadMessageCallback();
+ quit.Run();
+ },
+ loop.QuitClosure(), &bad_message_callback));
+ loop.Run();
+
+ EXPECT_FALSE(error);
+
+ // Invoking this callback should report a bad message and trigger the error
+ // handler immediately.
+ bad_message_callback.Run("this message is bad and should feel bad");
+ EXPECT_TRUE(error);
+}
+
+TEST_F(ReportBadMessageTest, ResponseSync) {
+ bool error = false;
+ SetErrorHandler(base::Bind([] (bool* flag) { *flag = true; }, &error));
+
+ SyncMessageResponseContext context;
+ proxy()->RequestResponseSync();
+
+ EXPECT_FALSE(error);
+ context.ReportBadMessage("i don't like this response");
+ EXPECT_TRUE(error);
+}
+
+TEST_F(ReportBadMessageTest, ResponseSyncDeferred) {
+ bool error = false;
+ SetErrorHandler(base::Bind([] (bool* flag) { *flag = true; }, &error));
+
+ ReportBadMessageCallback bad_message_callback;
+ {
+ SyncMessageResponseContext context;
+ proxy()->RequestResponseSync();
+ bad_message_callback = context.GetBadMessageCallback();
+ }
+
+ EXPECT_FALSE(error);
+ bad_message_callback.Run("nope nope nope");
+ EXPECT_TRUE(error);
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/request_response_unittest.cc b/mojo/public/cpp/bindings/tests/request_response_unittest.cc
index 5e203a2..43b8f0d 100644
--- a/mojo/public/cpp/bindings/tests/request_response_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/request_response_unittest.cc
@@ -95,7 +95,7 @@ class RequestResponseTest : public testing::Test {
TEST_F(RequestResponseTest, EchoString) {
sample::ProviderPtr provider;
- ProviderImpl provider_impl(GetProxy(&provider));
+ ProviderImpl provider_impl(MakeRequest(&provider));
std::string buf;
base::RunLoop run_loop;
@@ -109,7 +109,7 @@ TEST_F(RequestResponseTest, EchoString) {
TEST_F(RequestResponseTest, EchoStrings) {
sample::ProviderPtr provider;
- ProviderImpl provider_impl(GetProxy(&provider));
+ ProviderImpl provider_impl(MakeRequest(&provider));
std::string buf;
base::RunLoop run_loop;
@@ -123,7 +123,7 @@ TEST_F(RequestResponseTest, EchoStrings) {
TEST_F(RequestResponseTest, EchoMessagePipeHandle) {
sample::ProviderPtr provider;
- ProviderImpl provider_impl(GetProxy(&provider));
+ ProviderImpl provider_impl(MakeRequest(&provider));
MessagePipe pipe2;
base::RunLoop run_loop;
@@ -141,7 +141,7 @@ TEST_F(RequestResponseTest, EchoMessagePipeHandle) {
TEST_F(RequestResponseTest, EchoEnum) {
sample::ProviderPtr provider;
- ProviderImpl provider_impl(GetProxy(&provider));
+ ProviderImpl provider_impl(MakeRequest(&provider));
sample::Enum value;
base::RunLoop run_loop;
diff --git a/mojo/public/cpp/bindings/tests/router_test_util.cc b/mojo/public/cpp/bindings/tests/router_test_util.cc
index 3e32ec7..b9b93d8 100644
--- a/mojo/public/cpp/bindings/tests/router_test_util.cc
+++ b/mojo/public/cpp/bindings/tests/router_test_util.cc
@@ -17,10 +17,10 @@ namespace test {
void AllocRequestMessage(uint32_t name, const char* text, Message* message) {
size_t payload_size = strlen(text) + 1; // Plus null terminator.
- internal::RequestMessageBuilder builder(name, payload_size);
+ internal::MessageBuilder builder(name, Message::kFlagExpectsResponse,
+ payload_size, 0);
memcpy(builder.buffer()->Allocate(payload_size), text, payload_size);
-
- builder.message()->MoveTo(message);
+ *message = std::move(*builder.message());
}
void AllocResponseMessage(uint32_t name,
@@ -28,10 +28,11 @@ void AllocResponseMessage(uint32_t name,
uint64_t request_id,
Message* message) {
size_t payload_size = strlen(text) + 1; // Plus null terminator.
- internal::ResponseMessageBuilder builder(name, payload_size, request_id);
+ internal::MessageBuilder builder(name, Message::kFlagIsResponse, payload_size,
+ 0);
+ builder.message()->set_request_id(request_id);
memcpy(builder.buffer()->Allocate(payload_size), text, payload_size);
-
- builder.message()->MoveTo(message);
+ *message = std::move(*builder.message());
}
MessageAccumulator::MessageAccumulator(MessageQueue* queue,
diff --git a/mojo/public/cpp/bindings/tests/router_unittest.cc b/mojo/public/cpp/bindings/tests/router_unittest.cc
deleted file mode 100644
index 39a8363..0000000
--- a/mojo/public/cpp/bindings/tests/router_unittest.cc
+++ /dev/null
@@ -1,316 +0,0 @@
-// Copyright 2014 The Chromium 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"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "mojo/public/cpp/bindings/tests/message_queue.h"
-#include "mojo/public/cpp/bindings/tests/router_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace mojo {
-namespace test {
-namespace {
-
-class RouterTest : public testing::Test {
- public:
- RouterTest() {}
-
- void SetUp() override {
- CreateMessagePipe(nullptr, &handle0_, &handle1_);
- }
-
- void TearDown() override {}
-
- void PumpMessages() { base::RunLoop().RunUntilIdle(); }
-
- protected:
- ScopedMessagePipeHandle handle0_;
- ScopedMessagePipeHandle handle1_;
-
- private:
- base::MessageLoop loop_;
-};
-
-TEST_F(RouterTest, BasicRequestResponse) {
- internal::Router router0(std::move(handle0_), internal::FilterChain(), false,
- base::ThreadTaskRunnerHandle::Get());
- internal::Router router1(std::move(handle1_), internal::FilterChain(), false,
- base::ThreadTaskRunnerHandle::Get());
-
- ResponseGenerator generator;
- router1.set_incoming_receiver(&generator);
-
- Message request;
- AllocRequestMessage(1, "hello", &request);
-
- MessageQueue message_queue;
- base::RunLoop run_loop;
- router0.AcceptWithResponder(
- &request, new MessageAccumulator(&message_queue, run_loop.QuitClosure()));
-
- run_loop.Run();
-
- EXPECT_FALSE(message_queue.IsEmpty());
-
- Message response;
- message_queue.Pop(&response);
-
- EXPECT_EQ(std::string("hello world!"),
- std::string(reinterpret_cast<const char*>(response.payload())));
-
- // Send a second message on the pipe.
- Message request2;
- AllocRequestMessage(1, "hello again", &request2);
-
- base::RunLoop run_loop2;
- router0.AcceptWithResponder(
- &request2,
- new MessageAccumulator(&message_queue, run_loop2.QuitClosure()));
-
- run_loop2.Run();
-
- EXPECT_FALSE(message_queue.IsEmpty());
-
- message_queue.Pop(&response);
-
- EXPECT_EQ(std::string("hello again world!"),
- std::string(reinterpret_cast<const char*>(response.payload())));
-}
-
-TEST_F(RouterTest, BasicRequestResponse_Synchronous) {
- internal::Router router0(std::move(handle0_), internal::FilterChain(), false,
- base::ThreadTaskRunnerHandle::Get());
- internal::Router router1(std::move(handle1_), internal::FilterChain(), false,
- base::ThreadTaskRunnerHandle::Get());
-
- ResponseGenerator generator;
- router1.set_incoming_receiver(&generator);
-
- Message request;
- AllocRequestMessage(1, "hello", &request);
-
- MessageQueue message_queue;
- router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
-
- router1.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
- router0.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
-
- EXPECT_FALSE(message_queue.IsEmpty());
-
- Message response;
- message_queue.Pop(&response);
-
- EXPECT_EQ(std::string("hello world!"),
- std::string(reinterpret_cast<const char*>(response.payload())));
-
- // Send a second message on the pipe.
- Message request2;
- AllocRequestMessage(1, "hello again", &request2);
-
- router0.AcceptWithResponder(&request2,
- new MessageAccumulator(&message_queue));
-
- router1.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
- router0.WaitForIncomingMessage(MOJO_DEADLINE_INDEFINITE);
-
- EXPECT_FALSE(message_queue.IsEmpty());
-
- message_queue.Pop(&response);
-
- EXPECT_EQ(std::string("hello again world!"),
- std::string(reinterpret_cast<const char*>(response.payload())));
-}
-
-TEST_F(RouterTest, RequestWithNoReceiver) {
- internal::Router router0(std::move(handle0_), internal::FilterChain(), false,
- base::ThreadTaskRunnerHandle::Get());
- internal::Router router1(std::move(handle1_), internal::FilterChain(), false,
- base::ThreadTaskRunnerHandle::Get());
-
- // Without an incoming receiver set on router1, we expect router0 to observe
- // an error as a result of sending a message.
-
- Message request;
- AllocRequestMessage(1, "hello", &request);
-
- MessageQueue message_queue;
- base::RunLoop run_loop, run_loop2;
- router0.set_connection_error_handler(run_loop.QuitClosure());
- router1.set_connection_error_handler(run_loop2.QuitClosure());
- router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
-
- run_loop.Run();
- run_loop2.Run();
-
- EXPECT_TRUE(router0.encountered_error());
- EXPECT_TRUE(router1.encountered_error());
- EXPECT_TRUE(message_queue.IsEmpty());
-}
-
-// Tests Router using the LazyResponseGenerator. The responses will not be
-// sent until after the requests have been accepted.
-TEST_F(RouterTest, LazyResponses) {
- internal::Router router0(std::move(handle0_), internal::FilterChain(), false,
- base::ThreadTaskRunnerHandle::Get());
- internal::Router router1(std::move(handle1_), internal::FilterChain(), false,
- base::ThreadTaskRunnerHandle::Get());
-
- base::RunLoop run_loop;
- LazyResponseGenerator generator(run_loop.QuitClosure());
- router1.set_incoming_receiver(&generator);
-
- Message request;
- AllocRequestMessage(1, "hello", &request);
-
- MessageQueue message_queue;
- base::RunLoop run_loop2;
- router0.AcceptWithResponder(
- &request,
- new MessageAccumulator(&message_queue, run_loop2.QuitClosure()));
- run_loop.Run();
-
- // The request has been received but the response has not been sent yet.
- EXPECT_TRUE(message_queue.IsEmpty());
-
- // Send the response.
- EXPECT_TRUE(generator.responder_is_valid());
- generator.CompleteWithResponse();
- run_loop2.Run();
-
- // Check the response.
- EXPECT_FALSE(message_queue.IsEmpty());
- Message response;
- message_queue.Pop(&response);
- EXPECT_EQ(std::string("hello world!"),
- std::string(reinterpret_cast<const char*>(response.payload())));
-
- // Send a second message on the pipe.
- base::RunLoop run_loop3;
- LazyResponseGenerator generator2(run_loop3.QuitClosure());
-
- router1.set_incoming_receiver(&generator2);
- Message request2;
- AllocRequestMessage(1, "hello again", &request2);
-
- base::RunLoop run_loop4;
- router0.AcceptWithResponder(
- &request2,
- new MessageAccumulator(&message_queue, run_loop4.QuitClosure()));
- run_loop3.Run();
-
- // The request has been received but the response has not been sent yet.
- EXPECT_TRUE(message_queue.IsEmpty());
-
- // Send the second response.
- EXPECT_TRUE(generator2.responder_is_valid());
- generator2.CompleteWithResponse();
- run_loop4.Run();
-
- // Check the second response.
- EXPECT_FALSE(message_queue.IsEmpty());
- message_queue.Pop(&response);
- EXPECT_EQ(std::string("hello again world!"),
- std::string(reinterpret_cast<const char*>(response.payload())));
-}
-
-void ForwardErrorHandler(bool* called, const base::Closure& callback) {
- *called = true;
- callback.Run();
-}
-
-// Tests that if the receiving application destroys the responder_ without
-// sending a response, then we trigger connection error at both sides. Moreover,
-// both sides still appear to have a valid message pipe handle bound.
-TEST_F(RouterTest, MissingResponses) {
- base::RunLoop run_loop0, run_loop1;
- internal::Router router0(std::move(handle0_), internal::FilterChain(), false,
- base::ThreadTaskRunnerHandle::Get());
- bool error_handler_called0 = false;
- router0.set_connection_error_handler(
- base::Bind(&ForwardErrorHandler, &error_handler_called0,
- run_loop0.QuitClosure()));
-
- internal::Router router1(std::move(handle1_), internal::FilterChain(), false,
- base::ThreadTaskRunnerHandle::Get());
- bool error_handler_called1 = false;
- router1.set_connection_error_handler(
- base::Bind(&ForwardErrorHandler, &error_handler_called1,
- run_loop1.QuitClosure()));
-
- base::RunLoop run_loop3;
- LazyResponseGenerator generator(run_loop3.QuitClosure());
- router1.set_incoming_receiver(&generator);
- router1.set_incoming_receiver(&generator);
-
- Message request;
- AllocRequestMessage(1, "hello", &request);
-
- MessageQueue message_queue;
- router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
- run_loop3.Run();
-
- // The request has been received but no response has been sent.
- EXPECT_TRUE(message_queue.IsEmpty());
-
- // Destroy the responder MessagerReceiver but don't send any response.
- generator.CompleteWithoutResponse();
- run_loop0.Run();
- run_loop1.Run();
-
- // Check that no response was received.
- EXPECT_TRUE(message_queue.IsEmpty());
-
- // Connection error handler is called at both sides.
- EXPECT_TRUE(error_handler_called0);
- EXPECT_TRUE(error_handler_called1);
-
- // The error flag is set at both sides.
- EXPECT_TRUE(router0.encountered_error());
- EXPECT_TRUE(router1.encountered_error());
-
- // The message pipe handle is valid at both sides.
- EXPECT_TRUE(router0.is_valid());
- EXPECT_TRUE(router1.is_valid());
-}
-
-TEST_F(RouterTest, LateResponse) {
- // Test that things won't blow up if we try to send a message to a
- // MessageReceiver, which was given to us via AcceptWithResponder,
- // after the router has gone away.
-
- base::RunLoop run_loop;
- LazyResponseGenerator generator(run_loop.QuitClosure());
- {
- internal::Router router0(std::move(handle0_), internal::FilterChain(),
- false, base::ThreadTaskRunnerHandle::Get());
- internal::Router router1(std::move(handle1_), internal::FilterChain(),
- false, base::ThreadTaskRunnerHandle::Get());
-
- router1.set_incoming_receiver(&generator);
-
- Message request;
- AllocRequestMessage(1, "hello", &request);
-
- MessageQueue message_queue;
- router0.AcceptWithResponder(&request,
- new MessageAccumulator(&message_queue));
-
- run_loop.Run();
-
- EXPECT_TRUE(generator.has_responder());
- }
-
- EXPECT_FALSE(generator.responder_is_valid());
- generator.CompleteWithResponse(); // This should end up doing nothing.
-}
-
-} // namespace
-} // namespace test
-} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/sample_service_unittest.cc b/mojo/public/cpp/bindings/tests/sample_service_unittest.cc
index 00a8dbf..579576f 100644
--- a/mojo/public/cpp/bindings/tests/sample_service_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/sample_service_unittest.cc
@@ -38,22 +38,13 @@ bool g_dump_message_as_text = false;
FooPtr MakeFoo() {
std::string name("foopy");
- BarPtr bar(Bar::New());
- bar->alpha = 20;
- bar->beta = 40;
- bar->gamma = 60;
- bar->type = Bar::Type::VERTICAL;
+ BarPtr bar(Bar::New(20, 40, 60, Bar::Type::VERTICAL));
std::vector<BarPtr> extra_bars(3);
for (size_t i = 0; i < extra_bars.size(); ++i) {
Bar::Type type = i % 2 == 0 ? Bar::Type::VERTICAL : Bar::Type::HORIZONTAL;
- BarPtr bar(Bar::New());
uint8_t base = static_cast<uint8_t>(i * 100);
- bar->alpha = base;
- bar->beta = base + 20;
- bar->gamma = base + 40;
- bar->type = type;
- extra_bars[i] = std::move(bar);
+ extra_bars[i] = Bar::New(base, base + 20, base + 40, type);
}
std::vector<uint8_t> data(10);
@@ -84,22 +75,11 @@ FooPtr MakeFoo() {
}
mojo::MessagePipe pipe;
- FooPtr foo(Foo::New());
- foo->name = name;
- foo->x = 1;
- foo->y = 2;
- foo->a = false;
- foo->b = true;
- foo->c = false;
- foo->bar = std::move(bar);
- foo->extra_bars = std::move(extra_bars);
- foo->data = std::move(data);
- foo->source = std::move(pipe.handle1);
- foo->input_streams = std::move(input_streams);
- foo->output_streams = std::move(output_streams);
- foo->array_of_array_of_bools = std::move(array_of_array_of_bools);
-
- return foo;
+ return Foo::New(name, 1, 2, false, true, false, std::move(bar),
+ std::move(extra_bars), std::move(data),
+ std::move(pipe.handle1), std::move(input_streams),
+ std::move(output_streams), std::move(array_of_array_of_bools),
+ base::nullopt, base::nullopt);
}
// Check that the given |Foo| is identical to the one made by |MakeFoo()|.
@@ -310,7 +290,7 @@ class SimpleMessageReceiver : public mojo::MessageReceiverWithResponder {
// the system. It receives the incoming message.
ServiceImpl impl;
- ServiceStub stub;
+ ServiceStub<> stub;
stub.set_sink(&impl);
return stub.Accept(message);
}
diff --git a/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc b/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc
index dcf2967..275f10f 100644
--- a/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/serialization_warning_unittest.cc
@@ -8,12 +8,10 @@
#include <stddef.h>
#include <utility>
-#include "mojo/public/cpp/bindings/array.h"
#include "mojo/public/cpp/bindings/lib/array_internal.h"
#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
#include "mojo/public/cpp/bindings/lib/serialization.h"
#include "mojo/public/cpp/bindings/lib/validation_errors.h"
-#include "mojo/public/cpp/bindings/string.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom.h"
#include "mojo/public/interfaces/bindings/tests/test_unions.mojom.h"
@@ -26,15 +24,16 @@ namespace {
using mojo::internal::ContainerValidateParams;
// Creates an array of arrays of handles (2 X 3) for testing.
-Array<Array<ScopedHandle>> CreateTestNestedHandleArray() {
- Array<Array<ScopedHandle>> array(2);
+std::vector<base::Optional<std::vector<ScopedHandle>>>
+CreateTestNestedHandleArray() {
+ std::vector<base::Optional<std::vector<ScopedHandle>>> array(2);
for (size_t i = 0; i < array.size(); ++i) {
- Array<ScopedHandle> nested_array(3);
+ std::vector<ScopedHandle> nested_array(3);
for (size_t j = 0; j < nested_array.size(); ++j) {
MessagePipe pipe;
nested_array[j] = ScopedHandle::From(std::move(pipe.handle1));
}
- array[i] = std::move(nested_array);
+ array[i].emplace(std::move(nested_array));
}
return array;
@@ -47,18 +46,20 @@ class SerializationWarningTest : public testing::Test {
protected:
template <typename T>
void TestWarning(T obj, mojo::internal::ValidationError expected_warning) {
+ using MojomType = typename T::Struct::DataView;
+
warning_observer_.set_last_warning(mojo::internal::VALIDATION_ERROR_NONE);
mojo::internal::SerializationContext context;
mojo::internal::FixedBufferForTesting buf(
- mojo::internal::PrepareToSerialize<T>(obj, &context));
- typename mojo::internal::MojomTypeTraits<T>::Data* data;
- mojo::internal::Serialize<T>(obj, &buf, &data, &context);
+ mojo::internal::PrepareToSerialize<MojomType>(obj, &context));
+ typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
+ mojo::internal::Serialize<MojomType>(obj, &buf, &data, &context);
EXPECT_EQ(expected_warning, warning_observer_.last_warning());
}
- template <typename T>
+ template <typename MojomType, typename T>
void TestArrayWarning(T obj,
mojo::internal::ValidationError expected_warning,
const ContainerValidateParams* validate_params) {
@@ -66,9 +67,10 @@ class SerializationWarningTest : public testing::Test {
mojo::internal::SerializationContext context;
mojo::internal::FixedBufferForTesting buf(
- mojo::internal::PrepareToSerialize<T>(obj, &context));
- typename mojo::internal::MojomTypeTraits<T>::Data* data;
- mojo::internal::Serialize<T>(obj, &buf, &data, validate_params, &context);
+ mojo::internal::PrepareToSerialize<MojomType>(obj, &context));
+ typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
+ mojo::internal::Serialize<MojomType>(obj, &buf, &data, validate_params,
+ &context);
EXPECT_EQ(expected_warning, warning_observer_.last_warning());
}
@@ -76,13 +78,15 @@ class SerializationWarningTest : public testing::Test {
template <typename T>
void TestUnionWarning(T obj,
mojo::internal::ValidationError expected_warning) {
+ using MojomType = typename T::Struct::DataView;
+
warning_observer_.set_last_warning(mojo::internal::VALIDATION_ERROR_NONE);
mojo::internal::SerializationContext context;
mojo::internal::FixedBufferForTesting buf(
- mojo::internal::PrepareToSerialize<T>(obj, false, &context));
- typename mojo::internal::MojomTypeTraits<T>::Data* data;
- mojo::internal::Serialize<T>(obj, &buf, &data, false, &context);
+ mojo::internal::PrepareToSerialize<MojomType>(obj, false, &context));
+ typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
+ mojo::internal::Serialize<MojomType>(obj, &buf, &data, false, &context);
EXPECT_EQ(expected_warning, warning_observer_.last_warning());
}
@@ -153,57 +157,64 @@ TEST_F(SerializationWarningTest, FixedArrayOfStructsInStruct) {
}
TEST_F(SerializationWarningTest, ArrayOfArraysOfHandles) {
- Array<Array<ScopedHandle>> test_array = CreateTestNestedHandleArray();
- test_array[0] = nullptr;
- test_array[1][0] = ScopedHandle();
+ using MojomType = ArrayDataView<ArrayDataView<ScopedHandle>>;
+ auto test_array = CreateTestNestedHandleArray();
+ test_array[0] = base::nullopt;
+ (*test_array[1])[0] = ScopedHandle();
ContainerValidateParams validate_params_0(
0, true, new ContainerValidateParams(0, true, nullptr));
- TestArrayWarning(std::move(test_array), mojo::internal::VALIDATION_ERROR_NONE,
- &validate_params_0);
+ TestArrayWarning<MojomType>(std::move(test_array),
+ mojo::internal::VALIDATION_ERROR_NONE,
+ &validate_params_0);
test_array = CreateTestNestedHandleArray();
- test_array[0] = nullptr;
+ test_array[0] = base::nullopt;
ContainerValidateParams validate_params_1(
0, false, new ContainerValidateParams(0, true, nullptr));
- TestArrayWarning(std::move(test_array),
- mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
- &validate_params_1);
+ TestArrayWarning<MojomType>(
+ std::move(test_array),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ &validate_params_1);
test_array = CreateTestNestedHandleArray();
- test_array[1][0] = ScopedHandle();
+ (*test_array[1])[0] = ScopedHandle();
ContainerValidateParams validate_params_2(
0, true, new ContainerValidateParams(0, false, nullptr));
- TestArrayWarning(std::move(test_array),
- mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
- &validate_params_2);
+ TestArrayWarning<MojomType>(
+ std::move(test_array),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+ &validate_params_2);
}
TEST_F(SerializationWarningTest, ArrayOfStrings) {
- Array<String> test_array(3);
+ using MojomType = ArrayDataView<StringDataView>;
+
+ std::vector<std::string> test_array(3);
for (size_t i = 0; i < test_array.size(); ++i)
test_array[i] = "hello";
ContainerValidateParams validate_params_0(
0, true, new ContainerValidateParams(0, false, nullptr));
- TestArrayWarning(std::move(test_array), mojo::internal::VALIDATION_ERROR_NONE,
- &validate_params_0);
+ TestArrayWarning<MojomType>(std::move(test_array),
+ mojo::internal::VALIDATION_ERROR_NONE,
+ &validate_params_0);
- test_array = Array<String>(3);
- for (size_t i = 0; i < test_array.size(); ++i)
- test_array[i] = nullptr;
+ std::vector<base::Optional<std::string>> optional_test_array(3);
ContainerValidateParams validate_params_1(
0, false, new ContainerValidateParams(0, false, nullptr));
- TestArrayWarning(std::move(test_array),
- mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
- &validate_params_1);
+ TestArrayWarning<MojomType>(
+ std::move(optional_test_array),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ &validate_params_1);
- test_array = Array<String>(2);
+ test_array = std::vector<std::string>(2);
ContainerValidateParams validate_params_2(
3, true, new ContainerValidateParams(0, false, nullptr));
- TestArrayWarning(std::move(test_array),
- mojo::internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
- &validate_params_2);
+ TestArrayWarning<MojomType>(
+ std::move(test_array),
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER,
+ &validate_params_2);
}
TEST_F(SerializationWarningTest, StructInUnion) {
diff --git a/mojo/public/cpp/bindings/tests/shared_rect.h b/mojo/public/cpp/bindings/tests/shared_rect.h
new file mode 100644
index 0000000..c0a4771
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/shared_rect.h
@@ -0,0 +1,43 @@
+// Copyright 2016 The Chromium 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_TESTS_SHARED_RECT_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_SHARED_RECT_H_
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace test {
+
+// An implementation of a hypothetical Rect type specifically for consumers in
+// both Chromium and Blink.
+class SharedRect {
+ public:
+ SharedRect() {}
+ SharedRect(int x, int y, int width, int height)
+ : x_(x), y_(y), width_(width), height_(height) {}
+
+ int x() const { return x_; }
+ void set_x(int x) { x_ = x; }
+
+ int y() const { return y_; }
+ void set_y(int y) { y_ = y; }
+
+ int width() const { return width_; }
+ void set_width(int width) { width_ = width; }
+
+ int height() const { return height_; }
+ void set_height(int height) { height_ = height; }
+
+ private:
+ int x_ = 0;
+ int y_ = 0;
+ int width_ = 0;
+ int height_ = 0;
+};
+
+} // namespace test
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_TESTS_SHARED_RECT_H_
diff --git a/mojo/public/cpp/bindings/tests/shared_rect_traits.h b/mojo/public/cpp/bindings/tests/shared_rect_traits.h
new file mode 100644
index 0000000..bbf04d5
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/shared_rect_traits.h
@@ -0,0 +1,33 @@
+// Copyright 2016 The Chromium 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_TESTS_SHARED_RECT_TRAITS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_TESTS_SHARED_RECT_TRAITS_H_
+
+#include "mojo/public/cpp/bindings/struct_traits.h"
+#include "mojo/public/cpp/bindings/tests/shared_rect.h"
+#include "mojo/public/interfaces/bindings/tests/rect.mojom-shared.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<test::SharedTypemappedRectDataView, test::SharedRect> {
+ static int x(const test::SharedRect& r) { return r.x(); }
+ static int y(const test::SharedRect& r) { return r.y(); }
+ static int width(const test::SharedRect& r) { return r.width(); }
+ static int height(const test::SharedRect& r) { return r.height(); }
+
+ static bool Read(test::SharedTypemappedRectDataView r,
+ test::SharedRect* out) {
+ out->set_x(r.x());
+ out->set_y(r.y());
+ out->set_width(r.width());
+ out->set_height(r.height());
+ return true;
+ }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_TESTS_SHARED_RECT_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/tests/stl_converters_unittest.cc b/mojo/public/cpp/bindings/tests/stl_converters_unittest.cc
deleted file mode 100644
index 92a31b3..0000000
--- a/mojo/public/cpp/bindings/tests/stl_converters_unittest.cc
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2016 The Chromium 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/stl_converters.h"
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include "mojo/public/cpp/bindings/array.h"
-#include "mojo/public/cpp/bindings/map.h"
-#include "mojo/public/cpp/bindings/string.h"
-#include "mojo/public/cpp/bindings/tests/container_test_util.h"
-#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace mojo {
-namespace test {
-
-using STLConvertersTest = testing::Test;
-
-TEST_F(STLConvertersTest, AvoidUnnecessaryCopies) {
- Array<CopyableType> mojo_array(1);
- std::vector<CopyableType> stl_vector = UnwrapToSTLType(std::move(mojo_array));
- ASSERT_EQ(1u, stl_vector.size());
- ASSERT_FALSE(stl_vector[0].copied());
- Array<CopyableType> mojo_array2 = WrapSTLType(std::move(stl_vector));
- ASSERT_EQ(1u, mojo_array2.size());
- ASSERT_FALSE(mojo_array2[0].copied());
-
- Map<int32_t, CopyableType> mojo_map;
- mojo_map.insert(42, CopyableType());
- mojo_map[42].ResetCopied();
- std::map<int32_t, CopyableType> stl_map =
- UnwrapToSTLType(std::move(mojo_map));
- ASSERT_EQ(1u, stl_map.size());
- ASSERT_FALSE(stl_map[42].copied());
- Map<int32_t, CopyableType> mojo_map2 = WrapSTLType(std::move(stl_map));
- ASSERT_EQ(1u, mojo_map2.size());
- ASSERT_FALSE(mojo_map2[42].copied());
-}
-
-TEST_F(STLConvertersTest, RecursiveConversion) {
- Array<Map<String, Array<int32_t>>> mojo_obj(2);
- mojo_obj[0] = nullptr;
- mojo_obj[1]["hello"].push_back(123);
- mojo_obj[1]["hello"].push_back(456);
-
- std::vector<std::map<std::string, std::vector<int32_t>>> stl_obj =
- UnwrapToSTLType(std::move(mojo_obj));
-
- ASSERT_EQ(2u, stl_obj.size());
- ASSERT_TRUE(stl_obj[0].empty());
- ASSERT_EQ(1u, stl_obj[1].size());
- ASSERT_EQ(2u, stl_obj[1]["hello"].size());
- ASSERT_EQ(123, stl_obj[1]["hello"][0]);
- ASSERT_EQ(456, stl_obj[1]["hello"][1]);
-
- Array<Map<String, Array<int32_t>>> mojo_obj2 =
- WrapSTLType(std::move(stl_obj));
-
- ASSERT_EQ(2u, mojo_obj2.size());
- ASSERT_EQ(0u, mojo_obj2[0].size());
- // The null flag has been lost when converted to std::map.
- ASSERT_FALSE(mojo_obj2[0].is_null());
- ASSERT_EQ(1u, mojo_obj2[1].size());
- ASSERT_EQ(2u, mojo_obj2[1]["hello"].size());
- ASSERT_EQ(123, mojo_obj2[1]["hello"][0]);
- ASSERT_EQ(456, mojo_obj2[1]["hello"][1]);
-}
-
-TEST_F(STLConvertersTest, StopsAtMojoStruct) {
- Array<NamedRegionPtr> mojo_obj(1);
- mojo_obj[0] = NamedRegion::New();
- mojo_obj[0]->name.emplace("hello");
- mojo_obj[0]->rects.emplace(3);
-
- std::vector<NamedRegionPtr> stl_obj = UnwrapToSTLType(std::move(mojo_obj));
-
- ASSERT_EQ(1u, stl_obj.size());
- ASSERT_EQ("hello", stl_obj[0]->name.value());
- ASSERT_EQ(3u, stl_obj[0]->rects->size());
-
- Array<NamedRegionPtr> mojo_obj2 = WrapSTLType(std::move(stl_obj));
-
- ASSERT_EQ(1u, mojo_obj2.size());
- ASSERT_EQ("hello", mojo_obj2[0]->name.value());
- ASSERT_EQ(3u, mojo_obj2[0]->rects->size());
-}
-
-} // namespace test
-} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/string_unittest.cc b/mojo/public/cpp/bindings/tests/string_unittest.cc
deleted file mode 100644
index 13486ae..0000000
--- a/mojo/public/cpp/bindings/tests/string_unittest.cc
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2014 The Chromium 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/string.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace mojo {
-namespace test {
-
-namespace {
-const char* kHelloWorld = "hello world";
-} // namespace
-
-TEST(StringTest, DefaultIsNotNull) {
- String s;
- EXPECT_FALSE(s.is_null());
-}
-
-TEST(StringTest, ConstructedWithNULL) {
- String s(nullptr);
- EXPECT_TRUE(s.is_null());
-}
-
-TEST(StringTest, ConstructedWithNullCharPointer) {
- const char* null = nullptr;
- String s(null);
- EXPECT_TRUE(s.is_null());
-}
-
-TEST(StringTest, AssignedNULL) {
- String s("");
- EXPECT_FALSE(s.is_null());
- s = nullptr;
- EXPECT_TRUE(s.is_null());
-}
-
-TEST(StringTest, Empty) {
- String s("");
- EXPECT_FALSE(s.is_null());
- EXPECT_TRUE(s.get().empty());
-}
-
-TEST(StringTest, Basic) {
- String s(kHelloWorld);
- EXPECT_EQ(std::string(kHelloWorld), s.get());
-}
-
-TEST(StringTest, Assignment) {
- String s(kHelloWorld);
- String t = s; // Makes a copy.
- EXPECT_FALSE(t.is_null());
- EXPECT_EQ(std::string(kHelloWorld), t.get());
- EXPECT_FALSE(s.is_null());
-}
-
-TEST(StringTest, Equality) {
- String s(kHelloWorld);
- String t(kHelloWorld);
- EXPECT_EQ(s, t);
- EXPECT_TRUE(s == s);
- EXPECT_FALSE(s != s);
- EXPECT_TRUE(s == t);
- EXPECT_FALSE(s != t);
- EXPECT_TRUE(kHelloWorld == s);
- EXPECT_TRUE(s == kHelloWorld);
- EXPECT_TRUE("not" != s);
- EXPECT_FALSE("not" == s);
- EXPECT_TRUE(s != "not");
- EXPECT_FALSE(s == "not");
-
- // Test null strings.
- String n1;
- String n2;
- EXPECT_TRUE(n1 == n1);
- EXPECT_FALSE(n1 != n2);
- EXPECT_TRUE(n1 == n2);
- EXPECT_FALSE(n1 != n2);
- EXPECT_TRUE(n1 != s);
- EXPECT_FALSE(n1 == s);
- EXPECT_TRUE(s != n1);
- EXPECT_FALSE(s == n1);
-}
-
-TEST(StringTest, LessThanNullness) {
- String null;
- String null2;
- EXPECT_FALSE(null < null2);
- EXPECT_FALSE(null2 < null);
-
- String real("real");
- EXPECT_TRUE(null < real);
- EXPECT_FALSE(real < null);
-}
-
-TEST(StringTest, MoveConstructors) {
- std::string std_str(kHelloWorld);
-
- String str1(std::move(std_str));
- EXPECT_TRUE(kHelloWorld == str1);
-
- String str2(std::move(str1));
- EXPECT_TRUE(kHelloWorld == str2);
- EXPECT_TRUE(str1.is_null());
-}
-
-TEST(StringTest, MoveAssignments) {
- std::string std_str(kHelloWorld);
-
- String str1;
- str1 = std::move(std_str);
- EXPECT_TRUE(kHelloWorld == str1);
-
- String str2;
- str2 = std::move(str1);
- EXPECT_TRUE(kHelloWorld == str2);
- EXPECT_TRUE(str1.is_null());
-}
-
-TEST(StringTest, Storage) {
- String str(kHelloWorld);
-
- EXPECT_TRUE(kHelloWorld == str.storage());
-
- std::string storage = str.PassStorage();
- EXPECT_TRUE(str.is_null());
- EXPECT_TRUE(kHelloWorld == storage);
-}
-
-} // namespace test
-} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/struct_traits_unittest.cc b/mojo/public/cpp/bindings/tests/struct_traits_unittest.cc
index 583dc46..4c06267 100644
--- a/mojo/public/cpp/bindings/tests/struct_traits_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/struct_traits_unittest.cc
@@ -5,6 +5,7 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/logging.h"
+#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "mojo/public/cpp/bindings/binding_set.h"
@@ -70,6 +71,11 @@ class ChromiumRectServiceImpl : public RectService {
callback.Run(largest_rect_);
}
+ void PassSharedRect(const SharedRect& r,
+ const PassSharedRectCallback& callback) override {
+ callback.Run(r);
+ }
+
private:
RectChromium largest_rect_;
};
@@ -93,6 +99,11 @@ class BlinkRectServiceImpl : public blink::RectService {
callback.Run(largest_rect_);
}
+ void PassSharedRect(const SharedRect& r,
+ const PassSharedRectCallback& callback) override {
+ callback.Run(r);
+ }
+
private:
RectBlink largest_rect_;
};
@@ -134,9 +145,21 @@ class StructTraitsTest : public testing::Test,
callback.Run(s);
}
- void EchoPassByValueStructWithTraits(
- PassByValueStructWithTraitsImpl s,
- const EchoPassByValueStructWithTraitsCallback& callback) override {
+ void EchoTrivialStructWithTraits(
+ TrivialStructWithTraitsImpl s,
+ const EchoTrivialStructWithTraitsCallback& callback) override {
+ callback.Run(s);
+ }
+
+ void EchoMoveOnlyStructWithTraits(
+ MoveOnlyStructWithTraitsImpl s,
+ const EchoMoveOnlyStructWithTraitsCallback& callback) override {
+ callback.Run(std::move(s));
+ }
+
+ void EchoNullableMoveOnlyStructWithTraits(
+ base::Optional<MoveOnlyStructWithTraitsImpl> s,
+ const EchoNullableMoveOnlyStructWithTraitsCallback& callback) override {
callback.Run(std::move(s));
}
@@ -145,12 +168,25 @@ class StructTraitsTest : public testing::Test,
callback.Run(e);
}
- void EchoStructWithTraitsForUniquePtrTest(
+ void EchoStructWithTraitsForUniquePtr(
std::unique_ptr<int> e,
- const EchoStructWithTraitsForUniquePtrTestCallback& callback) override {
+ const EchoStructWithTraitsForUniquePtrCallback& callback) override {
callback.Run(std::move(e));
}
+ void EchoNullableStructWithTraitsForUniquePtr(
+ std::unique_ptr<int> e,
+ const EchoNullableStructWithTraitsForUniquePtrCallback& callback)
+ override {
+ callback.Run(std::move(e));
+ }
+
+ void EchoUnionWithTraits(
+ std::unique_ptr<test::UnionWithTraitsBase> u,
+ const EchoUnionWithTraitsCallback& callback) override {
+ callback.Run(std::move(u));
+ }
+
base::MessageLoop loop_;
ChromiumRectServiceImpl chromium_service_;
@@ -166,7 +202,7 @@ class StructTraitsTest : public testing::Test,
TEST_F(StructTraitsTest, ChromiumProxyToChromiumService) {
RectServicePtr chromium_proxy;
- BindToChromiumService(GetProxy(&chromium_proxy));
+ BindToChromiumService(MakeRequest(&chromium_proxy));
{
base::RunLoop loop;
chromium_proxy->AddRect(RectChromium(1, 1, 4, 5));
@@ -175,11 +211,18 @@ TEST_F(StructTraitsTest, ChromiumProxyToChromiumService) {
ExpectResult(RectChromium(1, 1, 4, 5), loop.QuitClosure()));
loop.Run();
}
+ {
+ base::RunLoop loop;
+ chromium_proxy->PassSharedRect(
+ {1, 2, 3, 4},
+ ExpectResult(SharedRect({1, 2, 3, 4}), loop.QuitClosure()));
+ loop.Run();
+ }
}
TEST_F(StructTraitsTest, ChromiumToBlinkService) {
RectServicePtr chromium_proxy;
- BindToBlinkService(GetProxy(&chromium_proxy));
+ BindToBlinkService(MakeRequest(&chromium_proxy));
{
base::RunLoop loop;
chromium_proxy->AddRect(RectChromium(1, 1, 4, 5));
@@ -188,6 +231,13 @@ TEST_F(StructTraitsTest, ChromiumToBlinkService) {
ExpectResult(RectChromium(2, 2, 5, 5), loop.QuitClosure()));
loop.Run();
}
+ {
+ base::RunLoop loop;
+ chromium_proxy->PassSharedRect(
+ {1, 2, 3, 4},
+ ExpectResult(SharedRect({1, 2, 3, 4}), loop.QuitClosure()));
+ loop.Run();
+ }
// The Blink service should drop our connection because RectBlink's
// deserializer rejects negative origins.
{
@@ -202,7 +252,7 @@ TEST_F(StructTraitsTest, ChromiumToBlinkService) {
TEST_F(StructTraitsTest, BlinkProxyToBlinkService) {
blink::RectServicePtr blink_proxy;
- BindToBlinkService(GetProxy(&blink_proxy));
+ BindToBlinkService(MakeRequest(&blink_proxy));
{
base::RunLoop loop;
blink_proxy->AddRect(RectBlink(1, 1, 4, 5));
@@ -211,11 +261,18 @@ TEST_F(StructTraitsTest, BlinkProxyToBlinkService) {
ExpectResult(RectBlink(10, 10, 20, 20), loop.QuitClosure()));
loop.Run();
}
+ {
+ base::RunLoop loop;
+ blink_proxy->PassSharedRect(
+ {4, 3, 2, 1},
+ ExpectResult(SharedRect({4, 3, 2, 1}), loop.QuitClosure()));
+ loop.Run();
+ }
}
TEST_F(StructTraitsTest, BlinkProxyToChromiumService) {
blink::RectServicePtr blink_proxy;
- BindToChromiumService(GetProxy(&blink_proxy));
+ BindToChromiumService(MakeRequest(&blink_proxy));
{
base::RunLoop loop;
blink_proxy->AddRect(RectBlink(1, 1, 4, 5));
@@ -224,6 +281,13 @@ TEST_F(StructTraitsTest, BlinkProxyToChromiumService) {
ExpectResult(RectBlink(1, 1, 4, 5), loop.QuitClosure()));
loop.Run();
}
+ {
+ base::RunLoop loop;
+ blink_proxy->PassSharedRect(
+ {4, 3, 2, 1},
+ ExpectResult(SharedRect({4, 3, 2, 1}), loop.QuitClosure()));
+ loop.Run();
+ }
}
void ExpectStructWithTraits(const StructWithTraitsImpl& expected,
@@ -249,6 +313,8 @@ TEST_F(StructTraitsTest, EchoStructWithTraits) {
input.set_uint64(42);
input.set_string("hello world!");
input.get_mutable_string_array().assign({"hello", "world!"});
+ input.get_mutable_string_set().insert("hello");
+ input.get_mutable_string_set().insert("world!");
input.get_mutable_struct().value = 42;
input.get_mutable_struct_array().resize(2);
input.get_mutable_struct_array()[0].value = 1;
@@ -274,24 +340,44 @@ TEST_F(StructTraitsTest, CloneStructWithTraitsContainer) {
EXPECT_EQ(42u, cloned_container->f_struct.get_uint64());
}
+void ExpectTrivialStructWithTraits(TrivialStructWithTraitsImpl expected,
+ const base::Closure& closure,
+ TrivialStructWithTraitsImpl passed) {
+ EXPECT_EQ(expected.value, passed.value);
+ closure.Run();
+}
+
+TEST_F(StructTraitsTest, EchoTrivialStructWithTraits) {
+ TrivialStructWithTraitsImpl input;
+ input.value = 42;
+
+ base::RunLoop loop;
+ TraitsTestServicePtr proxy = GetTraitsTestProxy();
+
+ proxy->EchoTrivialStructWithTraits(
+ input,
+ base::Bind(&ExpectTrivialStructWithTraits, input, loop.QuitClosure()));
+ loop.Run();
+}
+
void CaptureMessagePipe(ScopedMessagePipeHandle* storage,
const base::Closure& closure,
- PassByValueStructWithTraitsImpl passed) {
+ MoveOnlyStructWithTraitsImpl passed) {
storage->reset(MessagePipeHandle(
passed.get_mutable_handle().release().value()));
closure.Run();
}
-TEST_F(StructTraitsTest, EchoPassByValueStructWithTraits) {
+TEST_F(StructTraitsTest, EchoMoveOnlyStructWithTraits) {
MessagePipe mp;
- PassByValueStructWithTraitsImpl input;
+ MoveOnlyStructWithTraitsImpl input;
input.get_mutable_handle().reset(mp.handle0.release());
base::RunLoop loop;
TraitsTestServicePtr proxy = GetTraitsTestProxy();
ScopedMessagePipeHandle received;
- proxy->EchoPassByValueStructWithTraits(
+ proxy->EchoMoveOnlyStructWithTraits(
std::move(input),
base::Bind(&CaptureMessagePipe, &received, loop.QuitClosure()));
loop.Run();
@@ -317,6 +403,27 @@ TEST_F(StructTraitsTest, EchoPassByValueStructWithTraits) {
EXPECT_STREQ(kHello, buffer);
}
+void CaptureNullableMoveOnlyStructWithTraitsImpl(
+ base::Optional<MoveOnlyStructWithTraitsImpl>* storage,
+ const base::Closure& closure,
+ base::Optional<MoveOnlyStructWithTraitsImpl> passed) {
+ *storage = std::move(passed);
+ closure.Run();
+}
+
+TEST_F(StructTraitsTest, EchoNullableMoveOnlyStructWithTraits) {
+ base::RunLoop loop;
+ TraitsTestServicePtr proxy = GetTraitsTestProxy();
+
+ base::Optional<MoveOnlyStructWithTraitsImpl> received;
+ proxy->EchoNullableMoveOnlyStructWithTraits(
+ base::nullopt, base::Bind(&CaptureNullableMoveOnlyStructWithTraitsImpl,
+ &received, loop.QuitClosure()));
+ loop.Run();
+
+ EXPECT_FALSE(received);
+}
+
void ExpectEnumWithTraits(EnumWithTraitsImpl expected_value,
const base::Closure& closure,
EnumWithTraitsImpl value) {
@@ -342,7 +449,9 @@ TEST_F(StructTraitsTest, SerializeStructWithTraits) {
input.set_uint32(7);
input.set_uint64(42);
input.set_string("hello world!");
- input.get_mutable_string_array().assign({"hello", "world!"});
+ input.get_mutable_string_array().assign({ "hello", "world!" });
+ input.get_mutable_string_set().insert("hello");
+ input.get_mutable_string_set().insert("world!");
input.get_mutable_struct().value = 42;
input.get_mutable_struct_array().resize(2);
input.get_mutable_struct_array()[0].value = 1;
@@ -350,7 +459,7 @@ TEST_F(StructTraitsTest, SerializeStructWithTraits) {
input.get_mutable_struct_map()["hello"] = NestedStructWithTraitsImpl(1024);
input.get_mutable_struct_map()["world"] = NestedStructWithTraitsImpl(2048);
- mojo::Array<uint8_t> data = StructWithTraits::Serialize(&input);
+ auto data = StructWithTraits::Serialize(&input);
StructWithTraitsImpl output;
ASSERT_TRUE(StructWithTraits::Deserialize(std::move(data), &output));
@@ -360,26 +469,84 @@ TEST_F(StructTraitsTest, SerializeStructWithTraits) {
EXPECT_EQ(input.get_uint64(), output.get_uint64());
EXPECT_EQ(input.get_string(), output.get_string());
EXPECT_EQ(input.get_string_array(), output.get_string_array());
+ EXPECT_EQ(input.get_string_set(), output.get_string_set());
EXPECT_EQ(input.get_struct(), output.get_struct());
EXPECT_EQ(input.get_struct_array(), output.get_struct_array());
EXPECT_EQ(input.get_struct_map(), output.get_struct_map());
}
-void ExpectUniquePtr(int expected,
+void ExpectUniquePtr(std::unique_ptr<int> expected,
const base::Closure& closure,
std::unique_ptr<int> value) {
- EXPECT_EQ(expected, *value);
+ ASSERT_EQ(!expected, !value);
+ if (expected)
+ EXPECT_EQ(*expected, *value);
closure.Run();
}
TEST_F(StructTraitsTest, TypemapUniquePtr) {
- base::RunLoop loop;
TraitsTestServicePtr proxy = GetTraitsTestProxy();
- proxy->EchoStructWithTraitsForUniquePtrTest(
- base::MakeUnique<int>(12345),
- base::Bind(&ExpectUniquePtr, 12345, loop.QuitClosure()));
- loop.Run();
+ {
+ base::RunLoop loop;
+ proxy->EchoStructWithTraitsForUniquePtr(
+ base::MakeUnique<int>(12345),
+ base::Bind(&ExpectUniquePtr, base::Passed(base::MakeUnique<int>(12345)),
+ loop.QuitClosure()));
+ loop.Run();
+ }
+ {
+ base::RunLoop loop;
+ proxy->EchoNullableStructWithTraitsForUniquePtr(
+ nullptr, base::Bind(&ExpectUniquePtr, nullptr, loop.QuitClosure()));
+ loop.Run();
+ }
+}
+
+TEST_F(StructTraitsTest, EchoUnionWithTraits) {
+ TraitsTestServicePtr proxy = GetTraitsTestProxy();
+
+ {
+ std::unique_ptr<test::UnionWithTraitsBase> input(
+ new test::UnionWithTraitsInt32(1234));
+ base::RunLoop loop;
+ proxy->EchoUnionWithTraits(
+ std::move(input),
+ base::Bind(
+ [](const base::Closure& quit_closure,
+ std::unique_ptr<test::UnionWithTraitsBase> passed) {
+ ASSERT_EQ(test::UnionWithTraitsBase::Type::INT32, passed->type());
+ EXPECT_EQ(1234,
+ static_cast<test::UnionWithTraitsInt32*>(passed.get())
+ ->value());
+ quit_closure.Run();
+
+ },
+ loop.QuitClosure()));
+ loop.Run();
+ }
+
+ {
+ std::unique_ptr<test::UnionWithTraitsBase> input(
+ new test::UnionWithTraitsStruct(4321));
+ base::RunLoop loop;
+ proxy->EchoUnionWithTraits(
+ std::move(input),
+ base::Bind(
+ [](const base::Closure& quit_closure,
+ std::unique_ptr<test::UnionWithTraitsBase> passed) {
+ ASSERT_EQ(test::UnionWithTraitsBase::Type::STRUCT,
+ passed->type());
+ EXPECT_EQ(4321,
+ static_cast<test::UnionWithTraitsStruct*>(passed.get())
+ ->get_struct()
+ .value);
+ quit_closure.Run();
+
+ },
+ loop.QuitClosure()));
+ loop.Run();
+ }
}
} // namespace test
diff --git a/mojo/public/cpp/bindings/tests/struct_unittest.cc b/mojo/public/cpp/bindings/tests/struct_unittest.cc
index 76b4cce..13ba507 100644
--- a/mojo/public/cpp/bindings/tests/struct_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/struct_unittest.cc
@@ -17,12 +17,7 @@ namespace test {
namespace {
RectPtr MakeRect(int32_t factor = 1) {
- RectPtr rect(Rect::New());
- rect->x = 1 * factor;
- rect->y = 2 * factor;
- rect->width = 10 * factor;
- rect->height = 20 * factor;
- return rect;
+ return Rect::New(1 * factor, 2 * factor, 10 * factor, 20 * factor);
}
void CheckRect(const Rect& rect, int32_t factor = 1) {
@@ -33,31 +28,28 @@ void CheckRect(const Rect& rect, int32_t factor = 1) {
}
MultiVersionStructPtr MakeMultiVersionStruct() {
- MultiVersionStructPtr output(MultiVersionStruct::New());
- output->f_int32 = 123;
- output->f_rect = MakeRect(5);
- output->f_string.emplace("hello");
- output->f_array.emplace(3);
- (*output->f_array)[0] = 10;
- (*output->f_array)[1] = 9;
- (*output->f_array)[2] = 8;
MessagePipe pipe;
- output->f_message_pipe = std::move(pipe.handle0);
- output->f_int16 = 42;
-
- return output;
+ return MultiVersionStruct::New(123, MakeRect(5), std::string("hello"),
+ std::vector<int8_t>{10, 9, 8},
+ std::move(pipe.handle0), false, 42);
}
template <typename U, typename T>
U SerializeAndDeserialize(T input) {
- using InputDataType = typename mojo::internal::MojomTypeTraits<T>::Data*;
- using OutputDataType = typename mojo::internal::MojomTypeTraits<U>::Data*;
+ using InputMojomType = typename T::Struct::DataView;
+ using OutputMojomType = typename U::Struct::DataView;
+
+ using InputDataType =
+ typename mojo::internal::MojomTypeTraits<InputMojomType>::Data*;
+ using OutputDataType =
+ typename mojo::internal::MojomTypeTraits<OutputMojomType>::Data*;
mojo::internal::SerializationContext context;
- size_t size = mojo::internal::PrepareToSerialize<T>(input, &context);
+ size_t size =
+ mojo::internal::PrepareToSerialize<InputMojomType>(input, &context);
mojo::internal::FixedBufferForTesting buf(size + 32);
InputDataType data;
- mojo::internal::Serialize<T>(input, &buf, &data, &context);
+ mojo::internal::Serialize<InputMojomType>(input, &buf, &data, &context);
// Set the subsequent area to a special value, so that we can find out if we
// mistakenly access the area.
@@ -67,7 +59,7 @@ U SerializeAndDeserialize(T input) {
OutputDataType output_data = reinterpret_cast<OutputDataType>(data);
U output;
- mojo::internal::Deserialize<U>(output_data, &output, &context);
+ mojo::internal::Deserialize<OutputMojomType>(output_data, &output, &context);
return std::move(output);
}
@@ -131,15 +123,15 @@ TEST_F(StructTest, Clone) {
TEST_F(StructTest, Serialization_Basic) {
RectPtr rect(MakeRect());
- size_t size = mojo::internal::PrepareToSerialize<RectPtr>(rect, nullptr);
+ size_t size = mojo::internal::PrepareToSerialize<RectDataView>(rect, nullptr);
EXPECT_EQ(8U + 16U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::Rect_Data* data;
- mojo::internal::Serialize<RectPtr>(rect, &buf, &data, nullptr);
+ mojo::internal::Serialize<RectDataView>(rect, &buf, &data, nullptr);
RectPtr rect2;
- mojo::internal::Deserialize<RectPtr>(data, &rect2, nullptr);
+ mojo::internal::Deserialize<RectDataView>(data, &rect2, nullptr);
CheckRect(*rect2);
}
@@ -160,19 +152,18 @@ TEST_F(StructTest, Construction_StructPointers) {
// Serialization test of a struct with struct pointers.
TEST_F(StructTest, Serialization_StructPointers) {
- RectPairPtr pair(RectPair::New());
- pair->first = MakeRect();
- pair->second = MakeRect();
+ RectPairPtr pair(RectPair::New(MakeRect(), MakeRect()));
- size_t size = mojo::internal::PrepareToSerialize<RectPairPtr>(pair, nullptr);
+ size_t size =
+ mojo::internal::PrepareToSerialize<RectPairDataView>(pair, nullptr);
EXPECT_EQ(8U + 16U + 2 * (8U + 16U), size);
mojo::internal::FixedBufferForTesting buf(size);
internal::RectPair_Data* data;
- mojo::internal::Serialize<RectPairPtr>(pair, &buf, &data, nullptr);
+ mojo::internal::Serialize<RectPairDataView>(pair, &buf, &data, nullptr);
RectPairPtr pair2;
- mojo::internal::Deserialize<RectPairPtr>(data, &pair2, nullptr);
+ mojo::internal::Deserialize<RectPairDataView>(data, &pair2, nullptr);
CheckRect(*pair2->first);
CheckRect(*pair2->second);
@@ -180,14 +171,15 @@ TEST_F(StructTest, Serialization_StructPointers) {
// Serialization test of a struct with an array member.
TEST_F(StructTest, Serialization_ArrayPointers) {
- NamedRegionPtr region(NamedRegion::New());
- region->name.emplace("region");
- region->rects.emplace(4);
- for (size_t i = 0; i < region->rects->size(); ++i)
- (*region->rects)[i] = MakeRect(static_cast<int32_t>(i) + 1);
+ std::vector<RectPtr> rects;
+ for (size_t i = 0; i < 4; ++i)
+ rects.push_back(MakeRect(static_cast<int32_t>(i) + 1));
+
+ NamedRegionPtr region(
+ NamedRegion::New(std::string("region"), std::move(rects)));
size_t size =
- mojo::internal::PrepareToSerialize<NamedRegionPtr>(region, nullptr);
+ mojo::internal::PrepareToSerialize<NamedRegionDataView>(region, nullptr);
EXPECT_EQ(8U + // header
8U + // name pointer
8U + // rects pointer
@@ -201,10 +193,10 @@ TEST_F(StructTest, Serialization_ArrayPointers) {
mojo::internal::FixedBufferForTesting buf(size);
internal::NamedRegion_Data* data;
- mojo::internal::Serialize<NamedRegionPtr>(region, &buf, &data, nullptr);
+ mojo::internal::Serialize<NamedRegionDataView>(region, &buf, &data, nullptr);
NamedRegionPtr region2;
- mojo::internal::Deserialize<NamedRegionPtr>(data, &region2, nullptr);
+ mojo::internal::Deserialize<NamedRegionDataView>(data, &region2, nullptr);
EXPECT_EQ("region", *region2->name);
@@ -220,7 +212,7 @@ TEST_F(StructTest, Serialization_NullArrayPointers) {
EXPECT_FALSE(region->rects);
size_t size =
- mojo::internal::PrepareToSerialize<NamedRegionPtr>(region, nullptr);
+ mojo::internal::PrepareToSerialize<NamedRegionDataView>(region, nullptr);
EXPECT_EQ(8U + // header
8U + // name pointer
8U, // rects pointer
@@ -228,10 +220,10 @@ TEST_F(StructTest, Serialization_NullArrayPointers) {
mojo::internal::FixedBufferForTesting buf(size);
internal::NamedRegion_Data* data;
- mojo::internal::Serialize<NamedRegionPtr>(region, &buf, &data, nullptr);
+ mojo::internal::Serialize<NamedRegionDataView>(region, &buf, &data, nullptr);
NamedRegionPtr region2;
- mojo::internal::Deserialize<NamedRegionPtr>(data, &region2, nullptr);
+ mojo::internal::Deserialize<NamedRegionDataView>(data, &region2, nullptr);
EXPECT_FALSE(region2->name);
EXPECT_FALSE(region2->rects);
@@ -240,10 +232,8 @@ TEST_F(StructTest, Serialization_NullArrayPointers) {
// Tests deserializing structs as a newer version.
TEST_F(StructTest, Versioning_OldToNew) {
{
- MultiVersionStructV0Ptr input(MultiVersionStructV0::New());
- input->f_int32 = 123;
- MultiVersionStructPtr expected_output(MultiVersionStruct::New());
- expected_output->f_int32 = 123;
+ MultiVersionStructV0Ptr input(MultiVersionStructV0::New(123));
+ MultiVersionStructPtr expected_output(MultiVersionStruct::New(123));
MultiVersionStructPtr output =
SerializeAndDeserialize<MultiVersionStructPtr>(std::move(input));
@@ -252,12 +242,9 @@ TEST_F(StructTest, Versioning_OldToNew) {
}
{
- MultiVersionStructV1Ptr input(MultiVersionStructV1::New());
- input->f_int32 = 123;
- input->f_rect = MakeRect(5);
- MultiVersionStructPtr expected_output(MultiVersionStruct::New());
- expected_output->f_int32 = 123;
- expected_output->f_rect = MakeRect(5);
+ MultiVersionStructV1Ptr input(MultiVersionStructV1::New(123, MakeRect(5)));
+ MultiVersionStructPtr expected_output(
+ MultiVersionStruct::New(123, MakeRect(5)));
MultiVersionStructPtr output =
SerializeAndDeserialize<MultiVersionStructPtr>(std::move(input));
@@ -266,14 +253,10 @@ TEST_F(StructTest, Versioning_OldToNew) {
}
{
- MultiVersionStructV3Ptr input(MultiVersionStructV3::New());
- input->f_int32 = 123;
- input->f_rect = MakeRect(5);
- input->f_string.emplace("hello");
- MultiVersionStructPtr expected_output(MultiVersionStruct::New());
- expected_output->f_int32 = 123;
- expected_output->f_rect = MakeRect(5);
- expected_output->f_string.emplace("hello");
+ MultiVersionStructV3Ptr input(
+ MultiVersionStructV3::New(123, MakeRect(5), std::string("hello")));
+ MultiVersionStructPtr expected_output(
+ MultiVersionStruct::New(123, MakeRect(5), std::string("hello")));
MultiVersionStructPtr output =
SerializeAndDeserialize<MultiVersionStructPtr>(std::move(input));
@@ -282,22 +265,10 @@ TEST_F(StructTest, Versioning_OldToNew) {
}
{
- MultiVersionStructV5Ptr input(MultiVersionStructV5::New());
- input->f_int32 = 123;
- input->f_rect = MakeRect(5);
- input->f_string.emplace("hello");
- input->f_array.emplace(3);
- (*input->f_array)[0] = 10;
- (*input->f_array)[1] = 9;
- (*input->f_array)[2] = 8;
- MultiVersionStructPtr expected_output(MultiVersionStruct::New());
- expected_output->f_int32 = 123;
- expected_output->f_rect = MakeRect(5);
- expected_output->f_string.emplace("hello");
- expected_output->f_array.emplace(3);
- (*expected_output->f_array)[0] = 10;
- (*expected_output->f_array)[1] = 9;
- (*expected_output->f_array)[2] = 8;
+ MultiVersionStructV5Ptr input(MultiVersionStructV5::New(
+ 123, MakeRect(5), std::string("hello"), std::vector<int8_t>{10, 9, 8}));
+ MultiVersionStructPtr expected_output(MultiVersionStruct::New(
+ 123, MakeRect(5), std::string("hello"), std::vector<int8_t>{10, 9, 8}));
MultiVersionStructPtr output =
SerializeAndDeserialize<MultiVersionStructPtr>(std::move(input));
@@ -306,25 +277,13 @@ TEST_F(StructTest, Versioning_OldToNew) {
}
{
- MultiVersionStructV7Ptr input(MultiVersionStructV7::New());
- input->f_int32 = 123;
- input->f_rect = MakeRect(5);
- input->f_string.emplace("hello");
- input->f_array.emplace(3);
- (*input->f_array)[0] = 10;
- (*input->f_array)[1] = 9;
- (*input->f_array)[2] = 8;
MessagePipe pipe;
- input->f_message_pipe = std::move(pipe.handle0);
-
- MultiVersionStructPtr expected_output(MultiVersionStruct::New());
- expected_output->f_int32 = 123;
- expected_output->f_rect = MakeRect(5);
- expected_output->f_string.emplace("hello");
- expected_output->f_array.emplace(3);
- (*expected_output->f_array)[0] = 10;
- (*expected_output->f_array)[1] = 9;
- (*expected_output->f_array)[2] = 8;
+ MultiVersionStructV7Ptr input(MultiVersionStructV7::New(
+ 123, MakeRect(5), std::string("hello"), std::vector<int8_t>{10, 9, 8},
+ std::move(pipe.handle0), false));
+
+ MultiVersionStructPtr expected_output(MultiVersionStruct::New(
+ 123, MakeRect(5), std::string("hello"), std::vector<int8_t>{10, 9, 8}));
// Save the raw handle value separately so that we can compare later.
MojoHandle expected_handle = input->f_message_pipe.get().value();
@@ -341,14 +300,8 @@ TEST_F(StructTest, Versioning_OldToNew) {
TEST_F(StructTest, Versioning_NewToOld) {
{
MultiVersionStructPtr input = MakeMultiVersionStruct();
- MultiVersionStructV7Ptr expected_output(MultiVersionStructV7::New());
- expected_output->f_int32 = 123;
- expected_output->f_rect = MakeRect(5);
- expected_output->f_string.emplace("hello");
- expected_output->f_array.emplace(3);
- (*expected_output->f_array)[0] = 10;
- (*expected_output->f_array)[1] = 9;
- (*expected_output->f_array)[2] = 8;
+ MultiVersionStructV7Ptr expected_output(MultiVersionStructV7::New(
+ 123, MakeRect(5), std::string("hello"), std::vector<int8_t>{10, 9, 8}));
// Save the raw handle value separately so that we can compare later.
MojoHandle expected_handle = input->f_message_pipe.get().value();
@@ -362,14 +315,8 @@ TEST_F(StructTest, Versioning_NewToOld) {
{
MultiVersionStructPtr input = MakeMultiVersionStruct();
- MultiVersionStructV5Ptr expected_output(MultiVersionStructV5::New());
- expected_output->f_int32 = 123;
- expected_output->f_rect = MakeRect(5);
- expected_output->f_string.emplace("hello");
- expected_output->f_array.emplace(3);
- (*expected_output->f_array)[0] = 10;
- (*expected_output->f_array)[1] = 9;
- (*expected_output->f_array)[2] = 8;
+ MultiVersionStructV5Ptr expected_output(MultiVersionStructV5::New(
+ 123, MakeRect(5), std::string("hello"), std::vector<int8_t>{10, 9, 8}));
MultiVersionStructV5Ptr output =
SerializeAndDeserialize<MultiVersionStructV5Ptr>(std::move(input));
@@ -379,10 +326,8 @@ TEST_F(StructTest, Versioning_NewToOld) {
{
MultiVersionStructPtr input = MakeMultiVersionStruct();
- MultiVersionStructV3Ptr expected_output(MultiVersionStructV3::New());
- expected_output->f_int32 = 123;
- expected_output->f_rect = MakeRect(5);
- expected_output->f_string.emplace("hello");
+ MultiVersionStructV3Ptr expected_output(
+ MultiVersionStructV3::New(123, MakeRect(5), std::string("hello")));
MultiVersionStructV3Ptr output =
SerializeAndDeserialize<MultiVersionStructV3Ptr>(std::move(input));
@@ -392,9 +337,8 @@ TEST_F(StructTest, Versioning_NewToOld) {
{
MultiVersionStructPtr input = MakeMultiVersionStruct();
- MultiVersionStructV1Ptr expected_output(MultiVersionStructV1::New());
- expected_output->f_int32 = 123;
- expected_output->f_rect = MakeRect(5);
+ MultiVersionStructV1Ptr expected_output(
+ MultiVersionStructV1::New(123, MakeRect(5)));
MultiVersionStructV1Ptr output =
SerializeAndDeserialize<MultiVersionStructV1Ptr>(std::move(input));
@@ -404,8 +348,7 @@ TEST_F(StructTest, Versioning_NewToOld) {
{
MultiVersionStructPtr input = MakeMultiVersionStruct();
- MultiVersionStructV0Ptr expected_output(MultiVersionStructV0::New());
- expected_output->f_int32 = 123;
+ MultiVersionStructV0Ptr expected_output(MultiVersionStructV0::New(123));
MultiVersionStructV0Ptr output =
SerializeAndDeserialize<MultiVersionStructV0Ptr>(std::move(input));
@@ -420,65 +363,66 @@ TEST_F(StructTest, Serialization_NativeStruct) {
{
// Serialization of a null native struct.
NativeStructPtr native;
- size_t size =
- mojo::internal::PrepareToSerialize<NativeStructPtr>(native, nullptr);
+ size_t size = mojo::internal::PrepareToSerialize<NativeStructDataView>(
+ native, nullptr);
EXPECT_EQ(0u, size);
mojo::internal::FixedBufferForTesting buf(size);
Data* data = nullptr;
- mojo::internal::Serialize<NativeStructPtr>(std::move(native), &buf, &data,
- nullptr);
+ mojo::internal::Serialize<NativeStructDataView>(std::move(native), &buf,
+ &data, nullptr);
EXPECT_EQ(nullptr, data);
NativeStructPtr output_native;
- mojo::internal::Deserialize<NativeStructPtr>(data, &output_native, nullptr);
+ mojo::internal::Deserialize<NativeStructDataView>(data, &output_native,
+ nullptr);
EXPECT_TRUE(output_native.is_null());
}
{
// Serialization of a native struct with null data.
NativeStructPtr native(NativeStruct::New());
- size_t size =
- mojo::internal::PrepareToSerialize<NativeStructPtr>(native, nullptr);
+ size_t size = mojo::internal::PrepareToSerialize<NativeStructDataView>(
+ native, nullptr);
EXPECT_EQ(0u, size);
mojo::internal::FixedBufferForTesting buf(size);
Data* data = nullptr;
- mojo::internal::Serialize<NativeStructPtr>(std::move(native), &buf, &data,
- nullptr);
+ mojo::internal::Serialize<NativeStructDataView>(std::move(native), &buf,
+ &data, nullptr);
EXPECT_EQ(nullptr, data);
NativeStructPtr output_native;
- mojo::internal::Deserialize<NativeStructPtr>(data, &output_native, nullptr);
+ mojo::internal::Deserialize<NativeStructDataView>(data, &output_native,
+ nullptr);
EXPECT_TRUE(output_native.is_null());
}
{
NativeStructPtr native(NativeStruct::New());
- native->data = Array<uint8_t>(2);
- native->data[0] = 'X';
- native->data[1] = 'Y';
+ native->data = std::vector<uint8_t>{'X', 'Y'};
- size_t size =
- mojo::internal::PrepareToSerialize<NativeStructPtr>(native, nullptr);
+ size_t size = mojo::internal::PrepareToSerialize<NativeStructDataView>(
+ native, nullptr);
EXPECT_EQ(16u, size);
mojo::internal::FixedBufferForTesting buf(size);
Data* data = nullptr;
- mojo::internal::Serialize<NativeStructPtr>(std::move(native), &buf, &data,
- nullptr);
+ mojo::internal::Serialize<NativeStructDataView>(std::move(native), &buf,
+ &data, nullptr);
EXPECT_NE(nullptr, data);
NativeStructPtr output_native;
- mojo::internal::Deserialize<NativeStructPtr>(data, &output_native, nullptr);
- EXPECT_FALSE(output_native.is_null());
- EXPECT_FALSE(output_native->data.is_null());
- EXPECT_EQ(2u, output_native->data.size());
- EXPECT_EQ('X', output_native->data[0]);
- EXPECT_EQ('Y', output_native->data[1]);
+ mojo::internal::Deserialize<NativeStructDataView>(data, &output_native,
+ nullptr);
+ ASSERT_TRUE(output_native);
+ ASSERT_FALSE(output_native->data->empty());
+ EXPECT_EQ(2u, output_native->data->size());
+ EXPECT_EQ('X', (*output_native->data)[0]);
+ EXPECT_EQ('Y', (*output_native->data)[1]);
}
}
@@ -486,7 +430,7 @@ TEST_F(StructTest, Serialization_PublicAPI) {
{
// A null struct pointer.
RectPtr null_struct;
- mojo::Array<uint8_t> data = Rect::Serialize(&null_struct);
+ auto data = Rect::Serialize(&null_struct);
EXPECT_TRUE(data.empty());
// Initialize it to non-null.
@@ -498,7 +442,7 @@ TEST_F(StructTest, Serialization_PublicAPI) {
{
// A struct with no fields.
EmptyStructPtr empty_struct(EmptyStruct::New());
- mojo::Array<uint8_t> data = EmptyStruct::Serialize(&empty_struct);
+ auto data = EmptyStruct::Serialize(&empty_struct);
EXPECT_FALSE(data.empty());
EmptyStructPtr output;
@@ -510,7 +454,7 @@ TEST_F(StructTest, Serialization_PublicAPI) {
// A simple struct.
RectPtr rect = MakeRect();
RectPtr cloned_rect = rect.Clone();
- mojo::Array<uint8_t> data = Rect::Serialize(&rect);
+ auto data = Rect::Serialize(&rect);
RectPtr output;
ASSERT_TRUE(Rect::Deserialize(std::move(data), &output));
@@ -519,17 +463,17 @@ TEST_F(StructTest, Serialization_PublicAPI) {
{
// A struct containing other objects.
- NamedRegionPtr region(NamedRegion::New());
- region->name.emplace("region");
- region->rects.emplace(3);
- for (size_t i = 0; i < region->rects->size(); ++i)
- (*region->rects)[i] = MakeRect(static_cast<int32_t>(i) + 1);
+ std::vector<RectPtr> rects;
+ for (size_t i = 0; i < 3; ++i)
+ rects.push_back(MakeRect(static_cast<int32_t>(i) + 1));
+ NamedRegionPtr region(
+ NamedRegion::New(std::string("region"), std::move(rects)));
NamedRegionPtr cloned_region = region.Clone();
- mojo::Array<uint8_t> data = NamedRegion::Serialize(&region);
+ auto data = NamedRegion::Serialize(&region);
// Make sure that the serialized result gets pointers encoded properly.
- mojo::Array<uint8_t> cloned_data = data.Clone();
+ auto cloned_data = data;
NamedRegionPtr output;
ASSERT_TRUE(NamedRegion::Deserialize(std::move(cloned_data), &output));
EXPECT_TRUE(output.Equals(cloned_region));
@@ -538,12 +482,34 @@ TEST_F(StructTest, Serialization_PublicAPI) {
{
// Deserialization failure.
RectPtr rect = MakeRect();
- mojo::Array<uint8_t> data = Rect::Serialize(&rect);
+ auto data = Rect::Serialize(&rect);
NamedRegionPtr output;
EXPECT_FALSE(NamedRegion::Deserialize(std::move(data), &output));
}
}
+TEST_F(StructTest, VersionedStructConstructor) {
+ auto reordered = ReorderedStruct::New(123, 456, 789);
+ EXPECT_EQ(123, reordered->a);
+ EXPECT_EQ(456, reordered->b);
+ EXPECT_EQ(789, reordered->c);
+
+ reordered = ReorderedStruct::New(123, 456);
+ EXPECT_EQ(123, reordered->a);
+ EXPECT_EQ(6, reordered->b);
+ EXPECT_EQ(456, reordered->c);
+
+ reordered = ReorderedStruct::New(123);
+ EXPECT_EQ(3, reordered->a);
+ EXPECT_EQ(6, reordered->b);
+ EXPECT_EQ(123, reordered->c);
+
+ reordered = ReorderedStruct::New();
+ EXPECT_EQ(3, reordered->a);
+ EXPECT_EQ(6, reordered->b);
+ EXPECT_EQ(1, reordered->c);
+}
+
} // namespace test
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/struct_with_traits.typemap b/mojo/public/cpp/bindings/tests/struct_with_traits.typemap
index da061de..752ce44 100644
--- a/mojo/public/cpp/bindings/tests/struct_with_traits.typemap
+++ b/mojo/public/cpp/bindings/tests/struct_with_traits.typemap
@@ -19,6 +19,8 @@ type_mappings = [
"mojo.test.EnumWithTraits=mojo::test::EnumWithTraitsImpl",
"mojo.test.StructWithTraits=mojo::test::StructWithTraitsImpl",
"mojo.test.NestedStructWithTraits=mojo::test::NestedStructWithTraitsImpl",
- "mojo.test.PassByValueStructWithTraits=mojo::test::PassByValueStructWithTraitsImpl[move_only]",
- "mojo.test.StructWithTraitsForUniquePtrTest=std::unique_ptr<int>[move_only]",
+ "mojo.test.TrivialStructWithTraits=mojo::test::TrivialStructWithTraitsImpl[copyable_pass_by_value]",
+ "mojo.test.MoveOnlyStructWithTraits=mojo::test::MoveOnlyStructWithTraitsImpl[move_only]",
+ "mojo.test.StructWithTraitsForUniquePtr=std::unique_ptr<int>[move_only,nullable_is_same_type]",
+ "mojo.test.UnionWithTraits=std::unique_ptr<mojo::test::UnionWithTraitsBase>[move_only,nullable_is_same_type]",
]
diff --git a/mojo/public/cpp/bindings/tests/struct_with_traits_impl.cc b/mojo/public/cpp/bindings/tests/struct_with_traits_impl.cc
index e99ec2a..cbdd4bf 100644
--- a/mojo/public/cpp/bindings/tests/struct_with_traits_impl.cc
+++ b/mojo/public/cpp/bindings/tests/struct_with_traits_impl.cc
@@ -18,12 +18,19 @@ StructWithTraitsImpl::~StructWithTraitsImpl() {}
StructWithTraitsImpl::StructWithTraitsImpl(const StructWithTraitsImpl& other) =
default;
-PassByValueStructWithTraitsImpl::PassByValueStructWithTraitsImpl() {}
+MoveOnlyStructWithTraitsImpl::MoveOnlyStructWithTraitsImpl() {}
-PassByValueStructWithTraitsImpl::PassByValueStructWithTraitsImpl(
- PassByValueStructWithTraitsImpl&& other) = default;
+MoveOnlyStructWithTraitsImpl::MoveOnlyStructWithTraitsImpl(
+ MoveOnlyStructWithTraitsImpl&& other) = default;
-PassByValueStructWithTraitsImpl::~PassByValueStructWithTraitsImpl() {}
+MoveOnlyStructWithTraitsImpl::~MoveOnlyStructWithTraitsImpl() {}
+
+MoveOnlyStructWithTraitsImpl& MoveOnlyStructWithTraitsImpl::operator=(
+ MoveOnlyStructWithTraitsImpl&& other) = default;
+
+UnionWithTraitsInt32::~UnionWithTraitsInt32() {}
+
+UnionWithTraitsStruct::~UnionWithTraitsStruct() {}
} // namespace test
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/struct_with_traits_impl.h b/mojo/public/cpp/bindings/tests/struct_with_traits_impl.h
index b62ba56..7b007cc 100644
--- a/mojo/public/cpp/bindings/tests/struct_with_traits_impl.h
+++ b/mojo/public/cpp/bindings/tests/struct_with_traits_impl.h
@@ -8,6 +8,7 @@
#include <stdint.h>
#include <map>
+#include <set>
#include <string>
#include <vector>
@@ -61,6 +62,11 @@ class StructWithTraitsImpl {
}
std::vector<std::string>& get_mutable_string_array() { return string_array_; }
+ const std::set<std::string>& get_string_set() const {
+ return string_set_;
+ }
+ std::set<std::string>& get_mutable_string_set() { return string_set_; }
+
const NestedStructWithTraitsImpl& get_struct() const { return struct_; }
NestedStructWithTraitsImpl& get_mutable_struct() { return struct_; }
@@ -86,24 +92,74 @@ class StructWithTraitsImpl {
uint64_t uint64_ = 0;
std::string string_;
std::vector<std::string> string_array_;
+ std::set<std::string> string_set_;
NestedStructWithTraitsImpl struct_;
std::vector<NestedStructWithTraitsImpl> struct_array_;
std::map<std::string, NestedStructWithTraitsImpl> struct_map_;
};
-// A type which knows how to look like a mojo::test::PassByValueStructWithTraits
+// A type which knows how to look like a mojo::test::TrivialStructWithTraits
+// mojom type by way of mojo::StructTraits.
+struct TrivialStructWithTraitsImpl {
+ int32_t value;
+};
+
+// A type which knows how to look like a mojo::test::MoveOnlyStructWithTraits
// mojom type by way of mojo::StructTraits.
-class PassByValueStructWithTraitsImpl {
+class MoveOnlyStructWithTraitsImpl {
public:
- PassByValueStructWithTraitsImpl();
- PassByValueStructWithTraitsImpl(PassByValueStructWithTraitsImpl&& other);
- ~PassByValueStructWithTraitsImpl();
+ MoveOnlyStructWithTraitsImpl();
+ MoveOnlyStructWithTraitsImpl(MoveOnlyStructWithTraitsImpl&& other);
+ ~MoveOnlyStructWithTraitsImpl();
ScopedHandle& get_mutable_handle() { return handle_; }
+ MoveOnlyStructWithTraitsImpl& operator=(MoveOnlyStructWithTraitsImpl&& other);
+
private:
ScopedHandle handle_;
- DISALLOW_COPY_AND_ASSIGN(PassByValueStructWithTraitsImpl);
+ DISALLOW_COPY_AND_ASSIGN(MoveOnlyStructWithTraitsImpl);
+};
+
+class UnionWithTraitsBase {
+ public:
+ enum class Type { INT32, STRUCT };
+
+ virtual ~UnionWithTraitsBase() {}
+
+ Type type() const { return type_; }
+
+ protected:
+ Type type_ = Type::INT32;
+};
+
+class UnionWithTraitsInt32 : public UnionWithTraitsBase {
+ public:
+ UnionWithTraitsInt32() {}
+ explicit UnionWithTraitsInt32(int32_t value) : value_(value) {}
+
+ ~UnionWithTraitsInt32() override;
+
+ int32_t value() const { return value_; }
+ void set_value(int32_t value) { value_ = value; }
+
+ private:
+ int32_t value_ = 0;
+};
+
+class UnionWithTraitsStruct : public UnionWithTraitsBase {
+ public:
+ UnionWithTraitsStruct() { type_ = Type::STRUCT; }
+ explicit UnionWithTraitsStruct(int32_t value) : struct_(value) {
+ type_ = Type::STRUCT;
+ }
+ ~UnionWithTraitsStruct() override;
+
+ NestedStructWithTraitsImpl& get_mutable_struct() { return struct_; }
+ const NestedStructWithTraitsImpl& get_struct() const { return struct_; }
+
+ private:
+ NestedStructWithTraitsImpl struct_;
};
} // namespace test
diff --git a/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.cc b/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.cc
index c11b37a..6b770b1 100644
--- a/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.cc
+++ b/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.cc
@@ -14,7 +14,7 @@ struct Context {
} // namespace
// static
-void* StructTraits<test::NestedStructWithTraits,
+void* StructTraits<test::NestedStructWithTraitsDataView,
test::NestedStructWithTraitsImpl>::
SetUpContext(const test::NestedStructWithTraitsImpl& input) {
Context* context = new Context;
@@ -23,7 +23,7 @@ void* StructTraits<test::NestedStructWithTraits,
}
// static
-void StructTraits<test::NestedStructWithTraits,
+void StructTraits<test::NestedStructWithTraitsDataView,
test::NestedStructWithTraitsImpl>::
TearDownContext(const test::NestedStructWithTraitsImpl& input,
void* context) {
@@ -33,7 +33,7 @@ void StructTraits<test::NestedStructWithTraits,
}
// static
-int32_t StructTraits<test::NestedStructWithTraits,
+int32_t StructTraits<test::NestedStructWithTraitsDataView,
test::NestedStructWithTraitsImpl>::
value(const test::NestedStructWithTraitsImpl& input, void* context) {
Context* context_obj = static_cast<Context*>(context);
@@ -42,7 +42,7 @@ int32_t StructTraits<test::NestedStructWithTraits,
}
// static
-bool StructTraits<test::NestedStructWithTraits,
+bool StructTraits<test::NestedStructWithTraitsDataView,
test::NestedStructWithTraitsImpl>::
Read(test::NestedStructWithTraits::DataView data,
test::NestedStructWithTraitsImpl* output) {
@@ -80,9 +80,9 @@ bool EnumTraits<test::EnumWithTraits, test::EnumWithTraitsImpl>::FromMojom(
}
// static
-bool StructTraits<test::StructWithTraits, test::StructWithTraitsImpl>::Read(
- test::StructWithTraits::DataView data,
- test::StructWithTraitsImpl* out) {
+bool StructTraits<test::StructWithTraitsDataView, test::StructWithTraitsImpl>::
+ Read(test::StructWithTraits::DataView data,
+ test::StructWithTraitsImpl* out) {
test::EnumWithTraitsImpl f_enum;
if (!data.ReadFEnum(&f_enum))
return false;
@@ -103,6 +103,16 @@ bool StructTraits<test::StructWithTraits, test::StructWithTraitsImpl>::Read(
if (!data.ReadFStringArray(&out->get_mutable_string_array()))
return false;
+ // We can't deserialize as a std::set, so we have to manually copy from the
+ // data view.
+ ArrayDataView<StringDataView> string_set_data_view;
+ data.GetFStringSetDataView(&string_set_data_view);
+ for (size_t i = 0; i < string_set_data_view.size(); ++i) {
+ std::string value;
+ string_set_data_view.Read(i, &value);
+ out->get_mutable_string_set().insert(value);
+ }
+
if (!data.ReadFStruct(&out->get_mutable_struct()))
return false;
@@ -116,10 +126,10 @@ bool StructTraits<test::StructWithTraits, test::StructWithTraitsImpl>::Read(
}
// static
-bool StructTraits<test::PassByValueStructWithTraits,
- test::PassByValueStructWithTraitsImpl>::
- Read(test::PassByValueStructWithTraits::DataView data,
- test::PassByValueStructWithTraitsImpl* out) {
+bool StructTraits<test::MoveOnlyStructWithTraitsDataView,
+ test::MoveOnlyStructWithTraitsImpl>::
+ Read(test::MoveOnlyStructWithTraits::DataView data,
+ test::MoveOnlyStructWithTraitsImpl* out) {
out->get_mutable_handle() = data.TakeFHandle();
return true;
}
diff --git a/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.h b/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.h
index 4550526..adcad8a 100644
--- a/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.h
+++ b/mojo/public/cpp/bindings/tests/struct_with_traits_impl_traits.h
@@ -18,7 +18,7 @@
namespace mojo {
template <>
-struct StructTraits<test::NestedStructWithTraits,
+struct StructTraits<test::NestedStructWithTraitsDataView,
test::NestedStructWithTraitsImpl> {
static void* SetUpContext(const test::NestedStructWithTraitsImpl& input);
static void TearDownContext(const test::NestedStructWithTraitsImpl& input,
@@ -27,7 +27,7 @@ struct StructTraits<test::NestedStructWithTraits,
static int32_t value(const test::NestedStructWithTraitsImpl& input,
void* context);
- static bool Read(test::NestedStructWithTraits::DataView data,
+ static bool Read(test::NestedStructWithTraitsDataView data,
test::NestedStructWithTraitsImpl* output);
};
@@ -39,9 +39,10 @@ struct EnumTraits<test::EnumWithTraits, test::EnumWithTraitsImpl> {
};
template <>
-struct StructTraits<test::StructWithTraits, test::StructWithTraitsImpl> {
+struct StructTraits<test::StructWithTraitsDataView,
+ test::StructWithTraitsImpl> {
// Deserialization to test::StructTraitsImpl.
- static bool Read(test::StructWithTraits::DataView data,
+ static bool Read(test::StructWithTraitsDataView data,
test::StructWithTraitsImpl* out);
// Fields in test::StructWithTraits.
@@ -76,6 +77,11 @@ struct StructTraits<test::StructWithTraits, test::StructWithTraitsImpl> {
return value.get_string_array();
}
+ static const std::set<std::string>& f_string_set(
+ const test::StructWithTraitsImpl& value) {
+ return value.get_string_set();
+ }
+
static const test::NestedStructWithTraitsImpl& f_struct(
const test::StructWithTraitsImpl& value) {
return value.get_struct();
@@ -93,31 +99,98 @@ struct StructTraits<test::StructWithTraits, test::StructWithTraitsImpl> {
};
template <>
-struct StructTraits<test::PassByValueStructWithTraits,
- test::PassByValueStructWithTraitsImpl> {
- // Deserialization to test::PassByValueStructTraitsImpl.
- static bool Read(test::PassByValueStructWithTraits::DataView data,
- test::PassByValueStructWithTraitsImpl* out);
+struct StructTraits<test::TrivialStructWithTraitsDataView,
+ test::TrivialStructWithTraitsImpl> {
+ // Deserialization to test::TrivialStructTraitsImpl.
+ static bool Read(test::TrivialStructWithTraitsDataView data,
+ test::TrivialStructWithTraitsImpl* out) {
+ out->value = data.value();
+ return true;
+ }
+
+ // Fields in test::TrivialStructWithTraits.
+ // See src/mojo/public/interfaces/bindings/tests/struct_with_traits.mojom.
+ static int32_t value(test::TrivialStructWithTraitsImpl& input) {
+ return input.value;
+ }
+};
+
+template <>
+struct StructTraits<test::MoveOnlyStructWithTraitsDataView,
+ test::MoveOnlyStructWithTraitsImpl> {
+ // Deserialization to test::MoveOnlyStructTraitsImpl.
+ static bool Read(test::MoveOnlyStructWithTraitsDataView data,
+ test::MoveOnlyStructWithTraitsImpl* out);
- // Fields in test::PassByValueStructWithTraits.
+ // Fields in test::MoveOnlyStructWithTraits.
// See src/mojo/public/interfaces/bindings/tests/struct_with_traits.mojom.
- static ScopedHandle f_handle(test::PassByValueStructWithTraitsImpl& value) {
+ static ScopedHandle f_handle(test::MoveOnlyStructWithTraitsImpl& value) {
return std::move(value.get_mutable_handle());
}
};
template <>
-struct StructTraits<test::StructWithTraitsForUniquePtrTest,
+struct StructTraits<test::StructWithTraitsForUniquePtrDataView,
std::unique_ptr<int>> {
+ static bool IsNull(const std::unique_ptr<int>& data) { return !data; }
+ static void SetToNull(std::unique_ptr<int>* data) { data->reset(); }
+
static int f_int32(const std::unique_ptr<int>& data) { return *data; }
- static bool Read(test::StructWithTraitsForUniquePtrTest::DataView data,
+ static bool Read(test::StructWithTraitsForUniquePtrDataView data,
std::unique_ptr<int>* out) {
out->reset(new int(data.f_int32()));
return true;
}
};
+template <>
+struct UnionTraits<test::UnionWithTraitsDataView,
+ std::unique_ptr<test::UnionWithTraitsBase>> {
+ static bool IsNull(const std::unique_ptr<test::UnionWithTraitsBase>& data) {
+ return !data;
+ }
+ static void SetToNull(std::unique_ptr<test::UnionWithTraitsBase>* data) {
+ data->reset();
+ }
+
+ static test::UnionWithTraitsDataView::Tag GetTag(
+ const std::unique_ptr<test::UnionWithTraitsBase>& data) {
+ if (data->type() == test::UnionWithTraitsBase::Type::INT32)
+ return test::UnionWithTraitsDataView::Tag::F_INT32;
+
+ return test::UnionWithTraitsDataView::Tag::F_STRUCT;
+ }
+
+ static int32_t f_int32(
+ const std::unique_ptr<test::UnionWithTraitsBase>& data) {
+ return static_cast<test::UnionWithTraitsInt32*>(data.get())->value();
+ }
+
+ static const test::NestedStructWithTraitsImpl& f_struct(
+ const std::unique_ptr<test::UnionWithTraitsBase>& data) {
+ return static_cast<test::UnionWithTraitsStruct*>(data.get())->get_struct();
+ }
+
+ static bool Read(test::UnionWithTraitsDataView data,
+ std::unique_ptr<test::UnionWithTraitsBase>* out) {
+ switch (data.tag()) {
+ case test::UnionWithTraitsDataView::Tag::F_INT32: {
+ out->reset(new test::UnionWithTraitsInt32(data.f_int32()));
+ return true;
+ }
+ case test::UnionWithTraitsDataView::Tag::F_STRUCT: {
+ auto* struct_object = new test::UnionWithTraitsStruct();
+ out->reset(struct_object);
+ return data.ReadFStruct(&struct_object->get_mutable_struct());
+ }
+ }
+
+ NOTREACHED();
+ return false;
+ }
+};
+
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_TESTS_STRUCT_WITH_TRAITS_IMPL_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/tests/sync_method_unittest.cc b/mojo/public/cpp/bindings/tests/sync_method_unittest.cc
index 1253930..d0e5f10 100644
--- a/mojo/public/cpp/bindings/tests/sync_method_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/sync_method_unittest.cc
@@ -267,13 +267,10 @@ class SyncMethodAssociatedTest : public SyncMethodTest {
protected:
void SetUp() override {
- master_impl_.reset(new TestSyncMasterImpl(GetProxy(&master_ptr_)));
+ master_impl_.reset(new TestSyncMasterImpl(MakeRequest(&master_ptr_)));
- master_ptr_.associated_group()->CreateAssociatedInterface(
- AssociatedGroup::WILL_PASS_REQUEST, &asso_ptr_info_, &asso_request_);
- master_ptr_.associated_group()->CreateAssociatedInterface(
- AssociatedGroup::WILL_PASS_PTR, &opposite_asso_ptr_info_,
- &opposite_asso_request_);
+ asso_request_ = MakeRequest(&asso_ptr_info_);
+ opposite_asso_request_ = MakeRequest(&opposite_asso_ptr_info_);
master_impl_->set_send_interface_handler(
[this](TestSyncAssociatedPtrInfo ptr) {
@@ -335,14 +332,14 @@ TestSync::AsyncEchoCallback BindAsyncEchoCallback(Func func) {
return base::Bind(&CallAsyncEchoCallback<Func>, func);
}
-// TestSync and TestSyncMaster exercise Router and MultiplexRouter,
-// respectively.
+// TestSync (without associated interfaces) and TestSyncMaster (with associated
+// interfaces) exercise MultiplexRouter with different configurations.
using InterfaceTypes = testing::Types<TestSync, TestSyncMaster>;
TYPED_TEST_CASE(SyncMethodCommonTest, InterfaceTypes);
TYPED_TEST(SyncMethodCommonTest, CallSyncMethodAsynchronously) {
InterfacePtr<TypeParam> ptr;
- typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+ typename ImplTraits<TypeParam>::Type impl(MakeRequest(&ptr));
base::RunLoop run_loop;
ptr->Echo(123, base::Bind(&ExpectValueAndRunClosure, 123,
@@ -357,7 +354,7 @@ TYPED_TEST(SyncMethodCommonTest, BasicSyncCalls) {
service_thread.thread()->task_runner()->PostTask(
FROM_HERE, base::Bind(&TestSyncServiceThread<TypeParam>::SetUp,
base::Unretained(&service_thread),
- base::Passed(GetProxy(&ptr))));
+ base::Passed(MakeRequest(&ptr))));
ASSERT_TRUE(ptr->Ping());
ASSERT_TRUE(service_thread.ping_called());
@@ -379,7 +376,7 @@ TYPED_TEST(SyncMethodCommonTest, ReenteredBySyncMethodBinding) {
InterfacePtr<TypeParam> ptr;
// The binding lives on the same thread as the interface pointer.
- typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+ typename ImplTraits<TypeParam>::Type impl(MakeRequest(&ptr));
int32_t output_value = -1;
ASSERT_TRUE(ptr->Echo(42, &output_value));
EXPECT_EQ(42, output_value);
@@ -390,7 +387,7 @@ TYPED_TEST(SyncMethodCommonTest, InterfacePtrDestroyedDuringSyncCall) {
// destroyed while it is waiting for a sync call response.
InterfacePtr<TypeParam> ptr;
- typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+ typename ImplTraits<TypeParam>::Type impl(MakeRequest(&ptr));
impl.set_ping_handler([&ptr](const TestSync::PingCallback& callback) {
ptr.reset();
callback.Run();
@@ -404,7 +401,7 @@ TYPED_TEST(SyncMethodCommonTest, BindingDestroyedDuringSyncCall) {
// corresponding interface pointer is waiting for a sync call response.
InterfacePtr<TypeParam> ptr;
- typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+ typename ImplTraits<TypeParam>::Type impl(MakeRequest(&ptr));
impl.set_ping_handler([&impl](const TestSync::PingCallback& callback) {
impl.binding()->Close();
callback.Run();
@@ -417,7 +414,7 @@ TYPED_TEST(SyncMethodCommonTest, NestedSyncCallsWithInOrderResponses) {
// already a sync call ongoing. The responses arrive in order.
InterfacePtr<TypeParam> ptr;
- typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+ typename ImplTraits<TypeParam>::Type impl(MakeRequest(&ptr));
// The same variable is used to store the output of the two sync calls, in
// order to test that responses are handled in the correct order.
@@ -443,7 +440,7 @@ TYPED_TEST(SyncMethodCommonTest, NestedSyncCallsWithOutOfOrderResponses) {
// already a sync call ongoing. The responses arrive out of order.
InterfacePtr<TypeParam> ptr;
- typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+ typename ImplTraits<TypeParam>::Type impl(MakeRequest(&ptr));
// The same variable is used to store the output of the two sync calls, in
// order to test that responses are handled in the correct order.
@@ -469,7 +466,7 @@ TYPED_TEST(SyncMethodCommonTest, AsyncResponseQueuedDuringSyncCall) {
// call, async responses are queued until the sync call completes.
InterfacePtr<TypeParam> ptr;
- typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+ typename ImplTraits<TypeParam>::Type impl(MakeRequest(&ptr));
int32_t async_echo_request_value = -1;
TestSync::AsyncEchoCallback async_echo_request_callback;
@@ -525,7 +522,7 @@ TYPED_TEST(SyncMethodCommonTest, AsyncRequestQueuedDuringSyncCall) {
// until the sync call completes.
InterfacePtr<TypeParam> ptr;
- typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+ typename ImplTraits<TypeParam>::Type impl(MakeRequest(&ptr));
bool async_echo_request_dispatched = false;
impl.set_async_echo_handler([&async_echo_request_dispatched](
@@ -576,7 +573,7 @@ TYPED_TEST(SyncMethodCommonTest,
// notification is delayed until all the queued messages are processed.
InterfacePtr<TypeParam> ptr;
- typename ImplTraits<TypeParam>::Type impl(GetProxy(&ptr));
+ typename ImplTraits<TypeParam>::Type impl(MakeRequest(&ptr));
int32_t async_echo_request_value = -1;
TestSync::AsyncEchoCallback async_echo_request_callback;
diff --git a/mojo/public/cpp/bindings/tests/test_native_types_blink.typemap b/mojo/public/cpp/bindings/tests/test_native_types_blink.typemap
index 258acd5..1bdfbbc 100644
--- a/mojo/public/cpp/bindings/tests/test_native_types_blink.typemap
+++ b/mojo/public/cpp/bindings/tests/test_native_types_blink.typemap
@@ -13,5 +13,5 @@ deps = [
type_mappings = [
"mojo.test.PickledEnum=mojo::test::PickledEnumBlink",
- "mojo.test.PickledStruct=mojo::test::PickledStructBlink"
+ "mojo.test.PickledStruct=mojo::test::PickledStructBlink[move_only]",
]
diff --git a/mojo/public/cpp/bindings/tests/test_native_types_chromium.typemap b/mojo/public/cpp/bindings/tests/test_native_types_chromium.typemap
index a4c0f5a..50e8076 100644
--- a/mojo/public/cpp/bindings/tests/test_native_types_chromium.typemap
+++ b/mojo/public/cpp/bindings/tests/test_native_types_chromium.typemap
@@ -3,8 +3,7 @@
# found in the LICENSE file.
mojom = "//mojo/public/interfaces/bindings/tests/test_native_types.mojom"
-public_headers =
- [ "//mojo/public/cpp/bindings/tests/pickled_types_chromium.h" ]
+public_headers = [ "//mojo/public/cpp/bindings/tests/pickled_types_chromium.h" ]
sources = [
"//mojo/public/cpp/bindings/tests/pickled_types_chromium.cc",
]
@@ -14,5 +13,5 @@ deps = [
type_mappings = [
"mojo.test.PickledEnum=mojo::test::PickledEnumChromium",
- "mojo.test.PickledStruct=mojo::test::PickledStructChromium"
+ "mojo.test.PickledStruct=mojo::test::PickledStructChromium[move_only]",
]
diff --git a/mojo/public/cpp/bindings/tests/type_conversion_unittest.cc b/mojo/public/cpp/bindings/tests/type_conversion_unittest.cc
index 7eb24ea..b0124aa 100644
--- a/mojo/public/cpp/bindings/tests/type_conversion_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/type_conversion_unittest.cc
@@ -23,8 +23,8 @@ struct RedmondNamedRegion {
std::vector<RedmondRect> rects;
};
-bool AreEqualRectArrays(const Array<test::RectPtr>& rects1,
- const Array<test::RectPtr>& rects2) {
+bool AreEqualRectArrays(const std::vector<test::RectPtr>& rects1,
+ const std::vector<test::RectPtr>& rects2) {
if (rects1.size() != rects2.size())
return false;
@@ -44,12 +44,8 @@ bool AreEqualRectArrays(const Array<test::RectPtr>& rects1,
template <>
struct TypeConverter<test::RectPtr, RedmondRect> {
static test::RectPtr Convert(const RedmondRect& input) {
- test::RectPtr rect(test::Rect::New());
- rect->x = input.left;
- rect->y = input.top;
- rect->width = input.right - input.left;
- rect->height = input.bottom - input.top;
- return rect;
+ return test::Rect::New(input.left, input.top, input.right - input.left,
+ input.bottom - input.top);
}
};
@@ -68,10 +64,8 @@ struct TypeConverter<RedmondRect, test::RectPtr> {
template <>
struct TypeConverter<test::NamedRegionPtr, RedmondNamedRegion> {
static test::NamedRegionPtr Convert(const RedmondNamedRegion& input) {
- test::NamedRegionPtr region(test::NamedRegion::New());
- region->name.emplace(input.name);
- region->rects = Array<test::RectPtr>::From(input.rects).PassStorage();
- return region;
+ return test::NamedRegion::New(
+ input.name, ConvertTo<std::vector<test::RectPtr>>(input.rects));
}
};
@@ -93,53 +87,8 @@ struct TypeConverter<RedmondNamedRegion, test::NamedRegionPtr> {
namespace test {
namespace {
-TEST(TypeConversionTest, String) {
- const char kText[6] = "hello";
-
- String a = std::string(kText);
- String b(kText);
- String c(static_cast<const char*>(kText));
-
- EXPECT_EQ(std::string(kText), a.To<std::string>());
- EXPECT_EQ(std::string(kText), b.To<std::string>());
- EXPECT_EQ(std::string(kText), c.To<std::string>());
-}
-
-TEST(TypeConversionTest, String_Null) {
- String a(nullptr);
- EXPECT_TRUE(a.is_null());
- EXPECT_EQ(std::string(), a.To<std::string>());
-
- String b = String::From(static_cast<const char*>(nullptr));
- EXPECT_TRUE(b.is_null());
-}
-
-TEST(TypeConversionTest, String_Empty) {
- String a = "";
- EXPECT_EQ(std::string(), a.To<std::string>());
-
- String b = std::string();
- EXPECT_FALSE(b.is_null());
- EXPECT_EQ(std::string(), b.To<std::string>());
-}
-
-TEST(TypeConversionTest, StringWithEmbeddedNull) {
- const std::string kText("hel\0lo", 6);
-
- String a(kText);
- EXPECT_EQ(kText, a.To<std::string>());
-
- // Expect truncation:
- String b(kText.c_str());
- EXPECT_EQ(std::string("hel"), b.To<std::string>());
-}
-
TEST(TypeConversionTest, CustomTypeConverter) {
- RectPtr rect(Rect::New());
- rect->x = 10;
- rect->y = 20;
- rect->width = 50;
- rect->height = 45;
+ RectPtr rect(Rect::New(10, 20, 50, 45));
RedmondRect rr = rect.To<RedmondRect>();
EXPECT_EQ(10, rr.left);
@@ -155,9 +104,9 @@ TEST(TypeConversionTest, CustomTypeConverter) {
}
TEST(TypeConversionTest, CustomTypeConverter_Array_Null) {
- Array<RectPtr> rects;
+ std::vector<RectPtr> rects;
- std::vector<RedmondRect> redmond_rects = rects.To<std::vector<RedmondRect>>();
+ auto redmond_rects = ConvertTo<std::vector<RedmondRect>>(rects);
EXPECT_TRUE(redmond_rects.empty());
}
@@ -165,7 +114,7 @@ TEST(TypeConversionTest, CustomTypeConverter_Array_Null) {
TEST(TypeConversionTest, CustomTypeConverter_Array) {
const RedmondRect kBase = {10, 20, 30, 40};
- Array<RectPtr> rects(10);
+ std::vector<RectPtr> rects(10);
for (size_t i = 0; i < rects.size(); ++i) {
RedmondRect rr = kBase;
rr.left += static_cast<int32_t>(i);
@@ -173,9 +122,9 @@ TEST(TypeConversionTest, CustomTypeConverter_Array) {
rects[i] = Rect::From(rr);
}
- std::vector<RedmondRect> redmond_rects = rects.To<std::vector<RedmondRect>>();
+ auto redmond_rects = ConvertTo<std::vector<RedmondRect>>(rects);
- Array<RectPtr> rects2 = Array<RectPtr>::From(redmond_rects);
+ auto rects2 = ConvertTo<std::vector<RectPtr>>(redmond_rects);
EXPECT_TRUE(AreEqualRectArrays(rects, rects2));
}
diff --git a/mojo/public/cpp/bindings/tests/union_unittest.cc b/mojo/public/cpp/bindings/tests/union_unittest.cc
index d65a348..bdf27df 100644
--- a/mojo/public/cpp/bindings/tests/union_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/union_unittest.cc
@@ -9,14 +9,12 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
-#include "mojo/public/cpp/bindings/array.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/lib/array_internal.h"
#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
#include "mojo/public/cpp/bindings/lib/serialization.h"
#include "mojo/public/cpp/bindings/lib/validation_context.h"
#include "mojo/public/cpp/bindings/lib/validation_errors.h"
-#include "mojo/public/cpp/bindings/string.h"
#include "mojo/public/cpp/test_support/test_utils.h"
#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
#include "mojo/public/interfaces/bindings/tests/test_unions.mojom.h"
@@ -123,16 +121,17 @@ TEST(UnionTest, PodSerialization) {
pod1->set_f_int8(10);
mojo::internal::SerializationContext context;
- size_t size =
- mojo::internal::PrepareToSerialize<PodUnionPtr>(pod1, false, &context);
+ size_t size = mojo::internal::PrepareToSerialize<PodUnionDataView>(
+ pod1, false, &context);
EXPECT_EQ(16U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::PodUnion_Data* data = nullptr;
- mojo::internal::Serialize<PodUnionPtr>(pod1, &buf, &data, false, &context);
+ mojo::internal::Serialize<PodUnionDataView>(pod1, &buf, &data, false,
+ &context);
PodUnionPtr pod2;
- mojo::internal::Deserialize<PodUnionPtr>(data, &pod2, &context);
+ mojo::internal::Deserialize<PodUnionDataView>(data, &pod2, &context);
EXPECT_EQ(10, pod2->get_f_int8());
EXPECT_TRUE(pod2->is_f_int8());
@@ -143,16 +142,17 @@ TEST(UnionTest, EnumSerialization) {
PodUnionPtr pod1(PodUnion::New());
pod1->set_f_enum(AnEnum::SECOND);
- size_t size =
- mojo::internal::PrepareToSerialize<PodUnionPtr>(pod1, false, nullptr);
+ size_t size = mojo::internal::PrepareToSerialize<PodUnionDataView>(
+ pod1, false, nullptr);
EXPECT_EQ(16U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::PodUnion_Data* data = nullptr;
- mojo::internal::Serialize<PodUnionPtr>(pod1, &buf, &data, false, nullptr);
+ mojo::internal::Serialize<PodUnionDataView>(pod1, &buf, &data, false,
+ nullptr);
PodUnionPtr pod2;
- mojo::internal::Deserialize<PodUnionPtr>(data, &pod2, nullptr);
+ mojo::internal::Deserialize<PodUnionDataView>(data, &pod2, nullptr);
EXPECT_EQ(AnEnum::SECOND, pod2->get_f_enum());
EXPECT_TRUE(pod2->is_f_enum());
@@ -164,16 +164,16 @@ TEST(UnionTest, PodValidation) {
pod->set_f_int8(10);
size_t size =
- mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr);
+ mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false, nullptr);
EXPECT_EQ(16U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::PodUnion_Data* data = nullptr;
- mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, false, nullptr);
+ mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, false, nullptr);
void* raw_buf = buf.Leak();
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
EXPECT_TRUE(
internal::PodUnion_Data::Validate(raw_buf, &validation_context, false));
free(raw_buf);
@@ -183,17 +183,17 @@ TEST(UnionTest, SerializeNotNull) {
PodUnionPtr pod(PodUnion::New());
pod->set_f_int8(0);
size_t size =
- mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr);
+ mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false, nullptr);
mojo::internal::FixedBufferForTesting buf(size);
internal::PodUnion_Data* data = nullptr;
- mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, false, nullptr);
+ mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, false, nullptr);
EXPECT_FALSE(data->is_null());
}
TEST(UnionTest, SerializeIsNullInlined) {
PodUnionPtr pod;
size_t size =
- mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr);
+ mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false, nullptr);
EXPECT_EQ(16U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf);
@@ -203,28 +203,28 @@ TEST(UnionTest, SerializeIsNullInlined) {
data->tag = PodUnion::Tag::F_UINT16;
data->data.f_f_int16 = 20;
- mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, true, nullptr);
+ mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, true, nullptr);
EXPECT_TRUE(data->is_null());
PodUnionPtr pod2;
- mojo::internal::Deserialize<PodUnionPtr>(data, &pod2, nullptr);
+ mojo::internal::Deserialize<PodUnionDataView>(data, &pod2, nullptr);
EXPECT_TRUE(pod2.is_null());
}
TEST(UnionTest, SerializeIsNullNotInlined) {
PodUnionPtr pod;
size_t size =
- mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr);
+ mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false, nullptr);
EXPECT_EQ(16U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::PodUnion_Data* data = nullptr;
- mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, false, nullptr);
+ mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, false, nullptr);
EXPECT_EQ(nullptr, data);
}
TEST(UnionTest, NullValidation) {
void* buf = nullptr;
- mojo::internal::ValidationContext validation_context(buf, 0, 0);
+ mojo::internal::ValidationContext validation_context(buf, 0, 0, 0);
EXPECT_TRUE(internal::PodUnion_Data::Validate(
buf, &validation_context, false));
}
@@ -239,7 +239,7 @@ TEST(UnionTest, OutOfAlignmentValidation) {
internal::PodUnion_Data* data =
reinterpret_cast<internal::PodUnion_Data*>(buf);
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
EXPECT_FALSE(internal::PodUnion_Data::Validate(
buf, &validation_context, false));
free(raw_buf);
@@ -250,7 +250,7 @@ TEST(UnionTest, OOBValidation) {
mojo::internal::FixedBufferForTesting buf(size);
internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf);
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
void* raw_buf = buf.Leak();
EXPECT_FALSE(
internal::PodUnion_Data::Validate(raw_buf, &validation_context, false));
@@ -263,7 +263,7 @@ TEST(UnionTest, UnknownTagValidation) {
internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf);
data->tag = static_cast<internal::PodUnion_Data::PodUnion_Tag>(0xFFFFFF);
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
void* raw_buf = buf.Leak();
EXPECT_FALSE(
internal::PodUnion_Data::Validate(raw_buf, &validation_context, false));
@@ -275,16 +275,16 @@ TEST(UnionTest, UnknownEnumValueValidation) {
pod->set_f_enum(static_cast<AnEnum>(0xFFFF));
size_t size =
- mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr);
+ mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false, nullptr);
EXPECT_EQ(16U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::PodUnion_Data* data = nullptr;
- mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, false, nullptr);
+ mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, false, nullptr);
void* raw_buf = buf.Leak();
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
EXPECT_FALSE(
internal::PodUnion_Data::Validate(raw_buf, &validation_context, false));
free(raw_buf);
@@ -295,16 +295,16 @@ TEST(UnionTest, UnknownExtensibleEnumValueValidation) {
pod->set_f_extensible_enum(static_cast<AnExtensibleEnum>(0xFFFF));
size_t size =
- mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr);
+ mojo::internal::PrepareToSerialize<PodUnionDataView>(pod, false, nullptr);
EXPECT_EQ(16U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::PodUnion_Data* data = nullptr;
- mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, false, nullptr);
+ mojo::internal::Serialize<PodUnionDataView>(pod, &buf, &data, false, nullptr);
void* raw_buf = buf.Leak();
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
EXPECT_TRUE(
internal::PodUnion_Data::Validate(raw_buf, &validation_context, false));
free(raw_buf);
@@ -313,7 +313,7 @@ TEST(UnionTest, UnknownExtensibleEnumValueValidation) {
TEST(UnionTest, StringGetterSetter) {
ObjectUnionPtr pod(ObjectUnion::New());
- String hello("hello world");
+ std::string hello("hello world");
pod->set_f_string(hello);
EXPECT_EQ(hello, pod->get_f_string());
EXPECT_TRUE(pod->is_f_string());
@@ -335,7 +335,7 @@ TEST(UnionTest, StringEquals) {
TEST(UnionTest, StringClone) {
ObjectUnionPtr pod(ObjectUnion::New());
- String hello("hello world");
+ std::string hello("hello world");
pod->set_f_string(hello);
ObjectUnionPtr pod_clone = pod.Clone();
EXPECT_EQ(hello, pod_clone->get_f_string());
@@ -346,17 +346,18 @@ TEST(UnionTest, StringClone) {
TEST(UnionTest, StringSerialization) {
ObjectUnionPtr pod1(ObjectUnion::New());
- String hello("hello world");
+ std::string hello("hello world");
pod1->set_f_string(hello);
- size_t size =
- mojo::internal::PrepareToSerialize<ObjectUnionPtr>(pod1, false, nullptr);
+ size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
+ pod1, false, nullptr);
mojo::internal::FixedBufferForTesting buf(size);
internal::ObjectUnion_Data* data = nullptr;
- mojo::internal::Serialize<ObjectUnionPtr>(pod1, &buf, &data, false, nullptr);
+ mojo::internal::Serialize<ObjectUnionDataView>(pod1, &buf, &data, false,
+ nullptr);
ObjectUnionPtr pod2;
- mojo::internal::Deserialize<ObjectUnionPtr>(data, &pod2, nullptr);
+ mojo::internal::Deserialize<ObjectUnionDataView>(data, &pod2, nullptr);
EXPECT_EQ(hello, pod2->get_f_string());
EXPECT_TRUE(pod2->is_f_string());
EXPECT_EQ(pod2->which(), ObjectUnion::Tag::F_STRING);
@@ -369,7 +370,7 @@ TEST(UnionTest, NullStringValidation) {
data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING;
data->data.unknown = 0x0;
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
void* raw_buf = buf.Leak();
EXPECT_FALSE(internal::ObjectUnion_Data::Validate(
raw_buf, &validation_context, false));
@@ -383,7 +384,7 @@ TEST(UnionTest, StringPointerOverflowValidation) {
data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING;
data->data.unknown = 0xFFFFFFFFFFFFFFFF;
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
void* raw_buf = buf.Leak();
EXPECT_FALSE(internal::ObjectUnion_Data::Validate(
raw_buf, &validation_context, false));
@@ -402,7 +403,7 @@ TEST(UnionTest, StringValidateOOB) {
reinterpret_cast<mojo::internal::ArrayHeader*>(ptr + *ptr);
array_header->num_bytes = 20; // This should go out of bounds.
array_header->num_elements = 20;
- mojo::internal::ValidationContext validation_context(data, 32, 0);
+ mojo::internal::ValidationContext validation_context(data, 32, 0, 0);
void* raw_buf = buf.Leak();
EXPECT_FALSE(internal::ObjectUnion_Data::Validate(
raw_buf, &validation_context, false));
@@ -425,7 +426,7 @@ TEST(UnionTest, PodUnionInArray) {
}
TEST(UnionTest, PodUnionInArraySerialization) {
- Array<PodUnionPtr> array(2);
+ std::vector<PodUnionPtr> array(2);
array[0] = PodUnion::New();
array[1] = PodUnion::New();
@@ -434,17 +435,19 @@ TEST(UnionTest, PodUnionInArraySerialization) {
EXPECT_EQ(2U, array.size());
size_t size =
- mojo::internal::PrepareToSerialize<Array<PodUnionPtr>>(array, nullptr);
+ mojo::internal::PrepareToSerialize<ArrayDataView<PodUnionDataView>>(
+ array, nullptr);
EXPECT_EQ(40U, size);
mojo::internal::FixedBufferForTesting buf(size);
mojo::internal::Array_Data<internal::PodUnion_Data>* data;
mojo::internal::ContainerValidateParams validate_params(0, false, nullptr);
- mojo::internal::Serialize<Array<PodUnionPtr>>(array, &buf, &data,
- &validate_params, nullptr);
+ mojo::internal::Serialize<ArrayDataView<PodUnionDataView>>(
+ array, &buf, &data, &validate_params, nullptr);
- Array<PodUnionPtr> array2;
- mojo::internal::Deserialize<Array<PodUnionPtr>>(data, &array2, nullptr);
+ std::vector<PodUnionPtr> array2;
+ mojo::internal::Deserialize<ArrayDataView<PodUnionDataView>>(data, &array2,
+ nullptr);
EXPECT_EQ(2U, array2.size());
@@ -453,24 +456,26 @@ TEST(UnionTest, PodUnionInArraySerialization) {
}
TEST(UnionTest, PodUnionInArraySerializationWithNull) {
- Array<PodUnionPtr> array(2);
+ std::vector<PodUnionPtr> array(2);
array[0] = PodUnion::New();
array[0]->set_f_int8(10);
EXPECT_EQ(2U, array.size());
size_t size =
- mojo::internal::PrepareToSerialize<Array<PodUnionPtr>>(array, nullptr);
+ mojo::internal::PrepareToSerialize<ArrayDataView<PodUnionDataView>>(
+ array, nullptr);
EXPECT_EQ(40U, size);
mojo::internal::FixedBufferForTesting buf(size);
mojo::internal::Array_Data<internal::PodUnion_Data>* data;
mojo::internal::ContainerValidateParams validate_params(0, true, nullptr);
- mojo::internal::Serialize<Array<PodUnionPtr>>(array, &buf, &data,
- &validate_params, nullptr);
+ mojo::internal::Serialize<ArrayDataView<PodUnionDataView>>(
+ array, &buf, &data, &validate_params, nullptr);
- Array<PodUnionPtr> array2;
- mojo::internal::Deserialize<Array<PodUnionPtr>>(data, &array2, nullptr);
+ std::vector<PodUnionPtr> array2;
+ mojo::internal::Deserialize<ArrayDataView<PodUnionDataView>>(data, &array2,
+ nullptr);
EXPECT_EQ(2U, array2.size());
@@ -479,7 +484,7 @@ TEST(UnionTest, PodUnionInArraySerializationWithNull) {
}
TEST(UnionTest, ObjectUnionInArraySerialization) {
- Array<ObjectUnionPtr> array(2);
+ std::vector<ObjectUnionPtr> array(2);
array[0] = ObjectUnion::New();
array[1] = ObjectUnion::New();
@@ -488,15 +493,16 @@ TEST(UnionTest, ObjectUnionInArraySerialization) {
EXPECT_EQ(2U, array.size());
size_t size =
- mojo::internal::PrepareToSerialize<Array<ObjectUnionPtr>>(array, nullptr);
+ mojo::internal::PrepareToSerialize<ArrayDataView<ObjectUnionDataView>>(
+ array, nullptr);
EXPECT_EQ(72U, size);
mojo::internal::FixedBufferForTesting buf(size);
mojo::internal::Array_Data<internal::ObjectUnion_Data>* data;
mojo::internal::ContainerValidateParams validate_params(0, false, nullptr);
- mojo::internal::Serialize<Array<ObjectUnionPtr>>(array, &buf, &data,
- &validate_params, nullptr);
+ mojo::internal::Serialize<ArrayDataView<ObjectUnionDataView>>(
+ array, &buf, &data, &validate_params, nullptr);
std::vector<char> new_buf;
new_buf.resize(size);
@@ -509,17 +515,18 @@ TEST(UnionTest, ObjectUnionInArraySerialization) {
reinterpret_cast<mojo::internal::Array_Data<internal::ObjectUnion_Data>*>(
new_buf.data());
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
ASSERT_TRUE(mojo::internal::Array_Data<internal::ObjectUnion_Data>::Validate(
data, &validation_context, &validate_params));
- Array<ObjectUnionPtr> array2;
- mojo::internal::Deserialize<Array<ObjectUnionPtr>>(data, &array2, nullptr);
+ std::vector<ObjectUnionPtr> array2;
+ mojo::internal::Deserialize<ArrayDataView<ObjectUnionDataView>>(data, &array2,
+ nullptr);
EXPECT_EQ(2U, array2.size());
- EXPECT_EQ(String("hello"), array2[0]->get_f_string());
- EXPECT_EQ(String("world"), array2[1]->get_f_string());
+ EXPECT_EQ("hello", array2[0]->get_f_string());
+ EXPECT_EQ("world", array2[1]->get_f_string());
}
// TODO(azani): Move back in struct_unittest.cc when possible.
@@ -540,16 +547,17 @@ TEST(UnionTest, Serialization_UnionOfPods) {
small_struct->pod_union->set_f_int32(10);
mojo::internal::SerializationContext context;
- size_t size = mojo::internal::PrepareToSerialize<SmallStructPtr>(small_struct,
- &context);
+ size_t size = mojo::internal::PrepareToSerialize<SmallStructDataView>(
+ small_struct, &context);
mojo::internal::FixedBufferForTesting buf(size);
internal::SmallStruct_Data* data = nullptr;
- mojo::internal::Serialize<SmallStructPtr>(small_struct, &buf, &data,
- &context);
+ mojo::internal::Serialize<SmallStructDataView>(small_struct, &buf, &data,
+ &context);
SmallStructPtr deserialized;
- mojo::internal::Deserialize<SmallStructPtr>(data, &deserialized, &context);
+ mojo::internal::Deserialize<SmallStructDataView>(data, &deserialized,
+ &context);
EXPECT_EQ(10, deserialized->pod_union->get_f_int32());
}
@@ -558,19 +566,20 @@ TEST(UnionTest, Serialization_UnionOfPods) {
TEST(UnionTest, Serialization_UnionOfObjects) {
SmallObjStructPtr obj_struct(SmallObjStruct::New());
obj_struct->obj_union = ObjectUnion::New();
- String hello("hello world");
+ std::string hello("hello world");
obj_struct->obj_union->set_f_string(hello);
- size_t size = mojo::internal::PrepareToSerialize<SmallObjStructPtr>(
+ size_t size = mojo::internal::PrepareToSerialize<SmallObjStructDataView>(
obj_struct, nullptr);
mojo::internal::FixedBufferForTesting buf(size);
internal::SmallObjStruct_Data* data = nullptr;
- mojo::internal::Serialize<SmallObjStructPtr>(obj_struct, &buf, &data,
- nullptr);
+ mojo::internal::Serialize<SmallObjStructDataView>(obj_struct, &buf, &data,
+ nullptr);
SmallObjStructPtr deserialized;
- mojo::internal::Deserialize<SmallObjStructPtr>(data, &deserialized, nullptr);
+ mojo::internal::Deserialize<SmallObjStructDataView>(data, &deserialized,
+ nullptr);
EXPECT_EQ(hello, deserialized->obj_union->get_f_string());
}
@@ -582,18 +591,17 @@ TEST(UnionTest, Validation_UnionsInStruct) {
small_struct->pod_union->set_f_int32(10);
mojo::internal::SerializationContext context;
- size_t size = mojo::internal::PrepareToSerialize<SmallStructPtr>(small_struct,
- &context);
+ size_t size = mojo::internal::PrepareToSerialize<SmallStructDataView>(
+ small_struct, &context);
mojo::internal::FixedBufferForTesting buf(size);
internal::SmallStruct_Data* data = nullptr;
- mojo::internal::Serialize<SmallStructPtr>(small_struct, &buf, &data,
- &context);
-
+ mojo::internal::Serialize<SmallStructDataView>(small_struct, &buf, &data,
+ &context);
void* raw_buf = buf.Leak();
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
EXPECT_TRUE(internal::SmallStruct_Data::Validate(
raw_buf, &validation_context));
free(raw_buf);
@@ -606,18 +614,18 @@ TEST(UnionTest, Validation_PodUnionInStruct_Failure) {
small_struct->pod_union->set_f_int32(10);
mojo::internal::SerializationContext context;
- size_t size = mojo::internal::PrepareToSerialize<SmallStructPtr>(small_struct,
- &context);
+ size_t size = mojo::internal::PrepareToSerialize<SmallStructDataView>(
+ small_struct, &context);
mojo::internal::FixedBufferForTesting buf(size);
internal::SmallStruct_Data* data = nullptr;
- mojo::internal::Serialize<SmallStructPtr>(small_struct, &buf, &data,
- &context);
+ mojo::internal::Serialize<SmallStructDataView>(small_struct, &buf, &data,
+ &context);
data->pod_union.tag = static_cast<internal::PodUnion_Data::PodUnion_Tag>(100);
void* raw_buf = buf.Leak();
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
EXPECT_FALSE(internal::SmallStruct_Data::Validate(
raw_buf, &validation_context));
free(raw_buf);
@@ -629,7 +637,7 @@ TEST(UnionTest, Validation_NullUnion_Failure) {
SmallStructNonNullableUnion::New());
size_t size =
- mojo::internal::PrepareToSerialize<SmallStructNonNullableUnionPtr>(
+ mojo::internal::PrepareToSerialize<SmallStructNonNullableUnionDataView>(
small_struct, nullptr);
mojo::internal::FixedBufferForTesting buf(size);
@@ -638,7 +646,7 @@ TEST(UnionTest, Validation_NullUnion_Failure) {
void* raw_buf = buf.Leak();
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
EXPECT_FALSE(internal::SmallStructNonNullableUnion_Data::Validate(
raw_buf, &validation_context));
free(raw_buf);
@@ -649,17 +657,17 @@ TEST(UnionTest, Validation_NullableUnion) {
SmallStructPtr small_struct(SmallStruct::New());
mojo::internal::SerializationContext context;
- size_t size = mojo::internal::PrepareToSerialize<SmallStructPtr>(small_struct,
- &context);
+ size_t size = mojo::internal::PrepareToSerialize<SmallStructDataView>(
+ small_struct, &context);
mojo::internal::FixedBufferForTesting buf(size);
internal::SmallStruct_Data* data = nullptr;
- mojo::internal::Serialize<SmallStructPtr>(small_struct, &buf, &data,
- &context);
+ mojo::internal::Serialize<SmallStructDataView>(small_struct, &buf, &data,
+ &context);
void* raw_buf = buf.Leak();
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
EXPECT_TRUE(internal::SmallStruct_Data::Validate(
raw_buf, &validation_context));
free(raw_buf);
@@ -681,57 +689,58 @@ TEST(UnionTest, PodUnionInMap) {
}
TEST(UnionTest, PodUnionInMapSerialization) {
- Map<String, PodUnionPtr> map;
- map.insert("one", PodUnion::New());
- map.insert("two", PodUnion::New());
+ using MojomType = MapDataView<StringDataView, PodUnionDataView>;
+
+ std::unordered_map<std::string, PodUnionPtr> map;
+ map.insert(std::make_pair("one", PodUnion::New()));
+ map.insert(std::make_pair("two", PodUnion::New()));
map["one"]->set_f_int8(8);
map["two"]->set_f_int16(16);
mojo::internal::SerializationContext context;
- size_t size = mojo::internal::PrepareToSerialize<Map<String, PodUnionPtr>>(
- map, &context);
+ size_t size = mojo::internal::PrepareToSerialize<MojomType>(map, &context);
EXPECT_EQ(120U, size);
mojo::internal::FixedBufferForTesting buf(size);
- typename mojo::internal::MojomTypeTraits<Map<String, PodUnionPtr>>::Data*
- data;
+
+ typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
mojo::internal::ContainerValidateParams validate_params(
new mojo::internal::ContainerValidateParams(0, false, nullptr),
new mojo::internal::ContainerValidateParams(0, false, nullptr));
- mojo::internal::Serialize<Map<String, PodUnionPtr>>(
- map, &buf, &data, &validate_params, &context);
+ mojo::internal::Serialize<MojomType>(map, &buf, &data, &validate_params,
+ &context);
- Map<String, PodUnionPtr> map2;
- mojo::internal::Deserialize<Map<String, PodUnionPtr>>(data, &map2, &context);
+ std::unordered_map<std::string, PodUnionPtr> map2;
+ mojo::internal::Deserialize<MojomType>(data, &map2, &context);
EXPECT_EQ(8, map2["one"]->get_f_int8());
EXPECT_EQ(16, map2["two"]->get_f_int16());
}
TEST(UnionTest, PodUnionInMapSerializationWithNull) {
- Map<String, PodUnionPtr> map;
- map.insert("one", PodUnion::New());
- map.insert("two", nullptr);
+ using MojomType = MapDataView<StringDataView, PodUnionDataView>;
+
+ std::unordered_map<std::string, PodUnionPtr> map;
+ map.insert(std::make_pair("one", PodUnion::New()));
+ map.insert(std::make_pair("two", nullptr));
map["one"]->set_f_int8(8);
mojo::internal::SerializationContext context;
- size_t size = mojo::internal::PrepareToSerialize<Map<String, PodUnionPtr>>(
- map, &context);
+ size_t size = mojo::internal::PrepareToSerialize<MojomType>(map, &context);
EXPECT_EQ(120U, size);
mojo::internal::FixedBufferForTesting buf(size);
- typename mojo::internal::MojomTypeTraits<Map<String, PodUnionPtr>>::Data*
- data;
+ typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
mojo::internal::ContainerValidateParams validate_params(
new mojo::internal::ContainerValidateParams(0, false, nullptr),
new mojo::internal::ContainerValidateParams(0, true, nullptr));
- mojo::internal::Serialize<Map<String, PodUnionPtr>>(
- map, &buf, &data, &validate_params, &context);
+ mojo::internal::Serialize<MojomType>(map, &buf, &data, &validate_params,
+ &context);
- Map<String, PodUnionPtr> map2;
- mojo::internal::Deserialize<Map<String, PodUnionPtr>>(data, &map2, &context);
+ std::unordered_map<std::string, PodUnionPtr> map2;
+ mojo::internal::Deserialize<MojomType>(data, &map2, &context);
EXPECT_EQ(8, map2["one"]->get_f_int8());
EXPECT_TRUE(map2["two"].is_null());
@@ -754,16 +763,17 @@ TEST(UnionTest, StructInUnionSerialization) {
ObjectUnionPtr obj(ObjectUnion::New());
obj->set_f_dummy(std::move(dummy));
- size_t size =
- mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+ size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
+ obj, false, nullptr);
EXPECT_EQ(32U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::ObjectUnion_Data* data = nullptr;
- mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+ mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
+ nullptr);
ObjectUnionPtr obj2;
- mojo::internal::Deserialize<ObjectUnionPtr>(data, &obj2, nullptr);
+ mojo::internal::Deserialize<ObjectUnionDataView>(data, &obj2, nullptr);
EXPECT_EQ(8, obj2->get_f_dummy()->f_int8);
}
@@ -774,16 +784,17 @@ TEST(UnionTest, StructInUnionValidation) {
ObjectUnionPtr obj(ObjectUnion::New());
obj->set_f_dummy(std::move(dummy));
- size_t size =
- mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+ size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
+ obj, false, nullptr);
mojo::internal::FixedBufferForTesting buf(size);
internal::ObjectUnion_Data* data = nullptr;
- mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+ mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
+ nullptr);
void* raw_buf = buf.Leak();
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
EXPECT_TRUE(internal::ObjectUnion_Data::Validate(
raw_buf, &validation_context, false));
free(raw_buf);
@@ -797,16 +808,17 @@ TEST(UnionTest, StructInUnionValidationNonNullable) {
ObjectUnionPtr obj(ObjectUnion::New());
obj->set_f_dummy(std::move(dummy));
- size_t size =
- mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+ size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
+ obj, false, nullptr);
mojo::internal::FixedBufferForTesting buf(size);
internal::ObjectUnion_Data* data = nullptr;
- mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+ mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
+ nullptr);
void* raw_buf = buf.Leak();
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
EXPECT_FALSE(internal::ObjectUnion_Data::Validate(
raw_buf, &validation_context, false));
free(raw_buf);
@@ -818,23 +830,24 @@ TEST(UnionTest, StructInUnionValidationNullable) {
ObjectUnionPtr obj(ObjectUnion::New());
obj->set_f_nullable(std::move(dummy));
- size_t size =
- mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+ size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
+ obj, false, nullptr);
mojo::internal::FixedBufferForTesting buf(size);
internal::ObjectUnion_Data* data = nullptr;
- mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+ mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
+ nullptr);
void* raw_buf = buf.Leak();
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
EXPECT_TRUE(internal::ObjectUnion_Data::Validate(
raw_buf, &validation_context, false));
free(raw_buf);
}
TEST(UnionTest, ArrayInUnionGetterSetter) {
- Array<int8_t> array(2);
+ std::vector<int8_t> array(2);
array[0] = 8;
array[1] = 9;
@@ -846,45 +859,47 @@ TEST(UnionTest, ArrayInUnionGetterSetter) {
}
TEST(UnionTest, ArrayInUnionSerialization) {
- Array<int8_t> array(2);
+ std::vector<int8_t> array(2);
array[0] = 8;
array[1] = 9;
ObjectUnionPtr obj(ObjectUnion::New());
obj->set_f_array_int8(std::move(array));
- size_t size =
- mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+ size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
+ obj, false, nullptr);
EXPECT_EQ(32U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::ObjectUnion_Data* data = nullptr;
- mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+ mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
+ nullptr);
ObjectUnionPtr obj2;
- mojo::internal::Deserialize<ObjectUnionPtr>(data, &obj2, nullptr);
+ mojo::internal::Deserialize<ObjectUnionDataView>(data, &obj2, nullptr);
EXPECT_EQ(8, obj2->get_f_array_int8()[0]);
EXPECT_EQ(9, obj2->get_f_array_int8()[1]);
}
TEST(UnionTest, ArrayInUnionValidation) {
- Array<int8_t> array(2);
+ std::vector<int8_t> array(2);
array[0] = 8;
array[1] = 9;
ObjectUnionPtr obj(ObjectUnion::New());
obj->set_f_array_int8(std::move(array));
- size_t size =
- mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+ size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
+ obj, false, nullptr);
mojo::internal::FixedBufferForTesting buf(size);
internal::ObjectUnion_Data* data = nullptr;
- mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+ mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
+ nullptr);
void* raw_buf = buf.Leak();
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
EXPECT_TRUE(internal::ObjectUnion_Data::Validate(
raw_buf, &validation_context, false));
@@ -912,16 +927,17 @@ TEST(UnionTest, MapInUnionSerialization) {
obj->set_f_map_int8(std::move(map));
mojo::internal::SerializationContext context;
- size_t size =
- mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, &context);
+ size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
+ obj, false, &context);
EXPECT_EQ(112U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::ObjectUnion_Data* data = nullptr;
- mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, &context);
+ mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
+ &context);
ObjectUnionPtr obj2;
- mojo::internal::Deserialize<ObjectUnionPtr>(data, &obj2, &context);
+ mojo::internal::Deserialize<ObjectUnionDataView>(data, &obj2, &context);
EXPECT_EQ(1, obj2->get_f_map_int8()["one"]);
EXPECT_EQ(2, obj2->get_f_map_int8()["two"]);
@@ -936,17 +952,18 @@ TEST(UnionTest, MapInUnionValidation) {
obj->set_f_map_int8(std::move(map));
mojo::internal::SerializationContext context;
- size_t size =
- mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, &context);
+ size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
+ obj, false, &context);
EXPECT_EQ(112U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::ObjectUnion_Data* data = nullptr;
- mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, &context);
+ mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
+ &context);
void* raw_buf = buf.Leak();
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
EXPECT_TRUE(internal::ObjectUnion_Data::Validate(
raw_buf, &validation_context, false));
@@ -970,16 +987,17 @@ TEST(UnionTest, UnionInUnionSerialization) {
ObjectUnionPtr obj(ObjectUnion::New());
obj->set_f_pod_union(std::move(pod));
- size_t size =
- mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+ size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
+ obj, false, nullptr);
EXPECT_EQ(32U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::ObjectUnion_Data* data = nullptr;
- mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+ mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
+ nullptr);
ObjectUnionPtr obj2;
- mojo::internal::Deserialize<ObjectUnionPtr>(data, &obj2, nullptr);
+ mojo::internal::Deserialize<ObjectUnionDataView>(data, &obj2, nullptr);
EXPECT_EQ(10, obj2->get_f_pod_union()->get_f_int8());
}
@@ -990,17 +1008,18 @@ TEST(UnionTest, UnionInUnionValidation) {
ObjectUnionPtr obj(ObjectUnion::New());
obj->set_f_pod_union(std::move(pod));
- size_t size =
- mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+ size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
+ obj, false, nullptr);
EXPECT_EQ(32U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::ObjectUnion_Data* data = nullptr;
- mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+ mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
+ nullptr);
void* raw_buf = buf.Leak();
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
EXPECT_TRUE(internal::ObjectUnion_Data::Validate(
raw_buf, &validation_context, false));
free(raw_buf);
@@ -1014,16 +1033,17 @@ TEST(UnionTest, UnionInUnionValidationNonNullable) {
ObjectUnionPtr obj(ObjectUnion::New());
obj->set_f_pod_union(std::move(pod));
- size_t size =
- mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr);
+ size_t size = mojo::internal::PrepareToSerialize<ObjectUnionDataView>(
+ obj, false, nullptr);
mojo::internal::FixedBufferForTesting buf(size);
internal::ObjectUnion_Data* data = nullptr;
- mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr);
+ mojo::internal::Serialize<ObjectUnionDataView>(obj, &buf, &data, false,
+ nullptr);
void* raw_buf = buf.Leak();
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 0);
+ data, static_cast<uint32_t>(size), 0, 0);
EXPECT_FALSE(internal::ObjectUnion_Data::Validate(
raw_buf, &validation_context, false));
free(raw_buf);
@@ -1057,18 +1077,18 @@ TEST(UnionTest, HandleInUnionSerialization) {
handle->set_f_message_pipe(std::move(pipe1));
mojo::internal::SerializationContext context;
- size_t size = mojo::internal::PrepareToSerialize<HandleUnionPtr>(
+ size_t size = mojo::internal::PrepareToSerialize<HandleUnionDataView>(
handle, false, &context);
EXPECT_EQ(16U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::HandleUnion_Data* data = nullptr;
- mojo::internal::Serialize<HandleUnionPtr>(handle, &buf, &data, false,
- &context);
+ mojo::internal::Serialize<HandleUnionDataView>(handle, &buf, &data, false,
+ &context);
EXPECT_EQ(1U, context.handles.size());
HandleUnionPtr handle2(HandleUnion::New());
- mojo::internal::Deserialize<HandleUnionPtr>(data, &handle2, &context);
+ mojo::internal::Deserialize<HandleUnionDataView>(data, &handle2, &context);
std::string golden("hello world");
WriteTextMessage(pipe0.get(), golden);
@@ -1089,18 +1109,18 @@ TEST(UnionTest, HandleInUnionValidation) {
handle->set_f_message_pipe(std::move(pipe1));
mojo::internal::SerializationContext context;
- size_t size = mojo::internal::PrepareToSerialize<HandleUnionPtr>(
+ size_t size = mojo::internal::PrepareToSerialize<HandleUnionDataView>(
handle, false, &context);
EXPECT_EQ(16U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::HandleUnion_Data* data = nullptr;
- mojo::internal::Serialize<HandleUnionPtr>(handle, &buf, &data, false,
- &context);
+ mojo::internal::Serialize<HandleUnionDataView>(handle, &buf, &data, false,
+ &context);
void* raw_buf = buf.Leak();
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 1);
+ data, static_cast<uint32_t>(size), 1, 0);
EXPECT_TRUE(internal::HandleUnion_Data::Validate(
raw_buf, &validation_context, false));
free(raw_buf);
@@ -1114,18 +1134,18 @@ TEST(UnionTest, HandleInUnionValidationNull) {
handle->set_f_message_pipe(std::move(pipe));
mojo::internal::SerializationContext context;
- size_t size = mojo::internal::PrepareToSerialize<HandleUnionPtr>(
+ size_t size = mojo::internal::PrepareToSerialize<HandleUnionDataView>(
handle, false, &context);
EXPECT_EQ(16U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::HandleUnion_Data* data = nullptr;
- mojo::internal::Serialize<HandleUnionPtr>(handle, &buf, &data, false,
- &context);
+ mojo::internal::Serialize<HandleUnionDataView>(handle, &buf, &data, false,
+ &context);
void* raw_buf = buf.Leak();
mojo::internal::ValidationContext validation_context(
- data, static_cast<uint32_t>(size), 1);
+ data, static_cast<uint32_t>(size), 1, 0);
EXPECT_FALSE(internal::HandleUnion_Data::Validate(
raw_buf, &validation_context, false));
free(raw_buf);
@@ -1156,7 +1176,7 @@ TEST(UnionTest, InterfaceInUnion) {
base::RunLoop run_loop;
SmallCacheImpl impl(run_loop.QuitClosure());
SmallCachePtr ptr;
- Binding<SmallCache> bindings(&impl, GetProxy(&ptr));
+ Binding<SmallCache> bindings(&impl, MakeRequest(&ptr));
HandleUnionPtr handle(HandleUnion::New());
handle->set_f_small_cache(std::move(ptr));
@@ -1171,23 +1191,23 @@ TEST(UnionTest, InterfaceInUnionSerialization) {
base::RunLoop run_loop;
SmallCacheImpl impl(run_loop.QuitClosure());
SmallCachePtr ptr;
- Binding<SmallCache> bindings(&impl, GetProxy(&ptr));
+ Binding<SmallCache> bindings(&impl, MakeRequest(&ptr));
mojo::internal::SerializationContext context;
HandleUnionPtr handle(HandleUnion::New());
handle->set_f_small_cache(std::move(ptr));
- size_t size = mojo::internal::PrepareToSerialize<HandleUnionPtr>(
+ size_t size = mojo::internal::PrepareToSerialize<HandleUnionDataView>(
handle, false, &context);
EXPECT_EQ(16U, size);
mojo::internal::FixedBufferForTesting buf(size);
internal::HandleUnion_Data* data = nullptr;
- mojo::internal::Serialize<HandleUnionPtr>(handle, &buf, &data, false,
- &context);
+ mojo::internal::Serialize<HandleUnionDataView>(handle, &buf, &data, false,
+ &context);
EXPECT_EQ(1U, context.handles.size());
HandleUnionPtr handle2(HandleUnion::New());
- mojo::internal::Deserialize<HandleUnionPtr>(data, &handle2, &context);
+ mojo::internal::Deserialize<HandleUnionDataView>(data, &handle2, &context);
handle2->get_f_small_cache()->SetIntValue(10);
run_loop.Run();
@@ -1213,7 +1233,7 @@ TEST(UnionTest, UnionInInterface) {
base::MessageLoop message_loop;
UnionInterfaceImpl impl;
UnionInterfacePtr ptr;
- Binding<UnionInterface> bindings(&impl, GetProxy(&ptr));
+ Binding<UnionInterface> bindings(&impl, MakeRequest(&ptr));
PodUnionPtr pod(PodUnion::New());
pod->set_f_int16(16);
diff --git a/mojo/public/cpp/bindings/tests/validation_context_unittest.cc b/mojo/public/cpp/bindings/tests/validation_context_unittest.cc
index aceaf1c..9ce9d60 100644
--- a/mojo/public/cpp/bindings/tests/validation_context_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/validation_context_unittest.cc
@@ -17,6 +17,8 @@ namespace test {
namespace {
using Handle_Data = mojo::internal::Handle_Data;
+using AssociatedEndpointHandle_Data =
+ mojo::internal::AssociatedEndpointHandle_Data;
const void* ToPtr(uintptr_t ptr) {
return reinterpret_cast<const void*>(ptr);
@@ -27,7 +29,7 @@ TEST(ValidationContextTest, ConstructorRangeOverflow) {
{
// Test memory range overflow.
internal::ValidationContext context(
- ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 5000, 0);
+ ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 5000, 0, 0);
EXPECT_FALSE(context.IsValidRange(
ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 1));
@@ -35,11 +37,14 @@ TEST(ValidationContextTest, ConstructorRangeOverflow) {
ToPtr(std::numeric_limits<uintptr_t>::max() - 3000), 1));
}
- if (sizeof(size_t) > sizeof(uint32_t)) {
+ if (sizeof(size_t) <= sizeof(uint32_t))
+ return;
+
+ {
// Test handle index range overflow.
size_t num_handles =
static_cast<size_t>(std::numeric_limits<uint32_t>::max()) + 5;
- internal::ValidationContext context(ToPtr(0), 0, num_handles);
+ internal::ValidationContext context(ToPtr(0), 0, num_handles, 0);
EXPECT_FALSE(context.ClaimHandle(Handle_Data(0)));
EXPECT_FALSE(context.ClaimHandle(
@@ -48,12 +53,28 @@ TEST(ValidationContextTest, ConstructorRangeOverflow) {
EXPECT_TRUE(context.ClaimHandle(
Handle_Data(internal::kEncodedInvalidHandleValue)));
}
+
+ {
+ size_t num_associated_endpoint_handles =
+ static_cast<size_t>(std::numeric_limits<uint32_t>::max()) + 5;
+ internal::ValidationContext context(ToPtr(0), 0, 0,
+ num_associated_endpoint_handles);
+
+ EXPECT_FALSE(context.ClaimAssociatedEndpointHandle(
+ AssociatedEndpointHandle_Data(0)));
+ EXPECT_FALSE(
+ context.ClaimAssociatedEndpointHandle(AssociatedEndpointHandle_Data(
+ std::numeric_limits<uint32_t>::max() - 1)));
+
+ EXPECT_TRUE(context.ClaimAssociatedEndpointHandle(
+ AssociatedEndpointHandle_Data(internal::kEncodedInvalidHandleValue)));
+ }
}
#endif
TEST(ValidationContextTest, IsValidRange) {
{
- internal::ValidationContext context(ToPtr(1234), 100, 0);
+ internal::ValidationContext context(ToPtr(1234), 100, 0, 0);
// Basics.
EXPECT_FALSE(context.IsValidRange(ToPtr(100), 5));
@@ -79,7 +100,7 @@ TEST(ValidationContextTest, IsValidRange) {
}
{
- internal::ValidationContext context(ToPtr(1234), 100, 0);
+ internal::ValidationContext context(ToPtr(1234), 100, 0, 0);
// Should return false for empty ranges.
EXPECT_FALSE(context.IsValidRange(ToPtr(0), 0));
EXPECT_FALSE(context.IsValidRange(ToPtr(1200), 0));
@@ -90,7 +111,7 @@ TEST(ValidationContextTest, IsValidRange) {
{
// The valid memory range is empty.
- internal::ValidationContext context(ToPtr(1234), 0, 0);
+ internal::ValidationContext context(ToPtr(1234), 0, 0, 0);
EXPECT_FALSE(context.IsValidRange(ToPtr(1234), 1));
EXPECT_FALSE(context.IsValidRange(ToPtr(1234), 0));
@@ -98,7 +119,7 @@ TEST(ValidationContextTest, IsValidRange) {
{
internal::ValidationContext context(
- ToPtr(std::numeric_limits<uintptr_t>::max() - 2000), 1000, 0);
+ ToPtr(std::numeric_limits<uintptr_t>::max() - 2000), 1000, 0, 0);
// Test overflow.
EXPECT_FALSE(context.IsValidRange(
@@ -115,7 +136,7 @@ TEST(ValidationContextTest, IsValidRange) {
TEST(ValidationContextTest, ClaimHandle) {
{
- internal::ValidationContext context(ToPtr(0), 0, 10);
+ internal::ValidationContext context(ToPtr(0), 0, 10, 0);
// Basics.
EXPECT_TRUE(context.ClaimHandle(Handle_Data(0)));
@@ -137,7 +158,7 @@ TEST(ValidationContextTest, ClaimHandle) {
{
// No handle to claim.
- internal::ValidationContext context(ToPtr(0), 0, 0);
+ internal::ValidationContext context(ToPtr(0), 0, 0, 0);
EXPECT_FALSE(context.ClaimHandle(Handle_Data(0)));
@@ -152,7 +173,7 @@ TEST(ValidationContextTest, ClaimHandle) {
EXPECT_EQ(internal::kEncodedInvalidHandleValue,
std::numeric_limits<uint32_t>::max());
internal::ValidationContext context(
- ToPtr(0), 0, std::numeric_limits<uint32_t>::max());
+ ToPtr(0), 0, std::numeric_limits<uint32_t>::max(), 0);
EXPECT_TRUE(context.ClaimHandle(
Handle_Data(std::numeric_limits<uint32_t>::max() - 1)));
@@ -166,9 +187,71 @@ TEST(ValidationContextTest, ClaimHandle) {
}
}
+TEST(ValidationContextTest, ClaimAssociatedEndpointHandle) {
+ {
+ internal::ValidationContext context(ToPtr(0), 0, 0, 10);
+
+ // Basics.
+ EXPECT_TRUE(context.ClaimAssociatedEndpointHandle(
+ AssociatedEndpointHandle_Data(0)));
+ EXPECT_FALSE(context.ClaimAssociatedEndpointHandle(
+ AssociatedEndpointHandle_Data(0)));
+
+ EXPECT_TRUE(context.ClaimAssociatedEndpointHandle(
+ AssociatedEndpointHandle_Data(9)));
+ EXPECT_FALSE(context.ClaimAssociatedEndpointHandle(
+ AssociatedEndpointHandle_Data(10)));
+
+ // Should fail because it is smaller than the max index that has been
+ // claimed.
+ EXPECT_FALSE(context.ClaimAssociatedEndpointHandle(
+ AssociatedEndpointHandle_Data(8)));
+
+ // Should return true for invalid handle.
+ EXPECT_TRUE(context.ClaimAssociatedEndpointHandle(
+ AssociatedEndpointHandle_Data(internal::kEncodedInvalidHandleValue)));
+ EXPECT_TRUE(context.ClaimAssociatedEndpointHandle(
+ AssociatedEndpointHandle_Data(internal::kEncodedInvalidHandleValue)));
+ }
+
+ {
+ // No handle to claim.
+ internal::ValidationContext context(ToPtr(0), 0, 0, 0);
+
+ EXPECT_FALSE(context.ClaimAssociatedEndpointHandle(
+ AssociatedEndpointHandle_Data(0)));
+
+ // Should still return true for invalid handle.
+ EXPECT_TRUE(context.ClaimAssociatedEndpointHandle(
+ AssociatedEndpointHandle_Data(internal::kEncodedInvalidHandleValue)));
+ }
+
+ {
+ // Test the case that |num_associated_endpoint_handles| is the same value as
+ // |internal::kEncodedInvalidHandleValue|.
+ EXPECT_EQ(internal::kEncodedInvalidHandleValue,
+ std::numeric_limits<uint32_t>::max());
+ internal::ValidationContext context(ToPtr(0), 0, 0,
+ std::numeric_limits<uint32_t>::max());
+
+ EXPECT_TRUE(
+ context.ClaimAssociatedEndpointHandle(AssociatedEndpointHandle_Data(
+ std::numeric_limits<uint32_t>::max() - 1)));
+ EXPECT_FALSE(
+ context.ClaimAssociatedEndpointHandle(AssociatedEndpointHandle_Data(
+ std::numeric_limits<uint32_t>::max() - 1)));
+ EXPECT_FALSE(context.ClaimAssociatedEndpointHandle(
+ AssociatedEndpointHandle_Data(0)));
+
+ // Should still return true for invalid handle.
+ EXPECT_TRUE(context.ClaimAssociatedEndpointHandle(
+ AssociatedEndpointHandle_Data(internal::kEncodedInvalidHandleValue)));
+ }
+}
+
TEST(ValidationContextTest, ClaimMemory) {
{
- internal::ValidationContext context(ToPtr(1000), 2000, 0);
+ internal::ValidationContext context(ToPtr(1000), 2000, 0, 0);
// Basics.
EXPECT_FALSE(context.ClaimMemory(ToPtr(500), 100));
@@ -186,7 +269,7 @@ TEST(ValidationContextTest, ClaimMemory) {
{
// No memory to claim.
- internal::ValidationContext context(ToPtr(10000), 0, 0);
+ internal::ValidationContext context(ToPtr(10000), 0, 0, 0);
EXPECT_FALSE(context.ClaimMemory(ToPtr(10000), 1));
EXPECT_FALSE(context.ClaimMemory(ToPtr(10000), 0));
@@ -194,7 +277,7 @@ TEST(ValidationContextTest, ClaimMemory) {
{
internal::ValidationContext context(
- ToPtr(std::numeric_limits<uintptr_t>::max() - 1000), 500, 0);
+ ToPtr(std::numeric_limits<uintptr_t>::max() - 1000), 500, 0, 0);
// Test overflow.
EXPECT_FALSE(context.ClaimMemory(
diff --git a/mojo/public/cpp/bindings/tests/validation_unittest.cc b/mojo/public/cpp/bindings/tests/validation_unittest.cc
index d254383..7af7396 100644
--- a/mojo/public/cpp/bindings/tests/validation_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/validation_unittest.cc
@@ -16,9 +16,8 @@
#include "mojo/public/c/system/macros.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/connector.h"
+#include "mojo/public/cpp/bindings/filter_chain.h"
#include "mojo/public/cpp/bindings/interface_ptr.h"
-#include "mojo/public/cpp/bindings/lib/filter_chain.h"
-#include "mojo/public/cpp/bindings/lib/router.h"
#include "mojo/public/cpp/bindings/lib/validation_errors.h"
#include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/cpp/bindings/message_header_validator.h"
@@ -266,7 +265,7 @@ class IntegrationTestInterfaceImpl : public IntegrationTestInterface {
void Method0(BasicStructPtr param0,
const Method0Callback& callback) override {
- callback.Run(Array<uint8_t>::New(0u));
+ callback.Run(std::vector<uint8_t>());
}
};
@@ -376,20 +375,20 @@ TEST_F(ValidationTest, InputParser) {
TEST_F(ValidationTest, Conformance) {
DummyMessageReceiver dummy_receiver;
- mojo::internal::FilterChain validators(&dummy_receiver);
+ mojo::FilterChain validators(&dummy_receiver);
validators.Append<mojo::MessageHeaderValidator>();
validators.Append<ConformanceTestInterface::RequestValidator_>();
- RunValidationTests("conformance_", validators.GetHead());
+ RunValidationTests("conformance_", &validators);
}
TEST_F(ValidationTest, AssociatedConformace) {
DummyMessageReceiver dummy_receiver;
- mojo::internal::FilterChain validators(&dummy_receiver);
+ mojo::FilterChain validators(&dummy_receiver);
validators.Append<mojo::MessageHeaderValidator>();
validators.Append<AssociatedConformanceTestInterface::RequestValidator_>();
- RunValidationTests("associated_conformance_", validators.GetHead());
+ RunValidationTests("associated_conformance_", &validators);
}
// This test is similar to Conformance test but its goal is specifically
@@ -397,31 +396,31 @@ TEST_F(ValidationTest, AssociatedConformace) {
// detection of off-by-one errors in method ordinals.
TEST_F(ValidationTest, BoundsCheck) {
DummyMessageReceiver dummy_receiver;
- mojo::internal::FilterChain validators(&dummy_receiver);
+ mojo::FilterChain validators(&dummy_receiver);
validators.Append<mojo::MessageHeaderValidator>();
validators.Append<BoundsCheckTestInterface::RequestValidator_>();
- RunValidationTests("boundscheck_", validators.GetHead());
+ RunValidationTests("boundscheck_", &validators);
}
// This test is similar to the Conformance test but for responses.
TEST_F(ValidationTest, ResponseConformance) {
DummyMessageReceiver dummy_receiver;
- mojo::internal::FilterChain validators(&dummy_receiver);
+ mojo::FilterChain validators(&dummy_receiver);
validators.Append<mojo::MessageHeaderValidator>();
validators.Append<ConformanceTestInterface::ResponseValidator_>();
- RunValidationTests("resp_conformance_", validators.GetHead());
+ RunValidationTests("resp_conformance_", &validators);
}
// This test is similar to the BoundsCheck test but for responses.
TEST_F(ValidationTest, ResponseBoundsCheck) {
DummyMessageReceiver dummy_receiver;
- mojo::internal::FilterChain validators(&dummy_receiver);
+ mojo::FilterChain validators(&dummy_receiver);
validators.Append<mojo::MessageHeaderValidator>();
validators.Append<BoundsCheckTestInterface::ResponseValidator_>();
- RunValidationTests("resp_boundscheck_", validators.GetHead());
+ RunValidationTests("resp_boundscheck_", &validators);
}
// Test that InterfacePtr<X> applies the correct validators and they don't
diff --git a/mojo/public/cpp/bindings/tests/versioning_apptest.cc b/mojo/public/cpp/bindings/tests/versioning_apptest.cc
index 3a08cdd..95a89c0 100644
--- a/mojo/public/cpp/bindings/tests/versioning_apptest.cc
+++ b/mojo/public/cpp/bindings/tests/versioning_apptest.cc
@@ -7,8 +7,8 @@
#include "base/macros.h"
#include "mojo/public/interfaces/bindings/tests/versioning_test_client.mojom.h"
-#include "services/shell/public/cpp/application_test_base.h"
-#include "services/shell/public/cpp/connector.h"
+#include "services/service_manager/public/cpp/application_test_base.h"
+#include "services/service_manager/public/cpp/connector.h"
namespace mojo {
namespace test {
@@ -24,7 +24,7 @@ class VersioningApplicationTest : public ApplicationTestBase {
void SetUp() override {
ApplicationTestBase::SetUp();
- connector()->ConnectToInterface("mojo:versioning_test_service", &database_);
+ connector()->BindInterface("versioning_test_service", &database_);
}
HumanResourceDatabasePtr database_;
diff --git a/mojo/public/cpp/bindings/tests/versioning_test_service.cc b/mojo/public/cpp/bindings/tests/versioning_test_service.cc
index 2f554d9..313a624 100644
--- a/mojo/public/cpp/bindings/tests/versioning_test_service.cc
+++ b/mojo/public/cpp/bindings/tests/versioning_test_service.cc
@@ -8,12 +8,12 @@
#include <utility>
#include "base/macros.h"
-#include "mojo/public/c/system/main.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/interfaces/bindings/tests/versioning_test_service.mojom.h"
-#include "services/shell/public/cpp/application_runner.h"
-#include "services/shell/public/cpp/interface_factory.h"
-#include "services/shell/public/cpp/service.h"
+#include "services/service_manager/public/c/main.h"
+#include "services/service_manager/public/cpp/interface_factory.h"
+#include "services/service_manager/public/cpp/service.h"
+#include "services/service_manager/public/cpp/service_runner.h"
namespace mojo {
namespace test {
@@ -95,12 +95,12 @@ class HumanResourceDatabaseImpl : public HumanResourceDatabase {
};
class HumanResourceSystemServer
- : public shell::Service,
+ : public service_manager::Service,
public InterfaceFactory<HumanResourceDatabase> {
public:
HumanResourceSystemServer() {}
- // shell::Service implementation.
+ // service_manager::Service implementation.
bool OnConnect(Connection* connection) override {
connection->AddInterface<HumanResourceDatabase>(this);
return true;
@@ -119,8 +119,8 @@ class HumanResourceSystemServer
} // namespace test
} // namespace mojo
-MojoResult MojoMain(MojoHandle request) {
- mojo::ApplicationRunner runner(
+MojoResult ServiceMain(MojoHandle request) {
+ mojo::ServiceRunner runner(
new mojo::test::versioning::HumanResourceSystemServer());
return runner.Run(request);
diff --git a/mojo/public/cpp/bindings/tests/wtf_array_unittest.cc b/mojo/public/cpp/bindings/tests/wtf_array_unittest.cc
deleted file mode 100644
index bb54b9c..0000000
--- a/mojo/public/cpp/bindings/tests/wtf_array_unittest.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2016 The Chromium 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/wtf_array.h"
-
-#include "mojo/public/cpp/bindings/lib/serialization.h"
-#include "mojo/public/cpp/bindings/lib/wtf_serialization.h"
-#include "mojo/public/cpp/bindings/tests/array_common_test.h"
-#include "mojo/public/cpp/bindings/tests/container_test_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace mojo {
-namespace test {
-namespace {
-
-using WTFArrayTest = testing::Test;
-
-ARRAY_COMMON_TEST(WTFArray, NullAndEmpty)
-ARRAY_COMMON_TEST(WTFArray, Basic)
-ARRAY_COMMON_TEST(WTFArray, Bool)
-ARRAY_COMMON_TEST(WTFArray, Handle)
-ARRAY_COMMON_TEST(WTFArray, HandlesAreClosed)
-ARRAY_COMMON_TEST(WTFArray, Clone)
-ARRAY_COMMON_TEST(WTFArray, Serialization_ArrayOfPOD)
-ARRAY_COMMON_TEST(WTFArray, Serialization_EmptyArrayOfPOD)
-ARRAY_COMMON_TEST(WTFArray, Serialization_ArrayOfArrayOfPOD)
-ARRAY_COMMON_TEST(WTFArray, Serialization_ArrayOfBool)
-ARRAY_COMMON_TEST(WTFArray, Serialization_ArrayOfString)
-ARRAY_COMMON_TEST(WTFArray, Resize_Copyable)
-ARRAY_COMMON_TEST(WTFArray, Resize_MoveOnly)
-
-TEST_F(WTFArrayTest, MoveFromAndToWTFVector_Copyable) {
- WTF::Vector<CopyableType> vec1(1);
- WTFArray<CopyableType> arr(std::move(vec1));
- ASSERT_EQ(1u, arr.size());
- ASSERT_FALSE(arr[0].copied());
-
- WTF::Vector<CopyableType> vec2(arr.PassStorage());
- ASSERT_EQ(1u, vec2.size());
- ASSERT_FALSE(vec2[0].copied());
-
- ASSERT_EQ(0u, arr.size());
- ASSERT_TRUE(arr.is_null());
-}
-
-TEST_F(WTFArrayTest, MoveFromAndToWTFVector_MoveOnly) {
- WTF::Vector<MoveOnlyType> vec1(1);
- WTFArray<MoveOnlyType> arr(std::move(vec1));
-
- ASSERT_EQ(1u, arr.size());
-
- WTF::Vector<MoveOnlyType> vec2(arr.PassStorage());
- ASSERT_EQ(1u, vec2.size());
-
- ASSERT_EQ(0u, arr.size());
- ASSERT_TRUE(arr.is_null());
-}
-
-} // namespace
-} // namespace test
-} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/wtf_hash_unittest.cc b/mojo/public/cpp/bindings/tests/wtf_hash_unittest.cc
new file mode 100644
index 0000000..959d25b
--- /dev/null
+++ b/mojo/public/cpp/bindings/tests/wtf_hash_unittest.cc
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium 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/wtf_hash_util.h"
+
+#include "mojo/public/interfaces/bindings/tests/test_structs.mojom-blink.h"
+#include "mojo/public/interfaces/bindings/tests/test_wtf_types.mojom-blink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/Source/wtf/HashFunctions.h"
+
+namespace mojo {
+namespace test {
+namespace {
+
+using WTFHashTest = testing::Test;
+
+TEST_F(WTFHashTest, NestedStruct) {
+ // Just check that this template instantiation compiles.
+ ASSERT_EQ(::mojo::internal::Hash(
+ ::mojo::internal::kHashSeed,
+ blink::SimpleNestedStruct::New(blink::ContainsOther::New(1))),
+ ::mojo::internal::Hash(
+ ::mojo::internal::kHashSeed,
+ blink::SimpleNestedStruct::New(blink::ContainsOther::New(1))));
+}
+
+TEST_F(WTFHashTest, UnmappedNativeStruct) {
+ // Just check that this template instantiation compiles.
+ ASSERT_EQ(::mojo::internal::Hash(::mojo::internal::kHashSeed,
+ blink::UnmappedNativeStruct::New()),
+ ::mojo::internal::Hash(::mojo::internal::kHashSeed,
+ blink::UnmappedNativeStruct::New()));
+}
+
+TEST_F(WTFHashTest, Enum) {
+ // Just check that this template instantiation compiles.
+
+ // Top-level.
+ ASSERT_EQ(WTF::DefaultHash<blink::TopLevelEnum>::Hash().hash(
+ blink::TopLevelEnum::E0),
+ WTF::DefaultHash<blink::TopLevelEnum>::Hash().hash(
+ blink::TopLevelEnum::E0));
+
+ // Nested in struct.
+ ASSERT_EQ(WTF::DefaultHash<blink::TestWTFStruct::NestedEnum>::Hash().hash(
+ blink::TestWTFStruct::NestedEnum::E0),
+ WTF::DefaultHash<blink::TestWTFStruct::NestedEnum>::Hash().hash(
+ blink::TestWTFStruct::NestedEnum::E0));
+
+ // Nested in interface.
+ ASSERT_EQ(WTF::DefaultHash<blink::TestWTF::NestedEnum>::Hash().hash(
+ blink::TestWTF::NestedEnum::E0),
+ WTF::DefaultHash<blink::TestWTF::NestedEnum>::Hash().hash(
+ blink::TestWTF::NestedEnum::E0));
+}
+
+} // namespace
+} // namespace test
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/tests/wtf_map_unittest.cc b/mojo/public/cpp/bindings/tests/wtf_map_unittest.cc
index a70c134..5028087 100644
--- a/mojo/public/cpp/bindings/tests/wtf_map_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/wtf_map_unittest.cc
@@ -1,65 +1,39 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
+// Copyright 2017 The Chromium 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/wtf_map.h"
-
-#include "mojo/public/cpp/bindings/lib/wtf_serialization.h"
-#include "mojo/public/cpp/bindings/tests/container_test_util.h"
-#include "mojo/public/cpp/bindings/tests/map_common_test.h"
-#include "mojo/public/cpp/bindings/wtf_array.h"
+#include "mojo/public/cpp/bindings/tests/rect_blink.h"
+#include "mojo/public/interfaces/bindings/tests/rect.mojom-blink.h"
+#include "mojo/public/interfaces/bindings/tests/test_structs.mojom-blink.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/WebKit/Source/wtf/text/WTFString.h"
namespace mojo {
namespace test {
-
namespace {
-using WTFMapTest = testing::Test;
-
-MAP_COMMON_TEST(WTFMap, NullAndEmpty)
-MAP_COMMON_TEST(WTFMap, InsertWorks)
-MAP_COMMON_TEST(WTFMap, TestIndexOperator)
-MAP_COMMON_TEST(WTFMap, TestIndexOperatorAsRValue)
-MAP_COMMON_TEST(WTFMap, TestIndexOperatorMoveOnly)
-MAP_COMMON_TEST(WTFMap, MapArrayClone)
-MAP_COMMON_TEST(WTFMap, ArrayOfMap)
+TEST(WTFMapTest, StructKey) {
+ WTF::HashMap<blink::RectPtr, int32_t> map;
+ map.insert(blink::Rect::New(1, 2, 3, 4), 123);
-TEST_F(WTFMapTest, MoveFromAndToWTFHashMap_Copyable) {
- WTF::HashMap<int32_t, CopyableType> map1;
- map1.add(123, CopyableType());
- map1.find(123)->value.ResetCopied();
- ASSERT_FALSE(map1.find(123)->value.copied());
+ blink::RectPtr key = blink::Rect::New(1, 2, 3, 4);
+ ASSERT_NE(map.end(), map.find(key));
+ ASSERT_EQ(123, map.find(key)->value);
- WTFMap<int32_t, CopyableType> mojo_map(std::move(map1));
- ASSERT_EQ(1u, mojo_map.size());
- ASSERT_NE(mojo_map.end(), mojo_map.find(123));
- ASSERT_FALSE(mojo_map[123].copied());
-
- WTF::HashMap<int32_t, CopyableType> map2(mojo_map.PassStorage());
- ASSERT_EQ(1u, map2.size());
- ASSERT_NE(map2.end(), map2.find(123));
- ASSERT_FALSE(map2.find(123)->value.copied());
-
- ASSERT_EQ(0u, mojo_map.size());
- ASSERT_TRUE(mojo_map.is_null());
+ map.remove(key);
+ ASSERT_EQ(0u, map.size());
}
-TEST_F(WTFMapTest, MoveFromAndToWTFHashMap_MoveOnly) {
- WTF::HashMap<int32_t, MoveOnlyType> map1;
- map1.add(123, MoveOnlyType());
-
- WTFMap<int32_t, MoveOnlyType> mojo_map(std::move(map1));
- ASSERT_EQ(1u, mojo_map.size());
- ASSERT_NE(mojo_map.end(), mojo_map.find(123));
+TEST(WTFMapTest, TypemappedStructKey) {
+ WTF::HashMap<blink::ContainsHashablePtr, int32_t> map;
+ map.insert(blink::ContainsHashable::New(RectBlink(1, 2, 3, 4)), 123);
- WTF::HashMap<int32_t, MoveOnlyType> map2(mojo_map.PassStorage());
- ASSERT_EQ(1u, map2.size());
- ASSERT_NE(map2.end(), map2.find(123));
+ blink::ContainsHashablePtr key =
+ blink::ContainsHashable::New(RectBlink(1, 2, 3, 4));
+ ASSERT_NE(map.end(), map.find(key));
+ ASSERT_EQ(123, map.find(key)->value);
- ASSERT_EQ(0u, mojo_map.size());
- ASSERT_TRUE(mojo_map.is_null());
+ map.remove(key);
+ ASSERT_EQ(0u, map.size());
}
} // namespace
diff --git a/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc b/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc
index 97392d4..363ef7c 100644
--- a/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc
+++ b/mojo/public/cpp/bindings/tests/wtf_types_unittest.cc
@@ -14,6 +14,7 @@
#include "mojo/public/interfaces/bindings/tests/test_wtf_types.mojom-blink.h"
#include "mojo/public/interfaces/bindings/tests/test_wtf_types.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/Source/wtf/text/StringHash.h"
namespace mojo {
namespace test {
@@ -75,9 +76,9 @@ WTF::Vector<WTF::String> ConstructStringArray() {
WTF::HashMap<WTF::String, WTF::String> ConstructStringMap() {
WTF::HashMap<WTF::String, WTF::String> str_map;
// A null string as value.
- str_map.add("0", WTF::String());
- str_map.add("1", kHelloWorld);
- str_map.add("2", WTF::String::fromUTF8(kUTF8HelloWorld));
+ str_map.insert("0", WTF::String());
+ str_map.insert("1", kHelloWorld);
+ str_map.insert("2", WTF::String::fromUTF8(kUTF8HelloWorld));
return str_map;
}
@@ -106,140 +107,62 @@ void ExpectStringMap(
} // namespace
-TEST_F(WTFTypesTest, Serialization_WTFArrayToWTFArray) {
- WTFArray<WTF::String> strs = ConstructStringArray();
- auto cloned_strs = strs.Clone();
-
- mojo::internal::SerializationContext context;
- size_t size = mojo::internal::PrepareToSerialize<Array<mojo::String>>(
- cloned_strs, &context);
-
- mojo::internal::FixedBufferForTesting buf(size);
- typename mojo::internal::MojomTypeTraits<Array<mojo::String>>::Data* data;
- mojo::internal::ContainerValidateParams validate_params(
- 0, true, new mojo::internal::ContainerValidateParams(0, false, nullptr));
- mojo::internal::Serialize<Array<mojo::String>>(cloned_strs, &buf, &data,
- &validate_params, &context);
-
- WTFArray<WTF::String> strs2;
- mojo::internal::Deserialize<Array<mojo::String>>(data, &strs2, &context);
-
- EXPECT_TRUE(strs.Equals(strs2));
-}
-
TEST_F(WTFTypesTest, Serialization_WTFVectorToWTFVector) {
+ using MojomType = ArrayDataView<StringDataView>;
+
WTF::Vector<WTF::String> strs = ConstructStringArray();
auto cloned_strs = strs;
mojo::internal::SerializationContext context;
- size_t size = mojo::internal::PrepareToSerialize<Array<mojo::String>>(
- cloned_strs, &context);
+ size_t size =
+ mojo::internal::PrepareToSerialize<MojomType>(cloned_strs, &context);
mojo::internal::FixedBufferForTesting buf(size);
- typename mojo::internal::MojomTypeTraits<Array<mojo::String>>::Data* data;
+ typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
mojo::internal::ContainerValidateParams validate_params(
0, true, new mojo::internal::ContainerValidateParams(0, false, nullptr));
- mojo::internal::Serialize<Array<mojo::String>>(cloned_strs, &buf, &data,
- &validate_params, &context);
+ mojo::internal::Serialize<MojomType>(cloned_strs, &buf, &data,
+ &validate_params, &context);
WTF::Vector<WTF::String> strs2;
- mojo::internal::Deserialize<Array<mojo::String>>(data, &strs2, &context);
+ mojo::internal::Deserialize<MojomType>(data, &strs2, &context);
EXPECT_EQ(strs, strs2);
}
-TEST_F(WTFTypesTest, Serialization_WTFArrayToMojoArray) {
- WTFArray<WTF::String> strs = ConstructStringArray();
-
- mojo::internal::SerializationContext context;
- size_t size =
- mojo::internal::PrepareToSerialize<Array<mojo::String>>(strs, &context);
-
- mojo::internal::FixedBufferForTesting buf(size);
- typename mojo::internal::MojomTypeTraits<Array<mojo::String>>::Data* data;
- mojo::internal::ContainerValidateParams validate_params(
- 0, true, new mojo::internal::ContainerValidateParams(0, false, nullptr));
- mojo::internal::Serialize<Array<mojo::String>>(strs, &buf, &data,
- &validate_params, &context);
-
- Array<mojo::String> strs2;
- mojo::internal::Deserialize<Array<mojo::String>>(data, &strs2, &context);
-
- ASSERT_EQ(4u, strs2.size());
- EXPECT_TRUE(strs2[0].is_null());
- EXPECT_TRUE("" == strs2[1]);
- EXPECT_TRUE(kHelloWorld == strs2[2]);
- EXPECT_TRUE(kUTF8HelloWorld == strs2[3]);
-}
-
-TEST_F(WTFTypesTest, Serialization_WTFMapToWTFMap) {
- using WTFType = WTFMap<WTF::String, WTF::String>;
- using MojomType = Map<mojo::String, mojo::String>;
+TEST_F(WTFTypesTest, Serialization_WTFVectorToStlVector) {
+ using MojomType = ArrayDataView<StringDataView>;
- WTFType str_map = ConstructStringMap();
- WTFType cloned_str_map = str_map.Clone();
+ WTF::Vector<WTF::String> strs = ConstructStringArray();
+ auto cloned_strs = strs;
mojo::internal::SerializationContext context;
size_t size =
- mojo::internal::PrepareToSerialize<MojomType>(cloned_str_map, &context);
+ mojo::internal::PrepareToSerialize<MojomType>(cloned_strs, &context);
mojo::internal::FixedBufferForTesting buf(size);
typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
mojo::internal::ContainerValidateParams validate_params(
- new mojo::internal::ContainerValidateParams(
- 0, false,
- new mojo::internal::ContainerValidateParams(0, false, nullptr)),
- new mojo::internal::ContainerValidateParams(
- 0, true,
- new mojo::internal::ContainerValidateParams(0, false, nullptr)));
- mojo::internal::Serialize<MojomType>(cloned_str_map, &buf, &data,
+ 0, true, new mojo::internal::ContainerValidateParams(0, false, nullptr));
+ mojo::internal::Serialize<MojomType>(cloned_strs, &buf, &data,
&validate_params, &context);
- WTFType str_map2;
- mojo::internal::Deserialize<MojomType>(data, &str_map2, &context);
-
- EXPECT_TRUE(str_map.Equals(str_map2));
-}
-
-TEST_F(WTFTypesTest, Serialization_WTFMapToMojoMap) {
- using WTFType = WTFMap<WTF::String, WTF::String>;
- using MojomType = Map<mojo::String, mojo::String>;
-
- WTFType str_map = ConstructStringMap();
-
- mojo::internal::SerializationContext context;
- size_t size =
- mojo::internal::PrepareToSerialize<MojomType>(str_map, &context);
+ std::vector<base::Optional<std::string>> strs2;
+ mojo::internal::Deserialize<MojomType>(data, &strs2, &context);
- mojo::internal::FixedBufferForTesting buf(size);
- typename mojo::internal::MojomTypeTraits<MojomType>::Data* data;
- mojo::internal::ContainerValidateParams validate_params(
- new mojo::internal::ContainerValidateParams(
- 0, false,
- new mojo::internal::ContainerValidateParams(0, false, nullptr)),
- new mojo::internal::ContainerValidateParams(
- 0, true,
- new mojo::internal::ContainerValidateParams(0, false, nullptr)));
- mojo::internal::Serialize<MojomType>(str_map, &buf, &data, &validate_params,
- &context);
-
- MojomType str_map2;
- mojo::internal::Deserialize<MojomType>(data, &str_map2, &context);
-
- ASSERT_EQ(3u, str_map2.size());
- EXPECT_TRUE(str_map2["0"].is_null());
- EXPECT_TRUE(kHelloWorld == str_map2["1"]);
- EXPECT_TRUE(kUTF8HelloWorld == str_map2["2"]);
+ ASSERT_EQ(4u, strs2.size());
+ EXPECT_FALSE(strs2[0]);
+ EXPECT_EQ("", *strs2[1]);
+ EXPECT_EQ(kHelloWorld, *strs2[2]);
+ EXPECT_EQ(kUTF8HelloWorld, *strs2[3]);
}
TEST_F(WTFTypesTest, Serialization_PublicAPI) {
- blink::TestWTFStructPtr input(blink::TestWTFStruct::New());
- input->str = kHelloWorld;
- input->integer = 42;
+ blink::TestWTFStructPtr input(blink::TestWTFStruct::New(kHelloWorld, 42));
blink::TestWTFStructPtr cloned_input = input.Clone();
- WTFArray<uint8_t> data = blink::TestWTFStruct::Serialize(&input);
+ auto data = blink::TestWTFStruct::Serialize(&input);
blink::TestWTFStructPtr output;
ASSERT_TRUE(blink::TestWTFStruct::Deserialize(std::move(data), &output));
@@ -248,7 +171,7 @@ TEST_F(WTFTypesTest, Serialization_PublicAPI) {
TEST_F(WTFTypesTest, SendString) {
blink::TestWTFPtr ptr;
- TestWTFImpl impl(ConvertInterfaceRequest<TestWTF>(GetProxy(&ptr)));
+ TestWTFImpl impl(ConvertInterfaceRequest<TestWTF>(MakeRequest(&ptr)));
WTF::Vector<WTF::String> strs = ConstructStringArray();
@@ -267,7 +190,7 @@ TEST_F(WTFTypesTest, SendString) {
TEST_F(WTFTypesTest, SendStringArray) {
blink::TestWTFPtr ptr;
- TestWTFImpl impl(ConvertInterfaceRequest<TestWTF>(GetProxy(&ptr)));
+ TestWTFImpl impl(ConvertInterfaceRequest<TestWTF>(MakeRequest(&ptr)));
WTF::Optional<WTF::Vector<WTF::String>> arrs[3];
// arrs[0] is empty.
@@ -293,7 +216,7 @@ TEST_F(WTFTypesTest, SendStringArray) {
TEST_F(WTFTypesTest, SendStringMap) {
blink::TestWTFPtr ptr;
- TestWTFImpl impl(ConvertInterfaceRequest<TestWTF>(GetProxy(&ptr)));
+ TestWTFImpl impl(ConvertInterfaceRequest<TestWTF>(MakeRequest(&ptr)));
WTF::Optional<WTF::HashMap<WTF::String, WTF::String>> maps[3];
// maps[0] is empty.
diff --git a/mojo/public/cpp/bindings/unique_ptr_impl_ref_traits.h b/mojo/public/cpp/bindings/unique_ptr_impl_ref_traits.h
new file mode 100644
index 0000000..f1ac097
--- /dev/null
+++ b/mojo/public/cpp/bindings/unique_ptr_impl_ref_traits.h
@@ -0,0 +1,22 @@
+// Copyright 2017 The Chromium 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_UNIQUE_PTR_IMPL_REF_TRAITS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_UNIQUE_PTR_IMPL_REF_TRAITS_H_
+
+namespace mojo {
+
+// Traits for a binding's implementation reference type.
+// This corresponds to a unique_ptr reference type.
+template <typename Interface>
+struct UniquePtrImplRefTraits {
+ using PointerType = std::unique_ptr<Interface>;
+
+ static bool IsNull(const PointerType& ptr) { return !ptr; }
+ static Interface* GetRawPointer(PointerType* ptr) { return ptr->get(); }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_UNIQUE_PTR_IMPL_REF_TRAITS_H_
diff --git a/mojo/public/cpp/system/tests/watcher_unittest.cc b/mojo/public/cpp/system/tests/watcher_unittest.cc
index 5cb3ed2..9b59240 100644
--- a/mojo/public/cpp/system/tests/watcher_unittest.cc
+++ b/mojo/public/cpp/system/tests/watcher_unittest.cc
@@ -8,6 +8,7 @@
#include "base/bind.h"
#include "base/callback.h"
+#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/threading/thread_task_runner_handle.h"
@@ -35,16 +36,10 @@ class WatcherTest : public testing::Test {
WatcherTest() {}
~WatcherTest() override {}
- void SetUp() override {
- message_loop_.reset(new base::MessageLoop);
- }
-
- void TearDown() override {
- message_loop_.reset();
- }
+ private:
+ base::MessageLoop message_loop_;
- protected:
- std::unique_ptr<base::MessageLoop> message_loop_;
+ DISALLOW_COPY_AND_ASSIGN(WatcherTest);
};
TEST_F(WatcherTest, WatchBasic) {
@@ -53,7 +48,7 @@ TEST_F(WatcherTest, WatchBasic) {
bool notified = false;
base::RunLoop run_loop;
- Watcher b_watcher;
+ Watcher b_watcher(FROM_HERE);
EXPECT_EQ(MOJO_RESULT_OK,
b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
OnReady([&] (MojoResult result) {
@@ -76,7 +71,7 @@ TEST_F(WatcherTest, WatchUnsatisfiable) {
CreateMessagePipe(nullptr, &a, &b);
a.reset();
- Watcher b_watcher;
+ Watcher b_watcher(FROM_HERE);
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
NotReached()));
@@ -89,7 +84,7 @@ TEST_F(WatcherTest, WatchInvalidHandle) {
a.reset();
b.reset();
- Watcher b_watcher;
+ Watcher b_watcher(FROM_HERE);
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
NotReached()));
@@ -101,7 +96,7 @@ TEST_F(WatcherTest, Cancel) {
CreateMessagePipe(nullptr, &a, &b);
base::RunLoop run_loop;
- Watcher b_watcher;
+ Watcher b_watcher(FROM_HERE);
EXPECT_EQ(MOJO_RESULT_OK,
b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
NotReached()));
@@ -123,7 +118,7 @@ TEST_F(WatcherTest, CancelOnClose) {
CreateMessagePipe(nullptr, &a, &b);
base::RunLoop run_loop;
- Watcher b_watcher;
+ Watcher b_watcher(FROM_HERE);
EXPECT_EQ(MOJO_RESULT_OK,
b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
OnReady([&] (MojoResult result) {
@@ -145,7 +140,7 @@ TEST_F(WatcherTest, CancelOnDestruction) {
CreateMessagePipe(nullptr, &a, &b);
base::RunLoop run_loop;
{
- Watcher b_watcher;
+ Watcher b_watcher(FROM_HERE);
EXPECT_EQ(MOJO_RESULT_OK,
b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
NotReached()));
@@ -162,33 +157,11 @@ TEST_F(WatcherTest, CancelOnDestruction) {
run_loop.Run();
}
-TEST_F(WatcherTest, NotifyOnMessageLoopDestruction) {
- ScopedMessagePipeHandle a, b;
- CreateMessagePipe(nullptr, &a, &b);
-
- bool notified = false;
- Watcher b_watcher;
- EXPECT_EQ(MOJO_RESULT_OK,
- b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
- OnReady([&] (MojoResult result) {
- EXPECT_EQ(MOJO_RESULT_ABORTED, result);
- notified = true;
- })));
- EXPECT_TRUE(b_watcher.IsWatching());
-
- message_loop_.reset();
-
- EXPECT_TRUE(notified);
-
- EXPECT_TRUE(b_watcher.IsWatching());
- b_watcher.Cancel();
-}
-
TEST_F(WatcherTest, CloseAndCancel) {
ScopedMessagePipeHandle a, b;
CreateMessagePipe(nullptr, &a, &b);
- Watcher b_watcher;
+ Watcher b_watcher(FROM_HERE);
EXPECT_EQ(MOJO_RESULT_OK,
b_watcher.Start(b.get(), MOJO_HANDLE_SIGNAL_READABLE,
OnReady([](MojoResult result) { FAIL(); })));
diff --git a/mojo/public/interfaces/bindings/pipe_control_messages.mojom b/mojo/public/interfaces/bindings/pipe_control_messages.mojom
index c1453fc..74e9cc7 100644
--- a/mojo/public/interfaces/bindings/pipe_control_messages.mojom
+++ b/mojo/public/interfaces/bindings/pipe_control_messages.mojom
@@ -43,3 +43,4 @@ struct PeerAssociatedEndpointClosedEvent {
uint32 id;
DisconnectReason? disconnect_reason;
};
+
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_invalid_interface_id.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_invalid_interface_id.data
new file mode 100644
index 0000000..a36d807
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_invalid_interface_id.data
@@ -0,0 +1,24 @@
+[dist4]message_header // num_bytes
+[u4]2 // version
+[u4]0 // interface ID
+[u4]0 // name
+[u4]0 // flags
+[u4]0 // padding
+[u8]0 // request_id
+[dist8]payload
+[dist8]payload_interface_ids
+[anchr]message_header
+
+[anchr]payload
+[dist4]method0_params // num_bytes
+[u4]0 // version
+[u4]1 // associated interface pointer: interface ID index
+[u4]1 // associated interface pointer: version
+[anchr]method0_params
+
+[anchr]payload_interface_ids
+[dist4]interface_id_array // num_bytes
+[u4]2 // num_elements
+[u4]3
+[u4]0xFFFFFFFF // Unexpected invalid interface ID.
+[anchr]interface_id_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_invalid_interface_id.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_invalid_interface_id.expected
new file mode 100644
index 0000000..420b421
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_invalid_interface_id.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_INTERFACE_ID
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_master_interface_id.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_master_interface_id.data
new file mode 100644
index 0000000..e3fa5bb
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_master_interface_id.data
@@ -0,0 +1,24 @@
+[dist4]message_header // num_bytes
+[u4]2 // version
+[u4]0 // interface ID
+[u4]0 // name
+[u4]0 // flags
+[u4]0 // padding
+[u8]0 // request_id
+[dist8]payload
+[dist8]payload_interface_ids
+[anchr]message_header
+
+[anchr]payload
+[dist4]method0_params // num_bytes
+[u4]0 // version
+[u4]1 // associated interface pointer: interface ID index
+[u4]1 // associated interface pointer: version
+[anchr]method0_params
+
+[anchr]payload_interface_ids
+[dist4]interface_id_array // num_bytes
+[u4]2 // num_elements
+[u4]3
+[u4]0 // Unexpected master interface ID.
+[anchr]interface_id_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_master_interface_id.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_master_interface_id.expected
new file mode 100644
index 0000000..420b421
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_illegal_master_interface_id.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_INTERFACE_ID
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_interface_id_index_out_of_range.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_interface_id_index_out_of_range.data
new file mode 100644
index 0000000..f9e6201
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_interface_id_index_out_of_range.data
@@ -0,0 +1,27 @@
+[dist4]message_header // num_bytes
+[u4]2 // version
+[u4]0 // interface ID
+[u4]0 // name
+[u4]0 // flags
+[u4]0 // padding
+[u8]0 // request_id
+[dist8]payload
+[dist8]payload_interface_ids
+[anchr]message_header
+
+[anchr]payload
+[dist4]method0_params // num_bytes
+[u4]0 // version
+[u4]1111 // associated interface pointer: The interface ID index
+ // is out of range.
+[u4]1 // associated interface pointer: version
+[anchr]method0_params
+
+[anchr]payload_interface_ids
+[dist4]interface_id_array // num_bytes
+[u4]3 // num_elements : It is okay to have IDs that are not
+ // referred to.
+[u4]4
+[u4]5
+[u4]8
+[anchr]interface_id_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_interface_id_index_out_of_range.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_interface_id_index_out_of_range.expected
new file mode 100644
index 0000000..420b421
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_interface_id_index_out_of_range.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_INTERFACE_ID
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_good.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_good.data
new file mode 100644
index 0000000..ab29603
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_good.data
@@ -0,0 +1,18 @@
+[dist4]message_header // num_bytes
+[u4]2 // version
+[u4]0 // interface ID
+[u4]2 // name
+[u4]0 // flags
+[u4]0 // padding
+[u8]0 // request_id
+[dist8]payload
+[u8]0 // payload_interface_ids: This array is a nullable field.
+[anchr]message_header
+
+[anchr]payload
+[dist4]method2_params // num_bytes
+[u4]0 // version
+[u4]0xFFFFFFFF // associated interface pointer: Invalid interface ID
+ // index.
+[u4]1 // associated interface pointer: version
+[anchr]method2_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_collided_interface_id_indices.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_collided_interface_id_indices.data
new file mode 100644
index 0000000..6cb71d3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_collided_interface_id_indices.data
@@ -0,0 +1,36 @@
+[dist4]message_header // num_bytes
+[u4]2 // version
+[u4]0 // interface ID
+[u4]3 // name
+[u4]0 // flags
+[u4]0 // padding
+[u8]0 // request_id
+[dist8]payload
+[dist8]payload_interface_ids
+[anchr]message_header
+
+[anchr]payload
+[dist4]method3_params // num_bytes
+[u4]0 // version
+[dist8]param0_ptr // param0
+[anchr]method3_params
+
+[anchr]param0_ptr
+[dist4]associated_interface_array // num_bytes
+[u4]2 // num_elements
+[u4]2 // interface ID index
+[u4]14 // version
+[u4]2 // interface ID index: The same value as the
+ // one above.
+[u4]18 // version
+[anchr]associated_interface_array
+
+[anchr]payload_interface_ids
+[dist4]interface_id_array // num_bytes
+[u4]4 // num_elements : It is okay to have IDs that are not
+ // referred to.
+[u4]4
+[u4]5
+[u4]8
+[u4]19
+[anchr]interface_id_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_collided_interface_id_indices.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_collided_interface_id_indices.expected
new file mode 100644
index 0000000..420b421
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_collided_interface_id_indices.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_INTERFACE_ID
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_wrong_interface_id_index_order.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_wrong_interface_id_index_order.data
new file mode 100644
index 0000000..4a63003
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_wrong_interface_id_index_order.data
@@ -0,0 +1,38 @@
+[dist4]message_header // num_bytes
+[u4]2 // version
+[u4]0 // interface ID
+[u4]3 // name
+[u4]0 // flags
+[u4]0 // padding
+[u8]0 // request_id
+[dist8]payload
+[dist8]payload_interface_ids
+[anchr]message_header
+
+[anchr]payload
+[dist4]method3_params // num_bytes
+[u4]0 // version
+[dist8]param0_ptr // param0
+[anchr]method3_params
+
+[anchr]param0_ptr
+[dist4]associated_interface_array // num_bytes
+[u4]3 // num_elements
+[u4]2 // interface ID index
+[u4]14 // version
+[u4]3 // interface ID index
+[u4]0 // version
+[u4]0 // interface ID index : It is smaller than
+ // the first element above, which is wrong.
+[u4]18 // version
+[anchr]associated_interface_array
+
+[anchr]payload_interface_ids
+[dist4]interface_id_array // num_bytes
+[u4]4 // num_elements : It is okay to have IDs that are not
+ // referred to.
+[u4]4
+[u4]5
+[u4]8
+[u4]19
+[anchr]interface_id_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_wrong_interface_id_index_order.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_wrong_interface_id_index_order.expected
new file mode 100644
index 0000000..420b421
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_wrong_interface_id_index_order.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_INTERFACE_ID
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_empy_enum_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_empy_enum_array.data
new file mode 100644
index 0000000..21af8a3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_empy_enum_array.data
@@ -0,0 +1,22 @@
+[dist4]message_header // num_bytes
+[u4]0 // version
+[u4]0 // interface ID
+[u4]15 // name
+[u4]0 // flags
+[u4]0 // padding
+[anchr]message_header
+
+[dist4]method15_params // num_bytes
+[u4]0 // version
+[dist8]enum_array_0 // param0
+[u8]0 // param1
+[anchr]method15_params
+
+[anchr]enum_array_0
+[dist4]enum_array_0_member // num_bytes
+[u4]0 // num_elements
+[anchr]enum_array_0_member
+
+[u8]0x5678 // This is not part of the array above (which is
+ // empty), so enum validation shouldn't be done on
+ // it.
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_empy_enum_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_empy_enum_array.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_good_empy_enum_array.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd18_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd18_good.data
new file mode 100644
index 0000000..663796d
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd18_good.data
@@ -0,0 +1,14 @@
+[dist4]message_header // num_bytes
+[u4]0 // version
+[u4]0 // interface ID
+[u4]18 // name
+[u4]0 // flags
+[u4]0 // padding
+[anchr]message_header
+
+[dist4]method18_params // num_bytes
+[u4]0 // version
+[u4]0 // param0: Size 0 indicating the inlined union is null.
+[u4]0 // param0: Tag field ignored.
+[u8]0 // param0: Payload field ignored.
+[anchr]method18_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd18_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd18_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd18_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd19_exceed_recursion_limit.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd19_exceed_recursion_limit.data
new file mode 100644
index 0000000..96e52d5
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd19_exceed_recursion_limit.data
@@ -0,0 +1,612 @@
+[dist4]message_header // num_bytes
+[u4]0 // version
+[u4]0 // interface ID
+[u4]19 // name
+[u4]0 // flags
+[u4]0 // padding
+[anchr]message_header
+
+[dist4]method0_params // num_bytes
+[u4]0 // version
+[dist8]param0_ptr // param0
+[anchr]method0_params
+
+[anchr]param0_ptr
+[dist4]struct_a1 // num_bytes
+[u4]0 // version
+[dist8]struct_a2_ptr // struct_a2
+[anchr]struct_a1
+
+[anchr]struct_a2_ptr
+[dist4]struct_a2 // num_bytes
+[u4]0 // version
+[dist8]struct_a3_ptr // struct_a2
+[anchr]struct_a2
+
+[anchr]struct_a3_ptr
+[dist4]struct_a3 // num_bytes
+[u4]0 // version
+[dist8]struct_a4_ptr // struct_a3
+[anchr]struct_a3
+
+[anchr]struct_a4_ptr
+[dist4]struct_a4 // num_bytes
+[u4]0 // version
+[dist8]struct_a5_ptr // struct_a4
+[anchr]struct_a4
+
+[anchr]struct_a5_ptr
+[dist4]struct_a5 // num_bytes
+[u4]0 // version
+[dist8]struct_a6_ptr // struct_a5
+[anchr]struct_a5
+
+[anchr]struct_a6_ptr
+[dist4]struct_a6 // num_bytes
+[u4]0 // version
+[dist8]struct_a7_ptr // struct_a6
+[anchr]struct_a6
+
+[anchr]struct_a7_ptr
+[dist4]struct_a7 // num_bytes
+[u4]0 // version
+[dist8]struct_a8_ptr // struct_a7
+[anchr]struct_a7
+
+[anchr]struct_a8_ptr
+[dist4]struct_a8 // num_bytes
+[u4]0 // version
+[dist8]struct_a9_ptr // struct_a8
+[anchr]struct_a8
+
+[anchr]struct_a9_ptr
+[dist4]struct_a9 // num_bytes
+[u4]0 // version
+[dist8]struct_a10_ptr // struct_a9
+[anchr]struct_a9
+
+[anchr]struct_a10_ptr
+[dist4]struct_a10 // num_bytes
+[u4]0 // version
+[dist8]struct_a11_ptr // struct_a10
+[anchr]struct_a10
+
+[anchr]struct_a11_ptr
+[dist4]struct_a11 // num_bytes
+[u4]0 // version
+[dist8]struct_a12_ptr // struct_a11
+[anchr]struct_a11
+
+[anchr]struct_a12_ptr
+[dist4]struct_a12 // num_bytes
+[u4]0 // version
+[dist8]struct_a13_ptr // struct_a12
+[anchr]struct_a12
+
+[anchr]struct_a13_ptr
+[dist4]struct_a13 // num_bytes
+[u4]0 // version
+[dist8]struct_a14_ptr // struct_a13
+[anchr]struct_a13
+
+[anchr]struct_a14_ptr
+[dist4]struct_a14 // num_bytes
+[u4]0 // version
+[dist8]struct_a15_ptr // struct_a14
+[anchr]struct_a14
+
+[anchr]struct_a15_ptr
+[dist4]struct_a15 // num_bytes
+[u4]0 // version
+[dist8]struct_a16_ptr // struct_a15
+[anchr]struct_a15
+
+[anchr]struct_a16_ptr
+[dist4]struct_a16 // num_bytes
+[u4]0 // version
+[dist8]struct_a17_ptr // struct_a16
+[anchr]struct_a16
+
+[anchr]struct_a17_ptr
+[dist4]struct_a17 // num_bytes
+[u4]0 // version
+[dist8]struct_a18_ptr // struct_a17
+[anchr]struct_a17
+
+[anchr]struct_a18_ptr
+[dist4]struct_a18 // num_bytes
+[u4]0 // version
+[dist8]struct_a19_ptr // struct_a18
+[anchr]struct_a18
+
+[anchr]struct_a19_ptr
+[dist4]struct_a19 // num_bytes
+[u4]0 // version
+[dist8]struct_a20_ptr // struct_a19
+[anchr]struct_a19
+
+[anchr]struct_a20_ptr
+[dist4]struct_a20 // num_bytes
+[u4]0 // version
+[dist8]struct_a21_ptr // struct_a20
+[anchr]struct_a20
+
+[anchr]struct_a21_ptr
+[dist4]struct_a21 // num_bytes
+[u4]0 // version
+[dist8]struct_a22_ptr // struct_a21
+[anchr]struct_a21
+
+[anchr]struct_a22_ptr
+[dist4]struct_a22 // num_bytes
+[u4]0 // version
+[dist8]struct_a23_ptr // struct_a22
+[anchr]struct_a22
+
+[anchr]struct_a23_ptr
+[dist4]struct_a23 // num_bytes
+[u4]0 // version
+[dist8]struct_a24_ptr // struct_a23
+[anchr]struct_a23
+
+[anchr]struct_a24_ptr
+[dist4]struct_a24 // num_bytes
+[u4]0 // version
+[dist8]struct_a25_ptr // struct_a24
+[anchr]struct_a24
+
+[anchr]struct_a25_ptr
+[dist4]struct_a25 // num_bytes
+[u4]0 // version
+[dist8]struct_a26_ptr // struct_a25
+[anchr]struct_a25
+
+[anchr]struct_a26_ptr
+[dist4]struct_a26 // num_bytes
+[u4]0 // version
+[dist8]struct_a27_ptr // struct_a26
+[anchr]struct_a26
+
+[anchr]struct_a27_ptr
+[dist4]struct_a27 // num_bytes
+[u4]0 // version
+[dist8]struct_a28_ptr // struct_a27
+[anchr]struct_a27
+
+[anchr]struct_a28_ptr
+[dist4]struct_a28 // num_bytes
+[u4]0 // version
+[dist8]struct_a29_ptr // struct_a28
+[anchr]struct_a28
+
+[anchr]struct_a29_ptr
+[dist4]struct_a29 // num_bytes
+[u4]0 // version
+[dist8]struct_a30_ptr // struct_a29
+[anchr]struct_a29
+
+[anchr]struct_a30_ptr
+[dist4]struct_a30 // num_bytes
+[u4]0 // version
+[dist8]struct_a31_ptr // struct_a30
+[anchr]struct_a30
+
+[anchr]struct_a31_ptr
+[dist4]struct_a31 // num_bytes
+[u4]0 // version
+[dist8]struct_a32_ptr // struct_a31
+[anchr]struct_a31
+
+[anchr]struct_a32_ptr
+[dist4]struct_a32 // num_bytes
+[u4]0 // version
+[dist8]struct_a33_ptr // struct_a32
+[anchr]struct_a32
+
+[anchr]struct_a33_ptr
+[dist4]struct_a33 // num_bytes
+[u4]0 // version
+[dist8]struct_a34_ptr // struct_a33
+[anchr]struct_a33
+
+[anchr]struct_a34_ptr
+[dist4]struct_a34 // num_bytes
+[u4]0 // version
+[dist8]struct_a35_ptr // struct_a34
+[anchr]struct_a34
+
+[anchr]struct_a35_ptr
+[dist4]struct_a35 // num_bytes
+[u4]0 // version
+[dist8]struct_a36_ptr // struct_a35
+[anchr]struct_a35
+
+[anchr]struct_a36_ptr
+[dist4]struct_a36 // num_bytes
+[u4]0 // version
+[dist8]struct_a37_ptr // struct_a36
+[anchr]struct_a36
+
+[anchr]struct_a37_ptr
+[dist4]struct_a37 // num_bytes
+[u4]0 // version
+[dist8]struct_a38_ptr // struct_a37
+[anchr]struct_a37
+
+[anchr]struct_a38_ptr
+[dist4]struct_a38 // num_bytes
+[u4]0 // version
+[dist8]struct_a39_ptr // struct_a38
+[anchr]struct_a38
+
+[anchr]struct_a39_ptr
+[dist4]struct_a39 // num_bytes
+[u4]0 // version
+[dist8]struct_a40_ptr // struct_a39
+[anchr]struct_a39
+
+[anchr]struct_a40_ptr
+[dist4]struct_a40 // num_bytes
+[u4]0 // version
+[dist8]struct_a41_ptr // struct_a40
+[anchr]struct_a40
+
+[anchr]struct_a41_ptr
+[dist4]struct_a41 // num_bytes
+[u4]0 // version
+[dist8]struct_a42_ptr // struct_a41
+[anchr]struct_a41
+
+[anchr]struct_a42_ptr
+[dist4]struct_a42 // num_bytes
+[u4]0 // version
+[dist8]struct_a43_ptr // struct_a42
+[anchr]struct_a42
+
+[anchr]struct_a43_ptr
+[dist4]struct_a43 // num_bytes
+[u4]0 // version
+[dist8]struct_a44_ptr // struct_a43
+[anchr]struct_a43
+
+[anchr]struct_a44_ptr
+[dist4]struct_a44 // num_bytes
+[u4]0 // version
+[dist8]struct_a45_ptr // struct_a44
+[anchr]struct_a44
+
+[anchr]struct_a45_ptr
+[dist4]struct_a45 // num_bytes
+[u4]0 // version
+[dist8]struct_a46_ptr // struct_a45
+[anchr]struct_a45
+
+[anchr]struct_a46_ptr
+[dist4]struct_a46 // num_bytes
+[u4]0 // version
+[dist8]struct_a47_ptr // struct_a46
+[anchr]struct_a46
+
+[anchr]struct_a47_ptr
+[dist4]struct_a47 // num_bytes
+[u4]0 // version
+[dist8]struct_a48_ptr // struct_a47
+[anchr]struct_a47
+
+[anchr]struct_a48_ptr
+[dist4]struct_a48 // num_bytes
+[u4]0 // version
+[dist8]struct_a49_ptr // struct_a48
+[anchr]struct_a48
+
+[anchr]struct_a49_ptr
+[dist4]struct_a49 // num_bytes
+[u4]0 // version
+[dist8]struct_a50_ptr // struct_a49
+[anchr]struct_a49
+
+[anchr]struct_a50_ptr
+[dist4]struct_a50 // num_bytes
+[u4]0 // version
+[dist8]struct_a51_ptr // struct_a50
+[anchr]struct_a50
+
+[anchr]struct_a51_ptr
+[dist4]struct_a51 // num_bytes
+[u4]0 // version
+[dist8]struct_a52_ptr // struct_a51
+[anchr]struct_a51
+
+[anchr]struct_a52_ptr
+[dist4]struct_a52 // num_bytes
+[u4]0 // version
+[dist8]struct_a53_ptr // struct_a52
+[anchr]struct_a52
+
+[anchr]struct_a53_ptr
+[dist4]struct_a53 // num_bytes
+[u4]0 // version
+[dist8]struct_a54_ptr // struct_a53
+[anchr]struct_a53
+
+[anchr]struct_a54_ptr
+[dist4]struct_a54 // num_bytes
+[u4]0 // version
+[dist8]struct_a55_ptr // struct_a54
+[anchr]struct_a54
+
+[anchr]struct_a55_ptr
+[dist4]struct_a55 // num_bytes
+[u4]0 // version
+[dist8]struct_a56_ptr // struct_a55
+[anchr]struct_a55
+
+[anchr]struct_a56_ptr
+[dist4]struct_a56 // num_bytes
+[u4]0 // version
+[dist8]struct_a57_ptr // struct_a56
+[anchr]struct_a56
+
+[anchr]struct_a57_ptr
+[dist4]struct_a57 // num_bytes
+[u4]0 // version
+[dist8]struct_a58_ptr // struct_a57
+[anchr]struct_a57
+
+[anchr]struct_a58_ptr
+[dist4]struct_a58 // num_bytes
+[u4]0 // version
+[dist8]struct_a59_ptr // struct_a58
+[anchr]struct_a58
+
+[anchr]struct_a59_ptr
+[dist4]struct_a59 // num_bytes
+[u4]0 // version
+[dist8]struct_a60_ptr // struct_a59
+[anchr]struct_a59
+
+[anchr]struct_a60_ptr
+[dist4]struct_a60 // num_bytes
+[u4]0 // version
+[dist8]struct_a61_ptr // struct_a60
+[anchr]struct_a60
+
+[anchr]struct_a61_ptr
+[dist4]struct_a61 // num_bytes
+[u4]0 // version
+[dist8]struct_a62_ptr // struct_a61
+[anchr]struct_a61
+
+[anchr]struct_a62_ptr
+[dist4]struct_a62 // num_bytes
+[u4]0 // version
+[dist8]struct_a63_ptr // struct_a62
+[anchr]struct_a62
+
+[anchr]struct_a63_ptr
+[dist4]struct_a63 // num_bytes
+[u4]0 // version
+[dist8]struct_a64_ptr // struct_a63
+[anchr]struct_a63
+
+[anchr]struct_a64_ptr
+[dist4]struct_a64 // num_bytes
+[u4]0 // version
+[dist8]struct_a65_ptr // struct_a64
+[anchr]struct_a64
+
+[anchr]struct_a65_ptr
+[dist4]struct_a65 // num_bytes
+[u4]0 // version
+[dist8]struct_a66_ptr // struct_a65
+[anchr]struct_a65
+
+[anchr]struct_a66_ptr
+[dist4]struct_a66 // num_bytes
+[u4]0 // version
+[dist8]struct_a67_ptr // struct_a66
+[anchr]struct_a66
+
+[anchr]struct_a67_ptr
+[dist4]struct_a67 // num_bytes
+[u4]0 // version
+[dist8]struct_a68_ptr // struct_a67
+[anchr]struct_a67
+
+[anchr]struct_a68_ptr
+[dist4]struct_a68 // num_bytes
+[u4]0 // version
+[dist8]struct_a69_ptr // struct_a68
+[anchr]struct_a68
+
+[anchr]struct_a69_ptr
+[dist4]struct_a69 // num_bytes
+[u4]0 // version
+[dist8]struct_a70_ptr // struct_a69
+[anchr]struct_a69
+
+[anchr]struct_a70_ptr
+[dist4]struct_a70 // num_bytes
+[u4]0 // version
+[dist8]struct_a71_ptr // struct_a70
+[anchr]struct_a70
+
+[anchr]struct_a71_ptr
+[dist4]struct_a71 // num_bytes
+[u4]0 // version
+[dist8]struct_a72_ptr // struct_a71
+[anchr]struct_a71
+
+[anchr]struct_a72_ptr
+[dist4]struct_a72 // num_bytes
+[u4]0 // version
+[dist8]struct_a73_ptr // struct_a72
+[anchr]struct_a72
+
+[anchr]struct_a73_ptr
+[dist4]struct_a73 // num_bytes
+[u4]0 // version
+[dist8]struct_a74_ptr // struct_a73
+[anchr]struct_a73
+
+[anchr]struct_a74_ptr
+[dist4]struct_a74 // num_bytes
+[u4]0 // version
+[dist8]struct_a75_ptr // struct_a74
+[anchr]struct_a74
+
+[anchr]struct_a75_ptr
+[dist4]struct_a75 // num_bytes
+[u4]0 // version
+[dist8]struct_a76_ptr // struct_a75
+[anchr]struct_a75
+
+[anchr]struct_a76_ptr
+[dist4]struct_a76 // num_bytes
+[u4]0 // version
+[dist8]struct_a77_ptr // struct_a76
+[anchr]struct_a76
+
+[anchr]struct_a77_ptr
+[dist4]struct_a77 // num_bytes
+[u4]0 // version
+[dist8]struct_a78_ptr // struct_a77
+[anchr]struct_a77
+
+[anchr]struct_a78_ptr
+[dist4]struct_a78 // num_bytes
+[u4]0 // version
+[dist8]struct_a79_ptr // struct_a78
+[anchr]struct_a78
+
+[anchr]struct_a79_ptr
+[dist4]struct_a79 // num_bytes
+[u4]0 // version
+[dist8]struct_a80_ptr // struct_a79
+[anchr]struct_a79
+
+[anchr]struct_a80_ptr
+[dist4]struct_a80 // num_bytes
+[u4]0 // version
+[dist8]struct_a81_ptr // struct_a80
+[anchr]struct_a80
+
+[anchr]struct_a81_ptr
+[dist4]struct_a81 // num_bytes
+[u4]0 // version
+[dist8]struct_a82_ptr // struct_a81
+[anchr]struct_a81
+
+[anchr]struct_a82_ptr
+[dist4]struct_a82 // num_bytes
+[u4]0 // version
+[dist8]struct_a83_ptr // struct_a82
+[anchr]struct_a82
+
+[anchr]struct_a83_ptr
+[dist4]struct_a83 // num_bytes
+[u4]0 // version
+[dist8]struct_a84_ptr // struct_a83
+[anchr]struct_a83
+
+[anchr]struct_a84_ptr
+[dist4]struct_a84 // num_bytes
+[u4]0 // version
+[dist8]struct_a85_ptr // struct_a84
+[anchr]struct_a84
+
+[anchr]struct_a85_ptr
+[dist4]struct_a85 // num_bytes
+[u4]0 // version
+[dist8]struct_a86_ptr // struct_a85
+[anchr]struct_a85
+
+[anchr]struct_a86_ptr
+[dist4]struct_a86 // num_bytes
+[u4]0 // version
+[dist8]struct_a87_ptr // struct_a86
+[anchr]struct_a86
+
+[anchr]struct_a87_ptr
+[dist4]struct_a87 // num_bytes
+[u4]0 // version
+[dist8]struct_a88_ptr // struct_a87
+[anchr]struct_a87
+
+[anchr]struct_a88_ptr
+[dist4]struct_a88 // num_bytes
+[u4]0 // version
+[dist8]struct_a89_ptr // struct_a88
+[anchr]struct_a88
+
+[anchr]struct_a89_ptr
+[dist4]struct_a89 // num_bytes
+[u4]0 // version
+[dist8]struct_a90_ptr // struct_a89
+[anchr]struct_a89
+
+[anchr]struct_a90_ptr
+[dist4]struct_a90 // num_bytes
+[u4]0 // version
+[dist8]struct_a91_ptr // struct_a90
+[anchr]struct_a90
+
+[anchr]struct_a91_ptr
+[dist4]struct_a91 // num_bytes
+[u4]0 // version
+[dist8]struct_a92_ptr // struct_a91
+[anchr]struct_a91
+
+[anchr]struct_a92_ptr
+[dist4]struct_a92 // num_bytes
+[u4]0 // version
+[dist8]struct_a93_ptr // struct_a92
+[anchr]struct_a92
+
+[anchr]struct_a93_ptr
+[dist4]struct_a93 // num_bytes
+[u4]0 // version
+[dist8]struct_a94_ptr // struct_a93
+[anchr]struct_a93
+
+[anchr]struct_a94_ptr
+[dist4]struct_a94 // num_bytes
+[u4]0 // version
+[dist8]struct_a95_ptr // struct_a94
+[anchr]struct_a94
+
+[anchr]struct_a95_ptr
+[dist4]struct_a95 // num_bytes
+[u4]0 // version
+[dist8]struct_a96_ptr // struct_a95
+[anchr]struct_a95
+
+[anchr]struct_a96_ptr
+[dist4]struct_a96 // num_bytes
+[u4]0 // version
+[dist8]struct_a97_ptr // struct_a96
+[anchr]struct_a96
+
+[anchr]struct_a97_ptr
+[dist4]struct_a97 // num_bytes
+[u4]0 // version
+[dist8]struct_a98_ptr // struct_a97
+[anchr]struct_a97
+
+[anchr]struct_a98_ptr
+[dist4]struct_a98 // num_bytes
+[u4]0 // version
+[dist8]struct_a99_ptr // struct_a98
+[anchr]struct_a98
+
+[anchr]struct_a99_ptr
+[dist4]struct_a99 // num_bytes
+[u4]0 // version
+[dist8]struct_a100_ptr // struct_a99
+[anchr]struct_a99
+
+[anchr]struct_a100_ptr
+[dist4]struct_a100 // num_bytes
+[u4]0 // version
+[u8]0 // struct_a100
+[anchr]struct_a100
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd19_exceed_recursion_limit.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd19_exceed_recursion_limit.expected
new file mode 100644
index 0000000..81d6cd8
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd19_exceed_recursion_limit.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_MAX_RECURSION_DEPTH \ No newline at end of file
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd20_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd20_good.data
new file mode 100644
index 0000000..8ec608b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd20_good.data
@@ -0,0 +1,57 @@
+[dist4]message_header // num_bytes
+[u4]0 // version
+[u4]0 // interface ID
+[u4]20 // name
+[u4]0 // flags
+[u4]0 // padding
+[anchr]message_header
+
+[dist4]method20_params // num_bytes
+[u4]0 // version
+[dist8]map_data_ptr // param0
+[anchr]method20_params
+
+[anchr]map_data_ptr
+[dist4]map_data_struct_header // num_bytes
+[u4]0 // version
+[dist8]key_array_ptr
+[dist8]value_array_ptr
+[anchr]map_data_struct_header
+
+[anchr]key_array_ptr
+[dist4]key_array_member // num_bytes
+[u4]2 // num_elements
+[dist8]key_struct_b_1
+[dist8]key_struct_b_2
+[anchr]key_array_member
+
+[anchr]key_struct_b_1
+[dist4]key_struct_b_1_member // num_bytes
+[u4]0 // version
+[dist8]key_struct_a_1 // struct_a
+[anchr]key_struct_b_1_member
+
+[anchr]key_struct_a_1
+[dist4]key_struct_a_1_member // num_bytes
+[u4]0 // version
+[u8]1234 // i
+[anchr]key_struct_a_1_member
+
+[anchr]key_struct_b_2
+[dist4]key_struct_b_2_member // num_bytes
+[u4]0 // version
+[dist8]key_struct_a_2 // struct_a
+[anchr]key_struct_b_2_member
+
+[anchr]key_struct_a_2
+[dist4]key_struct_a_2_member // num_bytes
+[u4]0 // version
+[u8]5678 // i
+[anchr]key_struct_a_2_member
+
+[anchr]value_array_ptr
+[dist4]value_array_member // num_bytes
+[u4]2 // num_elements
+[u1]1
+[u1]2
+[anchr]value_array_member
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd20_good.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd20_good.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd20_good.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd21_empty_extensible_enum_accepts_any_value.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd21_empty_extensible_enum_accepts_any_value.data
new file mode 100644
index 0000000..d3ae88e
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd21_empty_extensible_enum_accepts_any_value.data
@@ -0,0 +1,13 @@
+[dist4]message_header // num_bytes
+[u4]0 // version
+[u4]0 // interface ID
+[u4]21 // name
+[u4]0 // flags
+[u4]0 // padding
+[anchr]message_header
+
+[dist4]method21_params // num_bytes
+[u4]0 // version
+[u4]7 // param0. All values are valid for an extensible enum.
+[u4]0 // padding
+[anchr]method21_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd21_empty_extensible_enum_accepts_any_value.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd21_empty_extensible_enum_accepts_any_value.expected
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd21_empty_extensible_enum_accepts_any_value.expected
@@ -0,0 +1 @@
+PASS
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd22_empty_nonextensible_enum_accepts_no_values.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd22_empty_nonextensible_enum_accepts_no_values.data
new file mode 100644
index 0000000..414785c
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd22_empty_nonextensible_enum_accepts_no_values.data
@@ -0,0 +1,14 @@
+[dist4]message_header // num_bytes
+[u4]0 // version
+[u4]0 // interface ID
+[u4]22 // name
+[u4]0 // flags
+[u4]0 // padding
+[anchr]message_header
+
+[dist4]method21_params // num_bytes
+[u4]0 // version
+[u4]0 // param0. No values are valid for an empty
+ // non-extensible enum.
+[u4]0 // padding
+[anchr]method21_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd22_empty_nonextensible_enum_accepts_no_values.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd22_empty_nonextensible_enum_accepts_no_values.expected
new file mode 100644
index 0000000..9ef4ce3
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd22_empty_nonextensible_enum_accepts_no_values.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_UNKNOWN_ENUM_VALUE
diff --git a/mojo/public/interfaces/bindings/tests/test_bad_messages.mojom b/mojo/public/interfaces/bindings/tests/test_bad_messages.mojom
new file mode 100644
index 0000000..dcd5947
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/test_bad_messages.mojom
@@ -0,0 +1,13 @@
+// Copyright 2016 The Chromium 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 TestBadMessages {
+ RejectEventually() => ();
+ RequestResponse() => ();
+
+ [Sync] RejectSync() => ();
+ [Sync] RequestResponseSync() => ();
+};
diff --git a/mojo/public/interfaces/bindings/tests/test_data_view.mojom b/mojo/public/interfaces/bindings/tests/test_data_view.mojom
new file mode 100644
index 0000000..1fe8c6a
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/test_data_view.mojom
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium 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.data_view;
+
+enum TestEnum {
+ VALUE_0,
+ VALUE_1
+};
+
+interface TestInterface {
+ [Sync]
+ Echo(int32 value) => (int32 out_value);
+};
+
+struct NestedStruct {
+ int32 f_int32;
+};
+
+[Native]
+struct TestNativeStruct;
+
+union TestUnion {
+ bool f_bool;
+ int32 f_int32;
+};
+
+struct TestStruct {
+ string f_string;
+ NestedStruct? f_struct;
+ TestNativeStruct? f_native_struct;
+ array<bool> f_bool_array;
+ array<int32> f_int32_array;
+ array<TestEnum> f_enum_array;
+ array<TestInterface> f_interface_array;
+ array<array<int32>> f_nested_array;
+ array<NestedStruct> f_struct_array;
+ array<TestUnion> f_union_array;
+ map<string, int32> f_map;
+};
diff --git a/mojo/public/interfaces/bindings/tests/test_export.mojom b/mojo/public/interfaces/bindings/tests/test_export.mojom
new file mode 100644
index 0000000..319a15b
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/test_export.mojom
@@ -0,0 +1,20 @@
+// Copyright 2016 The Chromium 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.test_export;
+
+struct StringPair {
+ string s1;
+ string s2;
+};
+
+// This is a regression test. On Windows, if we export the generated class *and*
+// not explicitly disallow copy constructor and assign operator, compilation
+// will fail because it tries to use copy constructor of
+// InlinedStructPtr<StringPair>.
+struct StringPairContainer {
+ array<StringPair> pairs;
+};
+
+interface ExportedInterface {};
diff --git a/mojo/public/interfaces/bindings/tests/test_import.mojom b/mojo/public/interfaces/bindings/tests/test_import.mojom
new file mode 100644
index 0000000..42014c6
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/test_import.mojom
@@ -0,0 +1,11 @@
+// Copyright 2016 The Chromium 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.test_import;
+
+import "mojo/public/interfaces/bindings/tests/test_export.mojom";
+
+struct ImportingStruct {
+ mojo.test.test_export.StringPair strings;
+};
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java
index 385eb40..2699ab8 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java
@@ -201,12 +201,12 @@ public interface Interface extends ConnectionErrorHandler, Closeable {
new Callback1<RunResponseMessageParams>() {
@Override
public void call(RunResponseMessageParams response) {
- if (response.output != null
+ if (response.output != null
&& response.output.which()
== RunOutput.Tag.QueryVersionResult) {
mVersion = response.output.getQueryVersionResult().version;
}
- try {
+ try {
callback.call(mVersion);
} catch (RuntimeException e) {
// TODO(lhchavez): Remove this hack. See b/28986534 for details.
diff --git a/mojo/public/js/interface_types.js b/mojo/public/js/interface_types.js
new file mode 100644
index 0000000..01ea2d1
--- /dev/null
+++ b/mojo/public/js/interface_types.js
@@ -0,0 +1,52 @@
+// Copyright 2016 The Chromium 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/interface_types", [
+ "mojo/public/js/core",
+], function(core) {
+
+ // ---------------------------------------------------------------------------
+
+ function InterfacePtrInfo(handle, version) {
+ this.handle = handle;
+ this.version = version;
+ }
+
+ InterfacePtrInfo.prototype.isValid = function() {
+ return core.isHandle(this.handle);
+ };
+
+ InterfacePtrInfo.prototype.close = function() {
+ if (!this.isValid())
+ return;
+
+ core.close(this.handle);
+ this.handle = null;
+ this.version = 0;
+ };
+
+ // ---------------------------------------------------------------------------
+
+ function InterfaceRequest(handle) {
+ this.handle = handle;
+ }
+
+ InterfaceRequest.prototype.isValid = function() {
+ return core.isHandle(this.handle);
+ };
+
+ InterfaceRequest.prototype.close = function() {
+ if (!this.isValid())
+ return;
+
+ core.close(this.handle);
+ this.handle = null;
+ };
+
+ var exports = {};
+ exports.InterfacePtrInfo = InterfacePtrInfo;
+ exports.InterfaceRequest = InterfaceRequest;
+
+ return exports;
+});
diff --git a/mojo/public/js/lib/control_message_handler.js b/mojo/public/js/lib/control_message_handler.js
new file mode 100644
index 0000000..81d9002
--- /dev/null
+++ b/mojo/public/js/lib/control_message_handler.js
@@ -0,0 +1,111 @@
+// Copyright 2017 The Chromium 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/lib/control_message_handler", [
+ "mojo/public/js/codec",
+ "mojo/public/interfaces/bindings/interface_control_messages.mojom",
+ "mojo/public/js/validator",
+], function(codec, controlMessages, validator) {
+
+ var Validator = validator.Validator;
+
+ function validateControlRequestWithResponse(message) {
+ var messageValidator = new Validator(message);
+ var error = messageValidator.validateMessageIsRequestExpectingResponse();
+ if (error !== validator.validationError.NONE) {
+ throw error;
+ }
+
+ if (message.getName() != controlMessages.kRunMessageId) {
+ throw new Error("Control message name is not kRunMessageId");
+ }
+
+ // Validate payload.
+ error = controlMessages.RunMessageParams.validate(messageValidator,
+ message.getHeaderNumBytes());
+ if (error != validator.validationError.NONE) {
+ throw error;
+ }
+ }
+
+ function validateControlRequestWithoutResponse(message) {
+ var messageValidator = new Validator(message);
+ var error = messageValidator.validateMessageIsRequestWithoutResponse();
+ if (error != validator.validationError.NONE) {
+ throw error;
+ }
+
+ if (message.getName() != controlMessages.kRunOrClosePipeMessageId) {
+ throw new Error("Control message name is not kRunOrClosePipeMessageId");
+ }
+
+ // Validate payload.
+ error = controlMessages.RunOrClosePipeMessageParams.validate(
+ messageValidator, message.getHeaderNumBytes());
+ if (error != validator.validationError.NONE) {
+ throw error;
+ }
+ }
+
+ function runOrClosePipe(message, interface_version) {
+ var reader = new codec.MessageReader(message);
+ var runOrClosePipeMessageParams = reader.decodeStruct(
+ controlMessages.RunOrClosePipeMessageParams);
+ return interface_version >=
+ runOrClosePipeMessageParams.input.require_version.version;
+ }
+
+ function run(message, responder, interface_version) {
+ var reader = new codec.MessageReader(message);
+ var runMessageParams =
+ reader.decodeStruct(controlMessages.RunMessageParams);
+ var runOutput = null;
+
+ if (runMessageParams.input.query_version) {
+ runOutput = new controlMessages.RunOutput();
+ runOutput.query_version_result = new
+ controlMessages.QueryVersionResult({'version': interface_version});
+ }
+
+ var runResponseMessageParams = new
+ controlMessages.RunResponseMessageParams();
+ runResponseMessageParams.output = runOutput;
+
+ var messageName = controlMessages.kRunMessageId;
+ var payloadSize = controlMessages.RunResponseMessageParams.encodedSize;
+ var requestID = reader.requestID;
+ var builder = new codec.MessageWithRequestIDBuilder(messageName,
+ payloadSize, codec.kMessageIsResponse, requestID);
+ builder.encodeStruct(controlMessages.RunResponseMessageParams,
+ runResponseMessageParams);
+ responder.accept(builder.finish());
+ return true;
+ }
+
+ function isControlMessage(message) {
+ return message.getName() == controlMessages.kRunMessageId ||
+ message.getName() == controlMessages.kRunOrClosePipeMessageId;
+ }
+
+ function ControlMessageHandler(interface_version) {
+ this.interface_version = interface_version;
+ }
+
+ ControlMessageHandler.prototype.accept = function(message) {
+ validateControlRequestWithoutResponse(message);
+ return runOrClosePipe(message, this.interface_version);
+ };
+
+ ControlMessageHandler.prototype.acceptWithResponder = function(message,
+ responder) {
+ validateControlRequestWithResponse(message);
+ return run(message, responder, this.interface_version);
+ };
+
+ var exports = {};
+ exports.ControlMessageHandler = ControlMessageHandler;
+ exports.isControlMessage = isControlMessage;
+
+ return exports;
+});
diff --git a/mojo/public/js/lib/control_message_proxy.js b/mojo/public/js/lib/control_message_proxy.js
new file mode 100644
index 0000000..d6c0734
--- /dev/null
+++ b/mojo/public/js/lib/control_message_proxy.js
@@ -0,0 +1,102 @@
+// Copyright 2017 The Chromium 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/lib/control_message_proxy", [
+ "mojo/public/interfaces/bindings/interface_control_messages.mojom",
+ "mojo/public/js/codec",
+ "mojo/public/js/validator",
+], function(controlMessages, codec, validator) {
+
+ var Validator = validator.Validator;
+
+ function sendRunOrClosePipeMessage(receiver, runOrClosePipeMessageParams) {
+ var messageName = controlMessages.kRunOrClosePipeMessageId;
+ var payloadSize = controlMessages.RunOrClosePipeMessageParams.encodedSize;
+ var builder = new codec.MessageBuilder(messageName, payloadSize);
+ builder.encodeStruct(controlMessages.RunOrClosePipeMessageParams,
+ runOrClosePipeMessageParams);
+ var message = builder.finish();
+ receiver.accept(message);
+ }
+
+ function validateControlResponse(message) {
+ var messageValidator = new Validator(message);
+ var error = messageValidator.validateMessageIsResponse();
+ if (error != validator.validationError.NONE) {
+ throw error;
+ }
+
+ if (message.getName() != controlMessages.kRunMessageId) {
+ throw new Error("Control message name is not kRunMessageId");
+ }
+
+ // Validate payload.
+ error = controlMessages.RunResponseMessageParams.validate(
+ messageValidator, message.getHeaderNumBytes());
+ if (error != validator.validationError.NONE) {
+ throw error;
+ }
+ }
+
+ function acceptRunResponse(message) {
+ validateControlResponse(message);
+
+ var reader = new codec.MessageReader(message);
+ var runResponseMessageParams = reader.decodeStruct(
+ controlMessages.RunResponseMessageParams);
+
+ return Promise.resolve(runResponseMessageParams);
+ }
+
+ /**
+ * Sends the given run message through the receiver.
+ * Accepts the response message from the receiver and decodes the message
+ * struct to RunResponseMessageParams.
+ *
+ * @param {Router} receiver.
+ * @param {RunMessageParams} runMessageParams to be sent via a message.
+ * @return {Promise} that resolves to a RunResponseMessageParams.
+ */
+ function sendRunMessage(receiver, runMessageParams) {
+ var messageName = controlMessages.kRunMessageId;
+ var payloadSize = controlMessages.RunMessageParams.encodedSize;
+ // |requestID| is set to 0, but is later properly set by Router.
+ var builder = new codec.MessageWithRequestIDBuilder(messageName,
+ payloadSize, codec.kMessageExpectsResponse, 0);
+ builder.encodeStruct(controlMessages.RunMessageParams, runMessageParams);
+ var message = builder.finish();
+
+ return receiver.acceptAndExpectResponse(message).then(acceptRunResponse);
+ }
+
+ function ControlMessageProxy(receiver) {
+ this.receiver = receiver;
+ }
+
+ ControlMessageProxy.prototype.queryVersion = function() {
+ var runMessageParams = new controlMessages.RunMessageParams();
+ runMessageParams.input = new controlMessages.RunInput();
+ runMessageParams.input.query_version = new controlMessages.QueryVersion();
+
+ return sendRunMessage(this.receiver, runMessageParams).then(function(
+ runResponseMessageParams) {
+ return runResponseMessageParams.output.query_version_result.version;
+ });
+ };
+
+ ControlMessageProxy.prototype.requireVersion = function(version) {
+ var runOrClosePipeMessageParams = new
+ controlMessages.RunOrClosePipeMessageParams();
+ runOrClosePipeMessageParams.input = new
+ controlMessages.RunOrClosePipeInput();
+ runOrClosePipeMessageParams.input.require_version = new
+ controlMessages.RequireVersion({'version': version});
+ sendRunOrClosePipeMessage(this.receiver, runOrClosePipeMessageParams);
+ };
+
+ var exports = {};
+ exports.ControlMessageProxy = ControlMessageProxy;
+
+ return exports;
+});
diff --git a/mojo/public/js/new_bindings/bindings.js b/mojo/public/js/new_bindings/bindings.js
new file mode 100644
index 0000000..f3e40d2
--- /dev/null
+++ b/mojo/public/js/new_bindings/bindings.js
@@ -0,0 +1,285 @@
+// Copyright 2014 The Chromium 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", [
+ "mojo/public/js/core",
+ "mojo/public/js/lib/control_message_proxy",
+ "mojo/public/js/interface_types",
+ "mojo/public/js/router",
+], function(core, controlMessageProxy, types, router) {
+
+ // ---------------------------------------------------------------------------
+
+ function makeRequest(interfacePtr) {
+ var pipe = core.createMessagePipe();
+ interfacePtr.ptr.bind(new types.InterfacePtrInfo(pipe.handle0, 0));
+ return new types.InterfaceRequest(pipe.handle1);
+ }
+
+ // ---------------------------------------------------------------------------
+
+ // Operations used to setup/configure an interface pointer. Exposed as the
+ // |ptr| field of generated interface pointer classes.
+ // |ptrInfoOrHandle| could be omitted and passed into bind() later.
+ function InterfacePtrController(interfaceType, ptrInfoOrHandle) {
+ this.version = 0;
+
+ this.interfaceType_ = interfaceType;
+ this.router_ = null;
+ this.proxy_ = null;
+
+ // |router_| is lazily initialized. |handle_| is valid between bind() and
+ // the initialization of |router_|.
+ this.handle_ = null;
+ this.controlMessageProxy_ = null;
+
+ if (ptrInfoOrHandle)
+ this.bind(ptrInfoOrHandle);
+ }
+
+ InterfacePtrController.prototype.bind = function(ptrInfoOrHandle) {
+ this.reset();
+
+ if (ptrInfoOrHandle instanceof types.InterfacePtrInfo) {
+ this.version = ptrInfoOrHandle.version;
+ this.handle_ = ptrInfoOrHandle.handle;
+ } else {
+ this.handle_ = ptrInfoOrHandle;
+ }
+ };
+
+ InterfacePtrController.prototype.isBound = function() {
+ return this.router_ !== null || this.handle_ !== null;
+ };
+
+ // Although users could just discard the object, reset() closes the pipe
+ // immediately.
+ InterfacePtrController.prototype.reset = function() {
+ this.version = 0;
+ if (this.router_) {
+ this.router_.close();
+ this.router_ = null;
+
+ this.proxy_ = null;
+ }
+ if (this.handle_) {
+ core.close(this.handle_);
+ this.handle_ = null;
+ }
+ };
+
+ InterfacePtrController.prototype.setConnectionErrorHandler
+ = function(callback) {
+ if (!this.isBound())
+ throw new Error("Cannot set connection error handler if not bound.");
+
+ this.configureProxyIfNecessary_();
+ this.router_.setErrorHandler(callback);
+ };
+
+ InterfacePtrController.prototype.passInterface = function() {
+ var result;
+ if (this.router_) {
+ // TODO(yzshen): Fix Router interface to support extracting handle.
+ result = new types.InterfacePtrInfo(
+ this.router_.connector_.handle_, this.version);
+ this.router_.connector_.handle_ = null;
+ } else {
+ // This also handles the case when this object is not bound.
+ result = new types.InterfacePtrInfo(this.handle_, this.version);
+ this.handle_ = null;
+ }
+
+ this.reset();
+ return result;
+ };
+
+ InterfacePtrController.prototype.getProxy = function() {
+ this.configureProxyIfNecessary_();
+ return this.proxy_;
+ };
+
+ InterfacePtrController.prototype.enableTestingMode = function() {
+ this.configureProxyIfNecessary_();
+ return this.router_.enableTestingMode();
+ };
+
+ InterfacePtrController.prototype.configureProxyIfNecessary_ = function() {
+ if (!this.handle_)
+ return;
+
+ this.router_ = new router.Router(this.handle_);
+ this.handle_ = null;
+ this.router_ .setPayloadValidators([this.interfaceType_.validateResponse]);
+
+ this.controlMessageProxy_ = new
+ controlMessageProxy.ControlMessageProxy(this.router_);
+
+ this.proxy_ = new this.interfaceType_.proxyClass(this.router_);
+ };
+
+ InterfacePtrController.prototype.queryVersion = function() {
+ function onQueryVersion(version) {
+ this.version = version;
+ return version;
+ }
+
+ this.configureProxyIfNecessary_();
+ return this.controlMessageProxy_.queryVersion().then(
+ onQueryVersion.bind(this));
+ };
+
+ InterfacePtrController.prototype.requireVersion = function(version) {
+ this.configureProxyIfNecessary_();
+
+ if (this.version >= version) {
+ return;
+ }
+ this.version = version;
+ this.controlMessageProxy_.requireVersion(version);
+ };
+
+ // ---------------------------------------------------------------------------
+
+ // |request| could be omitted and passed into bind() later.
+ //
+ // Example:
+ //
+ // // FooImpl implements mojom.Foo.
+ // function FooImpl() { ... }
+ // FooImpl.prototype.fooMethod1 = function() { ... }
+ // FooImpl.prototype.fooMethod2 = function() { ... }
+ //
+ // var fooPtr = new mojom.FooPtr();
+ // var request = makeRequest(fooPtr);
+ // var binding = new Binding(mojom.Foo, new FooImpl(), request);
+ // fooPtr.fooMethod1();
+ function Binding(interfaceType, impl, requestOrHandle) {
+ this.interfaceType_ = interfaceType;
+ this.impl_ = impl;
+ this.router_ = null;
+ this.stub_ = null;
+
+ if (requestOrHandle)
+ this.bind(requestOrHandle);
+ }
+
+ Binding.prototype.isBound = function() {
+ return this.router_ !== null;
+ };
+
+ Binding.prototype.createInterfacePtrAndBind = function() {
+ var ptr = new this.interfaceType_.ptrClass();
+ // TODO(yzshen): Set the version of the interface pointer.
+ this.bind(makeRequest(ptr));
+ return ptr;
+ }
+
+ Binding.prototype.bind = function(requestOrHandle) {
+ this.close();
+
+ var handle = requestOrHandle instanceof types.InterfaceRequest ?
+ requestOrHandle.handle : requestOrHandle;
+ if (!core.isHandle(handle))
+ return;
+
+ this.stub_ = new this.interfaceType_.stubClass(this.impl_);
+ this.router_ = new router.Router(handle, this.interfaceType_.kVersion);
+ this.router_.setIncomingReceiver(this.stub_);
+ this.router_ .setPayloadValidators([this.interfaceType_.validateRequest]);
+ };
+
+ Binding.prototype.close = function() {
+ if (!this.isBound())
+ return;
+
+ this.router_.close();
+ this.router_ = null;
+ this.stub_ = null;
+ };
+
+ Binding.prototype.setConnectionErrorHandler
+ = function(callback) {
+ if (!this.isBound())
+ throw new Error("Cannot set connection error handler if not bound.");
+ this.router_.setErrorHandler(callback);
+ };
+
+ Binding.prototype.unbind = function() {
+ if (!this.isBound())
+ return new types.InterfaceRequest(null);
+
+ var result = new types.InterfaceRequest(this.router_.connector_.handle_);
+ this.router_.connector_.handle_ = null;
+ this.close();
+ return result;
+ };
+
+ Binding.prototype.enableTestingMode = function() {
+ return this.router_.enableTestingMode();
+ };
+
+ // ---------------------------------------------------------------------------
+
+ function BindingSetEntry(bindingSet, interfaceType, impl, requestOrHandle,
+ bindingId) {
+ this.bindingSet_ = bindingSet;
+ this.bindingId_ = bindingId;
+ this.binding_ = new Binding(interfaceType, impl, requestOrHandle);
+
+ this.binding_.setConnectionErrorHandler(function() {
+ this.bindingSet_.onConnectionError(bindingId);
+ }.bind(this));
+ }
+
+ BindingSetEntry.prototype.close = function() {
+ this.binding_.close();
+ };
+
+ function BindingSet(interfaceType) {
+ this.interfaceType_ = interfaceType;
+ this.nextBindingId_ = 0;
+ this.bindings_ = new Map();
+ this.errorHandler_ = null;
+ }
+
+ BindingSet.prototype.isEmpty = function() {
+ return this.bindings_.size == 0;
+ };
+
+ BindingSet.prototype.addBinding = function(impl, requestOrHandle) {
+ this.bindings_.set(
+ this.nextBindingId_,
+ new BindingSetEntry(this, this.interfaceType_, impl, requestOrHandle,
+ this.nextBindingId_));
+ ++this.nextBindingId_;
+ };
+
+ BindingSet.prototype.closeAllBindings = function() {
+ for (var entry of this.bindings_.values())
+ entry.close();
+ this.bindings_.clear();
+ };
+
+ BindingSet.prototype.setConnectionErrorHandler = function(callback) {
+ this.errorHandler_ = callback;
+ };
+
+ BindingSet.prototype.onConnectionError = function(bindingId) {
+ this.bindings_.delete(bindingId);
+
+ if (this.errorHandler_)
+ this.errorHandler_();
+ };
+
+ var exports = {};
+ exports.InterfacePtrInfo = types.InterfacePtrInfo;
+ exports.InterfaceRequest = types.InterfaceRequest;
+ exports.makeRequest = makeRequest;
+ exports.InterfacePtrController = InterfacePtrController;
+ exports.Binding = Binding;
+ exports.BindingSet = BindingSet;
+
+ return exports;
+});
diff --git a/mojo/public/js/new_bindings/buffer.js b/mojo/public/js/new_bindings/buffer.js
new file mode 100644
index 0000000..e35f695
--- /dev/null
+++ b/mojo/public/js/new_bindings/buffer.js
@@ -0,0 +1,156 @@
+// Copyright 2014 The Chromium 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/buffer", function() {
+
+ 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;
+ })();
+
+ var kHighWordMultiplier = 0x100000000;
+
+ 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;
+ }
+
+ Object.defineProperty(Buffer.prototype, "byteLength", {
+ get: function() { return this.arrayBuffer.byteLength; }
+ });
+
+ Buffer.prototype.alloc = function(size) {
+ var pointer = this.next;
+ this.next += size;
+ if (this.next > this.byteLength) {
+ var newSize = (1.5 * (this.byteLength + size)) | 0;
+ this.grow(newSize);
+ }
+ return pointer;
+ };
+
+ function copyArrayBuffer(dstArrayBuffer, srcArrayBuffer) {
+ (new Uint8Array(dstArrayBuffer)).set(new Uint8Array(srcArrayBuffer));
+ }
+
+ 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);
+ };
+
+ Buffer.prototype.getUint8 = function(offset) {
+ return this.dataView.getUint8(offset);
+ }
+ Buffer.prototype.getUint16 = function(offset) {
+ return this.dataView.getUint16(offset, kHostIsLittleEndian);
+ }
+ Buffer.prototype.getUint32 = function(offset) {
+ return this.dataView.getUint32(offset, kHostIsLittleEndian);
+ }
+ Buffer.prototype.getUint64 = function(offset) {
+ var lo, hi;
+ if (kHostIsLittleEndian) {
+ lo = this.dataView.getUint32(offset, kHostIsLittleEndian);
+ hi = this.dataView.getUint32(offset + 4, kHostIsLittleEndian);
+ } else {
+ hi = this.dataView.getUint32(offset, kHostIsLittleEndian);
+ lo = this.dataView.getUint32(offset + 4, kHostIsLittleEndian);
+ }
+ return lo + hi * kHighWordMultiplier;
+ }
+
+ Buffer.prototype.getInt8 = function(offset) {
+ return this.dataView.getInt8(offset);
+ }
+ Buffer.prototype.getInt16 = function(offset) {
+ return this.dataView.getInt16(offset, kHostIsLittleEndian);
+ }
+ Buffer.prototype.getInt32 = function(offset) {
+ return this.dataView.getInt32(offset, kHostIsLittleEndian);
+ }
+ Buffer.prototype.getInt64 = function(offset) {
+ var lo, hi;
+ if (kHostIsLittleEndian) {
+ lo = this.dataView.getUint32(offset, kHostIsLittleEndian);
+ hi = this.dataView.getInt32(offset + 4, kHostIsLittleEndian);
+ } else {
+ hi = this.dataView.getInt32(offset, kHostIsLittleEndian);
+ lo = this.dataView.getUint32(offset + 4, kHostIsLittleEndian);
+ }
+ return lo + hi * kHighWordMultiplier;
+ }
+
+ Buffer.prototype.getFloat32 = function(offset) {
+ return this.dataView.getFloat32(offset, kHostIsLittleEndian);
+ }
+ Buffer.prototype.getFloat64 = function(offset) {
+ return this.dataView.getFloat64(offset, kHostIsLittleEndian);
+ }
+
+ Buffer.prototype.setUint8 = function(offset, value) {
+ this.dataView.setUint8(offset, value);
+ }
+ Buffer.prototype.setUint16 = function(offset, value) {
+ this.dataView.setUint16(offset, value, kHostIsLittleEndian);
+ }
+ Buffer.prototype.setUint32 = function(offset, value) {
+ this.dataView.setUint32(offset, value, kHostIsLittleEndian);
+ }
+ Buffer.prototype.setUint64 = function(offset, value) {
+ var hi = (value / kHighWordMultiplier) | 0;
+ if (kHostIsLittleEndian) {
+ this.dataView.setInt32(offset, value, kHostIsLittleEndian);
+ this.dataView.setInt32(offset + 4, hi, kHostIsLittleEndian);
+ } else {
+ this.dataView.setInt32(offset, hi, kHostIsLittleEndian);
+ this.dataView.setInt32(offset + 4, value, kHostIsLittleEndian);
+ }
+ }
+
+ Buffer.prototype.setInt8 = function(offset, value) {
+ this.dataView.setInt8(offset, value);
+ }
+ Buffer.prototype.setInt16 = function(offset, value) {
+ this.dataView.setInt16(offset, value, kHostIsLittleEndian);
+ }
+ Buffer.prototype.setInt32 = function(offset, value) {
+ this.dataView.setInt32(offset, value, kHostIsLittleEndian);
+ }
+ Buffer.prototype.setInt64 = function(offset, value) {
+ var hi = Math.floor(value / kHighWordMultiplier);
+ if (kHostIsLittleEndian) {
+ this.dataView.setInt32(offset, value, kHostIsLittleEndian);
+ this.dataView.setInt32(offset + 4, hi, kHostIsLittleEndian);
+ } else {
+ this.dataView.setInt32(offset, hi, kHostIsLittleEndian);
+ this.dataView.setInt32(offset + 4, value, kHostIsLittleEndian);
+ }
+ }
+
+ Buffer.prototype.setFloat32 = function(offset, value) {
+ this.dataView.setFloat32(offset, value, kHostIsLittleEndian);
+ }
+ Buffer.prototype.setFloat64 = function(offset, value) {
+ this.dataView.setFloat64(offset, value, kHostIsLittleEndian);
+ }
+
+ var exports = {};
+ exports.Buffer = Buffer;
+ return exports;
+});
diff --git a/mojo/public/js/new_bindings/codec.js b/mojo/public/js/new_bindings/codec.js
new file mode 100644
index 0000000..ff5d31a
--- /dev/null
+++ b/mojo/public/js/new_bindings/codec.js
@@ -0,0 +1,922 @@
+// Copyright 2014 The Chromium 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/codec", [
+ "mojo/public/js/buffer",
+ "mojo/public/js/interface_types",
+ "mojo/public/js/unicode",
+], function(buffer, types, unicode) {
+
+ var kErrorUnsigned = "Passing negative value to unsigned";
+ var kErrorArray = "Passing non Array for array type";
+ var kErrorString = "Passing non String for string type";
+ var kErrorMap = "Passing non Map for map type";
+
+ // Memory -------------------------------------------------------------------
+
+ var kAlignment = 8;
+
+ function align(size) {
+ return size + (kAlignment - (size % kAlignment)) % kAlignment;
+ }
+
+ function isAligned(offset) {
+ return offset >= 0 && (offset % kAlignment) === 0;
+ }
+
+ // Constants ----------------------------------------------------------------
+
+ var kArrayHeaderSize = 8;
+ var kStructHeaderSize = 8;
+ var kMessageHeaderSize = 24;
+ var kMessageWithRequestIDHeaderSize = 32;
+ var kMapStructPayloadSize = 16;
+
+ var kStructHeaderNumBytesOffset = 0;
+ var kStructHeaderVersionOffset = 4;
+
+ var kEncodedInvalidHandleValue = 0xFFFFFFFF;
+
+ // Decoder ------------------------------------------------------------------
+
+ function Decoder(buffer, handles, base) {
+ this.buffer = buffer;
+ this.handles = handles;
+ this.base = base;
+ this.next = base;
+ }
+
+ Decoder.prototype.align = function() {
+ this.next = align(this.next);
+ };
+
+ Decoder.prototype.skip = function(offset) {
+ this.next += offset;
+ };
+
+ Decoder.prototype.readInt8 = function() {
+ var result = this.buffer.getInt8(this.next);
+ this.next += 1;
+ return result;
+ };
+
+ Decoder.prototype.readUint8 = function() {
+ var result = this.buffer.getUint8(this.next);
+ this.next += 1;
+ return result;
+ };
+
+ Decoder.prototype.readInt16 = function() {
+ var result = this.buffer.getInt16(this.next);
+ this.next += 2;
+ return result;
+ };
+
+ Decoder.prototype.readUint16 = function() {
+ var result = this.buffer.getUint16(this.next);
+ this.next += 2;
+ return result;
+ };
+
+ Decoder.prototype.readInt32 = function() {
+ var result = this.buffer.getInt32(this.next);
+ this.next += 4;
+ return result;
+ };
+
+ Decoder.prototype.readUint32 = function() {
+ var result = this.buffer.getUint32(this.next);
+ this.next += 4;
+ return result;
+ };
+
+ Decoder.prototype.readInt64 = function() {
+ var result = this.buffer.getInt64(this.next);
+ this.next += 8;
+ return result;
+ };
+
+ Decoder.prototype.readUint64 = function() {
+ var result = this.buffer.getUint64(this.next);
+ this.next += 8;
+ return result;
+ };
+
+ Decoder.prototype.readFloat = function() {
+ var result = this.buffer.getFloat32(this.next);
+ this.next += 4;
+ return result;
+ };
+
+ Decoder.prototype.readDouble = function() {
+ var result = this.buffer.getFloat64(this.next);
+ 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()] || null;
+ };
+
+ 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);
+ if (cls === PackedBool) {
+ var byte;
+ for (var i = 0; i < numberOfElements; ++i) {
+ if (i % 8 === 0)
+ byte = this.readUint8();
+ val[i] = (byte & (1 << i % 8)) ? true : false;
+ }
+ } else {
+ 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();
+ };
+
+ Decoder.prototype.decodeMap = function(keyClass, valueClass) {
+ this.skip(4); // numberOfBytes
+ this.skip(4); // version
+ var keys = this.decodeArrayPointer(keyClass);
+ var values = this.decodeArrayPointer(valueClass);
+ var val = new Map();
+ for (var i = 0; i < keys.length; i++)
+ val.set(keys[i], values[i]);
+ return val;
+ };
+
+ Decoder.prototype.decodeMapPointer = function(keyClass, valueClass) {
+ var pointer = this.decodePointer();
+ if (!pointer) {
+ return null;
+ }
+ var decoder = this.decodeAndCreateDecoder(pointer);
+ return decoder.decodeMap(keyClass, valueClass);
+ };
+
+ // Encoder ------------------------------------------------------------------
+
+ function Encoder(buffer, handles, base) {
+ this.buffer = buffer;
+ this.handles = handles;
+ this.base = base;
+ this.next = base;
+ }
+
+ Encoder.prototype.align = function() {
+ this.next = align(this.next);
+ };
+
+ Encoder.prototype.skip = function(offset) {
+ this.next += offset;
+ };
+
+ Encoder.prototype.writeInt8 = function(val) {
+ this.buffer.setInt8(this.next, val);
+ this.next += 1;
+ };
+
+ Encoder.prototype.writeUint8 = function(val) {
+ if (val < 0) {
+ throw new Error(kErrorUnsigned);
+ }
+ this.buffer.setUint8(this.next, val);
+ this.next += 1;
+ };
+
+ Encoder.prototype.writeInt16 = function(val) {
+ this.buffer.setInt16(this.next, val);
+ this.next += 2;
+ };
+
+ Encoder.prototype.writeUint16 = function(val) {
+ if (val < 0) {
+ throw new Error(kErrorUnsigned);
+ }
+ this.buffer.setUint16(this.next, val);
+ this.next += 2;
+ };
+
+ Encoder.prototype.writeInt32 = function(val) {
+ this.buffer.setInt32(this.next, val);
+ this.next += 4;
+ };
+
+ Encoder.prototype.writeUint32 = function(val) {
+ if (val < 0) {
+ throw new Error(kErrorUnsigned);
+ }
+ this.buffer.setUint32(this.next, val);
+ this.next += 4;
+ };
+
+ Encoder.prototype.writeInt64 = function(val) {
+ this.buffer.setInt64(this.next, val);
+ this.next += 8;
+ };
+
+ Encoder.prototype.writeUint64 = function(val) {
+ if (val < 0) {
+ throw new Error(kErrorUnsigned);
+ }
+ this.buffer.setUint64(this.next, val);
+ this.next += 8;
+ };
+
+ Encoder.prototype.writeFloat = function(val) {
+ this.buffer.setFloat32(this.next, val);
+ this.next += 4;
+ };
+
+ Encoder.prototype.writeDouble = function(val) {
+ this.buffer.setFloat64(this.next, val);
+ 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) {
+ if (handle) {
+ this.handles.push(handle);
+ this.writeUint32(this.handles.length - 1);
+ } else {
+ this.writeUint32(kEncodedInvalidHandleValue);
+ }
+ };
+
+ 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, numberOfElements, encodedSize) {
+ if (numberOfElements === undefined)
+ numberOfElements = val.length;
+ if (encodedSize === undefined)
+ encodedSize = kArrayHeaderSize + cls.encodedSize * numberOfElements;
+
+ this.writeUint32(encodedSize);
+ this.writeUint32(numberOfElements);
+
+ if (cls === PackedBool) {
+ var byte = 0;
+ for (i = 0; i < numberOfElements; ++i) {
+ if (val[i])
+ byte |= (1 << i % 8);
+ if (i % 8 === 7 || i == numberOfElements - 1) {
+ Uint8.encode(this, byte);
+ byte = 0;
+ }
+ }
+ } else {
+ 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 == null) {
+ // Also handles undefined, since undefined == null.
+ this.encodePointer(val);
+ return;
+ }
+ var encoder = this.createAndEncodeEncoder(cls.encodedSize);
+ cls.encode(encoder, val);
+ };
+
+ Encoder.prototype.encodeArrayPointer = function(cls, val) {
+ if (val == null) {
+ // Also handles undefined, since undefined == null.
+ this.encodePointer(val);
+ return;
+ }
+
+ var numberOfElements = val.length;
+ if (!Number.isSafeInteger(numberOfElements) || numberOfElements < 0)
+ throw new Error(kErrorArray);
+
+ var encodedSize = kArrayHeaderSize + ((cls === PackedBool) ?
+ Math.ceil(numberOfElements / 8) : cls.encodedSize * numberOfElements);
+ var encoder = this.createAndEncodeEncoder(encodedSize);
+ encoder.encodeArray(cls, val, numberOfElements, encodedSize);
+ };
+
+ Encoder.prototype.encodeStringPointer = function(val) {
+ if (val == null) {
+ // Also handles undefined, since undefined == null.
+ this.encodePointer(val);
+ return;
+ }
+ // Only accepts string primivites, not String Objects like new String("foo")
+ if (typeof(val) !== "string") {
+ throw new Error(kErrorString);
+ }
+ var encodedSize = kArrayHeaderSize + unicode.utf8Length(val);
+ var encoder = this.createAndEncodeEncoder(encodedSize);
+ encoder.encodeString(val);
+ };
+
+ Encoder.prototype.encodeMap = function(keyClass, valueClass, val) {
+ var keys = new Array(val.size);
+ var values = new Array(val.size);
+ var i = 0;
+ val.forEach(function(value, key) {
+ values[i] = value;
+ keys[i++] = key;
+ });
+ this.writeUint32(kStructHeaderSize + kMapStructPayloadSize);
+ this.writeUint32(0); // version
+ this.encodeArrayPointer(keyClass, keys);
+ this.encodeArrayPointer(valueClass, values);
+ }
+
+ Encoder.prototype.encodeMapPointer = function(keyClass, valueClass, val) {
+ if (val == null) {
+ // Also handles undefined, since undefined == null.
+ this.encodePointer(val);
+ return;
+ }
+ if (!(val instanceof Map)) {
+ throw new Error(kErrorMap);
+ }
+ var encodedSize = kStructHeaderSize + kMapStructPayloadSize;
+ var encoder = this.createAndEncodeEncoder(encodedSize);
+ encoder.encodeMap(keyClass, valueClass, val);
+ };
+
+ // Message ------------------------------------------------------------------
+
+ var kMessageInterfaceIdOffset = kStructHeaderSize;
+ var kMessageNameOffset = kMessageInterfaceIdOffset + 4;
+ var kMessageFlagsOffset = kMessageNameOffset + 4;
+ var kMessageRequestIDOffset = kMessageFlagsOffset + 8;
+
+ var kMessageExpectsResponse = 1 << 0;
+ var kMessageIsResponse = 1 << 1;
+
+ function Message(buffer, handles) {
+ this.buffer = buffer;
+ this.handles = handles;
+ }
+
+ Message.prototype.getHeaderNumBytes = function() {
+ return this.buffer.getUint32(kStructHeaderNumBytesOffset);
+ };
+
+ Message.prototype.getHeaderVersion = function() {
+ return this.buffer.getUint32(kStructHeaderVersionOffset);
+ };
+
+ Message.prototype.getName = function() {
+ return this.buffer.getUint32(kMessageNameOffset);
+ };
+
+ Message.prototype.getFlags = function() {
+ return this.buffer.getUint32(kMessageFlagsOffset);
+ };
+
+ Message.prototype.isResponse = function() {
+ return (this.getFlags() & kMessageIsResponse) != 0;
+ };
+
+ Message.prototype.expectsResponse = function() {
+ return (this.getFlags() & kMessageExpectsResponse) != 0;
+ };
+
+ Message.prototype.setRequestID = function(requestID) {
+ // TODO(darin): Verify that space was reserved for this field!
+ this.buffer.setUint64(kMessageRequestIDOffset, requestID);
+ };
+
+
+ // 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.Buffer(numberOfBytes);
+ this.handles = [];
+ var encoder = this.createEncoder(kMessageHeaderSize);
+ encoder.writeUint32(kMessageHeaderSize);
+ encoder.writeUint32(0); // version.
+ encoder.writeUint32(0); // interface ID.
+ encoder.writeUint32(messageName);
+ encoder.writeUint32(0); // flags.
+ encoder.writeUint32(0); // padding.
+ }
+
+ 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.Buffer(numberOfBytes);
+ this.handles = [];
+ var encoder = this.createEncoder(kMessageWithRequestIDHeaderSize);
+ encoder.writeUint32(kMessageWithRequestIDHeaderSize);
+ encoder.writeUint32(1); // version.
+ encoder.writeUint32(0); // interface ID.
+ encoder.writeUint32(messageName);
+ encoder.writeUint32(flags);
+ encoder.writeUint32(0); // padding.
+ 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.byteLength - messageHeaderSize;
+ var version = this.decoder.readUint32();
+ var interface_id = this.decoder.readUint32();
+ if (interface_id != 0) {
+ throw new Error("Receiving non-zero interface ID. Associated interfaces " +
+ "are not yet supported.");
+ }
+ this.messageName = this.decoder.readUint32();
+ this.flags = this.decoder.readUint32();
+ // Skip the padding.
+ this.decoder.skip(4);
+ if (version >= 1)
+ 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 -----------------------------------------------------------
+
+ // This type is only used with ArrayOf(PackedBool).
+ function PackedBool() {
+ }
+
+ 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 NullableString() {
+ }
+
+ NullableString.encodedSize = String.encodedSize;
+
+ NullableString.decode = String.decode;
+
+ NullableString.encode = String.encode;
+
+ 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 Enum(cls) {
+ this.cls = cls;
+ }
+
+ Enum.prototype.encodedSize = 4;
+
+ Enum.prototype.decode = function(decoder) {
+ return decoder.readInt32();
+ };
+
+ Enum.prototype.encode = function(encoder, val) {
+ encoder.writeInt32(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 NullablePointerTo(cls) {
+ PointerTo.call(this, cls);
+ }
+
+ NullablePointerTo.prototype = Object.create(PointerTo.prototype);
+
+ function ArrayOf(cls, length) {
+ this.cls = cls;
+ this.length = length || 0;
+ }
+
+ ArrayOf.prototype.encodedSize = 8;
+
+ ArrayOf.prototype.dimensions = function() {
+ return [this.length].concat(
+ (this.cls instanceof ArrayOf) ? this.cls.dimensions() : []);
+ }
+
+ ArrayOf.prototype.decode = function(decoder) {
+ return decoder.decodeArrayPointer(this.cls);
+ };
+
+ ArrayOf.prototype.encode = function(encoder, val) {
+ encoder.encodeArrayPointer(this.cls, val);
+ };
+
+ function NullableArrayOf(cls) {
+ ArrayOf.call(this, cls);
+ }
+
+ NullableArrayOf.prototype = Object.create(ArrayOf.prototype);
+
+ function Handle() {
+ }
+
+ Handle.encodedSize = 4;
+
+ Handle.decode = function(decoder) {
+ return decoder.decodeHandle();
+ };
+
+ Handle.encode = function(encoder, val) {
+ encoder.encodeHandle(val);
+ };
+
+ function NullableHandle() {
+ }
+
+ NullableHandle.encodedSize = Handle.encodedSize;
+
+ NullableHandle.decode = Handle.decode;
+
+ NullableHandle.encode = Handle.encode;
+
+ function Interface(cls) {
+ this.cls = cls;
+ }
+
+ Interface.prototype.encodedSize = 8;
+
+ Interface.prototype.decode = function(decoder) {
+ var interfacePtrInfo = new types.InterfacePtrInfo(
+ decoder.decodeHandle(), decoder.readUint32());
+ var interfacePtr = new this.cls();
+ interfacePtr.ptr.bind(interfacePtrInfo);
+ return interfacePtr;
+ };
+
+ Interface.prototype.encode = function(encoder, val) {
+ var interfacePtrInfo =
+ val ? val.ptr.passInterface() : new types.InterfacePtrInfo(null, 0);
+ encoder.encodeHandle(interfacePtrInfo.handle);
+ encoder.writeUint32(interfacePtrInfo.version);
+ };
+
+ function NullableInterface(cls) {
+ Interface.call(this, cls);
+ }
+
+ NullableInterface.prototype = Object.create(Interface.prototype);
+
+ function InterfaceRequest() {
+ }
+
+ InterfaceRequest.encodedSize = 4;
+
+ InterfaceRequest.decode = function(decoder) {
+ return new types.InterfaceRequest(decoder.decodeHandle());
+ };
+
+ InterfaceRequest.encode = function(encoder, val) {
+ encoder.encodeHandle(val ? val.handle : null);
+ };
+
+ function NullableInterfaceRequest() {
+ }
+
+ NullableInterfaceRequest.encodedSize = InterfaceRequest.encodedSize;
+
+ NullableInterfaceRequest.decode = InterfaceRequest.decode;
+
+ NullableInterfaceRequest.encode = InterfaceRequest.encode;
+
+ function MapOf(keyClass, valueClass) {
+ this.keyClass = keyClass;
+ this.valueClass = valueClass;
+ }
+
+ MapOf.prototype.encodedSize = 8;
+
+ MapOf.prototype.decode = function(decoder) {
+ return decoder.decodeMapPointer(this.keyClass, this.valueClass);
+ };
+
+ MapOf.prototype.encode = function(encoder, val) {
+ encoder.encodeMapPointer(this.keyClass, this.valueClass, val);
+ };
+
+ function NullableMapOf(keyClass, valueClass) {
+ MapOf.call(this, keyClass, valueClass);
+ }
+
+ NullableMapOf.prototype = Object.create(MapOf.prototype);
+
+ var exports = {};
+ exports.align = align;
+ exports.isAligned = isAligned;
+ exports.Message = Message;
+ exports.MessageBuilder = MessageBuilder;
+ exports.MessageWithRequestIDBuilder = MessageWithRequestIDBuilder;
+ exports.MessageReader = MessageReader;
+ exports.kArrayHeaderSize = kArrayHeaderSize;
+ exports.kMapStructPayloadSize = kMapStructPayloadSize;
+ exports.kStructHeaderSize = kStructHeaderSize;
+ exports.kEncodedInvalidHandleValue = kEncodedInvalidHandleValue;
+ exports.kMessageHeaderSize = kMessageHeaderSize;
+ exports.kMessageWithRequestIDHeaderSize = kMessageWithRequestIDHeaderSize;
+ 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.Enum = Enum;
+ exports.NullableString = NullableString;
+ exports.PointerTo = PointerTo;
+ exports.NullablePointerTo = NullablePointerTo;
+ exports.ArrayOf = ArrayOf;
+ exports.NullableArrayOf = NullableArrayOf;
+ exports.PackedBool = PackedBool;
+ exports.Handle = Handle;
+ exports.NullableHandle = NullableHandle;
+ exports.Interface = Interface;
+ exports.NullableInterface = NullableInterface;
+ exports.InterfaceRequest = InterfaceRequest;
+ exports.NullableInterfaceRequest = NullableInterfaceRequest;
+ exports.MapOf = MapOf;
+ exports.NullableMapOf = NullableMapOf;
+ return exports;
+});
diff --git a/mojo/public/js/new_bindings/connector.js b/mojo/public/js/new_bindings/connector.js
new file mode 100644
index 0000000..ee16be8
--- /dev/null
+++ b/mojo/public/js/new_bindings/connector.js
@@ -0,0 +1,115 @@
+// Copyright 2014 The Chromium 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/connector", [
+ "mojo/public/js/buffer",
+ "mojo/public/js/codec",
+ "mojo/public/js/core",
+ "mojo/public/js/support",
+], function(buffer, codec, core, support) {
+
+ function Connector(handle) {
+ if (!core.isHandle(handle))
+ throw new Error("Connector: not a handle " + handle);
+ this.handle_ = handle;
+ this.dropWrites_ = false;
+ this.error_ = false;
+ this.incomingReceiver_ = null;
+ this.readWatcher_ = null;
+ this.errorHandler_ = null;
+
+ if (handle) {
+ this.readWatcher_ = support.watch(handle,
+ core.HANDLE_SIGNAL_READABLE,
+ this.readMore_.bind(this));
+ }
+ }
+
+ Connector.prototype.close = function() {
+ if (this.readWatcher_) {
+ support.cancelWatch(this.readWatcher_);
+ this.readWatcher_ = 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.waitForNextMessageForTesting = function() {
+ var wait = core.wait(this.handle_, core.HANDLE_SIGNAL_READABLE,
+ core.DEADLINE_INDEFINITE);
+ this.readMore_(wait.result);
+ };
+
+ Connector.prototype.readMore_ = function(result) {
+ for (;;) {
+ var read = core.readMessage(this.handle_,
+ core.READ_MESSAGE_FLAG_NONE);
+ if (this.handle_ == null) // The connector has been closed.
+ return;
+ if (read.result == core.RESULT_SHOULD_WAIT)
+ return;
+ if (read.result != core.RESULT_OK) {
+ this.error_ = true;
+ if (this.errorHandler_)
+ this.errorHandler_.onError(read.result);
+ return;
+ }
+ var messageBuffer = new buffer.Buffer(read.buffer);
+ var message = new codec.Message(messageBuffer, read.handles);
+ if (this.incomingReceiver_)
+ this.incomingReceiver_.accept(message);
+ }
+ };
+
+ var exports = {};
+ exports.Connector = Connector;
+ return exports;
+});
diff --git a/mojo/public/js/new_bindings/interface_types.js b/mojo/public/js/new_bindings/interface_types.js
new file mode 100644
index 0000000..01ea2d1
--- /dev/null
+++ b/mojo/public/js/new_bindings/interface_types.js
@@ -0,0 +1,52 @@
+// Copyright 2016 The Chromium 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/interface_types", [
+ "mojo/public/js/core",
+], function(core) {
+
+ // ---------------------------------------------------------------------------
+
+ function InterfacePtrInfo(handle, version) {
+ this.handle = handle;
+ this.version = version;
+ }
+
+ InterfacePtrInfo.prototype.isValid = function() {
+ return core.isHandle(this.handle);
+ };
+
+ InterfacePtrInfo.prototype.close = function() {
+ if (!this.isValid())
+ return;
+
+ core.close(this.handle);
+ this.handle = null;
+ this.version = 0;
+ };
+
+ // ---------------------------------------------------------------------------
+
+ function InterfaceRequest(handle) {
+ this.handle = handle;
+ }
+
+ InterfaceRequest.prototype.isValid = function() {
+ return core.isHandle(this.handle);
+ };
+
+ InterfaceRequest.prototype.close = function() {
+ if (!this.isValid())
+ return;
+
+ core.close(this.handle);
+ this.handle = null;
+ };
+
+ var exports = {};
+ exports.InterfacePtrInfo = InterfacePtrInfo;
+ exports.InterfaceRequest = InterfaceRequest;
+
+ return exports;
+});
diff --git a/mojo/public/js/new_bindings/lib/control_message_handler.js b/mojo/public/js/new_bindings/lib/control_message_handler.js
new file mode 100644
index 0000000..81d9002
--- /dev/null
+++ b/mojo/public/js/new_bindings/lib/control_message_handler.js
@@ -0,0 +1,111 @@
+// Copyright 2017 The Chromium 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/lib/control_message_handler", [
+ "mojo/public/js/codec",
+ "mojo/public/interfaces/bindings/interface_control_messages.mojom",
+ "mojo/public/js/validator",
+], function(codec, controlMessages, validator) {
+
+ var Validator = validator.Validator;
+
+ function validateControlRequestWithResponse(message) {
+ var messageValidator = new Validator(message);
+ var error = messageValidator.validateMessageIsRequestExpectingResponse();
+ if (error !== validator.validationError.NONE) {
+ throw error;
+ }
+
+ if (message.getName() != controlMessages.kRunMessageId) {
+ throw new Error("Control message name is not kRunMessageId");
+ }
+
+ // Validate payload.
+ error = controlMessages.RunMessageParams.validate(messageValidator,
+ message.getHeaderNumBytes());
+ if (error != validator.validationError.NONE) {
+ throw error;
+ }
+ }
+
+ function validateControlRequestWithoutResponse(message) {
+ var messageValidator = new Validator(message);
+ var error = messageValidator.validateMessageIsRequestWithoutResponse();
+ if (error != validator.validationError.NONE) {
+ throw error;
+ }
+
+ if (message.getName() != controlMessages.kRunOrClosePipeMessageId) {
+ throw new Error("Control message name is not kRunOrClosePipeMessageId");
+ }
+
+ // Validate payload.
+ error = controlMessages.RunOrClosePipeMessageParams.validate(
+ messageValidator, message.getHeaderNumBytes());
+ if (error != validator.validationError.NONE) {
+ throw error;
+ }
+ }
+
+ function runOrClosePipe(message, interface_version) {
+ var reader = new codec.MessageReader(message);
+ var runOrClosePipeMessageParams = reader.decodeStruct(
+ controlMessages.RunOrClosePipeMessageParams);
+ return interface_version >=
+ runOrClosePipeMessageParams.input.require_version.version;
+ }
+
+ function run(message, responder, interface_version) {
+ var reader = new codec.MessageReader(message);
+ var runMessageParams =
+ reader.decodeStruct(controlMessages.RunMessageParams);
+ var runOutput = null;
+
+ if (runMessageParams.input.query_version) {
+ runOutput = new controlMessages.RunOutput();
+ runOutput.query_version_result = new
+ controlMessages.QueryVersionResult({'version': interface_version});
+ }
+
+ var runResponseMessageParams = new
+ controlMessages.RunResponseMessageParams();
+ runResponseMessageParams.output = runOutput;
+
+ var messageName = controlMessages.kRunMessageId;
+ var payloadSize = controlMessages.RunResponseMessageParams.encodedSize;
+ var requestID = reader.requestID;
+ var builder = new codec.MessageWithRequestIDBuilder(messageName,
+ payloadSize, codec.kMessageIsResponse, requestID);
+ builder.encodeStruct(controlMessages.RunResponseMessageParams,
+ runResponseMessageParams);
+ responder.accept(builder.finish());
+ return true;
+ }
+
+ function isControlMessage(message) {
+ return message.getName() == controlMessages.kRunMessageId ||
+ message.getName() == controlMessages.kRunOrClosePipeMessageId;
+ }
+
+ function ControlMessageHandler(interface_version) {
+ this.interface_version = interface_version;
+ }
+
+ ControlMessageHandler.prototype.accept = function(message) {
+ validateControlRequestWithoutResponse(message);
+ return runOrClosePipe(message, this.interface_version);
+ };
+
+ ControlMessageHandler.prototype.acceptWithResponder = function(message,
+ responder) {
+ validateControlRequestWithResponse(message);
+ return run(message, responder, this.interface_version);
+ };
+
+ var exports = {};
+ exports.ControlMessageHandler = ControlMessageHandler;
+ exports.isControlMessage = isControlMessage;
+
+ return exports;
+});
diff --git a/mojo/public/js/new_bindings/lib/control_message_proxy.js b/mojo/public/js/new_bindings/lib/control_message_proxy.js
new file mode 100644
index 0000000..d6c0734
--- /dev/null
+++ b/mojo/public/js/new_bindings/lib/control_message_proxy.js
@@ -0,0 +1,102 @@
+// Copyright 2017 The Chromium 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/lib/control_message_proxy", [
+ "mojo/public/interfaces/bindings/interface_control_messages.mojom",
+ "mojo/public/js/codec",
+ "mojo/public/js/validator",
+], function(controlMessages, codec, validator) {
+
+ var Validator = validator.Validator;
+
+ function sendRunOrClosePipeMessage(receiver, runOrClosePipeMessageParams) {
+ var messageName = controlMessages.kRunOrClosePipeMessageId;
+ var payloadSize = controlMessages.RunOrClosePipeMessageParams.encodedSize;
+ var builder = new codec.MessageBuilder(messageName, payloadSize);
+ builder.encodeStruct(controlMessages.RunOrClosePipeMessageParams,
+ runOrClosePipeMessageParams);
+ var message = builder.finish();
+ receiver.accept(message);
+ }
+
+ function validateControlResponse(message) {
+ var messageValidator = new Validator(message);
+ var error = messageValidator.validateMessageIsResponse();
+ if (error != validator.validationError.NONE) {
+ throw error;
+ }
+
+ if (message.getName() != controlMessages.kRunMessageId) {
+ throw new Error("Control message name is not kRunMessageId");
+ }
+
+ // Validate payload.
+ error = controlMessages.RunResponseMessageParams.validate(
+ messageValidator, message.getHeaderNumBytes());
+ if (error != validator.validationError.NONE) {
+ throw error;
+ }
+ }
+
+ function acceptRunResponse(message) {
+ validateControlResponse(message);
+
+ var reader = new codec.MessageReader(message);
+ var runResponseMessageParams = reader.decodeStruct(
+ controlMessages.RunResponseMessageParams);
+
+ return Promise.resolve(runResponseMessageParams);
+ }
+
+ /**
+ * Sends the given run message through the receiver.
+ * Accepts the response message from the receiver and decodes the message
+ * struct to RunResponseMessageParams.
+ *
+ * @param {Router} receiver.
+ * @param {RunMessageParams} runMessageParams to be sent via a message.
+ * @return {Promise} that resolves to a RunResponseMessageParams.
+ */
+ function sendRunMessage(receiver, runMessageParams) {
+ var messageName = controlMessages.kRunMessageId;
+ var payloadSize = controlMessages.RunMessageParams.encodedSize;
+ // |requestID| is set to 0, but is later properly set by Router.
+ var builder = new codec.MessageWithRequestIDBuilder(messageName,
+ payloadSize, codec.kMessageExpectsResponse, 0);
+ builder.encodeStruct(controlMessages.RunMessageParams, runMessageParams);
+ var message = builder.finish();
+
+ return receiver.acceptAndExpectResponse(message).then(acceptRunResponse);
+ }
+
+ function ControlMessageProxy(receiver) {
+ this.receiver = receiver;
+ }
+
+ ControlMessageProxy.prototype.queryVersion = function() {
+ var runMessageParams = new controlMessages.RunMessageParams();
+ runMessageParams.input = new controlMessages.RunInput();
+ runMessageParams.input.query_version = new controlMessages.QueryVersion();
+
+ return sendRunMessage(this.receiver, runMessageParams).then(function(
+ runResponseMessageParams) {
+ return runResponseMessageParams.output.query_version_result.version;
+ });
+ };
+
+ ControlMessageProxy.prototype.requireVersion = function(version) {
+ var runOrClosePipeMessageParams = new
+ controlMessages.RunOrClosePipeMessageParams();
+ runOrClosePipeMessageParams.input = new
+ controlMessages.RunOrClosePipeInput();
+ runOrClosePipeMessageParams.input.require_version = new
+ controlMessages.RequireVersion({'version': version});
+ sendRunOrClosePipeMessage(this.receiver, runOrClosePipeMessageParams);
+ };
+
+ var exports = {};
+ exports.ControlMessageProxy = ControlMessageProxy;
+
+ return exports;
+});
diff --git a/mojo/public/js/new_bindings/router.js b/mojo/public/js/new_bindings/router.js
new file mode 100644
index 0000000..e94c5eb
--- /dev/null
+++ b/mojo/public/js/new_bindings/router.js
@@ -0,0 +1,203 @@
+// Copyright 2014 The Chromium 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/router", [
+ "console",
+ "mojo/public/js/codec",
+ "mojo/public/js/core",
+ "mojo/public/js/connector",
+ "mojo/public/js/lib/control_message_handler",
+ "mojo/public/js/validator",
+], function(console, codec, core, connector, controlMessageHandler, validator) {
+
+ var Connector = connector.Connector;
+ var MessageReader = codec.MessageReader;
+ var Validator = validator.Validator;
+ var ControlMessageHandler = controlMessageHandler.ControlMessageHandler;
+
+ function Router(handle, interface_version, connectorFactory) {
+ if (!core.isHandle(handle))
+ throw new Error("Router constructor: Not a handle");
+ if (connectorFactory === undefined)
+ connectorFactory = Connector;
+ this.connector_ = new connectorFactory(handle);
+ this.incomingReceiver_ = null;
+ this.errorHandler_ = null;
+ this.nextRequestID_ = 0;
+ this.completers_ = new Map();
+ this.payloadValidators_ = [];
+ this.testingController_ = null;
+
+ if (interface_version !== undefined) {
+ this.controlMessageHandler_ = new
+ ControlMessageHandler(interface_version);
+ }
+
+ this.connector_.setIncomingReceiver({
+ accept: this.handleIncomingMessage_.bind(this),
+ });
+ this.connector_.setErrorHandler({
+ onError: this.handleConnectionError_.bind(this),
+ });
+ }
+
+ Router.prototype.close = function() {
+ this.completers_.clear(); // Drop any responders.
+ this.connector_.close();
+ this.testingController_ = null;
+ };
+
+ 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.acceptAndExpectResponse = function(message) {
+ // 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);
+ if (!result)
+ return Promise.reject(Error("Connection error"));
+
+ var completer = {};
+ this.completers_.set(requestID, completer);
+ return new Promise(function(resolve, reject) {
+ completer.resolve = resolve;
+ completer.reject = reject;
+ });
+ };
+
+ Router.prototype.setIncomingReceiver = function(receiver) {
+ this.incomingReceiver_ = receiver;
+ };
+
+ Router.prototype.setPayloadValidators = function(payloadValidators) {
+ this.payloadValidators_ = payloadValidators;
+ };
+
+ Router.prototype.setErrorHandler = function(handler) {
+ this.errorHandler_ = handler;
+ };
+
+ Router.prototype.encounteredError = function() {
+ return this.connector_.encounteredError();
+ };
+
+ Router.prototype.enableTestingMode = function() {
+ this.testingController_ = new RouterTestingController(this.connector_);
+ return this.testingController_;
+ };
+
+ Router.prototype.handleIncomingMessage_ = function(message) {
+ var noError = validator.validationError.NONE;
+ var messageValidator = new Validator(message);
+ var err = messageValidator.validateMessageHeader();
+ for (var i = 0; err === noError && i < this.payloadValidators_.length; ++i)
+ err = this.payloadValidators_[i](messageValidator);
+
+ if (err == noError)
+ this.handleValidIncomingMessage_(message);
+ else
+ this.handleInvalidIncomingMessage_(message, err);
+ };
+
+ Router.prototype.handleValidIncomingMessage_ = function(message) {
+ if (this.testingController_)
+ return;
+
+ if (message.expectsResponse()) {
+ if (controlMessageHandler.isControlMessage(message)) {
+ if (this.controlMessageHandler_) {
+ this.controlMessageHandler_.acceptWithResponder(message, this);
+ } else {
+ this.close();
+ }
+ } else 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 (message.isResponse()) {
+ var reader = new MessageReader(message);
+ var requestID = reader.requestID;
+ var completer = this.completers_.get(requestID);
+ if (completer) {
+ this.completers_.delete(requestID);
+ completer.resolve(message);
+ } else {
+ console.log("Unexpected response with request ID: " + requestID);
+ }
+ } else {
+ if (controlMessageHandler.isControlMessage(message)) {
+ if (this.controlMessageHandler_) {
+ var ok = this.controlMessageHandler_.accept(message);
+ if (ok) return;
+ }
+ this.close();
+ } else if (this.incomingReceiver_) {
+ this.incomingReceiver_.accept(message);
+ }
+ }
+ };
+
+ Router.prototype.handleInvalidIncomingMessage_ = function(message, error) {
+ if (!this.testingController_) {
+ // TODO(yzshen): Consider notifying the embedder.
+ // TODO(yzshen): This should also trigger connection error handler.
+ // Consider making accept() return a boolean and let the connector deal
+ // with this, as the C++ code does.
+ console.log("Invalid message: " + validator.validationError[error]);
+
+ this.close();
+ return;
+ }
+
+ this.testingController_.onInvalidIncomingMessage(error);
+ };
+
+ Router.prototype.handleConnectionError_ = function(result) {
+ this.completers_.forEach(function(value) {
+ value.reject(result);
+ });
+ if (this.errorHandler_)
+ this.errorHandler_();
+ this.close();
+ };
+
+ // The RouterTestingController is used in unit tests. It defeats valid message
+ // handling and delgates invalid message handling.
+
+ function RouterTestingController(connector) {
+ this.connector_ = connector;
+ this.invalidMessageHandler_ = null;
+ }
+
+ RouterTestingController.prototype.waitForNextMessage = function() {
+ this.connector_.waitForNextMessageForTesting();
+ };
+
+ RouterTestingController.prototype.setInvalidIncomingMessageHandler =
+ function(callback) {
+ this.invalidMessageHandler_ = callback;
+ };
+
+ RouterTestingController.prototype.onInvalidIncomingMessage =
+ function(error) {
+ if (this.invalidMessageHandler_)
+ this.invalidMessageHandler_(error);
+ };
+
+ var exports = {};
+ exports.Router = Router;
+ return exports;
+});
diff --git a/mojo/public/js/new_bindings/unicode.js b/mojo/public/js/new_bindings/unicode.js
new file mode 100644
index 0000000..be2ba0e
--- /dev/null
+++ b/mojo/public/js/new_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/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/mojo/public/js/new_bindings/validator.js b/mojo/public/js/new_bindings/validator.js
new file mode 100644
index 0000000..fee742d
--- /dev/null
+++ b/mojo/public/js/new_bindings/validator.js
@@ -0,0 +1,512 @@
+// Copyright 2014 The Chromium 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/validator", [
+ "mojo/public/js/codec",
+], function(codec) {
+
+ var validationError = {
+ NONE: 'VALIDATION_ERROR_NONE',
+ MISALIGNED_OBJECT: 'VALIDATION_ERROR_MISALIGNED_OBJECT',
+ ILLEGAL_MEMORY_RANGE: 'VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE',
+ UNEXPECTED_STRUCT_HEADER: 'VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER',
+ UNEXPECTED_ARRAY_HEADER: 'VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER',
+ ILLEGAL_HANDLE: 'VALIDATION_ERROR_ILLEGAL_HANDLE',
+ UNEXPECTED_INVALID_HANDLE: 'VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE',
+ ILLEGAL_POINTER: 'VALIDATION_ERROR_ILLEGAL_POINTER',
+ UNEXPECTED_NULL_POINTER: 'VALIDATION_ERROR_UNEXPECTED_NULL_POINTER',
+ MESSAGE_HEADER_INVALID_FLAGS:
+ 'VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS',
+ MESSAGE_HEADER_MISSING_REQUEST_ID:
+ 'VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID',
+ DIFFERENT_SIZED_ARRAYS_IN_MAP:
+ 'VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP',
+ INVALID_UNION_SIZE: 'VALIDATION_ERROR_INVALID_UNION_SIZE',
+ UNEXPECTED_NULL_UNION: 'VALIDATION_ERROR_UNEXPECTED_NULL_UNION',
+ UNKNOWN_ENUM_VALUE: 'VALIDATION_ERROR_UNKNOWN_ENUM_VALUE',
+ };
+
+ var NULL_MOJO_POINTER = "NULL_MOJO_POINTER";
+
+ function isEnumClass(cls) {
+ return cls instanceof codec.Enum;
+ }
+
+ function isStringClass(cls) {
+ return cls === codec.String || cls === codec.NullableString;
+ }
+
+ function isHandleClass(cls) {
+ return cls === codec.Handle || cls === codec.NullableHandle;
+ }
+
+ function isInterfaceClass(cls) {
+ return cls instanceof codec.Interface;
+ }
+
+ function isInterfaceRequestClass(cls) {
+ return cls === codec.InterfaceRequest ||
+ cls === codec.NullableInterfaceRequest;
+ }
+
+ function isNullable(type) {
+ return type === codec.NullableString || type === codec.NullableHandle ||
+ type === codec.NullableInterface ||
+ type === codec.NullableInterfaceRequest ||
+ type instanceof codec.NullableArrayOf ||
+ type instanceof codec.NullablePointerTo;
+ }
+
+ function Validator(message) {
+ this.message = message;
+ this.offset = 0;
+ this.handleIndex = 0;
+ }
+
+ Object.defineProperty(Validator.prototype, "offsetLimit", {
+ get: function() { return this.message.buffer.byteLength; }
+ });
+
+ Object.defineProperty(Validator.prototype, "handleIndexLimit", {
+ get: function() { return this.message.handles.length; }
+ });
+
+ // True if we can safely allocate a block of bytes from start to
+ // to start + numBytes.
+ Validator.prototype.isValidRange = function(start, numBytes) {
+ // Only positive JavaScript integers that are less than 2^53
+ // (Number.MAX_SAFE_INTEGER) can be represented exactly.
+ if (start < this.offset || numBytes <= 0 ||
+ !Number.isSafeInteger(start) ||
+ !Number.isSafeInteger(numBytes))
+ return false;
+
+ var newOffset = start + numBytes;
+ if (!Number.isSafeInteger(newOffset) || newOffset > this.offsetLimit)
+ return false;
+
+ return true;
+ };
+
+ Validator.prototype.claimRange = function(start, numBytes) {
+ if (this.isValidRange(start, numBytes)) {
+ this.offset = start + numBytes;
+ return true;
+ }
+ return false;
+ };
+
+ Validator.prototype.claimHandle = function(index) {
+ if (index === codec.kEncodedInvalidHandleValue)
+ return true;
+
+ if (index < this.handleIndex || index >= this.handleIndexLimit)
+ return false;
+
+ // This is safe because handle indices are uint32.
+ this.handleIndex = index + 1;
+ return true;
+ };
+
+ Validator.prototype.validateEnum = function(offset, enumClass) {
+ // Note: Assumes that enums are always 32 bits! But this matches
+ // mojom::generate::pack::PackedField::GetSizeForKind, so it should be okay.
+ var value = this.message.buffer.getInt32(offset);
+ return enumClass.validate(value);
+ }
+
+ Validator.prototype.validateHandle = function(offset, nullable) {
+ var index = this.message.buffer.getUint32(offset);
+
+ if (index === codec.kEncodedInvalidHandleValue)
+ return nullable ?
+ validationError.NONE : validationError.UNEXPECTED_INVALID_HANDLE;
+
+ if (!this.claimHandle(index))
+ return validationError.ILLEGAL_HANDLE;
+
+ return validationError.NONE;
+ };
+
+ Validator.prototype.validateInterface = function(offset, nullable) {
+ return this.validateHandle(offset, nullable);
+ };
+
+ Validator.prototype.validateInterfaceRequest = function(offset, nullable) {
+ return this.validateHandle(offset, nullable);
+ };
+
+ Validator.prototype.validateStructHeader = function(offset, minNumBytes) {
+ if (!codec.isAligned(offset))
+ return validationError.MISALIGNED_OBJECT;
+
+ if (!this.isValidRange(offset, codec.kStructHeaderSize))
+ return validationError.ILLEGAL_MEMORY_RANGE;
+
+ var numBytes = this.message.buffer.getUint32(offset);
+
+ if (numBytes < minNumBytes)
+ return validationError.UNEXPECTED_STRUCT_HEADER;
+
+ if (!this.claimRange(offset, numBytes))
+ return validationError.ILLEGAL_MEMORY_RANGE;
+
+ return validationError.NONE;
+ };
+
+ Validator.prototype.validateStructVersion = function(offset, versionSizes) {
+ var numBytes = this.message.buffer.getUint32(offset);
+ var version = this.message.buffer.getUint32(offset + 4);
+
+ if (version <= versionSizes[versionSizes.length - 1].version) {
+ // Scan in reverse order to optimize for more recent versionSizes.
+ for (var i = versionSizes.length - 1; i >= 0; --i) {
+ if (version >= versionSizes[i].version) {
+ if (numBytes == versionSizes[i].numBytes)
+ break;
+ return validationError.UNEXPECTED_STRUCT_HEADER;
+ }
+ }
+ } else if (numBytes < versionSizes[versionSizes.length-1].numBytes) {
+ return validationError.UNEXPECTED_STRUCT_HEADER;
+ }
+
+ return validationError.NONE;
+ };
+
+ Validator.prototype.isFieldInStructVersion = function(offset, fieldVersion) {
+ var structVersion = this.message.buffer.getUint32(offset + 4);
+ return fieldVersion <= structVersion;
+ };
+
+ Validator.prototype.validateMessageHeader = function() {
+
+ var err = this.validateStructHeader(0, codec.kMessageHeaderSize);
+ if (err != validationError.NONE)
+ return err;
+
+ var numBytes = this.message.getHeaderNumBytes();
+ var version = this.message.getHeaderVersion();
+
+ var validVersionAndNumBytes =
+ (version == 0 && numBytes == codec.kMessageHeaderSize) ||
+ (version == 1 &&
+ numBytes == codec.kMessageWithRequestIDHeaderSize) ||
+ (version > 1 &&
+ numBytes >= codec.kMessageWithRequestIDHeaderSize);
+ if (!validVersionAndNumBytes)
+ return validationError.UNEXPECTED_STRUCT_HEADER;
+
+ var expectsResponse = this.message.expectsResponse();
+ var isResponse = this.message.isResponse();
+
+ if (version == 0 && (expectsResponse || isResponse))
+ return validationError.MESSAGE_HEADER_MISSING_REQUEST_ID;
+
+ if (isResponse && expectsResponse)
+ return validationError.MESSAGE_HEADER_INVALID_FLAGS;
+
+ return validationError.NONE;
+ };
+
+ Validator.prototype.validateMessageIsRequestWithoutResponse = function() {
+ if (this.message.isResponse() || this.message.expectsResponse()) {
+ return validationError.MESSAGE_HEADER_INVALID_FLAGS;
+ }
+ return validationError.NONE;
+ };
+
+ Validator.prototype.validateMessageIsRequestExpectingResponse = function() {
+ if (this.message.isResponse() || !this.message.expectsResponse()) {
+ return validationError.MESSAGE_HEADER_INVALID_FLAGS;
+ }
+ return validationError.NONE;
+ };
+
+ Validator.prototype.validateMessageIsResponse = function() {
+ if (this.message.expectsResponse() || !this.message.isResponse()) {
+ return validationError.MESSAGE_HEADER_INVALID_FLAGS;
+ }
+ return validationError.NONE;
+ };
+
+ // Returns the message.buffer relative offset this pointer "points to",
+ // NULL_MOJO_POINTER if the pointer represents a null, or JS null if the
+ // pointer's value is not valid.
+ Validator.prototype.decodePointer = function(offset) {
+ var pointerValue = this.message.buffer.getUint64(offset);
+ if (pointerValue === 0)
+ return NULL_MOJO_POINTER;
+ var bufferOffset = offset + pointerValue;
+ return Number.isSafeInteger(bufferOffset) ? bufferOffset : null;
+ };
+
+ Validator.prototype.decodeUnionSize = function(offset) {
+ return this.message.buffer.getUint32(offset);
+ };
+
+ Validator.prototype.decodeUnionTag = function(offset) {
+ return this.message.buffer.getUint32(offset + 4);
+ };
+
+ Validator.prototype.validateArrayPointer = function(
+ offset, elementSize, elementType, nullable, expectedDimensionSizes,
+ currentDimension) {
+ var arrayOffset = this.decodePointer(offset);
+ if (arrayOffset === null)
+ return validationError.ILLEGAL_POINTER;
+
+ if (arrayOffset === NULL_MOJO_POINTER)
+ return nullable ?
+ validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
+
+ return this.validateArray(arrayOffset, elementSize, elementType,
+ expectedDimensionSizes, currentDimension);
+ };
+
+ Validator.prototype.validateStructPointer = function(
+ offset, structClass, nullable) {
+ var structOffset = this.decodePointer(offset);
+ if (structOffset === null)
+ return validationError.ILLEGAL_POINTER;
+
+ if (structOffset === NULL_MOJO_POINTER)
+ return nullable ?
+ validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
+
+ return structClass.validate(this, structOffset);
+ };
+
+ Validator.prototype.validateUnion = function(
+ offset, unionClass, nullable) {
+ var size = this.message.buffer.getUint32(offset);
+ if (size == 0) {
+ return nullable ?
+ validationError.NONE : validationError.UNEXPECTED_NULL_UNION;
+ }
+
+ return unionClass.validate(this, offset);
+ };
+
+ Validator.prototype.validateNestedUnion = function(
+ offset, unionClass, nullable) {
+ var unionOffset = this.decodePointer(offset);
+ if (unionOffset === null)
+ return validationError.ILLEGAL_POINTER;
+
+ if (unionOffset === NULL_MOJO_POINTER)
+ return nullable ?
+ validationError.NONE : validationError.UNEXPECTED_NULL_UNION;
+
+ return this.validateUnion(unionOffset, unionClass, nullable);
+ };
+
+ // This method assumes that the array at arrayPointerOffset has
+ // been validated.
+
+ Validator.prototype.arrayLength = function(arrayPointerOffset) {
+ var arrayOffset = this.decodePointer(arrayPointerOffset);
+ return this.message.buffer.getUint32(arrayOffset + 4);
+ };
+
+ Validator.prototype.validateMapPointer = function(
+ offset, mapIsNullable, keyClass, valueClass, valueIsNullable) {
+ // Validate the implicit map struct:
+ // struct {array<keyClass> keys; array<valueClass> values};
+ var structOffset = this.decodePointer(offset);
+ if (structOffset === null)
+ return validationError.ILLEGAL_POINTER;
+
+ if (structOffset === NULL_MOJO_POINTER)
+ return mapIsNullable ?
+ validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
+
+ var mapEncodedSize = codec.kStructHeaderSize + codec.kMapStructPayloadSize;
+ var err = this.validateStructHeader(structOffset, mapEncodedSize);
+ if (err !== validationError.NONE)
+ return err;
+
+ // Validate the keys array.
+ var keysArrayPointerOffset = structOffset + codec.kStructHeaderSize;
+ err = this.validateArrayPointer(
+ keysArrayPointerOffset, keyClass.encodedSize, keyClass, false, [0], 0);
+ if (err !== validationError.NONE)
+ return err;
+
+ // Validate the values array.
+ var valuesArrayPointerOffset = keysArrayPointerOffset + 8;
+ var valuesArrayDimensions = [0]; // Validate the actual length below.
+ if (valueClass instanceof codec.ArrayOf)
+ valuesArrayDimensions =
+ valuesArrayDimensions.concat(valueClass.dimensions());
+ var err = this.validateArrayPointer(valuesArrayPointerOffset,
+ valueClass.encodedSize,
+ valueClass,
+ valueIsNullable,
+ valuesArrayDimensions,
+ 0);
+ if (err !== validationError.NONE)
+ return err;
+
+ // Validate the lengths of the keys and values arrays.
+ var keysArrayLength = this.arrayLength(keysArrayPointerOffset);
+ var valuesArrayLength = this.arrayLength(valuesArrayPointerOffset);
+ if (keysArrayLength != valuesArrayLength)
+ return validationError.DIFFERENT_SIZED_ARRAYS_IN_MAP;
+
+ return validationError.NONE;
+ };
+
+ Validator.prototype.validateStringPointer = function(offset, nullable) {
+ return this.validateArrayPointer(
+ offset, codec.Uint8.encodedSize, codec.Uint8, nullable, [0], 0);
+ };
+
+ // Similar to Array_Data<T>::Validate()
+ // mojo/public/cpp/bindings/lib/array_internal.h
+
+ Validator.prototype.validateArray =
+ function (offset, elementSize, elementType, expectedDimensionSizes,
+ currentDimension) {
+ if (!codec.isAligned(offset))
+ return validationError.MISALIGNED_OBJECT;
+
+ if (!this.isValidRange(offset, codec.kArrayHeaderSize))
+ return validationError.ILLEGAL_MEMORY_RANGE;
+
+ var numBytes = this.message.buffer.getUint32(offset);
+ var numElements = this.message.buffer.getUint32(offset + 4);
+
+ // Note: this computation is "safe" because elementSize <= 8 and
+ // numElements is a uint32.
+ var elementsTotalSize = (elementType === codec.PackedBool) ?
+ Math.ceil(numElements / 8) : (elementSize * numElements);
+
+ if (numBytes < codec.kArrayHeaderSize + elementsTotalSize)
+ return validationError.UNEXPECTED_ARRAY_HEADER;
+
+ if (expectedDimensionSizes[currentDimension] != 0 &&
+ numElements != expectedDimensionSizes[currentDimension]) {
+ return validationError.UNEXPECTED_ARRAY_HEADER;
+ }
+
+ if (!this.claimRange(offset, numBytes))
+ return validationError.ILLEGAL_MEMORY_RANGE;
+
+ // Validate the array's elements if they are pointers or handles.
+
+ var elementsOffset = offset + codec.kArrayHeaderSize;
+ var nullable = isNullable(elementType);
+
+ if (isHandleClass(elementType))
+ return this.validateHandleElements(elementsOffset, numElements, nullable);
+ if (isInterfaceClass(elementType))
+ return this.validateInterfaceElements(
+ elementsOffset, numElements, nullable);
+ if (isInterfaceRequestClass(elementType))
+ return this.validateInterfaceRequestElements(
+ elementsOffset, numElements, nullable);
+ if (isStringClass(elementType))
+ return this.validateArrayElements(
+ elementsOffset, numElements, codec.Uint8, nullable, [0], 0);
+ if (elementType instanceof codec.PointerTo)
+ return this.validateStructElements(
+ elementsOffset, numElements, elementType.cls, nullable);
+ if (elementType instanceof codec.ArrayOf)
+ return this.validateArrayElements(
+ elementsOffset, numElements, elementType.cls, nullable,
+ expectedDimensionSizes, currentDimension + 1);
+ if (isEnumClass(elementType))
+ return this.validateEnumElements(elementsOffset, numElements,
+ elementType.cls);
+
+ return validationError.NONE;
+ };
+
+ // Note: the |offset + i * elementSize| computation in the validateFooElements
+ // methods below is "safe" because elementSize <= 8, offset and
+ // numElements are uint32, and 0 <= i < numElements.
+
+ Validator.prototype.validateHandleElements =
+ function(offset, numElements, nullable) {
+ var elementSize = codec.Handle.encodedSize;
+ for (var i = 0; i < numElements; i++) {
+ var elementOffset = offset + i * elementSize;
+ var err = this.validateHandle(elementOffset, nullable);
+ if (err != validationError.NONE)
+ return err;
+ }
+ return validationError.NONE;
+ };
+
+ Validator.prototype.validateInterfaceElements =
+ function(offset, numElements, nullable) {
+ var elementSize = codec.Interface.prototype.encodedSize;
+ for (var i = 0; i < numElements; i++) {
+ var elementOffset = offset + i * elementSize;
+ var err = this.validateInterface(elementOffset, nullable);
+ if (err != validationError.NONE)
+ return err;
+ }
+ return validationError.NONE;
+ };
+
+ Validator.prototype.validateInterfaceRequestElements =
+ function(offset, numElements, nullable) {
+ var elementSize = codec.InterfaceRequest.encodedSize;
+ for (var i = 0; i < numElements; i++) {
+ var elementOffset = offset + i * elementSize;
+ var err = this.validateInterfaceRequest(elementOffset, nullable);
+ if (err != validationError.NONE)
+ return err;
+ }
+ return validationError.NONE;
+ };
+
+ // The elementClass parameter is the element type of the element arrays.
+ Validator.prototype.validateArrayElements =
+ function(offset, numElements, elementClass, nullable,
+ expectedDimensionSizes, currentDimension) {
+ var elementSize = codec.PointerTo.prototype.encodedSize;
+ for (var i = 0; i < numElements; i++) {
+ var elementOffset = offset + i * elementSize;
+ var err = this.validateArrayPointer(
+ elementOffset, elementClass.encodedSize, elementClass, nullable,
+ expectedDimensionSizes, currentDimension);
+ if (err != validationError.NONE)
+ return err;
+ }
+ return validationError.NONE;
+ };
+
+ Validator.prototype.validateStructElements =
+ function(offset, numElements, structClass, nullable) {
+ var elementSize = codec.PointerTo.prototype.encodedSize;
+ for (var i = 0; i < numElements; i++) {
+ var elementOffset = offset + i * elementSize;
+ var err =
+ this.validateStructPointer(elementOffset, structClass, nullable);
+ if (err != validationError.NONE)
+ return err;
+ }
+ return validationError.NONE;
+ };
+
+ Validator.prototype.validateEnumElements =
+ function(offset, numElements, enumClass) {
+ var elementSize = codec.Enum.prototype.encodedSize;
+ for (var i = 0; i < numElements; i++) {
+ var elementOffset = offset + i * elementSize;
+ var err = this.validateEnum(elementOffset, enumClass);
+ if (err != validationError.NONE)
+ return err;
+ }
+ return validationError.NONE;
+ };
+
+ var exports = {};
+ exports.validationError = validationError;
+ exports.Validator = Validator;
+ return exports;
+});
diff --git a/mojo/public/js/tests/codec_unittest.js b/mojo/public/js/tests/codec_unittest.js
new file mode 100644
index 0000000..5fa4076
--- /dev/null
+++ b/mojo/public/js/tests/codec_unittest.js
@@ -0,0 +1,314 @@
+// Copyright 2014 The Chromium 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/codec",
+ "mojo/public/interfaces/bindings/tests/rect.mojom",
+ "mojo/public/interfaces/bindings/tests/sample_service.mojom",
+ "mojo/public/interfaces/bindings/tests/test_structs.mojom",
+ ], function(expect, codec, rect, sample, structs) {
+ testBar();
+ testFoo();
+ testNamedRegion();
+ testSingleBooleanStruct();
+ testTypes();
+ testAlign();
+ testUtf8();
+ testTypedPointerValidation();
+ 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([
+ 24, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 42, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+
+ 16, 0, 0, 0,
+ 0, 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]
+ ];
+ foo.array_of_bools = [
+ true, false, true, false, true, false, true, 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: */ 24, 0, 0, 0, 0, 0, 0, 0,
+ /* 8: */ 0, 0, 0, 0, 31, 0, 0, 0,
+ /* 16: */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 24: */ 96, 0, 0, 0, 0, 0, 0, 0,
+ /* 32: */ 0xD5, 0xB4, 0x12, 0x02, 0x93, 0x6E, 0x01, 0,
+ /* 40: */ 5, 0, 0, 0, 0, 0, 0, 0,
+ /* 48: */ 72, 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);
+
+ expect(foo2.array_of_bools).toEqual(foo.array_of_bools);
+ }
+
+ function createRect(x, y, width, height) {
+ var r = new rect.Rect();
+ r.x = x;
+ r.y = y;
+ r.width = width;
+ r.height = height;
+ return r;
+ }
+
+ // Verify that the references to the imported Rect type in test_structs.mojom
+ // are generated correctly.
+ function testNamedRegion() {
+ var r = new structs.NamedRegion();
+ r.name = "rectangle";
+ r.rects = new Array(createRect(1, 2, 3, 4), createRect(10, 20, 30, 40));
+
+ var builder = new codec.MessageBuilder(1, structs.NamedRegion.encodedSize);
+ builder.encodeStruct(structs.NamedRegion, r);
+ var reader = new codec.MessageReader(builder.finish());
+ var result = reader.decodeStruct(structs.NamedRegion);
+
+ expect(result.name).toEqual("rectangle");
+ expect(result.rects[0]).toEqual(createRect(1, 2, 3, 4));
+ expect(result.rects[1]).toEqual(createRect(10, 20, 30, 40));
+ }
+
+ // Verify that a single boolean field in a struct is correctly decoded to
+ // boolean type.
+ function testSingleBooleanStruct() {
+ var single_bool = new structs.SingleBoolStruct();
+ single_bool.value = true;
+
+ var builder = new codec.MessageBuilder(
+ 1, structs.SingleBoolStruct.encodedSize);
+ builder.encodeStruct(structs.SingleBoolStruct, single_bool);
+ var reader = new codec.MessageReader(builder.finish());
+ var result = reader.decodeStruct(structs.SingleBoolStruct);
+
+ // Use toEqual() instead of toBeTruthy() to make sure the field type is
+ // actually boolean.
+ expect(result.value).toEqual(true);
+ }
+
+ 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.NullableString, null, null, 8);
+ 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: */ 24, 0, 0, 0, 0, 0, 0, 0,
+ /* 8: */ 0, 0, 0, 0, 42, 0, 0, 0,
+ /* 16: */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 24: */ 8, 0, 0, 0, 0, 0, 0, 0,
+ /* 32: */ 14, 0, 0, 0, 6, 0, 0, 0,
+ /* 40: */ 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);
+ }
+
+ function testTypedPointerValidation() {
+ var encoder = new codec.MessageBuilder(42, 24).createEncoder(8);
+ function DummyClass() {};
+ var testCases = [
+ // method, args, invalid examples, valid examples
+ [encoder.encodeArrayPointer, [DummyClass], [75],
+ [[], null, undefined, new Uint8Array([])]],
+ [encoder.encodeStringPointer, [], [75, new String("foo")],
+ ["", "bar", null, undefined]],
+ [encoder.encodeMapPointer, [DummyClass, DummyClass], [75],
+ [new Map(), null, undefined]],
+ ];
+
+ testCases.forEach(function(test) {
+ var method = test[0];
+ var baseArgs = test[1];
+ var invalidExamples = test[2];
+ var validExamples = test[3];
+
+ var encoder = new codec.MessageBuilder(42, 24).createEncoder(8);
+ invalidExamples.forEach(function(invalid) {
+ expect(function() {
+ method.apply(encoder, baseArgs.concat(invalid));
+ }).toThrow();
+ });
+
+ validExamples.forEach(function(valid) {
+ var encoder = new codec.MessageBuilder(42, 24).createEncoder(8);
+ method.apply(encoder, baseArgs.concat(valid));
+ });
+ });
+ }
+});
diff --git a/mojo/public/js/tests/connection_unittest.js b/mojo/public/js/tests/connection_unittest.js
new file mode 100644
index 0000000..feba87d
--- /dev/null
+++ b/mojo/public/js/tests/connection_unittest.js
@@ -0,0 +1,136 @@
+// 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",
+ "mojo/public/js/core",
+ "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom",
+ "mojo/public/interfaces/bindings/tests/sample_service.mojom",
+ "mojo/public/js/threading",
+ "gc",
+], function(expect,
+ bindings,
+ core,
+ sample_interfaces,
+ sample_service,
+ threading,
+ gc) {
+ testClientServer()
+ .then(testWriteToClosedPipe)
+ .then(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() {
+ // ServiceImpl ------------------------------------------------------------
+
+ function ServiceImpl() {
+ }
+
+ ServiceImpl.prototype.frobinate = function(foo, baz, port) {
+ expect(foo.name).toBe("Example name");
+ expect(baz).toBe(sample_service.Service.BazOptions.REGULAR);
+ expect(port.ptr.isBound()).toBeTruthy();
+ port.ptr.reset();
+
+ return Promise.resolve({result: 42});
+ };
+
+ var service = new sample_service.ServicePtr();
+ var serviceBinding = new bindings.Binding(sample_service.Service,
+ new ServiceImpl(),
+ bindings.makeRequest(service));
+ var sourcePipe = core.createMessagePipe();
+ var port = new sample_service.PortPtr();
+ var portRequest = bindings.makeRequest(port);
+
+ var foo = new sample_service.Foo();
+ foo.bar = new sample_service.Bar();
+ foo.name = "Example name";
+ foo.source = sourcePipe.handle0;
+ var promise = service.frobinate(
+ foo, sample_service.Service.BazOptions.REGULAR, port)
+ .then(function(response) {
+ expect(response.result).toBe(42);
+
+ service.ptr.reset();
+ serviceBinding.close();
+
+ return Promise.resolve();
+ });
+
+ // sourcePipe.handle1 hasn't been closed yet.
+ expect(core.close(sourcePipe.handle1)).toBe(core.RESULT_OK);
+
+ // portRequest.handle hasn't been closed yet.
+ expect(core.close(portRequest.handle)).toBe(core.RESULT_OK);
+
+ return promise;
+ }
+
+ function testWriteToClosedPipe() {
+ var service = new sample_service.ServicePtr();
+ // Discard the interface request.
+ bindings.makeRequest(service);
+ gc.collectGarbage();
+
+ var promise = service.frobinate(
+ null, sample_service.Service.BazOptions.REGULAR, null)
+ .then(function(response) {
+ return Promise.reject("Unexpected response");
+ }).catch(function(e) {
+ // We should observe the closed pipe.
+ return Promise.resolve();
+ });
+
+ return promise;
+ }
+
+ function testRequestResponse() {
+ // ProviderImpl ------------------------------------------------------------
+
+ function ProviderImpl() {
+ }
+
+ ProviderImpl.prototype.echoString = function(a) {
+ return Promise.resolve({a: a});
+ };
+
+ ProviderImpl.prototype.echoStrings = function(a, b) {
+ return Promise.resolve({a: a, b: b});
+ };
+
+ var provider = new sample_interfaces.ProviderPtr();
+ var providerBinding = new bindings.Binding(sample_interfaces.Provider,
+ new ProviderImpl(),
+ bindings.makeRequest(provider));
+ var promise = provider.echoString("hello").then(function(response) {
+ expect(response.a).toBe("hello");
+ return provider.echoStrings("hello", "world");
+ }).then(function(response) {
+ expect(response.a).toBe("hello");
+ expect(response.b).toBe("world");
+ // Mock a read failure, expect it to fail.
+ core.readMessage = function() {
+ return { result: core.RESULT_UNKNOWN };
+ };
+ return provider.echoString("goodbye");
+ }).then(function() {
+ throw Error("Expected echoString to fail.");
+ }, function(error) {
+ expect(error.message).toBe("Connection error: " + core.RESULT_UNKNOWN);
+
+ return Promise.resolve();
+ });
+
+ return promise;
+ }
+});
diff --git a/mojo/public/js/tests/core_unittest.js b/mojo/public/js/tests/core_unittest.js
new file mode 100644
index 0000000..395ed05
--- /dev/null
+++ b/mojo/public/js/tests/core_unittest.js
@@ -0,0 +1,246 @@
+// Copyright 2014 The Chromium 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/core",
+ "gc",
+ ], function(expect, core, gc) {
+
+ var HANDLE_SIGNAL_READWRITABLE = core.HANDLE_SIGNAL_WRITABLE |
+ core.HANDLE_SIGNAL_READABLE;
+ var HANDLE_SIGNAL_ALL = core.HANDLE_SIGNAL_WRITABLE |
+ core.HANDLE_SIGNAL_READABLE |
+ core.HANDLE_SIGNAL_PEER_CLOSED;
+
+ runWithMessagePipe(testNop);
+ runWithMessagePipe(testReadAndWriteMessage);
+ runWithMessagePipeWithOptions(testNop);
+ runWithMessagePipeWithOptions(testReadAndWriteMessage);
+ runWithDataPipe(testNop);
+ runWithDataPipe(testReadAndWriteDataPipe);
+ runWithDataPipeWithOptions(testNop);
+ runWithDataPipeWithOptions(testReadAndWriteDataPipe);
+ runWithMessagePipe(testIsHandleMessagePipe);
+ runWithDataPipe(testIsHandleDataPipe);
+ runWithSharedBuffer(testSharedBuffer);
+ gc.collectGarbage(); // should not crash
+ this.result = "PASS";
+
+ function runWithMessagePipe(test) {
+ var pipe = core.createMessagePipe();
+ expect(pipe.result).toBe(core.RESULT_OK);
+
+ test(pipe);
+
+ expect(core.close(pipe.handle0)).toBe(core.RESULT_OK);
+ expect(core.close(pipe.handle1)).toBe(core.RESULT_OK);
+ }
+
+ function runWithMessagePipeWithOptions(test) {
+ var pipe = core.createMessagePipe({
+ flags: core.CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE
+ });
+ expect(pipe.result).toBe(core.RESULT_OK);
+
+ 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();
+ 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 runWithDataPipeWithOptions(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 runWithSharedBuffer(test) {
+ let buffer_size = 32;
+ let sharedBuffer = core.createSharedBuffer(buffer_size,
+ core.CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE);
+
+ expect(sharedBuffer.result).toBe(core.RESULT_OK);
+ expect(core.isHandle(sharedBuffer.handle)).toBeTruthy();
+
+ test(sharedBuffer, buffer_size);
+ }
+
+ function testNop(pipe) {
+ }
+
+ function testReadAndWriteMessage(pipe) {
+ var wait = core.waitMany([], [], 0);
+ expect(wait.result).toBe(core.RESULT_INVALID_ARGUMENT);
+ expect(wait.index).toBe(null);
+ expect(wait.signalsState).toBe(null);
+
+ wait = core.wait(pipe.handle0, core.HANDLE_SIGNAL_READABLE, 0);
+ expect(wait.result).toBe(core.RESULT_DEADLINE_EXCEEDED);
+ expect(wait.signalsState.satisfiedSignals).toBe(
+ core.HANDLE_SIGNAL_WRITABLE);
+ expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
+
+ wait = core.waitMany(
+ [pipe.handle0, pipe.handle1],
+ [core.HANDLE_SIGNAL_READABLE,core.HANDLE_SIGNAL_READABLE],
+ 0);
+ expect(wait.result).toBe(core.RESULT_DEADLINE_EXCEEDED);
+ expect(wait.index).toBe(null);
+ expect(wait.signalsState[0].satisfiedSignals).toBe(
+ core.HANDLE_SIGNAL_WRITABLE);
+ expect(wait.signalsState[0].satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
+ expect(wait.signalsState[1].satisfiedSignals).toBe(
+ core.HANDLE_SIGNAL_WRITABLE);
+ expect(wait.signalsState[1].satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
+
+ wait = core.wait(pipe.handle0, core.HANDLE_SIGNAL_WRITABLE, 0);
+ expect(wait.result).toBe(core.RESULT_OK);
+ expect(wait.signalsState.satisfiedSignals).toBe(
+ core.HANDLE_SIGNAL_WRITABLE);
+ expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
+
+ 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);
+
+ wait = core.wait(pipe.handle0, core.HANDLE_SIGNAL_WRITABLE, 0);
+ expect(wait.result).toBe(core.RESULT_OK);
+ expect(wait.signalsState.satisfiedSignals).toBe(
+ core.HANDLE_SIGNAL_WRITABLE);
+ expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
+
+ wait = core.wait(pipe.handle1, core.HANDLE_SIGNAL_READABLE,
+ core.DEADLINE_INDEFINITE);
+ expect(wait.result).toBe(core.RESULT_OK);
+ expect(wait.signalsState.satisfiedSignals).toBe(HANDLE_SIGNAL_READWRITABLE);
+ expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL);
+
+ 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 wait = core.wait(pipe.consumerHandle, core.HANDLE_SIGNAL_READABLE,
+ core.DEADLINE_INDEFINITE);
+ expect(wait.result).toBe(core.RESULT_OK);
+ var peeked = core.readData(
+ pipe.consumerHandle,
+ core.READ_DATA_FLAG_PEEK | core.READ_DATA_FLAG_ALL_OR_NONE);
+ expect(peeked.result).toBe(core.RESULT_OK);
+ expect(peeked.buffer.byteLength).toBe(42);
+
+ var peeked_memory = new Uint8Array(peeked.buffer);
+ for (var i = 0; i < peeked_memory.length; ++i)
+ expect(peeked_memory[i]).toBe((i * i) & 0xFF);
+
+ 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);
+ }
+
+ function testIsHandleMessagePipe(pipe) {
+ expect(core.isHandle(123).toBeFalsy);
+ expect(core.isHandle("123").toBeFalsy);
+ expect(core.isHandle({}).toBeFalsy);
+ expect(core.isHandle([]).toBeFalsy);
+ expect(core.isHandle(undefined).toBeFalsy);
+ expect(core.isHandle(pipe).toBeFalsy);
+ expect(core.isHandle(pipe.handle0)).toBeTruthy();
+ expect(core.isHandle(pipe.handle1)).toBeTruthy();
+ expect(core.isHandle(null)).toBeTruthy();
+ }
+
+ function testIsHandleDataPipe(pipe) {
+ expect(core.isHandle(pipe.consumerHandle)).toBeTruthy();
+ expect(core.isHandle(pipe.producerHandle)).toBeTruthy();
+ }
+
+ function testSharedBuffer(sharedBuffer, buffer_size) {
+ let offset = 0;
+ let mappedBuffer0 = core.mapBuffer(sharedBuffer.handle,
+ offset,
+ buffer_size,
+ core.MAP_BUFFER_FLAG_NONE);
+
+ expect(mappedBuffer0.result).toBe(core.RESULT_OK);
+
+ let dupedBufferHandle = core.duplicateBufferHandle(sharedBuffer.handle,
+ core.DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE);
+
+ expect(dupedBufferHandle.result).toBe(core.RESULT_OK);
+ expect(core.isHandle(dupedBufferHandle.handle)).toBeTruthy();
+
+ let mappedBuffer1 = core.mapBuffer(dupedBufferHandle.handle,
+ offset,
+ buffer_size,
+ core.MAP_BUFFER_FLAG_NONE);
+
+ expect(mappedBuffer1.result).toBe(core.RESULT_OK);
+
+ let buffer0 = new Uint8Array(mappedBuffer0.buffer);
+ let buffer1 = new Uint8Array(mappedBuffer1.buffer);
+ for(let i = 0; i < buffer0.length; ++i) {
+ buffer0[i] = i;
+ expect(buffer1[i]).toBe(i);
+ }
+
+ expect(core.unmapBuffer(mappedBuffer0.buffer)).toBe(core.RESULT_OK);
+ expect(core.unmapBuffer(mappedBuffer1.buffer)).toBe(core.RESULT_OK);
+
+ expect(core.close(dupedBufferHandle.handle)).toBe(core.RESULT_OK);
+ expect(core.close(sharedBuffer.handle)).toBe(core.RESULT_OK);
+ }
+
+});
diff --git a/mojo/public/js/tests/interface_ptr_unittest.js b/mojo/public/js/tests/interface_ptr_unittest.js
new file mode 100644
index 0000000..6203154
--- /dev/null
+++ b/mojo/public/js/tests/interface_ptr_unittest.js
@@ -0,0 +1,240 @@
+// Copyright 2016 The Chromium 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",
+ "mojo/public/js/core",
+ "mojo/public/interfaces/bindings/tests/math_calculator.mojom",
+ "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom",
+ "mojo/public/js/threading",
+ "gc",
+], function(expect,
+ bindings,
+ core,
+ math,
+ sampleInterfaces,
+ threading,
+ gc) {
+ testIsBound()
+ .then(testEndToEnd)
+ .then(testReusable)
+ .then(testConnectionError)
+ .then(testPassInterface)
+ .then(testBindRawHandle)
+ .then(testQueryVersion)
+ .then(testRequireVersion)
+ .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 CalculatorImpl() {
+ this.total = 0;
+ }
+
+ CalculatorImpl.prototype.clear = function() {
+ this.total = 0;
+ return Promise.resolve({value: this.total});
+ };
+
+ CalculatorImpl.prototype.add = function(value) {
+ this.total += value;
+ return Promise.resolve({value: this.total});
+ };
+
+ CalculatorImpl.prototype.multiply = function(value) {
+ this.total *= value;
+ return Promise.resolve({value: this.total});
+ };
+
+ function IntegerAccessorImpl() {
+ this.integer = 0;
+ }
+
+ IntegerAccessorImpl.prototype.getInteger = function() {
+ return Promise.resolve({data: this.integer});
+ };
+
+ IntegerAccessorImpl.prototype.setInteger = function(value) {
+ this.integer = value;
+ };
+
+ function testIsBound() {
+ var calc = new math.CalculatorPtr();
+ expect(calc.ptr.isBound()).toBeFalsy();
+
+ var request = bindings.makeRequest(calc);
+ expect(calc.ptr.isBound()).toBeTruthy();
+
+ calc.ptr.reset();
+ expect(calc.ptr.isBound()).toBeFalsy();
+
+ return Promise.resolve();
+ }
+
+ function testEndToEnd() {
+ var calc = new math.CalculatorPtr();
+ var calcBinding = new bindings.Binding(math.Calculator,
+ new CalculatorImpl(),
+ bindings.makeRequest(calc));
+
+ var promise = calc.add(2).then(function(response) {
+ expect(response.value).toBe(2);
+ return calc.multiply(5);
+ }).then(function(response) {
+ expect(response.value).toBe(10);
+ return calc.clear();
+ }).then(function(response) {
+ expect(response.value).toBe(0);
+ return Promise.resolve();
+ });
+
+ return promise;
+ }
+
+ function testReusable() {
+ var calc = new math.CalculatorPtr();
+ var calcImpl1 = new CalculatorImpl();
+ var calcBinding1 = new bindings.Binding(math.Calculator,
+ calcImpl1,
+ bindings.makeRequest(calc));
+ var calcImpl2 = new CalculatorImpl();
+ var calcBinding2 = new bindings.Binding(math.Calculator, calcImpl2);
+
+ var promise = calc.add(2).then(function(response) {
+ expect(response.value).toBe(2);
+ calcBinding2.bind(bindings.makeRequest(calc));
+ return calc.add(2);
+ }).then(function(response) {
+ expect(response.value).toBe(2);
+ expect(calcImpl1.total).toBe(2);
+ expect(calcImpl2.total).toBe(2);
+ return Promise.resolve();
+ });
+
+ return promise;
+ }
+
+ function testConnectionError() {
+ var calc = new math.CalculatorPtr();
+ var calcBinding = new bindings.Binding(math.Calculator,
+ new CalculatorImpl(),
+ bindings.makeRequest(calc));
+
+ var promise = new Promise(function(resolve, reject) {
+ calc.ptr.setConnectionErrorHandler(function() {
+ resolve();
+ });
+ calcBinding.close();
+ });
+
+ return promise;
+ }
+
+ function testPassInterface() {
+ var calc = new math.CalculatorPtr();
+ var newCalc = null;
+ var calcBinding = new bindings.Binding(math.Calculator,
+ new CalculatorImpl(),
+ bindings.makeRequest(calc));
+
+ var promise = calc.add(2).then(function(response) {
+ expect(response.value).toBe(2);
+ newCalc = new math.CalculatorPtr();
+ newCalc.ptr.bind(calc.ptr.passInterface());
+ expect(calc.ptr.isBound()).toBeFalsy();
+ return newCalc.add(2);
+ }).then(function(response) {
+ expect(response.value).toBe(4);
+ return Promise.resolve();
+ });
+
+ return promise;
+ }
+
+ function testBindRawHandle() {
+ var pipe = core.createMessagePipe();
+ var calc = new math.CalculatorPtr(pipe.handle0);
+ var newCalc = null;
+ var calcBinding = new bindings.Binding(math.Calculator,
+ new CalculatorImpl(),
+ pipe.handle1);
+
+ var promise = calc.add(2).then(function(response) {
+ expect(response.value).toBe(2);
+ return Promise.resolve();
+ });
+
+ return promise;
+ }
+
+ function testQueryVersion() {
+ var integerAccessorPtr = new sampleInterfaces.IntegerAccessorPtr();
+
+ var integerAccessorBinding = new bindings.Binding(
+ sampleInterfaces.IntegerAccessor,
+ new IntegerAccessorImpl(),
+ bindings.makeRequest(integerAccessorPtr));
+ expect(integerAccessorPtr.ptr.version).toBe(0);
+
+ return integerAccessorPtr.ptr.queryVersion().then(function(version) {
+ expect(version).toBe(3);
+ expect(integerAccessorPtr.ptr.version).toBe(3);
+ });
+ }
+
+ function testRequireVersion() {
+ var integerAccessorImpl = new IntegerAccessorImpl();
+ var integerAccessorPtr = new sampleInterfaces.IntegerAccessorPtr();
+ var integerAccessorBinding = new bindings.Binding(
+ sampleInterfaces.IntegerAccessor,
+ integerAccessorImpl,
+ bindings.makeRequest(integerAccessorPtr));
+
+ // Inital version is 0.
+ expect(integerAccessorPtr.ptr.version).toBe(0);
+
+ function requireVersion1() {
+ integerAccessorPtr.ptr.requireVersion(1);
+ expect(integerAccessorPtr.ptr.version).toBe(1);
+ integerAccessorPtr.setInteger(123, sampleInterfaces.Enum.VALUE);
+ return integerAccessorPtr.getInteger().then(function(responseParams) {
+ expect(responseParams.data).toBe(123);
+ });
+ }
+
+ function requireVersion3() {
+ integerAccessorPtr.ptr.requireVersion(3);
+ expect(integerAccessorPtr.ptr.version).toBe(3);
+ integerAccessorPtr.setInteger(456, sampleInterfaces.Enum.VALUE);
+ return integerAccessorPtr.getInteger().then(function(responseParams) {
+ expect(responseParams.data).toBe(456);
+ });
+ }
+
+ // Require a version that is not supported by the impl side.
+ function requireVersion4() {
+ integerAccessorPtr.ptr.requireVersion(4);
+ expect(integerAccessorPtr.ptr.version).toBe(4);
+ integerAccessorPtr.setInteger(789, sampleInterfaces.Enum.VALUE);
+
+ var promise = new Promise(function(resolve, reject) {
+ integerAccessorPtr.ptr.setConnectionErrorHandler(function() {
+ // The call to setInteger() after requireVersion(4) is ignored.
+ expect(integerAccessorImpl.integer).toBe(456);
+ resolve();
+ });
+ });
+
+ return promise;
+ }
+
+ return requireVersion1().then(requireVersion3).then(requireVersion4);
+ }
+});
diff --git a/mojo/public/js/tests/sample_service_unittest.js b/mojo/public/js/tests/sample_service_unittest.js
new file mode 100644
index 0000000..b9a2845
--- /dev/null
+++ b/mojo/public/js/tests/sample_service_unittest.js
@@ -0,0 +1,173 @@
+// 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/interfaces/bindings/tests/sample_service.mojom",
+ "mojo/public/interfaces/bindings/tests/sample_import.mojom",
+ "mojo/public/interfaces/bindings/tests/sample_import2.mojom",
+ "mojo/public/js/bindings",
+ "mojo/public/js/core",
+ "mojo/public/js/threading",
+ ], function(expect, sample, imported, imported2, bindings, core, threading) {
+ testDefaultValues()
+ .then(testSampleService)
+ .then(function() {
+ this.result = "PASS";
+ threading.quit();
+ }.bind(this)).catch(function(e) {
+ this.result = "FAIL: " + (e.stack || e);
+ threading.quit();
+ }.bind(this));
+
+ // Checks that values are set to the defaults if we don't override them.
+ function testDefaultValues() {
+ var bar = new sample.Bar();
+ expect(bar.alpha).toBe(255);
+ expect(bar.type).toBe(sample.Bar.Type.VERTICAL);
+
+ var foo = new sample.Foo();
+ expect(foo.name).toBe("Fooby");
+ expect(foo.a).toBeTruthy();
+ 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);
+ expect(defaults.a5).toBe(3456789012);
+ expect(defaults.a6).toBe(-111111111111);
+ // JS doesn't have a 64 bit integer type so this is just checking that the
+ // expected and actual values have the same closest double value.
+ 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.BOTH);
+ expect(defaults.a21).toBeNull();
+ expect(defaults.a22).toBeTruthy();
+ expect(defaults.a22.shape).toBe(imported.Shape.RECTANGLE);
+ expect(defaults.a22.color).toBe(imported2.Color.BLACK);
+ expect(defaults.a21).toBeNull();
+ expect(defaults.a23).toBe(0xFFFFFFFFFFFFFFFF);
+ expect(defaults.a24).toBe(0x123456789);
+ expect(defaults.a25).toBe(-0x123456789);
+
+ return Promise.resolve();
+ }
+
+ function testSampleService() {
+ function ServiceImpl() {
+ }
+
+ ServiceImpl.prototype.frobinate = function(foo, baz, port) {
+ checkFoo(foo);
+ expect(baz).toBe(sample.Service.BazOptions.EXTRA);
+ expect(port.ptr.isBound()).toBeTruthy();
+ return Promise.resolve({result: 1234});
+ };
+
+ var foo = makeFoo();
+ checkFoo(foo);
+
+ var service = new sample.ServicePtr();
+ var request = bindings.makeRequest(service);
+ var serviceBinding = new bindings.Binding(
+ sample.Service, new ServiceImpl(), request);
+
+ var port = new sample.PortPtr();
+ bindings.makeRequest(port);
+ var promise = service.frobinate(
+ foo, sample.Service.BazOptions.EXTRA, port)
+ .then(function(response) {
+ expect(response.result).toBe(1234);
+
+ return Promise.resolve();
+ });
+
+ return promise;
+ }
+
+ function makeFoo() {
+ var bar = new sample.Bar();
+ bar.alpha = 20;
+ bar.beta = 40;
+ bar.gamma = 60;
+ bar.type = sample.Bar.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.VERTICAL : sample.Bar.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 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;
+
+ // TODO(yzshen): currently setting it to null will cause connection error,
+ // even if the field is defined as nullable. crbug.com/575753
+ foo.source = core.createMessagePipe().handle0;
+
+ return foo;
+ }
+
+ // Checks 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.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.VERTICAL : sample.Bar.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(core.isHandle(foo.source)).toBeTruthy();
+ }
+});
diff --git a/mojo/public/js/tests/struct_unittest.js b/mojo/public/js/tests/struct_unittest.js
new file mode 100644
index 0000000..691d51b
--- /dev/null
+++ b/mojo/public/js/tests/struct_unittest.js
@@ -0,0 +1,279 @@
+// Copyright 2014 The Chromium 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/interfaces/bindings/tests/rect.mojom",
+ "mojo/public/interfaces/bindings/tests/test_structs.mojom",
+ "mojo/public/js/codec",
+ "mojo/public/js/validator",
+], function(expect,
+ rect,
+ testStructs,
+ codec,
+ validator) {
+
+ function testConstructors() {
+ var r = new rect.Rect();
+ expect(r).toEqual(new rect.Rect({x:0, y:0, width:0, height:0}));
+ expect(r).toEqual(new rect.Rect({foo:100, bar:200}));
+
+ r.x = 10;
+ r.y = 20;
+ r.width = 30;
+ r.height = 40;
+ var rp = new testStructs.RectPair({first: r, second: r});
+ expect(rp.first).toEqual(r);
+ expect(rp.second).toEqual(r);
+
+ expect(new testStructs.RectPair({second: r}).first).toBeNull();
+
+ var nr = new testStructs.NamedRegion();
+ expect(nr.name).toBeNull();
+ expect(nr.rects).toBeNull();
+ expect(nr).toEqual(new testStructs.NamedRegion({}));
+
+ nr.name = "foo";
+ nr.rects = [r, r, r];
+ expect(nr).toEqual(new testStructs.NamedRegion({
+ name: "foo",
+ rects: [r, r, r],
+ }));
+
+ var e = new testStructs.EmptyStruct();
+ expect(e).toEqual(new testStructs.EmptyStruct({foo:123}));
+ }
+
+ function testNoDefaultFieldValues() {
+ var s = new testStructs.NoDefaultFieldValues();
+ expect(s.f0).toEqual(false);
+
+ // f1 - f10, number type fields
+ for (var i = 1; i <= 10; i++)
+ expect(s["f" + i]).toEqual(0);
+
+ // f11,12 strings, f13-22 handles, f23-f26 arrays, f27,28 structs
+ for (var i = 11; i <= 28; i++)
+ expect(s["f" + i]).toBeNull();
+ }
+
+ function testDefaultFieldValues() {
+ var s = new testStructs.DefaultFieldValues();
+ expect(s.f0).toEqual(true);
+
+ // f1 - f12, number type fields
+ for (var i = 1; i <= 12; i++)
+ expect(s["f" + i]).toEqual(100);
+
+ // f13,14 "foo"
+ for (var i = 13; i <= 14; i++)
+ expect(s["f" + i]).toEqual("foo");
+
+ // f15,16 a default instance of Rect
+ var r = new rect.Rect();
+ expect(s.f15).toEqual(r);
+ expect(s.f16).toEqual(r);
+ }
+
+ function testScopedConstants() {
+ expect(testStructs.ScopedConstants.TEN).toEqual(10);
+ expect(testStructs.ScopedConstants.ALSO_TEN).toEqual(10);
+
+ expect(testStructs.ScopedConstants.EType.E0).toEqual(0);
+ expect(testStructs.ScopedConstants.EType.E1).toEqual(1);
+ expect(testStructs.ScopedConstants.EType.E2).toEqual(10);
+ expect(testStructs.ScopedConstants.EType.E3).toEqual(10);
+ expect(testStructs.ScopedConstants.EType.E4).toEqual(11);
+
+ var s = new testStructs.ScopedConstants();
+ expect(s.f0).toEqual(0);
+ expect(s.f1).toEqual(1);
+ expect(s.f2).toEqual(10);
+ expect(s.f3).toEqual(10);
+ expect(s.f4).toEqual(11);
+ expect(s.f5).toEqual(10);
+ expect(s.f6).toEqual(10);
+ }
+
+ function structEncodeDecode(struct) {
+ var structClass = struct.constructor;
+ var builder = new codec.MessageBuilder(1234, structClass.encodedSize);
+ builder.encodeStruct(structClass, struct);
+ var message = builder.finish();
+
+ var messageValidator = new validator.Validator(message);
+ var err = structClass.validate(messageValidator, codec.kMessageHeaderSize);
+ expect(err).toEqual(validator.validationError.NONE);
+
+ var reader = new codec.MessageReader(message);
+ return reader.decodeStruct(structClass);
+ }
+
+ function testMapKeyTypes() {
+ var mapFieldsStruct = new testStructs.MapKeyTypes({
+ f0: new Map([[true, false], [false, true]]), // map<bool, bool>
+ f1: new Map([[0, 0], [1, 127], [-1, -128]]), // map<int8, int8>
+ f2: new Map([[0, 0], [1, 127], [2, 255]]), // map<uint8, uint8>
+ f3: new Map([[0, 0], [1, 32767], [2, -32768]]), // map<int16, int16>
+ f4: new Map([[0, 0], [1, 32768], [2, 0xFFFF]]), // map<uint16, uint16>
+ f5: new Map([[0, 0], [1, 32767], [2, -32768]]), // map<int32, int32>
+ f6: new Map([[0, 0], [1, 32768], [2, 0xFFFF]]), // map<uint32, uint32>
+ f7: new Map([[0, 0], [1, 32767], [2, -32768]]), // map<int64, int64>
+ f8: new Map([[0, 0], [1, 32768], [2, 0xFFFF]]), // map<uint64, uint64>
+ f9: new Map([[1000.5, -50000], [100.5, 5000]]), // map<float, float>
+ f10: new Map([[-100.5, -50000], [0, 50000000]]), // map<double, double>
+ f11: new Map([["one", "two"], ["free", "four"]]), // map<string, string>
+ });
+ var decodedStruct = structEncodeDecode(mapFieldsStruct);
+ expect(decodedStruct.f0).toEqual(mapFieldsStruct.f0);
+ expect(decodedStruct.f1).toEqual(mapFieldsStruct.f1);
+ expect(decodedStruct.f2).toEqual(mapFieldsStruct.f2);
+ expect(decodedStruct.f3).toEqual(mapFieldsStruct.f3);
+ expect(decodedStruct.f4).toEqual(mapFieldsStruct.f4);
+ expect(decodedStruct.f5).toEqual(mapFieldsStruct.f5);
+ expect(decodedStruct.f6).toEqual(mapFieldsStruct.f6);
+ expect(decodedStruct.f7).toEqual(mapFieldsStruct.f7);
+ expect(decodedStruct.f8).toEqual(mapFieldsStruct.f8);
+ expect(decodedStruct.f9).toEqual(mapFieldsStruct.f9);
+ expect(decodedStruct.f10).toEqual(mapFieldsStruct.f10);
+ expect(decodedStruct.f11).toEqual(mapFieldsStruct.f11);
+ }
+
+ function testMapValueTypes() {
+ var mapFieldsStruct = new testStructs.MapValueTypes({
+ // map<string, array<string>>
+ f0: new Map([["a", ["b", "c"]], ["d", ["e"]]]),
+ // map<string, array<string>?>
+ f1: new Map([["a", null], ["b", ["c", "d"]]]),
+ // map<string, array<string?>>
+ f2: new Map([["a", [null]], ["b", [null, "d"]]]),
+ // map<string, array<string,2>>
+ f3: new Map([["a", ["1", "2"]], ["b", ["1", "2"]]]),
+ // map<string, array<array<string, 2>?>>
+ f4: new Map([["a", [["1", "2"]]], ["b", [null]]]),
+ // map<string, array<array<string, 2>, 1>>
+ f5: new Map([["a", [["1", "2"]]]]),
+ // map<string, Rect?>
+ f6: new Map([["a", null]]),
+ // map<string, map<string, string>>
+ f7: new Map([["a", new Map([["b", "c"]])]]),
+ // map<string, array<map<string, string>>>
+ f8: new Map([["a", [new Map([["b", "c"]])]]]),
+ // map<string, handle>
+ f9: new Map([["a", 1234]]),
+ // map<string, array<handle>>
+ f10: new Map([["a", [1234, 5678]]]),
+ // map<string, map<string, handle>>
+ f11: new Map([["a", new Map([["b", 1234]])]]),
+ });
+ var decodedStruct = structEncodeDecode(mapFieldsStruct);
+ expect(decodedStruct.f0).toEqual(mapFieldsStruct.f0);
+ expect(decodedStruct.f1).toEqual(mapFieldsStruct.f1);
+ expect(decodedStruct.f2).toEqual(mapFieldsStruct.f2);
+ expect(decodedStruct.f3).toEqual(mapFieldsStruct.f3);
+ expect(decodedStruct.f4).toEqual(mapFieldsStruct.f4);
+ expect(decodedStruct.f5).toEqual(mapFieldsStruct.f5);
+ expect(decodedStruct.f6).toEqual(mapFieldsStruct.f6);
+ expect(decodedStruct.f7).toEqual(mapFieldsStruct.f7);
+ expect(decodedStruct.f8).toEqual(mapFieldsStruct.f8);
+ expect(decodedStruct.f9).toEqual(mapFieldsStruct.f9);
+ expect(decodedStruct.f10).toEqual(mapFieldsStruct.f10);
+ expect(decodedStruct.f11).toEqual(mapFieldsStruct.f11);
+ }
+
+ function testFloatNumberValues() {
+ var decodedStruct = structEncodeDecode(new testStructs.FloatNumberValues);
+ expect(decodedStruct.f0).toEqual(testStructs.FloatNumberValues.V0);
+ expect(decodedStruct.f1).toEqual(testStructs.FloatNumberValues.V1);
+ expect(decodedStruct.f2).toEqual(testStructs.FloatNumberValues.V2);
+ expect(decodedStruct.f3).toEqual(testStructs.FloatNumberValues.V3);
+ expect(decodedStruct.f4).toEqual(testStructs.FloatNumberValues.V4);
+ expect(decodedStruct.f5).toEqual(testStructs.FloatNumberValues.V5);
+ expect(decodedStruct.f6).toEqual(testStructs.FloatNumberValues.V6);
+ expect(decodedStruct.f7).toEqual(testStructs.FloatNumberValues.V7);
+ expect(decodedStruct.f8).toEqual(testStructs.FloatNumberValues.V8);
+ expect(decodedStruct.f9).toEqual(testStructs.FloatNumberValues.V9);
+ }
+
+ function testIntegerNumberValues() {
+ var decodedStruct = structEncodeDecode(new testStructs.IntegerNumberValues);
+ expect(decodedStruct.f0).toEqual(testStructs.IntegerNumberValues.V0);
+ expect(decodedStruct.f1).toEqual(testStructs.IntegerNumberValues.V1);
+ expect(decodedStruct.f2).toEqual(testStructs.IntegerNumberValues.V2);
+ expect(decodedStruct.f3).toEqual(testStructs.IntegerNumberValues.V3);
+ expect(decodedStruct.f4).toEqual(testStructs.IntegerNumberValues.V4);
+ expect(decodedStruct.f5).toEqual(testStructs.IntegerNumberValues.V5);
+ expect(decodedStruct.f6).toEqual(testStructs.IntegerNumberValues.V6);
+ expect(decodedStruct.f7).toEqual(testStructs.IntegerNumberValues.V7);
+ expect(decodedStruct.f8).toEqual(testStructs.IntegerNumberValues.V8);
+ expect(decodedStruct.f9).toEqual(testStructs.IntegerNumberValues.V9);
+ expect(decodedStruct.f10).toEqual(testStructs.IntegerNumberValues.V10);
+ expect(decodedStruct.f11).toEqual(testStructs.IntegerNumberValues.V11);
+ expect(decodedStruct.f12).toEqual(testStructs.IntegerNumberValues.V12);
+ expect(decodedStruct.f13).toEqual(testStructs.IntegerNumberValues.V13);
+ expect(decodedStruct.f14).toEqual(testStructs.IntegerNumberValues.V14);
+ expect(decodedStruct.f15).toEqual(testStructs.IntegerNumberValues.V15);
+ expect(decodedStruct.f16).toEqual(testStructs.IntegerNumberValues.V16);
+ expect(decodedStruct.f17).toEqual(testStructs.IntegerNumberValues.V17);
+ expect(decodedStruct.f18).toEqual(testStructs.IntegerNumberValues.V18);
+ expect(decodedStruct.f19).toEqual(testStructs.IntegerNumberValues.V19);
+ }
+
+ function testUnsignedNumberValues() {
+ var decodedStruct =
+ structEncodeDecode(new testStructs.UnsignedNumberValues);
+ expect(decodedStruct.f0).toEqual(testStructs.UnsignedNumberValues.V0);
+ expect(decodedStruct.f1).toEqual(testStructs.UnsignedNumberValues.V1);
+ expect(decodedStruct.f2).toEqual(testStructs.UnsignedNumberValues.V2);
+ expect(decodedStruct.f3).toEqual(testStructs.UnsignedNumberValues.V3);
+ expect(decodedStruct.f4).toEqual(testStructs.UnsignedNumberValues.V4);
+ expect(decodedStruct.f5).toEqual(testStructs.UnsignedNumberValues.V5);
+ expect(decodedStruct.f6).toEqual(testStructs.UnsignedNumberValues.V6);
+ expect(decodedStruct.f7).toEqual(testStructs.UnsignedNumberValues.V7);
+ expect(decodedStruct.f8).toEqual(testStructs.UnsignedNumberValues.V8);
+ expect(decodedStruct.f9).toEqual(testStructs.UnsignedNumberValues.V9);
+ expect(decodedStruct.f10).toEqual(testStructs.UnsignedNumberValues.V10);
+ expect(decodedStruct.f11).toEqual(testStructs.UnsignedNumberValues.V11);
+ }
+
+
+ function testBitArrayValues() {
+ var bitArraysStruct = new testStructs.BitArrayValues({
+ // array<bool, 1> f0;
+ f0: [true],
+ // array<bool, 7> f1;
+ f1: [true, false, true, false, true, false, true],
+ // array<bool, 9> f2;
+ f2: [true, false, true, false, true, false, true, false, true],
+ // array<bool> f3;
+ f3: [true, false, true, false, true, false, true, false],
+ // array<array<bool>> f4;
+ f4: [[true], [false], [true, false], [true, false, true, false]],
+ // array<array<bool>?> f5;
+ f5: [[true], null, null, [true, false, true, false]],
+ // array<array<bool, 2>?> f6;
+ f6: [[true, false], [true, false], [true, false]],
+ });
+ var decodedStruct = structEncodeDecode(bitArraysStruct);
+ expect(decodedStruct.f0).toEqual(bitArraysStruct.f0);
+ expect(decodedStruct.f1).toEqual(bitArraysStruct.f1);
+ expect(decodedStruct.f2).toEqual(bitArraysStruct.f2);
+ expect(decodedStruct.f3).toEqual(bitArraysStruct.f3);
+ expect(decodedStruct.f4).toEqual(bitArraysStruct.f4);
+ expect(decodedStruct.f5).toEqual(bitArraysStruct.f5);
+ expect(decodedStruct.f6).toEqual(bitArraysStruct.f6);
+ }
+
+ testConstructors();
+ testNoDefaultFieldValues();
+ testDefaultFieldValues();
+ testScopedConstants();
+ testMapKeyTypes();
+ testMapValueTypes();
+ testFloatNumberValues();
+ testIntegerNumberValues();
+ testUnsignedNumberValues();
+ testBitArrayValues();
+ this.result = "PASS";
+});
diff --git a/mojo/public/js/tests/union_unittest.js b/mojo/public/js/tests/union_unittest.js
new file mode 100644
index 0000000..c3ee297
--- /dev/null
+++ b/mojo/public/js/tests/union_unittest.js
@@ -0,0 +1,194 @@
+// Copyright 2015 The Chromium 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/interfaces/bindings/tests/test_unions.mojom",
+ "mojo/public/js/codec",
+ "mojo/public/js/validator",
+], function(expect,
+ unions,
+ codec,
+ validator) {
+ function testConstructors() {
+ var u = new unions.PodUnion();
+ expect(u.$data).toEqual(null);
+ expect(u.$tag).toBeUndefined();
+
+ u.f_uint32 = 32;
+
+ expect(u.f_uint32).toEqual(32);
+ expect(u.$tag).toEqual(unions.PodUnion.Tags.f_uint32);
+
+ var u = new unions.PodUnion({f_uint64: 64});
+ expect(u.f_uint64).toEqual(64);
+ expect(u.$tag).toEqual(unions.PodUnion.Tags.f_uint64);
+ expect(function() {var v = u.f_uint32;}).toThrow();
+
+ expect(function() {
+ var u = new unions.PodUnion({
+ f_uint64: 64,
+ f_uint32: 32,
+ });
+ }).toThrow();
+
+ expect(function() {
+ var u = new unions.PodUnion({ foo: 64 }); }).toThrow();
+
+ expect(function() {
+ var u = new unions.PodUnion([1,2,3,4]); }).toThrow();
+ }
+
+ function structEncodeDecode(struct) {
+ var structClass = struct.constructor;
+ var builder = new codec.MessageBuilder(1234, structClass.encodedSize);
+ builder.encodeStruct(structClass, struct);
+
+ var message = builder.finish();
+
+ var messageValidator = new validator.Validator(message);
+ var err = structClass.validate(messageValidator, codec.kMessageHeaderSize);
+ expect(err).toEqual(validator.validationError.NONE);
+
+ var reader = new codec.MessageReader(message);
+ var view = reader.decoder.buffer.dataView;
+
+ return reader.decodeStruct(structClass);
+ }
+
+ function testBasicEncoding() {
+ var s = new unions.WrapperStruct({
+ pod_union: new unions.PodUnion({
+ f_uint64: 64})});
+
+ var decoded = structEncodeDecode(s);
+ expect(decoded).toEqual(s);
+
+ var s = new unions.WrapperStruct({
+ pod_union: new unions.PodUnion({
+ f_bool : true})});
+
+ var decoded = structEncodeDecode(s);
+ expect(decoded.pod_union.$tag).toEqual(unions.PodUnion.Tags.f_bool);
+ // Use toEqual() instead of toBeTruthy() to make sure the field type is
+ // actually boolean.
+ expect(decoded.pod_union.f_bool).toEqual(true);
+
+ var s = new unions.WrapperStruct({
+ object_union: new unions.ObjectUnion({
+ f_dummy: new unions.DummyStruct({
+ f_int8: 8})})});
+
+ var decoded = structEncodeDecode(s);
+ expect(decoded).toEqual(s);
+
+ var s = new unions.WrapperStruct({
+ object_union: new unions.ObjectUnion({
+ f_array_int8: [1, 2, 3]})});
+
+ var decoded = structEncodeDecode(s);
+ expect(decoded).toEqual(s);
+
+ var s = new unions.WrapperStruct({
+ object_union: new unions.ObjectUnion({
+ f_map_int8: new Map([
+ ["first", 1],
+ ["second", 2],
+ ])})});
+
+ var decoded = structEncodeDecode(s);
+ expect(decoded).toEqual(s);
+
+ // Encoding a union with no member set is an error.
+ var s = new unions.WrapperStruct({
+ object_union: new unions.ObjectUnion()});
+ expect(function() {
+ structEncodeDecode(s); }).toThrow();
+ }
+
+ function testUnionsInArrayEncoding() {
+ var s = new unions.SmallStruct({
+ pod_union_array: [
+ new unions.PodUnion({f_uint32: 32}),
+ new unions.PodUnion({f_uint64: 64}),
+ ]
+ });
+
+ var decoded = structEncodeDecode(s);
+ expect(decoded).toEqual(s);
+ }
+
+ function testUnionsInMapEncoding() {
+ var s = new unions.SmallStruct({
+ pod_union_map: new Map([
+ ["thirty-two", new unions.PodUnion({f_uint32: 32})],
+ ["sixty-four", new unions.PodUnion({f_uint64: 64})],
+ ])
+ });
+
+ var decoded = structEncodeDecode(s);
+ expect(decoded).toEqual(s);
+ }
+
+ function testNestedUnionsEncoding() {
+ var s = new unions.WrapperStruct({
+ object_union: new unions.ObjectUnion({
+ f_pod_union: new unions.PodUnion({f_uint32: 32})
+ })});
+ var decoded = structEncodeDecode(s);
+ expect(decoded).toEqual(s);
+ }
+
+ function structValidate(struct) {
+ var structClass = struct.constructor;
+ var builder = new codec.MessageBuilder(1234, structClass.encodedSize);
+ builder.encodeStruct(structClass, struct);
+
+ var message = builder.finish();
+
+ var messageValidator = new validator.Validator(message);
+ return structClass.validate(messageValidator, codec.kMessageHeaderSize);
+ }
+
+ function testNullUnionMemberValidation() {
+ var s = new unions.WrapperStruct({
+ object_union: new unions.ObjectUnion({
+ f_dummy: null})});
+
+ var err = structValidate(s);
+ expect(err).toEqual(validator.validationError.UNEXPECTED_NULL_POINTER);
+
+ var s = new unions.WrapperStruct({
+ object_union: new unions.ObjectUnion({
+ f_nullable: null})});
+
+ var err = structValidate(s);
+ expect(err).toEqual(validator.validationError.NONE);
+ }
+
+ function testNullUnionValidation() {
+ var s = new unions.SmallStructNonNullableUnion({
+ pod_union: null});
+
+ var err = structValidate(s);
+ expect(err).toEqual(validator.validationError.UNEXPECTED_NULL_UNION);
+
+ var s = new unions.WrapperStruct({
+ object_union: new unions.ObjectUnion({
+ f_pod_union: null})
+ });
+
+ var err = structValidate(s);
+ expect(err).toEqual(validator.validationError.UNEXPECTED_NULL_UNION);
+ }
+
+ testConstructors();
+ testBasicEncoding();
+ testUnionsInArrayEncoding();
+ testUnionsInMapEncoding();
+ testNestedUnionsEncoding();
+ testNullUnionMemberValidation();
+ testNullUnionValidation();
+ this.result = "PASS";
+});
diff --git a/mojo/public/js/tests/validation_test_input_parser.js b/mojo/public/js/tests/validation_test_input_parser.js
new file mode 100644
index 0000000..f5a57f9
--- /dev/null
+++ b/mojo/public/js/tests/validation_test_input_parser.js
@@ -0,0 +1,299 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Support for parsing binary sequences encoded as readable strings
+// or ".data" files. The input format is described here:
+// mojo/public/cpp/bindings/tests/validation_test_input_parser.h
+
+define([
+ "mojo/public/js/buffer"
+ ], function(buffer) {
+
+ // Files and Lines represent the raw text from an input string
+ // or ".data" file.
+
+ function InputError(message, line) {
+ this.message = message;
+ this.line = line;
+ }
+
+ InputError.prototype.toString = function() {
+ var s = 'Error: ' + this.message;
+ if (this.line)
+ s += ', at line ' +
+ (this.line.number + 1) + ': "' + this.line.contents + '"';
+ return s;
+ }
+
+ function File(contents) {
+ this.contents = contents;
+ this.index = 0;
+ this.lineNumber = 0;
+ }
+
+ File.prototype.endReached = function() {
+ return this.index >= this.contents.length;
+ }
+
+ File.prototype.nextLine = function() {
+ if (this.endReached())
+ return null;
+ var start = this.index;
+ var end = this.contents.indexOf('\n', start);
+ if (end == -1)
+ end = this.contents.length;
+ this.index = end + 1;
+ return new Line(this.contents.substring(start, end), this.lineNumber++);
+ }
+
+ function Line(contents, number) {
+ var i = contents.indexOf('//');
+ var s = (i == -1) ? contents.trim() : contents.substring(0, i).trim();
+ this.contents = contents;
+ this.items = (s.length > 0) ? s.split(/\s+/) : [];
+ this.index = 0;
+ this.number = number;
+ }
+
+ Line.prototype.endReached = function() {
+ return this.index >= this.items.length;
+ }
+
+ var ITEM_TYPE_SIZES = {
+ u1: 1, u2: 2, u4: 4, u8: 8, s1: 1, s2: 2, s4: 4, s8: 8, b: 1, f: 4, d: 8,
+ dist4: 4, dist8: 8, anchr: 0, handles: 0
+ };
+
+ function isValidItemType(type) {
+ return ITEM_TYPE_SIZES[type] !== undefined;
+ }
+
+ Line.prototype.nextItem = function() {
+ if (this.endReached())
+ return null;
+
+ var itemString = this.items[this.index++];
+ var type = 'u1';
+ var value = itemString;
+
+ if (itemString.charAt(0) == '[') {
+ var i = itemString.indexOf(']');
+ if (i != -1 && i + 1 < itemString.length) {
+ type = itemString.substring(1, i);
+ value = itemString.substring(i + 1);
+ } else {
+ throw new InputError('invalid item', this);
+ }
+ }
+ if (!isValidItemType(type))
+ throw new InputError('invalid item type', this);
+
+ return new Item(this, type, value);
+ }
+
+ // The text for each whitespace delimited binary data "item" is represented
+ // by an Item.
+
+ function Item(line, type, value) {
+ this.line = line;
+ this.type = type;
+ this.value = value;
+ this.size = ITEM_TYPE_SIZES[type];
+ }
+
+ Item.prototype.isFloat = function() {
+ return this.type == 'f' || this.type == 'd';
+ }
+
+ Item.prototype.isInteger = function() {
+ return ['u1', 'u2', 'u4', 'u8',
+ 's1', 's2', 's4', 's8'].indexOf(this.type) != -1;
+ }
+
+ Item.prototype.isNumber = function() {
+ return this.isFloat() || this.isInteger();
+ }
+
+ Item.prototype.isByte = function() {
+ return this.type == 'b';
+ }
+
+ Item.prototype.isDistance = function() {
+ return this.type == 'dist4' || this.type == 'dist8';
+ }
+
+ Item.prototype.isAnchor = function() {
+ return this.type == 'anchr';
+ }
+
+ Item.prototype.isHandles = function() {
+ return this.type == 'handles';
+ }
+
+ // A TestMessage represents the complete binary message loaded from an input
+ // string or ".data" file. The parseTestMessage() function below constructs
+ // a TestMessage from a File.
+
+ function TestMessage(byteLength) {
+ this.index = 0;
+ this.buffer = new buffer.Buffer(byteLength);
+ this.distances = {};
+ this.handleCount = 0;
+ }
+
+ function checkItemNumberValue(item, n, min, max) {
+ if (n < min || n > max)
+ throw new InputError('invalid item value', item.line);
+ }
+
+ TestMessage.prototype.addNumber = function(item) {
+ var n = item.isInteger() ? parseInt(item.value) : parseFloat(item.value);
+ if (Number.isNaN(n))
+ throw new InputError("can't parse item value", item.line);
+
+ switch(item.type) {
+ case 'u1':
+ checkItemNumberValue(item, n, 0, 0xFF);
+ this.buffer.setUint8(this.index, n);
+ break;
+ case 'u2':
+ checkItemNumberValue(item, n, 0, 0xFFFF);
+ this.buffer.setUint16(this.index, n);
+ break;
+ case 'u4':
+ checkItemNumberValue(item, n, 0, 0xFFFFFFFF);
+ this.buffer.setUint32(this.index, n);
+ break;
+ case 'u8':
+ checkItemNumberValue(item, n, 0, Number.MAX_SAFE_INTEGER);
+ this.buffer.setUint64(this.index, n);
+ break;
+ case 's1':
+ checkItemNumberValue(item, n, -128, 127);
+ this.buffer.setInt8(this.index, n);
+ break;
+ case 's2':
+ checkItemNumberValue(item, n, -32768, 32767);
+ this.buffer.setInt16(this.index, n);
+ break;
+ case 's4':
+ checkItemNumberValue(item, n, -2147483648, 2147483647);
+ this.buffer.setInt32(this.index, n);
+ break;
+ case 's8':
+ checkItemNumberValue(item, n,
+ Number.MIN_SAFE_INTEGER,
+ Number.MAX_SAFE_INTEGER);
+ this.buffer.setInt64(this.index, n);
+ break;
+ case 'f':
+ this.buffer.setFloat32(this.index, n);
+ break;
+ case 'd':
+ this.buffer.setFloat64(this.index, n);
+ break;
+
+ default:
+ throw new InputError('unrecognized item type', item.line);
+ }
+ }
+
+ TestMessage.prototype.addByte = function(item) {
+ if (!/^[01]{8}$/.test(item.value))
+ throw new InputError('invalid byte item value', item.line);
+ function b(i) {
+ return (item.value.charAt(7 - i) == '1') ? 1 << i : 0;
+ }
+ var n = b(0) | b(1) | b(2) | b(3) | b(4) | b(5) | b(6) | b(7);
+ this.buffer.setUint8(this.index, n);
+ }
+
+ TestMessage.prototype.addDistance = function(item) {
+ if (this.distances[item.value])
+ throw new InputError('duplicate distance item', item.line);
+ this.distances[item.value] = {index: this.index, item: item};
+ }
+
+ TestMessage.prototype.addAnchor = function(item) {
+ var dist = this.distances[item.value];
+ if (!dist)
+ throw new InputError('unmatched anchor item', item.line);
+ delete this.distances[item.value];
+
+ var n = this.index - dist.index;
+ // TODO(hansmuller): validate n
+
+ if (dist.item.type == 'dist4')
+ this.buffer.setUint32(dist.index, n);
+ else if (dist.item.type == 'dist8')
+ this.buffer.setUint64(dist.index, n);
+ else
+ throw new InputError('unrecognzed distance item type', dist.item.line);
+ }
+
+ TestMessage.prototype.addHandles = function(item) {
+ this.handleCount = parseInt(item.value);
+ if (Number.isNaN(this.handleCount))
+ throw new InputError("can't parse handleCount", item.line);
+ }
+
+ TestMessage.prototype.addItem = function(item) {
+ if (item.isNumber())
+ this.addNumber(item);
+ else if (item.isByte())
+ this.addByte(item);
+ else if (item.isDistance())
+ this.addDistance(item);
+ else if (item.isAnchor())
+ this.addAnchor(item);
+ else if (item.isHandles())
+ this.addHandles(item);
+ else
+ throw new InputError('unrecognized item type', item.line);
+
+ this.index += item.size;
+ }
+
+ TestMessage.prototype.unanchoredDistances = function() {
+ var names = null;
+ for (var name in this.distances) {
+ if (this.distances.hasOwnProperty(name))
+ names = (names === null) ? name : names + ' ' + name;
+ }
+ return names;
+ }
+
+ function parseTestMessage(text) {
+ var file = new File(text);
+ var items = [];
+ var messageLength = 0;
+ while(!file.endReached()) {
+ var line = file.nextLine();
+ while (!line.endReached()) {
+ var item = line.nextItem();
+ if (item.isHandles() && items.length > 0)
+ throw new InputError('handles item is not first');
+ messageLength += item.size;
+ items.push(item);
+ }
+ }
+
+ var msg = new TestMessage(messageLength);
+ for (var i = 0; i < items.length; i++)
+ msg.addItem(items[i]);
+
+ if (messageLength != msg.index)
+ throw new InputError('failed to compute message length');
+ var names = msg.unanchoredDistances();
+ if (names)
+ throw new InputError('no anchors for ' + names, 0);
+
+ return msg;
+ }
+
+ var exports = {};
+ exports.parseTestMessage = parseTestMessage;
+ exports.InputError = InputError;
+ return exports;
+});
diff --git a/mojo/public/js/tests/validation_unittest.js b/mojo/public/js/tests/validation_unittest.js
new file mode 100644
index 0000000..2a70248
--- /dev/null
+++ b/mojo/public/js/tests/validation_unittest.js
@@ -0,0 +1,338 @@
+// Copyright 2014 The Chromium 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",
+ "file",
+ "gin/test/expect",
+ "mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom",
+ "mojo/public/js/bindings",
+ "mojo/public/js/buffer",
+ "mojo/public/js/codec",
+ "mojo/public/js/core",
+ "mojo/public/js/tests/validation_test_input_parser",
+ "mojo/public/js/validator",
+], function(console,
+ file,
+ expect,
+ testInterface,
+ bindings,
+ buffer,
+ codec,
+ core,
+ parser,
+ validator) {
+
+ var noError = validator.validationError.NONE;
+
+ function checkTestMessageParser() {
+ function TestMessageParserFailure(message, input) {
+ this.message = message;
+ this.input = input;
+ }
+
+ TestMessageParserFailure.prototype.toString = function() {
+ return 'Error: ' + this.message + ' for "' + this.input + '"';
+ };
+
+ function checkData(data, expectedData, input) {
+ if (data.byteLength != expectedData.byteLength) {
+ var s = "message length (" + data.byteLength + ") doesn't match " +
+ "expected length: " + expectedData.byteLength;
+ throw new TestMessageParserFailure(s, input);
+ }
+
+ for (var i = 0; i < data.byteLength; i++) {
+ if (data.getUint8(i) != expectedData.getUint8(i)) {
+ var s = 'message data mismatch at byte offset ' + i;
+ throw new TestMessageParserFailure(s, input);
+ }
+ }
+ }
+
+ function testFloatItems() {
+ var input = '[f]+.3e9 [d]-10.03';
+ var msg = parser.parseTestMessage(input);
+ var expectedData = new buffer.Buffer(12);
+ expectedData.setFloat32(0, +.3e9);
+ expectedData.setFloat64(4, -10.03);
+ checkData(msg.buffer, expectedData, input);
+ }
+
+ function testUnsignedIntegerItems() {
+ var input = '[u1]0x10// hello world !! \n\r \t [u2]65535 \n' +
+ '[u4]65536 [u8]0xFFFFFFFFFFFFF 0 0Xff';
+ var msg = parser.parseTestMessage(input);
+ var expectedData = new buffer.Buffer(17);
+ expectedData.setUint8(0, 0x10);
+ expectedData.setUint16(1, 65535);
+ expectedData.setUint32(3, 65536);
+ expectedData.setUint64(7, 0xFFFFFFFFFFFFF);
+ expectedData.setUint8(15, 0);
+ expectedData.setUint8(16, 0xff);
+ checkData(msg.buffer, expectedData, input);
+ }
+
+ function testSignedIntegerItems() {
+ var input = '[s8]-0x800 [s1]-128\t[s2]+0 [s4]-40';
+ var msg = parser.parseTestMessage(input);
+ var expectedData = new buffer.Buffer(15);
+ expectedData.setInt64(0, -0x800);
+ expectedData.setInt8(8, -128);
+ expectedData.setInt16(9, 0);
+ expectedData.setInt32(11, -40);
+ checkData(msg.buffer, expectedData, input);
+ }
+
+ function testByteItems() {
+ var input = '[b]00001011 [b]10000000 // hello world\n [b]00000000';
+ var msg = parser.parseTestMessage(input);
+ var expectedData = new buffer.Buffer(3);
+ expectedData.setUint8(0, 11);
+ expectedData.setUint8(1, 128);
+ expectedData.setUint8(2, 0);
+ checkData(msg.buffer, expectedData, input);
+ }
+
+ function testAnchors() {
+ var input = '[dist4]foo 0 [dist8]bar 0 [anchr]foo [anchr]bar';
+ var msg = parser.parseTestMessage(input);
+ var expectedData = new buffer.Buffer(14);
+ expectedData.setUint32(0, 14);
+ expectedData.setUint8(4, 0);
+ expectedData.setUint64(5, 9);
+ expectedData.setUint8(13, 0);
+ checkData(msg.buffer, expectedData, input);
+ }
+
+ function testHandles() {
+ var input = '// This message has handles! \n[handles]50 [u8]2';
+ var msg = parser.parseTestMessage(input);
+ var expectedData = new buffer.Buffer(8);
+ expectedData.setUint64(0, 2);
+
+ if (msg.handleCount != 50) {
+ var s = 'wrong handle count (' + msg.handleCount + ')';
+ throw new TestMessageParserFailure(s, input);
+ }
+ checkData(msg.buffer, expectedData, input);
+ }
+
+ function testEmptyInput() {
+ var msg = parser.parseTestMessage('');
+ if (msg.buffer.byteLength != 0)
+ throw new TestMessageParserFailure('expected empty message', '');
+ }
+
+ function testBlankInput() {
+ var input = ' \t // hello world \n\r \t// the answer is 42 ';
+ var msg = parser.parseTestMessage(input);
+ if (msg.buffer.byteLength != 0)
+ throw new TestMessageParserFailure('expected empty message', input);
+ }
+
+ function testInvalidInput() {
+ function parserShouldFail(input) {
+ try {
+ parser.parseTestMessage(input);
+ } catch (e) {
+ if (e instanceof parser.InputError)
+ return;
+ throw new TestMessageParserFailure(
+ 'unexpected exception ' + e.toString(), input);
+ }
+ throw new TestMessageParserFailure("didn't detect invalid input", file);
+ }
+
+ ['/ hello world',
+ '[u1]x',
+ '[u2]-1000',
+ '[u1]0x100',
+ '[s2]-0x8001',
+ '[b]1',
+ '[b]1111111k',
+ '[dist4]unmatched',
+ '[anchr]hello [dist8]hello',
+ '[dist4]a [dist4]a [anchr]a',
+ // '[dist4]a [anchr]a [dist4]a [anchr]a',
+ '0 [handles]50'
+ ].forEach(parserShouldFail);
+ }
+
+ try {
+ testFloatItems();
+ testUnsignedIntegerItems();
+ testSignedIntegerItems();
+ testByteItems();
+ testInvalidInput();
+ testEmptyInput();
+ testBlankInput();
+ testHandles();
+ testAnchors();
+ } catch (e) {
+ return e.toString();
+ }
+ return null;
+ }
+
+ function getMessageTestFiles(prefix) {
+ var sourceRoot = file.getSourceRootDirectory();
+ expect(sourceRoot).not.toBeNull();
+
+ var testDir = sourceRoot +
+ "/mojo/public/interfaces/bindings/tests/data/validation/";
+ var testFiles = file.getFilesInDirectory(testDir);
+ expect(testFiles).not.toBeNull();
+ expect(testFiles.length).toBeGreaterThan(0);
+
+ // The matching ".data" pathnames with the extension removed.
+ return testFiles.filter(function(s) {
+ return s.substr(-5) == ".data" && s.indexOf(prefix) == 0;
+ }).map(function(s) {
+ return testDir + s.slice(0, -5);
+ });
+ }
+
+ function readTestMessage(filename) {
+ var contents = file.readFileToString(filename + ".data");
+ expect(contents).not.toBeNull();
+ return parser.parseTestMessage(contents);
+ }
+
+ function readTestExpected(filename) {
+ var contents = file.readFileToString(filename + ".expected");
+ expect(contents).not.toBeNull();
+ return contents.trim();
+ }
+
+ function checkValidationResult(testFile, err) {
+ var actualResult = (err === noError) ? "PASS" : err;
+ var expectedResult = readTestExpected(testFile);
+ if (actualResult != expectedResult)
+ console.log("[Test message validation failed: " + testFile + " ]");
+ expect(actualResult).toEqual(expectedResult);
+ }
+
+ function testMessageValidation(prefix, filters) {
+ var testFiles = getMessageTestFiles(prefix);
+ expect(testFiles.length).toBeGreaterThan(0);
+
+ for (var i = 0; i < testFiles.length; i++) {
+ // TODO(hansmuller) Temporarily skipping array pointer overflow tests
+ // because JS numbers are limited to 53 bits.
+ // TODO(rudominer): Temporarily skipping 'no-such-method',
+ // 'invalid_request_flags', and 'invalid_response_flags' until additional
+ // logic in *RequestValidator and *ResponseValidator is ported from
+ // cpp to js.
+ // TODO(crbug/640298): Implement max recursion depth for JS.
+ // TODO(crbug/628104): Support struct map keys for JS.
+ if (testFiles[i].indexOf("overflow") != -1 ||
+ testFiles[i].indexOf("conformance_mthd19") != -1 ||
+ testFiles[i].indexOf("conformance_mthd20") != -1 ||
+ testFiles[i].indexOf("no_such_method") != -1 ||
+ testFiles[i].indexOf("invalid_request_flags") != -1 ||
+ testFiles[i].indexOf("invalid_response_flags") != -1) {
+ console.log("[Skipping " + testFiles[i] + "]");
+ continue;
+ }
+
+ var testMessage = readTestMessage(testFiles[i]);
+ var handles = new Array(testMessage.handleCount);
+ var message = new codec.Message(testMessage.buffer, handles);
+ var messageValidator = new validator.Validator(message);
+
+ var err = messageValidator.validateMessageHeader();
+ for (var j = 0; err === noError && j < filters.length; ++j)
+ err = filters[j](messageValidator);
+
+ checkValidationResult(testFiles[i], err);
+ }
+ }
+
+ function testConformanceMessageValidation() {
+ testMessageValidation("conformance_", [
+ testInterface.ConformanceTestInterface.validateRequest]);
+ }
+
+ function testBoundsCheckMessageValidation() {
+ testMessageValidation("boundscheck_", [
+ testInterface.BoundsCheckTestInterface.validateRequest]);
+ }
+
+ function testResponseConformanceMessageValidation() {
+ testMessageValidation("resp_conformance_", [
+ testInterface.ConformanceTestInterface.validateResponse]);
+ }
+
+ function testResponseBoundsCheckMessageValidation() {
+ testMessageValidation("resp_boundscheck_", [
+ testInterface.BoundsCheckTestInterface.validateResponse]);
+ }
+
+ function testIntegratedMessageValidation(testFilesPattern, endpoint) {
+ var testFiles = getMessageTestFiles(testFilesPattern);
+ expect(testFiles.length).toBeGreaterThan(0);
+
+ var testMessagePipe = core.createMessagePipe();
+ expect(testMessagePipe.result).toBe(core.RESULT_OK);
+
+ endpoint.bind(testMessagePipe.handle1);
+ var testingController = endpoint.enableTestingMode();
+
+ var validationError;
+ testingController.setInvalidIncomingMessageHandler(function(error) {
+ validationError = error;
+ });
+
+ for (var i = 0; i < testFiles.length; i++) {
+ validationError = noError;
+ var testMessage = readTestMessage(testFiles[i]);
+ var handles = new Array(testMessage.handleCount);
+
+ var writeMessageValue = core.writeMessage(
+ testMessagePipe.handle0,
+ new Uint8Array(testMessage.buffer.arrayBuffer),
+ new Array(testMessage.handleCount),
+ core.WRITE_MESSAGE_FLAG_NONE);
+ expect(writeMessageValue).toBe(core.RESULT_OK);
+
+ testingController.waitForNextMessage();
+ checkValidationResult(testFiles[i], validationError);
+ }
+
+ expect(core.close(testMessagePipe.handle0)).toBe(core.RESULT_OK);
+ }
+
+ function testIntegratedMessageHeaderValidation() {
+ testIntegratedMessageValidation(
+ "integration_msghdr",
+ new bindings.Binding(testInterface.IntegrationTestInterface, {}));
+ testIntegratedMessageValidation(
+ "integration_msghdr",
+ new testInterface.IntegrationTestInterfacePtr().ptr);
+ }
+
+ function testIntegratedRequestMessageValidation() {
+ testIntegratedMessageValidation(
+ "integration_intf_rqst",
+ new bindings.Binding(testInterface.IntegrationTestInterface, {}));
+ }
+
+ function testIntegratedResponseMessageValidation() {
+ testIntegratedMessageValidation(
+ "integration_intf_resp",
+ new testInterface.IntegrationTestInterfacePtr().ptr);
+ }
+
+ expect(checkTestMessageParser()).toBeNull();
+ testConformanceMessageValidation();
+ testBoundsCheckMessageValidation();
+ testResponseConformanceMessageValidation();
+ testResponseBoundsCheckMessageValidation();
+ testIntegratedMessageHeaderValidation();
+ testIntegratedResponseMessageValidation();
+ testIntegratedRequestMessageValidation();
+
+ this.result = "PASS";
+});
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl
index 2a9e02d..5973ba2 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl
@@ -89,3 +89,4 @@ class {{union.name}}DataView {
mojo::internal::SerializationContext* context_ = nullptr;
{%- endif %}
};
+
diff --git a/mojo/public/tools/bindings/generators/mojom_java_generator.py b/mojo/public/tools/bindings/generators/mojom_java_generator.py
index 9265b3a..c7657ff 100644
--- a/mojo/public/tools/bindings/generators/mojom_java_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_java_generator.py
@@ -23,7 +23,6 @@ from mojom.generate.template_expander import UseJinja
sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir,
os.pardir, os.pardir, os.pardir, os.pardir,
'build', 'android', 'gyp'))
-
from util import build_utils