Revert "libmojo: Uprev the library to r456626 from Chromium"
This reverts commit 8ac9103e05b66812c25348943383f9365d1ce3e0.
Reason for revert: Broke the mac_sdk
Exempt-From-Owner-Approval: Fixing mac_sdk
Change-Id: I0b74d1abaa66933a93fd6f82ff018e8948c1204e
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn
index 070e2d1..2dac654 100644
--- a/mojo/BUILD.gn
+++ b/mojo/BUILD.gn
@@ -13,6 +13,9 @@
]
if (!(is_linux && current_cpu == "x86")) {
+ # TODO(GYP): Figure out if this needs to be supported. Right now
+ # it won't work on x86 official builds because it needs stuff in the
+ # sysroot that doesn't exist.
deps += [ "//mojo/public" ]
}
@@ -20,7 +23,7 @@
deps += [ "//mojo/android" ]
}
- deps += [ "//services/service_manager:all" ]
+ deps += [ "//services/shell:all" ]
}
group("tests") {
@@ -28,14 +31,17 @@
deps = [
"//ipc:ipc_tests",
"//mojo/common:mojo_common_unittests",
- "//mojo/edk/js/tests",
+ "//mojo/converters/blink:blink_converters_unittests",
+ "//mojo/edk/js/test:js_integration_tests",
+ "//mojo/edk/js/test:js_unittests",
"//mojo/edk/system:mojo_message_pipe_perftests",
"//mojo/edk/system:mojo_system_unittests",
"//mojo/edk/test:mojo_public_bindings_perftests",
"//mojo/edk/test:mojo_public_bindings_unittests",
"//mojo/edk/test:mojo_public_system_perftests",
"//mojo/edk/test:mojo_public_system_unittests",
- "//services/service_manager/public/cpp/tests:mojo_public_application_unittests",
- "//services/service_manager/tests",
+ "//services/shell/public/cpp/tests:mojo_public_application_unittests",
+ "//services/shell/runner/host:mojo_runner_host_unittests",
+ "//services/shell/tests",
]
}
diff --git a/mojo/DEPS b/mojo/DEPS
index 49d7fd3..a659200 100644
--- a/mojo/DEPS
+++ b/mojo/DEPS
@@ -3,5 +3,5 @@
"+build",
"+testing",
- "+services/service_manager",
+ "+services/shell",
]
diff --git a/mojo/android/BUILD.gn b/mojo/android/BUILD.gn
index 1a8cdbd..813701c 100644
--- a/mojo/android/BUILD.gn
+++ b/mojo/android/BUILD.gn
@@ -29,7 +29,6 @@
sources = [
"system/src/org/chromium/mojo/system/impl/BaseRunLoop.java",
"system/src/org/chromium/mojo/system/impl/CoreImpl.java",
- "system/src/org/chromium/mojo/system/impl/WatcherImpl.java",
]
jni_package = "mojo"
@@ -41,15 +40,12 @@
"system/base_run_loop.h",
"system/core_impl.cc",
"system/core_impl.h",
- "system/watcher_impl.cc",
- "system/watcher_impl.h",
]
deps = [
":system_java_jni_headers",
"//base",
- "//mojo/public/c/system",
- "//mojo/public/cpp/system",
+ "//mojo/message_pump",
]
}
@@ -63,12 +59,11 @@
"system/src/org/chromium/mojo/system/impl/MessagePipeHandleImpl.java",
"system/src/org/chromium/mojo/system/impl/SharedBufferHandleImpl.java",
"system/src/org/chromium/mojo/system/impl/UntypedHandleImpl.java",
- "system/src/org/chromium/mojo/system/impl/WatcherImpl.java",
]
deps = [
"//base:base_java",
- "//mojo/public/java:system_java",
+ "//mojo/public/java:system",
]
}
@@ -95,7 +90,6 @@
"javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java",
"javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java",
"javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java",
- "javatests/src/org/chromium/mojo/system/impl/WatcherImplTest.java",
]
deps = [
@@ -105,13 +99,8 @@
"//mojo/public/interfaces/bindings/tests:test_interfaces_java",
"//mojo/public/interfaces/bindings/tests:test_mojom_import2_java",
"//mojo/public/interfaces/bindings/tests:test_mojom_import_java",
- "//mojo/public/java:bindings_java",
- "//mojo/public/java:system_java",
- "//third_party/android_support_test_runner:runner_java",
- ]
-
- data = [
- "//mojo/public/interfaces/bindings/tests/data/validation/",
+ "//mojo/public/java:bindings",
+ "//mojo/public/java:system",
]
}
@@ -131,9 +120,10 @@
":libsystem_java",
":system_java_jni_headers",
"//base",
- "//base/test:test_support",
+ "//base/test/:test_support",
"//build/config/sanitizers:deps",
"//mojo/edk/system",
+ "//mojo/message_pump",
"//mojo/public/cpp/bindings/tests:mojo_public_bindings_test_utils",
"//mojo/public/cpp/test_support:test_utils",
]
@@ -146,10 +136,10 @@
":system_java",
"//base:base_java",
"//mojo/public/interfaces/bindings/tests:test_interfaces",
- "//mojo/public/java:bindings_java",
- "//third_party/android_support_test_runner:runner_java",
+ "//mojo/public/java:bindings",
]
shared_libraries = [ ":mojo_java_unittests" ]
apk_name = "MojoTest"
android_manifest = "javatests/AndroidManifest.xml"
+ isolate_file = "../mojo_test_apk.isolate"
}
diff --git a/mojo/android/javatests/init_library.cc b/mojo/android/javatests/init_library.cc
index 9e1a593..d2d9423 100644
--- a/mojo/android/javatests/init_library.cc
+++ b/mojo/android/javatests/init_library.cc
@@ -6,33 +6,27 @@
#include "base/android/base_jni_registrar.h"
#include "base/android/jni_android.h"
#include "base/android/jni_registrar.h"
-#include "base/android/library_loader/library_loader_hooks.h"
#include "base/bind.h"
#include "mojo/android/javatests/mojo_test_case.h"
#include "mojo/android/javatests/validation_test_util.h"
#include "mojo/android/system/core_impl.h"
-#include "mojo/android/system/watcher_impl.h"
#include "mojo/edk/embedder/embedder.h"
namespace {
base::android::RegistrationMethod kMojoRegisteredMethods[] = {
- {"CoreImpl", mojo::android::RegisterCoreImpl},
- {"MojoTestCase", mojo::android::RegisterMojoTestCase},
- {"ValidationTestUtil", mojo::android::RegisterValidationTestUtil},
- {"WatcherImpl", mojo::android::RegisterWatcherImpl},
+ { "CoreImpl", mojo::android::RegisterCoreImpl },
+ { "MojoTestCase", mojo::android::RegisterMojoTestCase },
+ { "ValidationTestUtil", mojo::android::RegisterValidationTestUtil },
};
bool RegisterJNI(JNIEnv* env) {
return base::android::RegisterJni(env) &&
- RegisterNativeMethods(env, kMojoRegisteredMethods,
- arraysize(kMojoRegisteredMethods));
+ RegisterNativeMethods(env, kMojoRegisteredMethods,
+ arraysize(kMojoRegisteredMethods));
}
-bool NativeInit() {
- if (!base::android::OnJNIOnLoadInit())
- return false;
-
+bool Init() {
mojo::edk::Init();
return true;
}
@@ -40,11 +34,13 @@
} // namespace
JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
- base::android::InitVM(vm);
- JNIEnv* env = base::android::AttachCurrentThread();
- if (!base::android::OnJNIOnLoadRegisterJNI(env) || !RegisterJNI(env) ||
- !NativeInit()) {
+ std::vector<base::android::RegisterCallback> register_callbacks;
+ register_callbacks.push_back(base::Bind(&RegisterJNI));
+ std::vector<base::android::InitCallback> init_callbacks;
+ init_callbacks.push_back(base::Bind(&Init));
+ if (!base::android::OnJNIOnLoadRegisterJNI(vm, register_callbacks) ||
+ !base::android::OnJNIOnLoadInit(init_callbacks))
return -1;
- }
+
return JNI_VERSION_1_4;
}
diff --git a/mojo/android/javatests/mojo_test_case.cc b/mojo/android/javatests/mojo_test_case.cc
index fc59009..6d88985 100644
--- a/mojo/android/javatests/mojo_test_case.cc
+++ b/mojo/android/javatests/mojo_test_case.cc
@@ -16,13 +16,12 @@
#include "base/test/test_support_android.h"
#include "base/threading/thread_task_runner_handle.h"
#include "jni/MojoTestCase_jni.h"
-
-using base::android::JavaParamRef;
+#include "mojo/message_pump/message_pump_mojo.h"
namespace {
struct TestEnvironment {
- TestEnvironment() {}
+ TestEnvironment() : message_loop(mojo::common::MessagePumpMojo::Create()) {}
base::ShadowingAtExitManager at_exit;
base::MessageLoop message_loop;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java b/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java
index f4d7ab7..c7348ca 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java
@@ -4,6 +4,7 @@
package org.chromium.mojo;
+import android.content.Context;
import android.test.InstrumentationTestCase;
import org.chromium.base.ContextUtils;
@@ -25,9 +26,9 @@
@Override
protected void setUp() throws Exception {
super.setUp();
- ContextUtils.initApplicationContext(
- getInstrumentation().getTargetContext().getApplicationContext());
- LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER).ensureInitialized();
+ Context appContext = getInstrumentation().getTargetContext().getApplicationContext();
+ ContextUtils.initApplicationContext(appContext);
+ LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER).ensureInitialized(appContext);
nativeInit();
mTestEnvironmentPointer = nativeSetupTestEnvironment();
}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java
index 38bd348..a46a157 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsHelperTest.java
@@ -4,7 +4,7 @@
package org.chromium.mojo.bindings;
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.TestCase;
@@ -34,8 +34,8 @@
BindingsHelper.utf8StringSizeInBytes(s));
}
assertEquals(1, BindingsHelper.utf8StringSizeInBytes("\0"));
- String s = new StringBuilder().appendCodePoint(0x0).appendCodePoint(0x80)
- .appendCodePoint(0x800).appendCodePoint(0x10000).toString();
+ String s = new StringBuilder().appendCodePoint(0x0).appendCodePoint(0x80).
+ appendCodePoint(0x800).appendCodePoint(0x10000).toString();
assertEquals(10, BindingsHelper.utf8StringSizeInBytes(s));
assertEquals(10, s.getBytes(Charset.forName("utf8")).length);
}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTest.java
index d280c77..6886023 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsTest.java
@@ -4,7 +4,7 @@
package org.chromium.mojo.bindings;
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.TestCase;
@@ -21,15 +21,12 @@
import org.chromium.mojo.bindings.test.mojom.sample.InterfaceConstants;
import org.chromium.mojo.bindings.test.mojom.sample.SampleServiceConstants;
import org.chromium.mojo.bindings.test.mojom.test_structs.EmptyStruct;
-import org.chromium.mojo.bindings.test.mojom.test_structs.Rect;
import org.chromium.mojo.system.DataPipe.ConsumerHandle;
import org.chromium.mojo.system.DataPipe.ProducerHandle;
import org.chromium.mojo.system.MessagePipeHandle;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
-import java.util.HashMap;
-import java.util.Map;
/**
* Testing generated classes and associated features.
@@ -84,15 +81,6 @@
return foo;
}
- private static Rect createRect(int x, int y, int width, int height) {
- Rect rect = new Rect();
- rect.x = x;
- rect.y = y;
- rect.width = width;
- rect.height = height;
- return rect;
- }
-
private static <T> void checkConstantField(
Field field, Class<T> expectedClass, T value) throws IllegalAccessException {
assertEquals(expectedClass, field.getType());
@@ -221,21 +209,4 @@
assertNotNull(emptyStruct);
}
- // In testing maps we want to make sure that the key used when inserting an
- // item the key used when looking it up again are different objects. Java
- // has default implementations of equals and hashCode that use reference
- // equality and hashing, respectively, and that's not what we want for our
- // mojom values.
- @SmallTest
- public void testHashMapStructKey() {
- Map<Rect, Integer> map = new HashMap<>();
- map.put(createRect(1, 2, 3, 4), 123);
-
- Rect key = createRect(1, 2, 3, 4);
- assertNotNull(map.get(key));
- assertEquals(123, map.get(key).intValue());
-
- map.remove(key);
- assertTrue(map.isEmpty());
- }
}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsVersioningTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsVersioningTest.java
index eea92ab..f0c0f7c 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsVersioningTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/BindingsVersioningTest.java
@@ -4,7 +4,7 @@
package org.chromium.mojo.bindings;
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
import org.chromium.mojo.MojoTestCase;
import org.chromium.mojo.bindings.test.mojom.test_structs.MultiVersionStruct;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/CallbacksTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/CallbacksTest.java
index 497be65..dfb8e21 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/CallbacksTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/CallbacksTest.java
@@ -4,7 +4,7 @@
package org.chromium.mojo.bindings;
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.TestCase;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ConnectorTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ConnectorTest.java
index 15f9f1f..9c26875 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/ConnectorTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ConnectorTest.java
@@ -4,7 +4,7 @@
package org.chromium.mojo.bindings;
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
import org.chromium.mojo.MojoTestCase;
import org.chromium.mojo.bindings.BindingsTestUtils.CapturingErrorHandler;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java
index cabe230..56f4b40 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ExecutorFactoryTest.java
@@ -4,7 +4,7 @@
package org.chromium.mojo.bindings;
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
import org.chromium.mojo.MojoTestCase;
import org.chromium.mojo.system.impl.CoreImpl;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfaceControlMessageTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfaceControlMessageTest.java
index 8cdd4ab..c101675 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfaceControlMessageTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfaceControlMessageTest.java
@@ -4,7 +4,7 @@
package org.chromium.mojo.bindings;
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
import org.chromium.mojo.MojoTestCase;
import org.chromium.mojo.bindings.Callbacks.Callback1;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java
index d8bd3e8..f96b1e3 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/InterfacesTest.java
@@ -4,7 +4,7 @@
package org.chromium.mojo.bindings;
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
import org.chromium.mojo.MojoTestCase;
import org.chromium.mojo.bindings.BindingsTestUtils.CapturingErrorHandler;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java
index b2e6ac8..8af8be1 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/MessageHeaderTest.java
@@ -4,7 +4,7 @@
package org.chromium.mojo.bindings;
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.TestCase;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java
index 51dbd22..f2edb01 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ReadAndDispatchMessageTest.java
@@ -4,7 +4,7 @@
package org.chromium.mojo.bindings;
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
import org.chromium.mojo.MojoTestCase;
import org.chromium.mojo.bindings.BindingsTestUtils.RecordingMessageReceiver;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java
index 5affb8f..0b632a9 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/RouterTest.java
@@ -4,7 +4,7 @@
package org.chromium.mojo.bindings;
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
import org.chromium.base.annotations.SuppressFBWarnings;
import org.chromium.mojo.MojoTestCase;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/SerializationTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/SerializationTest.java
index 2c17e3a..a7e213c 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/SerializationTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/SerializationTest.java
@@ -4,7 +4,7 @@
package org.chromium.mojo.bindings;
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.TestCase;
@@ -17,8 +17,6 @@
import org.chromium.mojo.bindings.test.mojom.mojo.Struct6;
import org.chromium.mojo.bindings.test.mojom.mojo.StructOfNullables;
-import java.nio.ByteBuffer;
-
/**
* Tests for the serialization logic of the generated structs, using structs defined in
* mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom .
@@ -130,46 +128,4 @@
assertNull(struct.str);
struct.serialize(null);
}
-
- /**
- * Verifies that a struct can be serialized to and deserialized from a ByteBuffer.
- */
- @SmallTest
- public void testByteBufferSerialization() {
- Struct1 input = new Struct1();
- input.i = 0x7F;
-
- ByteBuffer buf = input.serialize();
-
- byte[] expected_raw_bytes = {16, 0, 0, 0, 0, 0, 0, 0, 0x7F, 0, 0, 0, 0, 0, 0, 0};
- ByteBuffer expected_buf = ByteBuffer.wrap(expected_raw_bytes);
- assertEquals(expected_buf, buf);
-
- Struct1 output = Struct1.deserialize(buf);
- assertEquals(0x7F, output.i);
- }
-
- /**
- * Verifies that a struct with handles cannot be serialized to a ByteBuffer.
- */
- @SmallTest
- public void testByteBufferSerializationWithHandles() {
- StructOfNullables struct = new StructOfNullables();
- assertFalse(struct.hdl.isValid());
- assertNull(struct.struct1);
- assertNull(struct.str);
-
- // It is okay to serialize invalid handles.
- struct.serialize();
-
- struct.hdl = new HandleMock();
-
- try {
- struct.serialize();
- fail("Serializing a struct with handles to a ByteBuffer should have thrown an "
- + "exception.");
- } catch (UnsupportedOperationException ex) {
- // Expected.
- }
- }
}
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java
index 8424618..e5c4549 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTest.java
@@ -4,7 +4,7 @@
package org.chromium.mojo.bindings;
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
import org.chromium.base.test.util.UrlUtils;
import org.chromium.mojo.HandleMock;
@@ -13,7 +13,6 @@
import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterface;
import org.chromium.mojo.bindings.test.mojom.mojo.IntegrationTestInterfaceTestHelper;
import org.chromium.mojo.system.Handle;
-import org.chromium.mojo.system.impl.CoreImpl;
import java.io.File;
import java.io.FileFilter;
@@ -190,10 +189,8 @@
*/
@SmallTest
public void testConformance() throws FileNotFoundException {
- runTest("conformance_",
- ConformanceTestInterface.MANAGER.buildStub(CoreImpl.getInstance(),
- ConformanceTestInterface.MANAGER.buildProxy(
- CoreImpl.getInstance(), new SinkMessageReceiver())));
+ runTest("conformance_", ConformanceTestInterface.MANAGER.buildStub(null,
+ ConformanceTestInterface.MANAGER.buildProxy(null, new SinkMessageReceiver())));
}
/**
diff --git a/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java
index 623abc3..14259ef 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtilTest.java
@@ -4,7 +4,7 @@
package org.chromium.mojo.bindings;
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
import org.chromium.mojo.MojoTestCase;
diff --git a/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java b/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java
index 77a9bda..16c16f2 100644
--- a/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java
+++ b/mojo/android/javatests/src/org/chromium/mojo/system/impl/CoreImplTest.java
@@ -4,9 +4,12 @@
package org.chromium.mojo.system.impl;
-import android.support.test.filters.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
import org.chromium.mojo.MojoTestCase;
+import org.chromium.mojo.system.AsyncWaiter;
+import org.chromium.mojo.system.AsyncWaiter.Callback;
+import org.chromium.mojo.system.AsyncWaiter.Cancellable;
import org.chromium.mojo.system.Core;
import org.chromium.mojo.system.Core.HandleSignals;
import org.chromium.mojo.system.Core.HandleSignalsState;
@@ -581,6 +584,266 @@
}
}
+ private static class AsyncWaiterResult implements Callback {
+ private int mResult = Integer.MIN_VALUE;
+ private MojoException mException = null;
+
+ /**
+ * @see Callback#onResult(int)
+ */
+ @Override
+ public void onResult(int result) {
+ this.mResult = result;
+ }
+
+ /**
+ * @see Callback#onError(MojoException)
+ */
+ @Override
+ public void onError(MojoException exception) {
+ this.mException = exception;
+ }
+
+ /**
+ * @return the result
+ */
+ public int getResult() {
+ return mResult;
+ }
+
+ /**
+ * @return the exception
+ */
+ public MojoException getException() {
+ return mException;
+ }
+ }
+
+ /**
+ * Testing core {@link AsyncWaiter} implementation.
+ */
+ @SmallTest
+ public void testAsyncWaiterCorrectResult() {
+ Core core = CoreImpl.getInstance();
+
+ // Checking a correct result.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+ addHandlePairToClose(handles);
+ final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ core.getDefaultAsyncWaiter().asyncWait(handles.first, Core.HandleSignals.READABLE,
+ Core.DEADLINE_INFINITE, asyncWaiterResult);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ handles.second.writeMessage(
+ ByteBuffer.allocateDirect(1), null, MessagePipeHandle.WriteFlags.NONE);
+ runLoopUntilIdle();
+ assertNull(asyncWaiterResult.getException());
+ assertEquals(MojoResult.OK, asyncWaiterResult.getResult());
+ }
+
+ /**
+ * Testing core {@link AsyncWaiter} implementation.
+ */
+ @SmallTest
+ public void testAsyncWaiterClosingPeerHandle() {
+ Core core = CoreImpl.getInstance();
+
+ // Closing the peer handle.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+ addHandlePairToClose(handles);
+
+ final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ core.getDefaultAsyncWaiter().asyncWait(handles.first, Core.HandleSignals.READABLE,
+ Core.DEADLINE_INFINITE, asyncWaiterResult);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ runLoopUntilIdle();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ handles.second.close();
+ runLoopUntilIdle();
+ assertNull(asyncWaiterResult.getException());
+ assertEquals(MojoResult.FAILED_PRECONDITION, asyncWaiterResult.getResult());
+ }
+
+ /**
+ * Testing core {@link AsyncWaiter} implementation.
+ */
+ @SmallTest
+ public void testAsyncWaiterClosingWaitingHandle() {
+ Core core = CoreImpl.getInstance();
+
+ // Closing the peer handle.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+ addHandlePairToClose(handles);
+
+ final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ core.getDefaultAsyncWaiter().asyncWait(handles.first, Core.HandleSignals.READABLE,
+ Core.DEADLINE_INFINITE, asyncWaiterResult);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ runLoopUntilIdle();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ handles.first.close();
+ runLoopUntilIdle();
+ // TODO(qsr) Re-enable when MojoWaitMany handles it correctly.
+ // assertNull(asyncWaiterResult.getException());
+ // assertEquals(MojoResult.CANCELLED, asyncWaiterResult.getResult());
+ }
+
+ /**
+ * Testing core {@link AsyncWaiter} implementation.
+ */
+ @SmallTest
+ public void testAsyncWaiterWaitingOnInvalidHandle() {
+ Core core = CoreImpl.getInstance();
+
+ // Closing the peer handle.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+ addHandlePairToClose(handles);
+
+ final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ handles.first.close();
+ core.getDefaultAsyncWaiter().asyncWait(handles.first, Core.HandleSignals.READABLE,
+ Core.DEADLINE_INFINITE, asyncWaiterResult);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ runLoopUntilIdle();
+ assertNotNull(asyncWaiterResult.getException());
+ assertEquals(MojoResult.INVALID_ARGUMENT,
+ asyncWaiterResult.getException().getMojoResult());
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ }
+
+ /**
+ * Testing core {@link AsyncWaiter} implementation.
+ */
+ @SmallTest
+ public void testAsyncWaiterWaitingOnDefaultInvalidHandle() {
+ Core core = CoreImpl.getInstance();
+
+ final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ core.getDefaultAsyncWaiter().asyncWait(InvalidHandle.INSTANCE, Core.HandleSignals.READABLE,
+ Core.DEADLINE_INFINITE, asyncWaiterResult);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ runLoopUntilIdle();
+ assertNotNull(asyncWaiterResult.getException());
+ assertEquals(MojoResult.INVALID_ARGUMENT, asyncWaiterResult.getException().getMojoResult());
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ }
+
+ /**
+ * Testing core {@link AsyncWaiter} implementation.
+ */
+ @SmallTest
+ public void testAsyncWaiterWaitingWithTimeout() {
+ Core core = CoreImpl.getInstance();
+
+ // Closing the peer handle.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+ addHandlePairToClose(handles);
+
+ final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ core.getDefaultAsyncWaiter().asyncWait(
+ handles.first, Core.HandleSignals.READABLE, RUN_LOOP_TIMEOUT_MS, asyncWaiterResult);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ runLoopUntilIdle();
+ assertNull(asyncWaiterResult.getException());
+ assertEquals(MojoResult.DEADLINE_EXCEEDED, asyncWaiterResult.getResult());
+ }
+
+ /**
+ * Testing core {@link AsyncWaiter} implementation.
+ */
+ @SmallTest
+ public void testAsyncWaiterCancelWaiting() {
+ Core core = CoreImpl.getInstance();
+
+ // Closing the peer handle.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+ addHandlePairToClose(handles);
+
+ final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ Cancellable cancellable = core.getDefaultAsyncWaiter().asyncWait(handles.first,
+ Core.HandleSignals.READABLE, Core.DEADLINE_INFINITE, asyncWaiterResult);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ runLoopUntilIdle();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ cancellable.cancel();
+ runLoopUntilIdle();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ handles.second.writeMessage(
+ ByteBuffer.allocateDirect(1), null, MessagePipeHandle.WriteFlags.NONE);
+ runLoopUntilIdle();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+ }
+
+ /**
+ * Testing core {@link AsyncWaiter} implementation.
+ */
+ @SmallTest
+ public void testAsyncWaiterImmediateCancelOnInvalidHandle() {
+ Core core = CoreImpl.getInstance();
+
+ // Closing the peer handle.
+ Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(null);
+ addHandlePairToClose(handles);
+
+ final AsyncWaiterResult asyncWaiterResult = new AsyncWaiterResult();
+ handles.first.close();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+
+ Cancellable cancellable = core.getDefaultAsyncWaiter().asyncWait(handles.first,
+ Core.HandleSignals.READABLE, Core.DEADLINE_INFINITE, asyncWaiterResult);
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+ cancellable.cancel();
+
+ runLoopUntilIdle();
+ assertEquals(Integer.MIN_VALUE, asyncWaiterResult.getResult());
+ assertEquals(null, asyncWaiterResult.getException());
+ }
+
/**
* Testing the pass method on message pipes.
*/
diff --git a/mojo/android/javatests/validation_test_util.cc b/mojo/android/javatests/validation_test_util.cc
index 75f79b3..fb4d140 100644
--- a/mojo/android/javatests/validation_test_util.cc
+++ b/mojo/android/javatests/validation_test_util.cc
@@ -14,9 +14,6 @@
#include "jni/ValidationTestUtil_jni.h"
#include "mojo/public/cpp/bindings/tests/validation_test_input_parser.h"
-using base::android::JavaParamRef;
-using base::android::ScopedJavaLocalRef;
-
namespace mojo {
namespace android {
@@ -37,7 +34,8 @@
input, &data, &num_handles, &error_message)) {
ScopedJavaLocalRef<jstring> j_error_message =
base::android::ConvertUTF8ToJavaString(env, error_message);
- return Java_ValidationTestUtil_buildData(env, nullptr, 0, j_error_message);
+ return Java_ValidationTestUtil_buildData(env, NULL, 0,
+ j_error_message.obj());
}
void* data_ptr = &data[0];
if (!data_ptr) {
@@ -46,8 +44,7 @@
}
jobject byte_buffer =
env->NewDirectByteBuffer(data_ptr, data.size());
- return Java_ValidationTestUtil_buildData(env, byte_buffer, num_handles,
- nullptr);
+ return Java_ValidationTestUtil_buildData(env, byte_buffer, num_handles, NULL);
}
} // namespace android
diff --git a/mojo/android/system/base_run_loop.cc b/mojo/android/system/base_run_loop.cc
index 22511f3..e48d2f0 100644
--- a/mojo/android/system/base_run_loop.cc
+++ b/mojo/android/system/base_run_loop.cc
@@ -10,31 +10,30 @@
#include "base/android/jni_android.h"
#include "base/android/jni_registrar.h"
#include "base/bind.h"
-#include "base/logging.h"
#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/single_thread_task_runner.h"
#include "jni/BaseRunLoop_jni.h"
-
-using base::android::JavaParamRef;
+#include "mojo/message_pump/message_pump_mojo.h"
namespace mojo {
namespace android {
static jlong CreateBaseRunLoop(JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
- base::MessageLoop* message_loop = new base::MessageLoop;
+ base::MessageLoop* message_loop =
+ new base::MessageLoop(common::MessagePumpMojo::Create());
return reinterpret_cast<uintptr_t>(message_loop);
}
static void Run(JNIEnv* env,
- const JavaParamRef<jobject>& jcaller) {
- base::RunLoop().Run();
+ const JavaParamRef<jobject>& jcaller,
+ jlong runLoopID) {
+ reinterpret_cast<base::MessageLoop*>(runLoopID)->Run();
}
static void RunUntilIdle(JNIEnv* env,
- const JavaParamRef<jobject>& jcaller) {
- base::RunLoop().RunUntilIdle();
+ const JavaParamRef<jobject>& jcaller,
+ jlong runLoopID) {
+ reinterpret_cast<base::MessageLoop*>(runLoopID)->RunUntilIdle();
}
static void Quit(JNIEnv* env,
@@ -46,7 +45,7 @@
static void RunJavaRunnable(
const base::android::ScopedJavaGlobalRef<jobject>& runnable_ref) {
Java_BaseRunLoop_runRunnable(base::android::AttachCurrentThread(),
- runnable_ref);
+ runnable_ref.obj());
}
static void PostDelayedTask(JNIEnv* env,
@@ -59,10 +58,9 @@
// use it across threads. |RunJavaRunnable| will acquire a new JNIEnv before
// running the Runnable.
runnable_ref.Reset(env, runnable);
- reinterpret_cast<base::MessageLoop*>(runLoopID)
- ->task_runner()
- ->PostDelayedTask(FROM_HERE, base::Bind(&RunJavaRunnable, runnable_ref),
- base::TimeDelta::FromMicroseconds(delay));
+ reinterpret_cast<base::MessageLoop*>(runLoopID)->PostDelayedTask(
+ FROM_HERE, base::Bind(&RunJavaRunnable, runnable_ref),
+ base::TimeDelta::FromMicroseconds(delay));
}
static void DeleteMessageLoop(JNIEnv* env,
@@ -80,3 +78,4 @@
} // namespace android
} // namespace mojo
+
diff --git a/mojo/android/system/core_impl.cc b/mojo/android/system/core_impl.cc
index 5cbd754..526c050 100644
--- a/mojo/android/system/core_impl.cc
+++ b/mojo/android/system/core_impl.cc
@@ -7,20 +7,56 @@
#include <stddef.h>
#include <stdint.h>
+#include <memory>
+
#include "base/android/base_jni_registrar.h"
#include "base/android/jni_android.h"
#include "base/android/jni_registrar.h"
#include "base/android/library_loader/library_loader_hooks.h"
#include "base/android/scoped_java_ref.h"
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "jni/CoreImpl_jni.h"
+#include "mojo/message_pump/handle_watcher.h"
#include "mojo/public/c/system/core.h"
+namespace {
+
+using MojoAsyncWaitID = uintptr_t;
+const MojoAsyncWaitID kInvalidHandleCancelID = 0;
+
+struct AsyncWaitCallbackData {
+ base::android::ScopedJavaGlobalRef<jobject> core_impl;
+ base::android::ScopedJavaGlobalRef<jobject> callback;
+ base::android::ScopedJavaGlobalRef<jobject> cancellable;
+
+ AsyncWaitCallbackData(JNIEnv* env, jobject core_impl, jobject callback) {
+ this->core_impl.Reset(env, core_impl);
+ this->callback.Reset(env, callback);
+ }
+};
+
+void AsyncWaitCallback(mojo::common::HandleWatcher* watcher,
+ void* data,
+ MojoResult result) {
+ delete watcher;
+ std::unique_ptr<AsyncWaitCallbackData> callback_data(
+ static_cast<AsyncWaitCallbackData*>(data));
+ mojo::android::Java_CoreImpl_onAsyncWaitResult(
+ base::android::AttachCurrentThread(),
+ callback_data->core_impl.obj(),
+ result,
+ callback_data->callback.obj(),
+ callback_data->cancellable.obj());
+}
+
+} // namespace
+
namespace mojo {
namespace android {
-using base::android::JavaParamRef;
-using base::android::ScopedJavaLocalRef;
-
static jlong GetTimeTicksNow(JNIEnv* env,
const JavaParamRef<jobject>& jcaller) {
return MojoGetTimeTicksNow();
@@ -329,6 +365,51 @@
return MojoUnmapBuffer(buffer_start);
}
+static ScopedJavaLocalRef<jobject> AsyncWait(
+ JNIEnv* env,
+ const JavaParamRef<jobject>& jcaller,
+ jint mojo_handle,
+ jint signals,
+ jlong deadline,
+ const JavaParamRef<jobject>& callback) {
+ AsyncWaitCallbackData* callback_data =
+ new AsyncWaitCallbackData(env, jcaller, callback);
+ MojoAsyncWaitID cancel_id;
+ if (static_cast<MojoHandle>(mojo_handle) != MOJO_HANDLE_INVALID) {
+ common::HandleWatcher* watcher = new common::HandleWatcher();
+ cancel_id = reinterpret_cast<MojoAsyncWaitID>(watcher);
+ watcher->Start(Handle(static_cast<MojoHandle>(mojo_handle)), signals,
+ deadline,
+ base::Bind(&AsyncWaitCallback, watcher, callback_data));
+ } else {
+ cancel_id = kInvalidHandleCancelID;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(&AsyncWaitCallback, nullptr, callback_data,
+ MOJO_RESULT_INVALID_ARGUMENT));
+ }
+ base::android::ScopedJavaLocalRef<jobject> cancellable =
+ Java_CoreImpl_newAsyncWaiterCancellableImpl(
+ env, jcaller, cancel_id, reinterpret_cast<intptr_t>(callback_data));
+ callback_data->cancellable.Reset(env, cancellable.obj());
+ return cancellable;
+}
+
+static void CancelAsyncWait(JNIEnv* env,
+ const JavaParamRef<jobject>& jcaller,
+ jlong id,
+ jlong data_ptr) {
+ if (id == 0) {
+ // If |id| is |kInvalidHandleCancelID|, the async wait was done on an
+ // invalid handle, so the AsyncWaitCallback will be called and will clear
+ // the data_ptr.
+ return;
+ }
+ std::unique_ptr<AsyncWaitCallbackData> deleter(
+ reinterpret_cast<AsyncWaitCallbackData*>(data_ptr));
+ delete reinterpret_cast<common::HandleWatcher*>(
+ static_cast<MojoAsyncWaitID>(id));
+}
+
static jint GetNativeBufferOffset(JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
const JavaParamRef<jobject>& buffer,
diff --git a/mojo/android/system/src/org/chromium/mojo/system/impl/BaseRunLoop.java b/mojo/android/system/src/org/chromium/mojo/system/impl/BaseRunLoop.java
index 3db6670..dfe92ef 100644
--- a/mojo/android/system/src/org/chromium/mojo/system/impl/BaseRunLoop.java
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/BaseRunLoop.java
@@ -27,13 +27,13 @@
@Override
public void run() {
assert mRunLoopID != 0 : "The run loop cannot run once closed";
- nativeRun();
+ nativeRun(mRunLoopID);
}
@Override
public void runUntilIdle() {
assert mRunLoopID != 0 : "The run loop cannot run once closed";
- nativeRunUntilIdle();
+ nativeRunUntilIdle(mRunLoopID);
}
@Override
@@ -66,8 +66,8 @@
}
private native long nativeCreateBaseRunLoop();
- private native void nativeRun();
- private native void nativeRunUntilIdle();
+ private native void nativeRun(long runLoopID);
+ private native void nativeRunUntilIdle(long runLoopID);
private native void nativeQuit(long runLoopID);
private native void nativePostDelayedTask(long runLoopID, Runnable runnable, long delay);
private native void nativeDeleteMessageLoop(long runLoopID);
diff --git a/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java b/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java
index 8330586..a20ea0a 100644
--- a/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java
+++ b/mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java
@@ -7,6 +7,7 @@
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.MainDex;
+import org.chromium.mojo.system.AsyncWaiter;
import org.chromium.mojo.system.Core;
import org.chromium.mojo.system.DataPipe;
import org.chromium.mojo.system.DataPipe.ConsumerHandle;
@@ -22,7 +23,6 @@
import org.chromium.mojo.system.SharedBufferHandle.DuplicateOptions;
import org.chromium.mojo.system.SharedBufferHandle.MapFlags;
import org.chromium.mojo.system.UntypedHandle;
-import org.chromium.mojo.system.Watcher;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -35,7 +35,7 @@
*/
@JNINamespace("mojo::android")
@MainDex
-public class CoreImpl implements Core {
+public class CoreImpl implements Core, AsyncWaiter {
/**
* Discard flag for the |MojoReadData| operation.
*/
@@ -216,11 +216,11 @@
}
/**
- * @see Core#getWatcher()
+ * @see Core#getDefaultAsyncWaiter()
*/
@Override
- public Watcher getWatcher() {
- return new WatcherImpl();
+ public AsyncWaiter getDefaultAsyncWaiter() {
+ return this;
}
/**
@@ -251,6 +251,15 @@
mCurrentRunLoop.remove();
}
+ /**
+ * @see AsyncWaiter#asyncWait(Handle, Core.HandleSignals, long, Callback)
+ */
+ @Override
+ public Cancellable asyncWait(
+ Handle handle, HandleSignals signals, long deadline, Callback callback) {
+ return nativeAsyncWait(getMojoHandle(handle), signals.getFlags(), deadline, callback);
+ }
+
int closeWithResult(int mojoHandle) {
return nativeClose(mojoHandle);
}
@@ -488,6 +497,59 @@
return buffer.order(ByteOrder.nativeOrder());
}
+ /**
+ * Implementation of {@link org.chromium.mojo.system.AsyncWaiter.Cancellable}.
+ */
+ private class AsyncWaiterCancellableImpl implements AsyncWaiter.Cancellable {
+ private final long mId;
+ private final long mDataPtr;
+ private boolean mActive = true;
+
+ private AsyncWaiterCancellableImpl(long id, long dataPtr) {
+ this.mId = id;
+ this.mDataPtr = dataPtr;
+ }
+
+ /**
+ * @see org.chromium.mojo.system.AsyncWaiter.Cancellable#cancel()
+ */
+ @Override
+ public void cancel() {
+ if (mActive) {
+ mActive = false;
+ nativeCancelAsyncWait(mId, mDataPtr);
+ }
+ }
+
+ private boolean isActive() {
+ return mActive;
+ }
+
+ private void deactivate() {
+ mActive = false;
+ }
+ }
+
+ @CalledByNative
+ private AsyncWaiterCancellableImpl newAsyncWaiterCancellableImpl(long id, long dataPtr) {
+ return new AsyncWaiterCancellableImpl(id, dataPtr);
+ }
+
+ @CalledByNative
+ private void onAsyncWaitResult(
+ int mojoResult, AsyncWaiter.Callback callback, AsyncWaiterCancellableImpl cancellable) {
+ if (!cancellable.isActive()) {
+ // If cancellable is not active, the user cancelled the wait.
+ return;
+ }
+ cancellable.deactivate();
+ if (isUnrecoverableError(mojoResult)) {
+ callback.onError(new MojoException(mojoResult));
+ return;
+ }
+ callback.onResult(mojoResult);
+ }
+
@CalledByNative
private static ResultAnd<ByteBuffer> newResultAndBuffer(int mojoResult, ByteBuffer buffer) {
return new ResultAnd<>(mojoResult, buffer);
@@ -567,5 +629,10 @@
private native int nativeUnmap(ByteBuffer buffer);
+ private native AsyncWaiterCancellableImpl nativeAsyncWait(
+ int mojoHandle, int signals, long deadline, AsyncWaiter.Callback callback);
+
+ private native void nativeCancelAsyncWait(long mId, long dataPtr);
+
private native int nativeGetNativeBufferOffset(ByteBuffer buffer, int alignment);
}
diff --git a/mojo/android/system/src/org/chromium/mojo/system/impl/WatcherImpl.java b/mojo/android/system/src/org/chromium/mojo/system/impl/WatcherImpl.java
deleted file mode 100644
index 094ad90..0000000
--- a/mojo/android/system/src/org/chromium/mojo/system/impl/WatcherImpl.java
+++ /dev/null
@@ -1,63 +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.
-
-package org.chromium.mojo.system.impl;
-
-import org.chromium.base.annotations.CalledByNative;
-import org.chromium.base.annotations.JNINamespace;
-import org.chromium.mojo.system.Core;
-import org.chromium.mojo.system.Handle;
-import org.chromium.mojo.system.MojoResult;
-import org.chromium.mojo.system.Watcher;
-
-@JNINamespace("mojo::android")
-class WatcherImpl implements Watcher {
- private long mImplPtr = nativeCreateWatcher();
- private Callback mCallback;
-
- @Override
- public int start(Handle handle, Core.HandleSignals signals, Callback callback) {
- if (mImplPtr == 0) {
- return MojoResult.INVALID_ARGUMENT;
- }
- if (!(handle instanceof HandleBase)) {
- return MojoResult.INVALID_ARGUMENT;
- }
- int result =
- nativeStart(mImplPtr, ((HandleBase) handle).getMojoHandle(), signals.getFlags());
- if (result == MojoResult.OK) mCallback = callback;
- return result;
- }
-
- @Override
- public void cancel() {
- if (mImplPtr == 0) {
- return;
- }
- mCallback = null;
- nativeCancel(mImplPtr);
- }
-
- @Override
- public void destroy() {
- if (mImplPtr == 0) {
- return;
- }
- nativeDelete(mImplPtr);
- mImplPtr = 0;
- }
-
- @CalledByNative
- private void onHandleReady(int result) {
- mCallback.onResult(result);
- }
-
- private native long nativeCreateWatcher();
-
- private native int nativeStart(long implPtr, int mojoHandle, int flags);
-
- private native void nativeCancel(long implPtr);
-
- private native void nativeDelete(long implPtr);
-}
diff --git a/mojo/common/BUILD.gn b/mojo/common/BUILD.gn
index 9e74e58..57c85d5 100644
--- a/mojo/common/BUILD.gn
+++ b/mojo/common/BUILD.gn
@@ -12,27 +12,28 @@
]
}
+# GYP version: mojo/mojo_base.gyp:mojo_common_custom_types
mojom("common_custom_types") {
sources = [
- "file.mojom",
- "file_path.mojom",
+ "common_custom_types.mojom",
"string16.mojom",
- "text_direction.mojom",
- "time.mojom",
- "unguessable_token.mojom",
- "values.mojom",
- "version.mojom",
]
}
+# GYP version: mojo/mojo_base.gyp:mojo_common_lib
component("common_base") {
output_name = "mojo_common_lib"
sources = [
+ "common_type_converters.cc",
+ "common_type_converters.h",
"data_pipe_drainer.cc",
"data_pipe_drainer.h",
+ "data_pipe_file_utils.cc",
"data_pipe_utils.cc",
"data_pipe_utils.h",
+ "user_agent.cc",
+ "user_agent.h",
]
defines = [ "MOJO_COMMON_IMPLEMENTATION" ]
@@ -44,21 +45,21 @@
]
}
+# GYP version: mojo/mojo_base.gyp:mojo_test_common_custom_types
mojom("test_common_custom_types") {
sources = [
"test_common_custom_types.mojom",
- "traits_test_service.mojom",
]
public_deps = [
":common_custom_types",
]
}
+# GYP version: mojo/mojo_base.gyp:mojo_common_unittests
test("mojo_common_unittests") {
deps = [
":common",
":common_custom_types",
- ":struct_traits",
":test_common_custom_types",
"//base",
"//base:message_loop_tests",
@@ -73,21 +74,16 @@
sources = [
"common_custom_types_unittest.cc",
- "struct_traits_unittest.cc",
+ "common_type_converters_unittest.cc",
]
}
-source_set("struct_traits") {
- sources = [
- "common_custom_types_struct_traits.cc",
- "common_custom_types_struct_traits.h",
- ]
+test("mojo_common_perftests") {
deps = [
- ":common_custom_types_shared_cpp_sources",
- "//base:base",
- "//mojo/public/cpp/system",
- ]
- public_deps = [
- "//base:i18n",
+ ":common",
+ "//base",
+ "//mojo/edk/test:run_all_perftests",
+ "//mojo/public/cpp/test_support:test_utils",
+ "//testing/gtest",
]
}
diff --git a/mojo/common/DEPS b/mojo/common/DEPS
index e8ac428..588c68d 100644
--- a/mojo/common/DEPS
+++ b/mojo/common/DEPS
@@ -1,6 +1,16 @@
include_rules = [
# common must not depend on embedder.
"-mojo",
+ "+services/shell/public/cpp",
"+mojo/common",
"+mojo/public",
]
+
+specific_include_rules = {
+ "trace_controller_impl\.h": [
+ "+services/tracing/public/interfaces/tracing.mojom.h"
+ ],
+ "tracing_impl\.h": [
+ "+services/tracing/public/interfaces/tracing.mojom.h"
+ ],
+}
diff --git a/mojo/common/time.mojom b/mojo/common/common_custom_types.mojom
similarity index 67%
rename from mojo/common/time.mojom
rename to mojo/common/common_custom_types.mojom
index 43dfcc8..aa87106 100644
--- a/mojo/common/time.mojom
+++ b/mojo/common/common_custom_types.mojom
@@ -5,11 +5,19 @@
module mojo.common.mojom;
[Native]
+struct FilePath;
+
+[Native]
+struct ListValue;
+
+[Native]
+struct DictionaryValue;
+
+[Native]
struct Time;
-struct TimeDelta {
- int64 microseconds;
-};
+[Native]
+struct TimeDelta;
[Native]
struct TimeTicks;
diff --git a/mojo/common/common_custom_types.typemap b/mojo/common/common_custom_types.typemap
new file mode 100644
index 0000000..0b63f38
--- /dev/null
+++ b/mojo/common/common_custom_types.typemap
@@ -0,0 +1,24 @@
+# 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/common_custom_types.mojom"
+public_headers = [
+ "//base/files/file_path.h",
+ "//base/values.h",
+ "//base/time/time.h",
+]
+traits_headers = [ "//ipc/ipc_message_utils.h" ]
+public_deps = [
+ "//ipc",
+]
+
+type_mappings = [
+ "mojo.common.mojom.FilePath=base::FilePath",
+ "mojo.common.mojom.DictionaryValue=base::DictionaryValue",
+ "mojo.common.mojom.ListValue=base::ListValue",
+ "mojo.common.mojom.String16=base::string16",
+ "mojo.common.mojom.Time=base::Time",
+ "mojo.common.mojom.TimeDelta=base::TimeDelta",
+ "mojo.common.mojom.TimeTicks=base::TimeTicks",
+]
diff --git a/mojo/common/common_custom_types_struct_traits.cc b/mojo/common/common_custom_types_struct_traits.cc
index 6289504..3f9f7d9 100644
--- a/mojo/common/common_custom_types_struct_traits.cc
+++ b/mojo/common/common_custom_types_struct_traits.cc
@@ -1,108 +1,18 @@
-// 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/common/common_custom_types_struct_traits.h"
-#include "mojo/public/cpp/system/platform_handle.h"
-
namespace mojo {
// static
-bool StructTraits<common::mojom::String16DataView, base::string16>::Read(
- common::mojom::String16DataView data,
- base::string16* out) {
- ArrayDataView<uint16_t> view;
- data.GetDataDataView(&view);
+bool StructTraits<common::mojom::String16, base::string16>::Read(
+ common::mojom::String16DataView data, base::string16* out) {
+ std::vector<uint16_t> view;
+ data.ReadData(&view);
out->assign(reinterpret_cast<const base::char16*>(view.data()), view.size());
return true;
}
-// static
-const std::vector<uint32_t>&
-StructTraits<common::mojom::VersionDataView, base::Version>::components(
- const base::Version& version) {
- return version.components();
-}
-
-// static
-bool StructTraits<common::mojom::VersionDataView, base::Version>::Read(
- common::mojom::VersionDataView data,
- base::Version* out) {
- std::vector<uint32_t> components;
- if (!data.ReadComponents(&components))
- return false;
-
- *out = base::Version(base::Version(std::move(components)));
- return out->IsValid();
-}
-
-// static
-bool StructTraits<
- common::mojom::UnguessableTokenDataView,
- base::UnguessableToken>::Read(common::mojom::UnguessableTokenDataView data,
- base::UnguessableToken* out) {
- uint64_t high = data.high();
- uint64_t low = data.low();
-
- // Receiving a zeroed UnguessableToken is a security issue.
- if (high == 0 && low == 0)
- return false;
-
- *out = base::UnguessableToken::Deserialize(high, low);
- return true;
-}
-
-mojo::ScopedHandle StructTraits<common::mojom::FileDataView, base::File>::fd(
- base::File& file) {
- DCHECK(file.IsValid());
- return mojo::WrapPlatformFile(file.TakePlatformFile());
-}
-
-bool StructTraits<common::mojom::FileDataView, base::File>::Read(
- common::mojom::FileDataView data,
- base::File* file) {
- base::PlatformFile platform_handle = base::kInvalidPlatformFile;
- if (mojo::UnwrapPlatformFile(data.TakeFd(), &platform_handle) !=
- MOJO_RESULT_OK) {
- return false;
- }
- *file = base::File(platform_handle);
- return true;
-}
-
-// static
-common::mojom::TextDirection
-EnumTraits<common::mojom::TextDirection, base::i18n::TextDirection>::ToMojom(
- base::i18n::TextDirection text_direction) {
- switch (text_direction) {
- case base::i18n::UNKNOWN_DIRECTION:
- return common::mojom::TextDirection::UNKNOWN_DIRECTION;
- case base::i18n::RIGHT_TO_LEFT:
- return common::mojom::TextDirection::RIGHT_TO_LEFT;
- case base::i18n::LEFT_TO_RIGHT:
- return common::mojom::TextDirection::LEFT_TO_RIGHT;
- }
- NOTREACHED();
- return common::mojom::TextDirection::UNKNOWN_DIRECTION;
-}
-
-// static
-bool EnumTraits<common::mojom::TextDirection, base::i18n::TextDirection>::
- FromMojom(common::mojom::TextDirection input,
- base::i18n::TextDirection* out) {
- switch (input) {
- case common::mojom::TextDirection::UNKNOWN_DIRECTION:
- *out = base::i18n::UNKNOWN_DIRECTION;
- return true;
- case common::mojom::TextDirection::RIGHT_TO_LEFT:
- *out = base::i18n::RIGHT_TO_LEFT;
- return true;
- case common::mojom::TextDirection::LEFT_TO_RIGHT:
- *out = base::i18n::LEFT_TO_RIGHT;
- return true;
- }
- return false;
-}
-
-} // namespace mojo
+} // mojo
diff --git a/mojo/common/common_custom_types_struct_traits.h b/mojo/common/common_custom_types_struct_traits.h
index b20c795..22afdb6 100644
--- a/mojo/common/common_custom_types_struct_traits.h
+++ b/mojo/common/common_custom_types_struct_traits.h
@@ -1,98 +1,27 @@
-// 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.
#ifndef MOJO_COMMON_COMMON_CUSTOM_TYPES_STRUCT_TRAITS_H_
#define MOJO_COMMON_COMMON_CUSTOM_TYPES_STRUCT_TRAITS_H_
-#include "base/files/file.h"
-#include "base/i18n/rtl.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/unguessable_token.h"
-#include "base/version.h"
-#include "mojo/common/file.mojom-shared.h"
-#include "mojo/common/mojo_common_export.h"
-#include "mojo/common/string16.mojom-shared.h"
-#include "mojo/common/text_direction.mojom-shared.h"
-#include "mojo/common/time.mojom-shared.h"
-#include "mojo/common/unguessable_token.mojom-shared.h"
-#include "mojo/common/version.mojom-shared.h"
+#include <stdint.h>
+#include <vector>
+
+#include "mojo/common/string16.mojom.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
namespace mojo {
template <>
-struct StructTraits<common::mojom::String16DataView, base::string16> {
- static ConstCArray<uint16_t> data(const base::string16& str) {
- return ConstCArray<uint16_t>(str.size(),
- reinterpret_cast<const uint16_t*>(str.data()));
+struct StructTraits<common::mojom::String16, base::string16> {
+ static std::vector<uint16_t> data(const base::string16& str) {
+ const uint16_t* base = str.data();
+ return std::vector<uint16_t>(base, base + str.size());
}
-
- static bool Read(common::mojom::String16DataView data, base::string16* out);
+ static bool Read(common::mojom::String16DataView data, base::string16* output);
};
-template <>
-struct StructTraits<common::mojom::VersionDataView, base::Version> {
- static bool IsNull(const base::Version& version) {
- return !version.IsValid();
- }
- static void SetToNull(base::Version* out) {
- *out = base::Version(std::string());
- }
- static const std::vector<uint32_t>& components(const base::Version& version);
- static bool Read(common::mojom::VersionDataView data, base::Version* out);
-};
-
-// If base::UnguessableToken is no longer 128 bits, the logic below and the
-// mojom::UnguessableToken type should be updated.
-static_assert(sizeof(base::UnguessableToken) == 2 * sizeof(uint64_t),
- "base::UnguessableToken should be of size 2 * sizeof(uint64_t).");
-
-template <>
-struct StructTraits<common::mojom::UnguessableTokenDataView,
- base::UnguessableToken> {
- static uint64_t high(const base::UnguessableToken& token) {
- return token.GetHighForSerialization();
- }
-
- static uint64_t low(const base::UnguessableToken& token) {
- return token.GetLowForSerialization();
- }
-
- static bool Read(common::mojom::UnguessableTokenDataView data,
- base::UnguessableToken* out);
-};
-
-template <>
-struct StructTraits<common::mojom::TimeDeltaDataView, base::TimeDelta> {
- static int64_t microseconds(const base::TimeDelta& delta) {
- return delta.InMicroseconds();
- }
-
- static bool Read(common::mojom::TimeDeltaDataView data,
- base::TimeDelta* delta) {
- *delta = base::TimeDelta::FromMicroseconds(data.microseconds());
- return true;
- }
-};
-
-template <>
-struct StructTraits<common::mojom::FileDataView, base::File> {
- static bool IsNull(const base::File& file) { return !file.IsValid(); }
-
- static void SetToNull(base::File* file) { *file = base::File(); }
-
- static mojo::ScopedHandle fd(base::File& file);
- static bool Read(common::mojom::FileDataView data, base::File* file);
-};
-
-template <>
-struct EnumTraits<common::mojom::TextDirection, base::i18n::TextDirection> {
- static common::mojom::TextDirection ToMojom(
- base::i18n::TextDirection text_direction);
- static bool FromMojom(common::mojom::TextDirection input,
- base::i18n::TextDirection* out);
-};
-
-} // namespace mojo
+} // mojo
#endif // MOJO_COMMON_COMMON_CUSTOM_TYPES_STRUCT_TRAITS_H_
diff --git a/mojo/common/common_custom_types_unittest.cc b/mojo/common/common_custom_types_unittest.cc
index e3571d9..fe6bb5d 100644
--- a/mojo/common/common_custom_types_unittest.cc
+++ b/mojo/common/common_custom_types_unittest.cc
@@ -3,13 +3,10 @@
// found in the LICENSE file.
#include "base/files/file_path.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
-#include "base/numerics/safe_math.h"
#include "base/run_loop.h"
-#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
+#include "mojo/common/common_custom_types.mojom.h"
#include "mojo/common/test_common_custom_types.mojom.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -26,38 +23,33 @@
}
};
-template <typename T>
-struct PassTraits {
- using Type = const T&;
+template <>
+struct BounceTestTraits<base::DictionaryValue> {
+ static void ExpectEquality(const base::DictionaryValue& a,
+ const base::DictionaryValue& b) {
+ EXPECT_TRUE(a.Equals(&b));
+ }
};
template <>
-struct PassTraits<base::Time> {
- using Type = base::Time;
-};
-
-template <>
-struct PassTraits<base::TimeDelta> {
- using Type = base::TimeDelta;
-};
-
-template <>
-struct PassTraits<base::TimeTicks> {
- using Type = base::TimeTicks;
+struct BounceTestTraits<base::ListValue> {
+ static void ExpectEquality(const base::ListValue& a,
+ const base::ListValue& b) {
+ EXPECT_TRUE(a.Equals(&b));
+ }
};
template <typename T>
void DoExpectResponse(T* expected_value,
const base::Closure& closure,
- typename PassTraits<T>::Type value) {
+ const T& value) {
BounceTestTraits<T>::ExpectEquality(*expected_value, value);
closure.Run();
}
template <typename T>
-base::Callback<void(typename PassTraits<T>::Type)> ExpectResponse(
- T* expected_value,
- const base::Closure& closure) {
+base::Callback<void(const T&)> ExpectResponse(T* expected_value,
+ const base::Closure& closure) {
return base::Bind(&DoExpectResponse<T>, expected_value, closure);
}
@@ -76,38 +68,24 @@
mojo::Binding<TestFilePath> binding_;
};
-class TestUnguessableTokenImpl : public TestUnguessableToken {
- public:
- explicit TestUnguessableTokenImpl(TestUnguessableTokenRequest request)
- : binding_(this, std::move(request)) {}
-
- // TestUnguessableToken implementation:
- void BounceNonce(const base::UnguessableToken& in,
- const BounceNonceCallback& callback) override {
- callback.Run(in);
- }
-
- private:
- mojo::Binding<TestUnguessableToken> binding_;
-};
-
class TestTimeImpl : public TestTime {
public:
explicit TestTimeImpl(TestTimeRequest request)
: binding_(this, std::move(request)) {}
// TestTime implementation:
- void BounceTime(base::Time in, const BounceTimeCallback& callback) override {
+ void BounceTime(const base::Time& in,
+ const BounceTimeCallback& callback) override {
callback.Run(in);
}
- void BounceTimeDelta(base::TimeDelta in,
- const BounceTimeDeltaCallback& callback) override {
+ void BounceTimeDelta(const base::TimeDelta& in,
+ const BounceTimeDeltaCallback& callback) override {
callback.Run(in);
}
- void BounceTimeTicks(base::TimeTicks in,
- const BounceTimeTicksCallback& callback) override {
+ void BounceTimeTicks(const base::TimeTicks& in,
+ const BounceTimeTicksCallback& callback) override {
callback.Run(in);
}
@@ -122,70 +100,19 @@
// TestValue implementation:
void BounceDictionaryValue(
- std::unique_ptr<base::DictionaryValue> in,
+ const base::DictionaryValue& in,
const BounceDictionaryValueCallback& callback) override {
- callback.Run(std::move(in));
+ callback.Run(in);
}
-
- void BounceListValue(std::unique_ptr<base::ListValue> in,
+ void BounceListValue(const base::ListValue& in,
const BounceListValueCallback& callback) override {
- callback.Run(std::move(in));
- }
-
- void BounceValue(std::unique_ptr<base::Value> in,
- const BounceValueCallback& callback) override {
- callback.Run(std::move(in));
+ callback.Run(in);
}
private:
mojo::Binding<TestValue> binding_;
};
-class TestString16Impl : public TestString16 {
- public:
- explicit TestString16Impl(TestString16Request request)
- : binding_(this, std::move(request)) {}
-
- // TestString16 implementation:
- void BounceString16(const base::string16& in,
- const BounceString16Callback& callback) override {
- callback.Run(in);
- }
-
- private:
- mojo::Binding<TestString16> binding_;
-};
-
-class TestFileImpl : public TestFile {
- public:
- explicit TestFileImpl(TestFileRequest request)
- : binding_(this, std::move(request)) {}
-
- // TestFile implementation:
- void BounceFile(base::File in, const BounceFileCallback& callback) override {
- callback.Run(std::move(in));
- }
-
- private:
- mojo::Binding<TestFile> binding_;
-};
-
-class TestTextDirectionImpl : public TestTextDirection {
- public:
- explicit TestTextDirectionImpl(TestTextDirectionRequest request)
- : binding_(this, std::move(request)) {}
-
- // TestTextDirection:
- void BounceTextDirection(
- base::i18n::TextDirection in,
- const BounceTextDirectionCallback& callback) override {
- callback.Run(in);
- }
-
- private:
- mojo::Binding<TestTextDirection> binding_;
-};
-
class CommonCustomTypesTest : public testing::Test {
protected:
CommonCustomTypesTest() {}
@@ -203,7 +130,7 @@
base::RunLoop run_loop;
TestFilePathPtr ptr;
- TestFilePathImpl impl(MakeRequest(&ptr));
+ TestFilePathImpl impl(GetProxy(&ptr));
base::FilePath dir(FILE_PATH_LITERAL("hello"));
base::FilePath file = dir.Append(FILE_PATH_LITERAL("world"));
@@ -213,24 +140,11 @@
run_loop.Run();
}
-TEST_F(CommonCustomTypesTest, UnguessableToken) {
- base::RunLoop run_loop;
-
- TestUnguessableTokenPtr ptr;
- TestUnguessableTokenImpl impl(MakeRequest(&ptr));
-
- base::UnguessableToken token = base::UnguessableToken::Create();
-
- ptr->BounceNonce(token, ExpectResponse(&token, run_loop.QuitClosure()));
-
- run_loop.Run();
-}
-
TEST_F(CommonCustomTypesTest, Time) {
base::RunLoop run_loop;
TestTimePtr ptr;
- TestTimeImpl impl(MakeRequest(&ptr));
+ TestTimeImpl impl(GetProxy(&ptr));
base::Time t = base::Time::Now();
@@ -243,7 +157,7 @@
base::RunLoop run_loop;
TestTimePtr ptr;
- TestTimeImpl impl(MakeRequest(&ptr));
+ TestTimeImpl impl(GetProxy(&ptr));
base::TimeDelta t = base::TimeDelta::FromDays(123);
@@ -256,7 +170,7 @@
base::RunLoop run_loop;
TestTimePtr ptr;
- TestTimeImpl impl(MakeRequest(&ptr));
+ TestTimeImpl impl(GetProxy(&ptr));
base::TimeTicks t = base::TimeTicks::Now();
@@ -267,164 +181,43 @@
TEST_F(CommonCustomTypesTest, Value) {
TestValuePtr ptr;
- TestValueImpl impl(MakeRequest(&ptr));
+ TestValueImpl impl(GetProxy(&ptr));
- std::unique_ptr<base::Value> output;
-
- ASSERT_TRUE(ptr->BounceValue(nullptr, &output));
- EXPECT_FALSE(output);
-
- std::unique_ptr<base::Value> input = base::Value::CreateNullValue();
- ASSERT_TRUE(ptr->BounceValue(input->CreateDeepCopy(), &output));
- EXPECT_TRUE(base::Value::Equals(input.get(), output.get()));
-
- input = base::MakeUnique<base::Value>(123);
- ASSERT_TRUE(ptr->BounceValue(input->CreateDeepCopy(), &output));
- EXPECT_TRUE(base::Value::Equals(input.get(), output.get()));
-
- input = base::MakeUnique<base::Value>(1.23);
- ASSERT_TRUE(ptr->BounceValue(input->CreateDeepCopy(), &output));
- EXPECT_TRUE(base::Value::Equals(input.get(), output.get()));
-
- input = base::MakeUnique<base::Value>(false);
- ASSERT_TRUE(ptr->BounceValue(input->CreateDeepCopy(), &output));
- EXPECT_TRUE(base::Value::Equals(input.get(), output.get()));
-
- input = base::MakeUnique<base::Value>("test string");
- ASSERT_TRUE(ptr->BounceValue(input->CreateDeepCopy(), &output));
- EXPECT_TRUE(base::Value::Equals(input.get(), output.get()));
-
- input = base::BinaryValue::CreateWithCopiedBuffer("mojo", 4);
- ASSERT_TRUE(ptr->BounceValue(input->CreateDeepCopy(), &output));
- EXPECT_TRUE(base::Value::Equals(input.get(), output.get()));
-
- auto dict = base::MakeUnique<base::DictionaryValue>();
- dict->SetBoolean("bool", false);
- dict->SetInteger("int", 2);
- dict->SetString("string", "some string");
- dict->SetBoolean("nested.bool", true);
- dict->SetInteger("nested.int", 9);
- dict->Set("some_binary",
- base::BinaryValue::CreateWithCopiedBuffer("mojo", 4));
- dict->Set("null_value", base::Value::CreateNullValue());
- dict->SetIntegerWithoutPathExpansion("non_nested.int", 10);
+ base::DictionaryValue dict;
+ dict.SetBoolean("bool", false);
+ dict.SetInteger("int", 2);
+ dict.SetString("string", "some string");
+ dict.SetBoolean("nested.bool", true);
+ dict.SetInteger("nested.int", 9);
+ dict.Set("some_binary", base::BinaryValue::CreateWithCopiedBuffer("mojo", 4));
{
std::unique_ptr<base::ListValue> dict_list(new base::ListValue());
dict_list->AppendString("string");
dict_list->AppendBoolean(true);
- dict->Set("list", std::move(dict_list));
+ dict.Set("list", std::move(dict_list));
+ }
+ {
+ base::RunLoop run_loop;
+ ptr->BounceDictionaryValue(
+ dict, ExpectResponse(&dict, run_loop.QuitClosure()));
+ run_loop.Run();
}
- std::unique_ptr<base::DictionaryValue> dict_output;
- ASSERT_TRUE(ptr->BounceDictionaryValue(dict->CreateDeepCopy(), &dict_output));
- EXPECT_TRUE(base::Value::Equals(dict.get(), dict_output.get()));
-
- input = std::move(dict);
- ASSERT_TRUE(ptr->BounceValue(input->CreateDeepCopy(), &output));
- EXPECT_TRUE(base::Value::Equals(input.get(), output.get()));
-
- auto list = base::MakeUnique<base::ListValue>();
- list->AppendString("string");
- list->AppendDouble(42.1);
- list->AppendBoolean(true);
- list->Append(base::BinaryValue::CreateWithCopiedBuffer("mojo", 4));
- list->Append(base::Value::CreateNullValue());
+ base::ListValue list;
+ list.AppendString("string");
+ list.AppendDouble(42.1);
+ list.AppendBoolean(true);
+ list.Append(base::BinaryValue::CreateWithCopiedBuffer("mojo", 4));
{
std::unique_ptr<base::DictionaryValue> list_dict(
new base::DictionaryValue());
list_dict->SetString("string", "str");
- list->Append(std::move(list_dict));
+ list.Append(std::move(list_dict));
}
- std::unique_ptr<base::ListValue> list_output;
- ASSERT_TRUE(ptr->BounceListValue(list->CreateDeepCopy(), &list_output));
- EXPECT_TRUE(base::Value::Equals(list.get(), list_output.get()));
-
- input = std::move(list);
- ASSERT_TRUE(ptr->BounceValue(input->CreateDeepCopy(), &output));
- ASSERT_TRUE(base::Value::Equals(input.get(), output.get()));
-}
-
-TEST_F(CommonCustomTypesTest, String16) {
- base::RunLoop run_loop;
-
- TestString16Ptr ptr;
- TestString16Impl impl(MakeRequest(&ptr));
-
- base::string16 str16 = base::ASCIIToUTF16("hello world");
-
- ptr->BounceString16(str16, ExpectResponse(&str16, run_loop.QuitClosure()));
-
- run_loop.Run();
-}
-
-TEST_F(CommonCustomTypesTest, EmptyString16) {
- base::RunLoop run_loop;
-
- TestString16Ptr ptr;
- TestString16Impl impl(MakeRequest(&ptr));
-
- base::string16 str16;
-
- ptr->BounceString16(str16, ExpectResponse(&str16, run_loop.QuitClosure()));
-
- run_loop.Run();
-}
-
-TEST_F(CommonCustomTypesTest, File) {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-
- TestFilePtr ptr;
- TestFileImpl impl(MakeRequest(&ptr));
-
- base::File file(
- temp_dir.GetPath().AppendASCII("test_file.txt"),
- base::File::FLAG_CREATE | base::File::FLAG_WRITE | base::File::FLAG_READ);
- const base::StringPiece test_content =
- "A test string to be stored in a test file";
- file.WriteAtCurrentPos(
- test_content.data(),
- base::CheckedNumeric<int>(test_content.size()).ValueOrDie());
-
- base::File file_out;
- ASSERT_TRUE(ptr->BounceFile(std::move(file), &file_out));
- std::vector<char> content(test_content.size());
- ASSERT_TRUE(file_out.IsValid());
- ASSERT_EQ(static_cast<int>(test_content.size()),
- file_out.Read(
- 0, content.data(),
- base::CheckedNumeric<int>(test_content.size()).ValueOrDie()));
- EXPECT_EQ(test_content,
- base::StringPiece(content.data(), test_content.size()));
-}
-
-TEST_F(CommonCustomTypesTest, InvalidFile) {
- TestFilePtr ptr;
- TestFileImpl impl(MakeRequest(&ptr));
-
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- // Test that |file_out| is set to an invalid file.
- base::File file_out(
- temp_dir.GetPath().AppendASCII("test_file.txt"),
- base::File::FLAG_CREATE | base::File::FLAG_WRITE | base::File::FLAG_READ);
-
- ASSERT_TRUE(ptr->BounceFile(base::File(), &file_out));
- EXPECT_FALSE(file_out.IsValid());
-}
-
-TEST_F(CommonCustomTypesTest, TextDirection) {
- base::i18n::TextDirection kTestDirections[] = {base::i18n::LEFT_TO_RIGHT,
- base::i18n::RIGHT_TO_LEFT,
- base::i18n::UNKNOWN_DIRECTION};
-
- TestTextDirectionPtr ptr;
- TestTextDirectionImpl impl(MakeRequest(&ptr));
-
- for (size_t i = 0; i < arraysize(kTestDirections); i++) {
- base::i18n::TextDirection direction_out;
- ASSERT_TRUE(ptr->BounceTextDirection(kTestDirections[i], &direction_out));
- EXPECT_EQ(kTestDirections[i], direction_out);
+ {
+ base::RunLoop run_loop;
+ ptr->BounceListValue(list, ExpectResponse(&list, run_loop.QuitClosure()));
+ run_loop.Run();
}
}
diff --git a/mojo/common/common_type_converters.cc b/mojo/common/common_type_converters.cc
new file mode 100644
index 0000000..92ae3e2
--- /dev/null
+++ b/mojo/common/common_type_converters.cc
@@ -0,0 +1,84 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/common/common_type_converters.h"
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/strings/utf_string_conversions.h"
+
+namespace mojo {
+
+// static
+String TypeConverter<String, base::StringPiece>::Convert(
+ const base::StringPiece& input) {
+ if (input.empty()) {
+ char c = 0;
+ return String(&c, 0);
+ }
+ return String(input.data(), input.size());
+}
+// static
+base::StringPiece TypeConverter<base::StringPiece, String>::Convert(
+ const String& input) {
+ return input.get();
+}
+
+// static
+String TypeConverter<String, base::string16>::Convert(
+ const base::string16& input) {
+ return TypeConverter<String, base::StringPiece>::Convert(
+ base::UTF16ToUTF8(input));
+}
+// static
+base::string16 TypeConverter<base::string16, String>::Convert(
+ const String& input) {
+ return base::UTF8ToUTF16(input.To<base::StringPiece>());
+}
+
+std::string TypeConverter<std::string, Array<uint8_t>>::Convert(
+ const Array<uint8_t>& input) {
+ if (input.is_null() || input.empty())
+ return std::string();
+
+ return std::string(reinterpret_cast<const char*>(&input.front()),
+ input.size());
+}
+
+Array<uint8_t> TypeConverter<Array<uint8_t>, std::string>::Convert(
+ const std::string& input) {
+ Array<uint8_t> result(input.size());
+ if (!input.empty())
+ memcpy(&result.front(), input.c_str(), input.size());
+ return result;
+}
+
+Array<uint8_t> TypeConverter<Array<uint8_t>, base::StringPiece>::Convert(
+ const base::StringPiece& input) {
+ Array<uint8_t> result(input.size());
+ if (!input.empty())
+ memcpy(&result.front(), input.data(), input.size());
+ return result;
+}
+
+base::string16 TypeConverter<base::string16, Array<uint8_t>>::Convert(
+ const Array<uint8_t>& input) {
+ if (input.is_null() || input.empty())
+ return base::string16();
+
+ return base::string16(reinterpret_cast<const base::char16*>(&input.front()),
+ input.size() / sizeof(base::char16));
+}
+
+Array<uint8_t> TypeConverter<Array<uint8_t>, base::string16>::Convert(
+ const base::string16& input) {
+ Array<uint8_t> result(input.size() * sizeof(base::char16));
+ if (!input.empty())
+ memcpy(&result.front(), input.c_str(), input.size() * sizeof(base::char16));
+ return result;
+}
+
+} // namespace mojo
diff --git a/mojo/common/common_type_converters.h b/mojo/common/common_type_converters.h
new file mode 100644
index 0000000..a065f05
--- /dev/null
+++ b/mojo/common/common_type_converters.h
@@ -0,0 +1,66 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_COMMON_COMMON_TYPE_CONVERTERS_H_
+#define MOJO_COMMON_COMMON_TYPE_CONVERTERS_H_
+
+#include <stdint.h>
+
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "mojo/common/mojo_common_export.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+namespace mojo {
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<String, base::StringPiece> {
+ static String Convert(const base::StringPiece& input);
+};
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<base::StringPiece, String> {
+ static base::StringPiece Convert(const String& input);
+};
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<String, base::string16> {
+ static String Convert(const base::string16& input);
+};
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<base::string16, String> {
+ static base::string16 Convert(const String& input);
+};
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<std::string, Array<uint8_t>> {
+ static std::string Convert(const Array<uint8_t>& input);
+};
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<Array<uint8_t>, std::string> {
+ static Array<uint8_t> Convert(const std::string& input);
+};
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<Array<uint8_t>, base::StringPiece> {
+ static Array<uint8_t> Convert(const base::StringPiece& input);
+};
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<base::string16, Array<uint8_t>> {
+ static base::string16 Convert(const Array<uint8_t>& input);
+};
+
+template <>
+struct MOJO_COMMON_EXPORT TypeConverter<Array<uint8_t>, base::string16> {
+ static Array<uint8_t> Convert(const base::string16& input);
+};
+
+} // namespace mojo
+
+#endif // MOJO_COMMON_COMMON_TYPE_CONVERTERS_H_
diff --git a/mojo/common/common_type_converters_unittest.cc b/mojo/common/common_type_converters_unittest.cc
new file mode 100644
index 0000000..1740d06
--- /dev/null
+++ b/mojo/common/common_type_converters_unittest.cc
@@ -0,0 +1,130 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/common/common_type_converters.h"
+
+#include <stdint.h>
+
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace mojo {
+namespace common {
+namespace test {
+namespace {
+
+void ExpectEqualsStringPiece(const std::string& expected,
+ const base::StringPiece& str) {
+ EXPECT_EQ(expected, str.as_string());
+}
+
+void ExpectEqualsMojoString(const std::string& expected,
+ const String& str) {
+ EXPECT_EQ(expected, str.get());
+}
+
+void ExpectEqualsString16(const base::string16& expected,
+ const base::string16& actual) {
+ EXPECT_EQ(expected, actual);
+}
+
+void ExpectEqualsMojoString(const base::string16& expected,
+ const String& str) {
+ EXPECT_EQ(expected, str.To<base::string16>());
+}
+
+} // namespace
+
+TEST(CommonTypeConvertersTest, StringPiece) {
+ std::string kText("hello world");
+
+ base::StringPiece string_piece(kText);
+ String mojo_string(String::From(string_piece));
+
+ ExpectEqualsMojoString(kText, mojo_string);
+ ExpectEqualsStringPiece(kText, mojo_string.To<base::StringPiece>());
+
+ // Test implicit construction and conversion:
+ ExpectEqualsMojoString(kText, String::From(string_piece));
+ ExpectEqualsStringPiece(kText, mojo_string.To<base::StringPiece>());
+
+ // Test null String:
+ base::StringPiece empty_string_piece = String().To<base::StringPiece>();
+ EXPECT_TRUE(empty_string_piece.empty());
+}
+
+TEST(CommonTypeConvertersTest, String16) {
+ const base::string16 string16(base::ASCIIToUTF16("hello world"));
+ const String mojo_string(String::From(string16));
+
+ ExpectEqualsMojoString(string16, mojo_string);
+ EXPECT_EQ(string16, mojo_string.To<base::string16>());
+
+ // Test implicit construction and conversion:
+ ExpectEqualsMojoString(string16, String::From(string16));
+ ExpectEqualsString16(string16, mojo_string.To<base::string16>());
+
+ // Test empty string conversion.
+ ExpectEqualsMojoString(base::string16(), String::From(base::string16()));
+}
+
+TEST(CommonTypeConvertersTest, ArrayUint8ToStdString) {
+ Array<uint8_t> data(4);
+ data[0] = 'd';
+ data[1] = 'a';
+ data[2] = 't';
+ data[3] = 'a';
+
+ EXPECT_EQ("data", data.To<std::string>());
+}
+
+TEST(CommonTypeConvertersTest, StdStringToArrayUint8) {
+ std::string input("data");
+ Array<uint8_t> data = Array<uint8_t>::From(input);
+
+ ASSERT_EQ(4ul, data.size());
+ EXPECT_EQ('d', data[0]);
+ EXPECT_EQ('a', data[1]);
+ EXPECT_EQ('t', data[2]);
+ EXPECT_EQ('a', data[3]);
+}
+
+TEST(CommonTypeConvertersTest, ArrayUint8ToString16) {
+ Array<uint8_t> data(8);
+ data[0] = 'd';
+ data[2] = 'a';
+ data[4] = 't';
+ data[6] = 'a';
+
+ EXPECT_EQ(base::ASCIIToUTF16("data"), data.To<base::string16>());
+}
+
+TEST(CommonTypeConvertersTest, String16ToArrayUint8) {
+ base::string16 input(base::ASCIIToUTF16("data"));
+ Array<uint8_t> data = Array<uint8_t>::From(input);
+
+ ASSERT_EQ(8ul, data.size());
+ EXPECT_EQ('d', data[0]);
+ EXPECT_EQ('a', data[2]);
+ EXPECT_EQ('t', data[4]);
+ EXPECT_EQ('a', data[6]);
+}
+
+TEST(CommonTypeConvertersTest, String16ToArrayUint8AndBack) {
+ base::string16 input(base::ASCIIToUTF16("data"));
+ Array<uint8_t> data = Array<uint8_t>::From(input);
+ EXPECT_EQ(input, data.To<base::string16>());
+}
+
+TEST(CommonTypeConvertersTest, EmptyStringToArrayUint8) {
+ Array<uint8_t> data = Array<uint8_t>::From(std::string());
+
+ ASSERT_EQ(0ul, data.size());
+ EXPECT_FALSE(data.is_null());
+}
+
+} // namespace test
+} // namespace common
+} // namespace mojo
diff --git a/mojo/common/data_pipe_drainer.cc b/mojo/common/data_pipe_drainer.cc
index 27bd893..1133e11 100644
--- a/mojo/common/data_pipe_drainer.cc
+++ b/mojo/common/data_pipe_drainer.cc
@@ -15,10 +15,7 @@
DataPipeDrainer::DataPipeDrainer(Client* client,
mojo::ScopedDataPipeConsumerHandle source)
- : client_(client),
- source_(std::move(source)),
- handle_watcher_(FROM_HERE),
- weak_factory_(this) {
+ : client_(client), source_(std::move(source)), weak_factory_(this) {
DCHECK(client_);
handle_watcher_.Start(
source_.get(), MOJO_HANDLE_SIGNAL_READABLE,
diff --git a/mojo/common/data_pipe_file_utils.cc b/mojo/common/data_pipe_file_utils.cc
new file mode 100644
index 0000000..841dfde
--- /dev/null
+++ b/mojo/common/data_pipe_file_utils.cc
@@ -0,0 +1,81 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/common/data_pipe_utils.h"
+
+#include <stdint.h>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/location.h"
+#include "base/task_runner_util.h"
+
+namespace mojo {
+namespace common {
+namespace {
+
+bool BlockingCopyFromFile(const base::FilePath& source,
+ ScopedDataPipeProducerHandle destination,
+ uint32_t skip) {
+ base::File file(source, base::File::FLAG_OPEN | base::File::FLAG_READ);
+ if (!file.IsValid())
+ return false;
+ if (file.Seek(base::File::FROM_BEGIN, skip) != skip) {
+ LOG(ERROR) << "Seek of " << skip << " in " << source.value() << " failed";
+ return false;
+ }
+ for (;;) {
+ void* buffer = nullptr;
+ uint32_t buffer_num_bytes = 0;
+ MojoResult result =
+ BeginWriteDataRaw(destination.get(), &buffer, &buffer_num_bytes,
+ MOJO_WRITE_DATA_FLAG_NONE);
+ if (result == MOJO_RESULT_OK) {
+ int bytes_read =
+ file.ReadAtCurrentPos(static_cast<char*>(buffer), buffer_num_bytes);
+ if (bytes_read >= 0) {
+ EndWriteDataRaw(destination.get(), bytes_read);
+ if (bytes_read == 0) {
+ // eof
+ return true;
+ }
+ } else {
+ // error
+ EndWriteDataRaw(destination.get(), 0);
+ return false;
+ }
+ } else if (result == MOJO_RESULT_SHOULD_WAIT) {
+ result = Wait(destination.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
+ MOJO_DEADLINE_INDEFINITE, nullptr);
+ if (result != MOJO_RESULT_OK) {
+ // If the consumer handle was closed, then treat as EOF.
+ return result == MOJO_RESULT_FAILED_PRECONDITION;
+ }
+ } else {
+ // If the consumer handle was closed, then treat as EOF.
+ return result == MOJO_RESULT_FAILED_PRECONDITION;
+ }
+ }
+#if !defined(OS_WIN)
+ NOTREACHED();
+ return false;
+#endif
+}
+
+} // namespace
+
+void CopyFromFile(const base::FilePath& source,
+ ScopedDataPipeProducerHandle destination,
+ uint32_t skip,
+ base::TaskRunner* task_runner,
+ const base::Callback<void(bool)>& callback) {
+ base::PostTaskAndReplyWithResult(task_runner, FROM_HERE,
+ base::Bind(&BlockingCopyFromFile, source,
+ base::Passed(&destination), skip),
+ callback);
+}
+
+} // namespace common
+} // namespace mojo
diff --git a/mojo/common/data_pipe_utils.cc b/mojo/common/data_pipe_utils.cc
index bed5e85..8540ac6 100644
--- a/mojo/common/data_pipe_utils.cc
+++ b/mojo/common/data_pipe_utils.cc
@@ -4,9 +4,16 @@
#include "mojo/common/data_pipe_utils.h"
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
#include <utility>
-#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/message_loop/message_loop.h"
+#include "base/task_runner_util.h"
namespace mojo {
namespace common {
@@ -51,7 +58,12 @@
return num_bytes;
}
-} // namespace
+size_t CopyToFileHelper(FILE* fp, const void* buffer, uint32_t num_bytes) {
+ return fwrite(buffer, 1, num_bytes, fp);
+}
+
+} // namespace
+
// TODO(hansmuller): Add a max_size parameter.
bool BlockingCopyToString(ScopedDataPipeConsumerHandle source,
@@ -95,5 +107,25 @@
}
}
+bool BlockingCopyToFile(ScopedDataPipeConsumerHandle source,
+ const base::FilePath& destination) {
+ base::ScopedFILE fp(base::OpenFile(destination, "wb"));
+ if (!fp)
+ return false;
+ return BlockingCopyHelper(std::move(source),
+ base::Bind(&CopyToFileHelper, fp.get()));
+}
+
+void CopyToFile(ScopedDataPipeConsumerHandle source,
+ const base::FilePath& destination,
+ base::TaskRunner* task_runner,
+ const base::Callback<void(bool)>& callback) {
+ base::PostTaskAndReplyWithResult(
+ task_runner,
+ FROM_HERE,
+ base::Bind(&BlockingCopyToFile, base::Passed(&source), destination),
+ callback);
+}
+
} // namespace common
} // namespace mojo
diff --git a/mojo/common/data_pipe_utils.h b/mojo/common/data_pipe_utils.h
index a3f7c09..426912c 100644
--- a/mojo/common/data_pipe_utils.h
+++ b/mojo/common/data_pipe_utils.h
@@ -2,19 +2,41 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef MOJO_COMMON_DATA_PIPE_UTILS_H_
-#define MOJO_COMMON_DATA_PIPE_UTILS_H_
+#ifndef MOJO_SHELL_DATA_PIPE_UTILS_H_
+#define MOJO_SHELL_DATA_PIPE_UTILS_H_
#include <stdint.h>
#include <string>
+#include "base/callback_forward.h"
#include "mojo/common/mojo_common_export.h"
-#include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace base {
+class FilePath;
+class TaskRunner;
+}
namespace mojo {
namespace common {
+// Asynchronously copies data from source to the destination file. The given
+// |callback| is run upon completion. File writes will be scheduled to the
+// given |task_runner|.
+void MOJO_COMMON_EXPORT CopyToFile(
+ ScopedDataPipeConsumerHandle source,
+ const base::FilePath& destination,
+ base::TaskRunner* task_runner,
+ const base::Callback<void(bool /*success*/)>& callback);
+
+void MOJO_COMMON_EXPORT
+CopyFromFile(const base::FilePath& source,
+ ScopedDataPipeProducerHandle destination,
+ uint32_t skip,
+ base::TaskRunner* task_runner,
+ const base::Callback<void(bool /*success*/)>& callback);
+
// Copies the data from |source| into |contents| and returns true on success and
// false on error. In case of I/O error, |contents| holds the data that could
// be read from source before the error occurred.
@@ -26,7 +48,13 @@
const std::string& source,
const ScopedDataPipeProducerHandle& destination);
+// Synchronously copies data from source to the destination file returning true
+// on success and false on error. In case of an error, |destination| holds the
+// data that could be read from the source before the error occured.
+bool MOJO_COMMON_EXPORT BlockingCopyToFile(ScopedDataPipeConsumerHandle source,
+ const base::FilePath& destination);
+
} // namespace common
} // namespace mojo
-#endif // MOJO_COMMON_DATA_PIPE_UTILS_H_
+#endif // MOJO_SHELL_DATA_PIPE_UTILS_H_
diff --git a/mojo/common/file.mojom b/mojo/common/file.mojom
deleted file mode 100644
index fe22473..0000000
--- a/mojo/common/file.mojom
+++ /dev/null
@@ -1,10 +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.
-
-module mojo.common.mojom;
-
-// Corresponds to |base::File| in base/files/file.h
-struct File {
- handle fd;
-};
diff --git a/mojo/common/file.typemap b/mojo/common/file.typemap
deleted file mode 100644
index 26d4941..0000000
--- a/mojo/common/file.typemap
+++ /dev/null
@@ -1,13 +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.
-
-mojom = "//mojo/common/file.mojom"
-public_headers = [ "//base/files/file.h" ]
-traits_headers = [ "//mojo/common/common_custom_types_struct_traits.h" ]
-public_deps = [
- "//mojo/common:struct_traits",
-]
-
-type_mappings =
- [ "mojo.common.mojom.File=base::File[move_only,nullable_is_same_type]" ]
diff --git a/mojo/common/string16.mojom b/mojo/common/string16.mojom
index 173c867..eb1ace2 100644
--- a/mojo/common/string16.mojom
+++ b/mojo/common/string16.mojom
@@ -10,3 +10,4 @@
struct String16 {
array<uint16> data;
};
+
diff --git a/mojo/common/string16.typemap b/mojo/common/string16.typemap
deleted file mode 100644
index 223de29..0000000
--- a/mojo/common/string16.typemap
+++ /dev/null
@@ -1,12 +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.
-
-mojom = "//mojo/common/string16.mojom"
-public_headers = [ "//base/strings/string16.h" ]
-traits_headers = [ "//mojo/common/common_custom_types_struct_traits.h" ]
-public_deps = [
- "//mojo/common:struct_traits",
-]
-
-type_mappings = [ "mojo.common.mojom.String16=base::string16" ]
diff --git a/mojo/common/test_common_custom_types.mojom b/mojo/common/test_common_custom_types.mojom
index 0f13680..db91a4f 100644
--- a/mojo/common/test_common_custom_types.mojom
+++ b/mojo/common/test_common_custom_types.mojom
@@ -4,24 +4,13 @@
module mojo.common.test;
-import "mojo/common/file.mojom";
-import "mojo/common/file_path.mojom";
-import "mojo/common/string16.mojom";
-import "mojo/common/text_direction.mojom";
-import "mojo/common/time.mojom";
-import "mojo/common/unguessable_token.mojom";
-import "mojo/common/values.mojom";
+import "mojo/common/common_custom_types.mojom";
interface TestFilePath {
BounceFilePath(mojo.common.mojom.FilePath in)
=> (mojo.common.mojom.FilePath out);
};
-interface TestUnguessableToken {
- BounceNonce(mojo.common.mojom.UnguessableToken in)
- => (mojo.common.mojom.UnguessableToken out);
-};
-
interface TestTime {
BounceTime(mojo.common.mojom.Time time) => (mojo.common.mojom.Time time);
BounceTimeDelta(mojo.common.mojom.TimeDelta time_delta)
@@ -31,31 +20,8 @@
};
interface TestValue {
- [Sync]
BounceDictionaryValue(mojo.common.mojom.DictionaryValue in)
=> (mojo.common.mojom.DictionaryValue out);
- [Sync]
BounceListValue(mojo.common.mojom.ListValue in)
=> (mojo.common.mojom.ListValue out);
- [Sync]
- BounceValue(mojo.common.mojom.Value? in)
- => (mojo.common.mojom.Value? out);
-};
-
-interface TestString16 {
- [Sync]
- BounceString16(mojo.common.mojom.String16 in)
- => (mojo.common.mojom.String16 out);
-};
-
-interface TestFile {
- [Sync]
- BounceFile(mojo.common.mojom.File? in)
- => (mojo.common.mojom.File? out);
-};
-
-interface TestTextDirection {
- [Sync]
- BounceTextDirection(mojo.common.mojom.TextDirection in)
- => (mojo.common.mojom.TextDirection out);
};
diff --git a/mojo/common/text_direction.mojom b/mojo/common/text_direction.mojom
deleted file mode 100644
index 7d65124..0000000
--- a/mojo/common/text_direction.mojom
+++ /dev/null
@@ -1,12 +0,0 @@
-// 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.
-
-module mojo.common.mojom;
-
-// Corresponds to |base::i18n::TextDirection| in base/i18n/rtl.h
-enum TextDirection {
- UNKNOWN_DIRECTION,
- RIGHT_TO_LEFT,
- LEFT_TO_RIGHT
-};
diff --git a/mojo/common/text_direction.typemap b/mojo/common/text_direction.typemap
deleted file mode 100644
index 1f5be8e..0000000
--- a/mojo/common/text_direction.typemap
+++ /dev/null
@@ -1,12 +0,0 @@
-# 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.
-
-mojom = "//mojo/common/text_direction.mojom"
-public_headers = [ "//base/i18n/rtl.h" ]
-traits_headers = [ "//mojo/common/common_custom_types_struct_traits.h" ]
-public_deps = [
- "//base:i18n",
- "//mojo/common:struct_traits",
-]
-type_mappings = [ "mojo.common.mojom.TextDirection=base::i18n::TextDirection" ]
diff --git a/mojo/common/time.typemap b/mojo/common/time.typemap
deleted file mode 100644
index 99e9e3a..0000000
--- a/mojo/common/time.typemap
+++ /dev/null
@@ -1,20 +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.
-
-mojom = "//mojo/common/time.mojom"
-public_headers = [ "//base/time/time.h" ]
-traits_headers = [
- "//ipc/ipc_message_utils.h",
- "//mojo/common/common_custom_types_struct_traits.h",
-]
-public_deps = [
- "//ipc",
- "//mojo/common:struct_traits",
-]
-
-type_mappings = [
- "mojo.common.mojom.Time=base::Time[copyable_pass_by_value]",
- "mojo.common.mojom.TimeDelta=base::TimeDelta[copyable_pass_by_value]",
- "mojo.common.mojom.TimeTicks=base::TimeTicks[copyable_pass_by_value]",
-]
diff --git a/mojo/common/unguessable_token.mojom b/mojo/common/unguessable_token.mojom
deleted file mode 100644
index 3279717..0000000
--- a/mojo/common/unguessable_token.mojom
+++ /dev/null
@@ -1,11 +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.
-
-module mojo.common.mojom;
-
-// Corresponds to |base::UnguessableToken| in base/unguessable_token.h
-struct UnguessableToken {
- uint64 high;
- uint64 low;
-};
diff --git a/mojo/common/unguessable_token.typemap b/mojo/common/unguessable_token.typemap
deleted file mode 100644
index ec7b194..0000000
--- a/mojo/common/unguessable_token.typemap
+++ /dev/null
@@ -1,12 +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.
-
-mojom = "//mojo/common/unguessable_token.mojom"
-public_headers = [ "//base/unguessable_token.h" ]
-traits_headers = [ "//mojo/common/common_custom_types_struct_traits.h" ]
-public_deps = [
- "//mojo/common:struct_traits",
-]
-
-type_mappings = [ "mojo.common.mojom.UnguessableToken=base::UnguessableToken" ]
diff --git a/mojo/common/user_agent.cc b/mojo/common/user_agent.cc
new file mode 100644
index 0000000..6055cbe
--- /dev/null
+++ b/mojo/common/user_agent.cc
@@ -0,0 +1,25 @@
+// 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/common/user_agent.h"
+
+#include "build/build_config.h"
+
+namespace mojo {
+namespace common {
+
+std::string GetUserAgent() {
+ // TODO(jam): change depending on OS
+#if defined(OS_ANDROID)
+ return "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 7 Build/LMY48G) "
+ "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.68 "
+ "Safari/537.36";
+#else
+ return "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like "
+ "Gecko) Chrome/42.0.2311.68 Safari/537.36";
+#endif
+}
+
+} // namespace common
+} // namespace mojo
diff --git a/mojo/common/user_agent.h b/mojo/common/user_agent.h
new file mode 100644
index 0000000..031b102
--- /dev/null
+++ b/mojo/common/user_agent.h
@@ -0,0 +1,20 @@
+// 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_COMMON_USER_AGENT_H_
+#define MOJO_COMMON_USER_AGENT_H_
+
+#include <string>
+
+#include "mojo/common/mojo_common_export.h"
+
+namespace mojo {
+namespace common {
+
+std::string MOJO_COMMON_EXPORT GetUserAgent();
+
+} // namespace common
+} // namespace mojo
+
+#endif // MOJO_COMMON_USER_AGENT_H_
diff --git a/mojo/common/version.mojom b/mojo/common/version.mojom
deleted file mode 100644
index 6ddf6e6..0000000
--- a/mojo/common/version.mojom
+++ /dev/null
@@ -1,10 +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.
-
-module mojo.common.mojom;
-
-// Corresponds to |base::Version| in base/version.h
-struct Version {
- array<uint32> components;
-};
diff --git a/mojo/common/version.typemap b/mojo/common/version.typemap
deleted file mode 100644
index fa7fed9..0000000
--- a/mojo/common/version.typemap
+++ /dev/null
@@ -1,12 +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.
-
-mojom = "//mojo/common/version.mojom"
-public_headers = [ "//base/version.h" ]
-traits_headers = [ "//mojo/common/common_custom_types_struct_traits.h" ]
-public_deps = [
- "//mojo/common:struct_traits",
-]
-
-type_mappings = [ "mojo.common.mojom.Version=base::Version" ]
diff --git a/mojo/converters/blink/BUILD.gn b/mojo/converters/blink/BUILD.gn
new file mode 100644
index 0000000..0bb9295
--- /dev/null
+++ b/mojo/converters/blink/BUILD.gn
@@ -0,0 +1,44 @@
+# 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.
+
+import("//testing/test.gni")
+
+component("blink") {
+ output_name = "mojo_blink_lib"
+
+ sources = [
+ "blink_input_events_type_converters.cc",
+ "blink_input_events_type_converters.h",
+ "mojo_blink_export.h",
+ ]
+
+ defines = [ "MOJO_CONVERTERS_BLINK_IMPLEMENTATION" ]
+
+ public_deps = [
+ "//mojo/public/cpp/bindings",
+ ]
+
+ deps = [
+ "//base",
+ "//third_party/WebKit/public:blink",
+ "//ui/events",
+ "//ui/events:dom_keycode_converter",
+ ]
+}
+
+test("blink_converters_unittests") {
+ sources = [
+ "blink_input_events_type_converters_unittest.cc",
+ ]
+ deps = [
+ ":blink",
+ "//base:message_loop_tests",
+ "//base/test:run_all_unittests",
+ "//base/test:test_support",
+ "//mojo/public/cpp/bindings:bindings",
+ "//testing/gtest",
+ "//third_party/WebKit/public:blink",
+ "//ui/events",
+ ]
+}
diff --git a/mojo/converters/blink/DEPS b/mojo/converters/blink/DEPS
new file mode 100644
index 0000000..6678b7c
--- /dev/null
+++ b/mojo/converters/blink/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+base",
+ "+third_party/WebKit/public",
+ "+ui/events"
+]
diff --git a/mojo/converters/blink/blink_input_events_type_converters.cc b/mojo/converters/blink/blink_input_events_type_converters.cc
new file mode 100644
index 0000000..1114a5f
--- /dev/null
+++ b/mojo/converters/blink/blink_input_events_type_converters.cc
@@ -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.
+
+#include "mojo/converters/blink/blink_input_events_type_converters.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/event.h"
+#include "ui/events/keycodes/dom/keycode_converter.h"
+
+namespace mojo {
+namespace {
+
+// TODO(majidvp): remove this and directly use ui::EventFlagsToWebEventModifiers
+int EventFlagsToWebEventModifiers(int flags) {
+ int modifiers = 0;
+
+ if (flags & ui::EF_SHIFT_DOWN)
+ modifiers |= blink::WebInputEvent::ShiftKey;
+ if (flags & ui::EF_CONTROL_DOWN)
+ modifiers |= blink::WebInputEvent::ControlKey;
+ if (flags & ui::EF_ALT_DOWN)
+ modifiers |= blink::WebInputEvent::AltKey;
+ // TODO(beng): MetaKey/META_MASK
+ if (flags & ui::EF_LEFT_MOUSE_BUTTON)
+ modifiers |= blink::WebInputEvent::LeftButtonDown;
+ if (flags & ui::EF_MIDDLE_MOUSE_BUTTON)
+ modifiers |= blink::WebInputEvent::MiddleButtonDown;
+ if (flags & ui::EF_RIGHT_MOUSE_BUTTON)
+ modifiers |= blink::WebInputEvent::RightButtonDown;
+ if (flags & ui::EF_CAPS_LOCK_ON)
+ modifiers |= blink::WebInputEvent::CapsLockOn;
+ return modifiers;
+}
+
+// TODO(majidvp): remove this and directly use ui::EventFlagsToWebEventModifiers
+int EventFlagsToWebInputEventModifiers(int flags) {
+ return (flags & ui::EF_SHIFT_DOWN ? blink::WebInputEvent::ShiftKey : 0) |
+ (flags & ui::EF_CONTROL_DOWN ? blink::WebInputEvent::ControlKey : 0) |
+ (flags & ui::EF_CAPS_LOCK_ON ? blink::WebInputEvent::CapsLockOn : 0) |
+ (flags & ui::EF_ALT_DOWN ? blink::WebInputEvent::AltKey : 0);
+}
+
+int GetClickCount(int flags) {
+ if (flags & ui::EF_IS_TRIPLE_CLICK)
+ return 3;
+ else if (flags & ui::EF_IS_DOUBLE_CLICK)
+ return 2;
+
+ return 1;
+}
+
+void SetWebMouseEventLocation(const ui::LocatedEvent& located_event,
+ blink::WebMouseEvent* web_event) {
+ web_event->x = static_cast<int>(located_event.x());
+ web_event->y = static_cast<int>(located_event.y());
+ web_event->globalX = static_cast<int>(located_event.root_location_f().x());
+ web_event->globalY = static_cast<int>(located_event.root_location_f().y());
+}
+
+std::unique_ptr<blink::WebInputEvent> BuildWebMouseEventFrom(
+ const ui::PointerEvent& event) {
+ std::unique_ptr<blink::WebMouseEvent> web_event(new blink::WebMouseEvent);
+
+ web_event->pointerType = blink::WebPointerProperties::PointerType::Mouse;
+ SetWebMouseEventLocation(event, web_event.get());
+
+ web_event->modifiers = EventFlagsToWebEventModifiers(event.flags());
+ web_event->timeStampSeconds = ui::EventTimeStampToSeconds(event.time_stamp());
+
+ web_event->button = blink::WebMouseEvent::ButtonNone;
+ if (event.flags() & ui::EF_LEFT_MOUSE_BUTTON)
+ web_event->button = blink::WebMouseEvent::ButtonLeft;
+ if (event.flags() & ui::EF_MIDDLE_MOUSE_BUTTON)
+ web_event->button = blink::WebMouseEvent::ButtonMiddle;
+ if (event.flags() & ui::EF_RIGHT_MOUSE_BUTTON)
+ web_event->button = blink::WebMouseEvent::ButtonRight;
+
+ switch (event.type()) {
+ case ui::ET_POINTER_DOWN:
+ web_event->type = blink::WebInputEvent::MouseDown;
+ break;
+ case ui::ET_POINTER_UP:
+ web_event->type = blink::WebInputEvent::MouseUp;
+ break;
+ case ui::ET_POINTER_MOVED:
+ web_event->type = blink::WebInputEvent::MouseMove;
+ break;
+ case ui::ET_MOUSE_EXITED:
+ web_event->type = blink::WebInputEvent::MouseLeave;
+ break;
+ default:
+ NOTIMPLEMENTED() << "Received unexpected event: " << event.type();
+ break;
+ }
+
+ web_event->clickCount = GetClickCount(event.flags());
+
+ return std::move(web_event);
+}
+
+std::unique_ptr<blink::WebInputEvent> BuildWebKeyboardEvent(
+ const ui::KeyEvent& event) {
+ std::unique_ptr<blink::WebKeyboardEvent> web_event(
+ new blink::WebKeyboardEvent);
+
+ web_event->modifiers = EventFlagsToWebInputEventModifiers(event.flags());
+ web_event->timeStampSeconds = ui::EventTimeStampToSeconds(event.time_stamp());
+
+ switch (event.type()) {
+ case ui::ET_KEY_PRESSED:
+ web_event->type = event.is_char() ? blink::WebInputEvent::Char
+ : blink::WebInputEvent::RawKeyDown;
+ break;
+ case ui::ET_KEY_RELEASED:
+ web_event->type = blink::WebInputEvent::KeyUp;
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ if (web_event->modifiers & blink::WebInputEvent::AltKey)
+ web_event->isSystemKey = true;
+
+ web_event->windowsKeyCode = event.GetLocatedWindowsKeyboardCode();
+ web_event->nativeKeyCode =
+ ui::KeycodeConverter::DomCodeToNativeKeycode(event.code());
+ web_event->text[0] = event.GetText();
+ web_event->unmodifiedText[0] = event.GetUnmodifiedText();
+ return std::move(web_event);
+}
+
+std::unique_ptr<blink::WebInputEvent> BuildWebMouseWheelEventFrom(
+ const ui::MouseWheelEvent& event) {
+ std::unique_ptr<blink::WebMouseWheelEvent> web_event(
+ new blink::WebMouseWheelEvent);
+ web_event->type = blink::WebInputEvent::MouseWheel;
+ web_event->button = blink::WebMouseEvent::ButtonNone;
+ web_event->modifiers = EventFlagsToWebEventModifiers(event.flags());
+ web_event->timeStampSeconds = ui::EventTimeStampToSeconds(event.time_stamp());
+
+ SetWebMouseEventLocation(event, web_event.get());
+
+ // TODO(rjkroege): Update the following code once Blink supports
+ // DOM Level 3 wheel events
+ // (http://www.w3.org/TR/DOM-Level-3-Events/#events-wheelevents)
+ web_event->deltaX = event.x_offset();
+ web_event->deltaY = event.y_offset();
+
+ web_event->wheelTicksX = web_event->deltaX / ui::MouseWheelEvent::kWheelDelta;
+ web_event->wheelTicksY = web_event->deltaY / ui::MouseWheelEvent::kWheelDelta;
+
+ // TODO(moshayedi): ui::WheelEvent currently only supports WHEEL_MODE_LINE.
+ // Add support for other wheel modes once ui::WheelEvent has support for them.
+ web_event->hasPreciseScrollingDeltas = false;
+ web_event->scrollByPage = false;
+
+ return std::move(web_event);
+}
+
+void SetWebTouchEventLocation(const ui::PointerEvent& event,
+ blink::WebTouchPoint* touch) {
+ touch->position.x = event.x();
+ touch->position.y = event.y();
+ touch->screenPosition.x = event.root_location_f().x();
+ touch->screenPosition.y = event.root_location_f().y();
+}
+
+std::unique_ptr<blink::WebInputEvent> BuildWebTouchEvent(
+ const ui::PointerEvent& event) {
+ std::unique_ptr<blink::WebTouchEvent> web_event(new blink::WebTouchEvent);
+ blink::WebTouchPoint* touch = &web_event->touches[event.pointer_id()];
+
+ // TODO(jonross): we will need to buffer input events, as blink expects all
+ // active touch points to be in each WebInputEvent (crbug.com/578160)
+ SetWebTouchEventLocation(event, touch);
+ touch->pointerType = blink::WebPointerProperties::PointerType::Touch;
+ touch->radiusX = event.pointer_details().radius_x;
+ touch->radiusY = event.pointer_details().radius_y;
+
+ web_event->modifiers = EventFlagsToWebEventModifiers(event.flags());
+ web_event->timeStampSeconds = ui::EventTimeStampToSeconds(event.time_stamp());
+ web_event->uniqueTouchEventId = ui::GetNextTouchEventId();
+
+ switch (event.type()) {
+ case ui::ET_POINTER_DOWN:
+ web_event->type = blink::WebInputEvent::TouchStart;
+ touch->state = blink::WebTouchPoint::StatePressed;
+ break;
+ case ui::ET_POINTER_UP:
+ web_event->type = blink::WebInputEvent::TouchEnd;
+ touch->state = blink::WebTouchPoint::StateReleased;
+ break;
+ case ui::ET_POINTER_MOVED:
+ web_event->type = blink::WebInputEvent::TouchMove;
+ touch->state = blink::WebTouchPoint::StateMoved;
+ break;
+ case ui::ET_POINTER_CANCELLED:
+ web_event->type = blink::WebInputEvent::TouchCancel;
+ touch->state = blink::WebTouchPoint::StateCancelled;
+ break;
+ default:
+ NOTIMPLEMENTED() << "Received non touch pointer event action: "
+ << event.type();
+ break;
+ }
+
+ return std::move(web_event);
+}
+
+} // namespace
+
+// static
+std::unique_ptr<blink::WebInputEvent>
+TypeConverter<std::unique_ptr<blink::WebInputEvent>, ui::Event>::Convert(
+ const ui::Event& event) {
+ DCHECK(event.IsKeyEvent() || event.IsPointerEvent() ||
+ event.IsMouseWheelEvent());
+ switch (event.type()) {
+ case ui::ET_POINTER_DOWN:
+ case ui::ET_POINTER_UP:
+ case ui::ET_POINTER_CANCELLED:
+ case ui::ET_POINTER_MOVED:
+ case ui::ET_POINTER_EXITED:
+ if (event.IsMousePointerEvent())
+ return BuildWebMouseEventFrom(*event.AsPointerEvent());
+ else if (event.IsTouchPointerEvent())
+ return BuildWebTouchEvent(*event.AsPointerEvent());
+ else
+ return nullptr;
+ case ui::ET_MOUSEWHEEL:
+ return BuildWebMouseWheelEventFrom(*event.AsMouseWheelEvent());
+ case ui::ET_KEY_PRESSED:
+ case ui::ET_KEY_RELEASED:
+ return BuildWebKeyboardEvent(*event.AsKeyEvent());
+ default:
+ return nullptr;
+ }
+}
+
+} // namespace mojo
diff --git a/mojo/converters/blink/blink_input_events_type_converters.h b/mojo/converters/blink/blink_input_events_type_converters.h
new file mode 100644
index 0000000..3231eab
--- /dev/null
+++ b/mojo/converters/blink/blink_input_events_type_converters.h
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_CONVERTERS_BLINK_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_
+#define MOJO_CONVERTERS_BLINK_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_
+
+#include <memory>
+
+#include "mojo/converters/blink/mojo_blink_export.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+namespace blink {
+class WebInputEvent;
+}
+
+namespace ui {
+class Event;
+}
+
+namespace mojo {
+
+template <>
+struct MOJO_BLINK_EXPORT
+ TypeConverter<std::unique_ptr<blink::WebInputEvent>, ui::Event> {
+ static std::unique_ptr<blink::WebInputEvent> Convert(const ui::Event& input);
+};
+
+} // namespace mojo
+
+#endif // MOJO_CONVERTERS_BLINK_BLINK_INPUT_EVENTS_TYPE_CONVERTERS_H_
diff --git a/mojo/converters/blink/mojo_blink_export.h b/mojo/converters/blink/mojo_blink_export.h
new file mode 100644
index 0000000..1a77184
--- /dev/null
+++ b/mojo/converters/blink/mojo_blink_export.h
@@ -0,0 +1,32 @@
+// 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_CONVERTERS_BLINK_MOJO_BLINK_EXPORT_H_
+#define MOJO_CONVERTERS_BLINK_MOJO_BLINK_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_CONVERTERS_BLINK_IMPLEMENTATION)
+#define MOJO_BLINK_EXPORT __declspec(dllexport)
+#else
+#define MOJO_BLINK_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_CONVERTERS_BLINK_IMPLEMENTATION)
+#define MOJO_BLINK_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_BLINK_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#else // !defined(COMPONENT_BUILD)
+#define MOJO_BLINK_EXPORT
+#endif
+
+#endif // MOJO_CONVERTERS_BLINK_MOJO_BLINK_EXPORT_H_
diff --git a/mojo/edk/embedder/BUILD.gn b/mojo/edk/embedder/BUILD.gn
index 8105bed..f20fd40 100644
--- a/mojo/edk/embedder/BUILD.gn
+++ b/mojo/edk/embedder/BUILD.gn
@@ -7,16 +7,13 @@
source_set("headers") {
sources = [
"configuration.h",
- "connection_params.h",
"embedder.h",
"embedder_internal.h",
"named_platform_channel_pair.h",
- "named_platform_handle.h",
- "named_platform_handle_utils.h",
- "pending_process_connection.h",
"platform_channel_pair.h",
"platform_handle.h",
"platform_handle_utils.h",
+ "process_delegate.h",
"scoped_platform_handle.h",
]
@@ -36,14 +33,11 @@
sources = [
"configuration.h",
- "connection_params.cc",
- "connection_params.h",
"embedder.cc",
"embedder.h",
"embedder_internal.h",
"entrypoints.cc",
"entrypoints.h",
- "pending_process_connection.cc",
"scoped_ipc_support.cc",
"scoped_ipc_support.h",
@@ -58,6 +52,7 @@
defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ]
public_deps = [
+ ":delegates",
":headers",
":platform",
"//base",
@@ -82,9 +77,6 @@
sources = [
"named_platform_channel_pair.h",
"named_platform_channel_pair_win.cc",
- "named_platform_handle.h",
- "named_platform_handle_utils.h",
- "named_platform_handle_utils_win.cc",
"platform_channel_pair.cc",
"platform_channel_pair.h",
"platform_channel_pair_posix.cc",
@@ -101,9 +93,6 @@
"platform_shared_buffer.h",
"scoped_platform_handle.h",
]
- if (!is_nacl) {
- sources += [ "named_platform_handle_utils_posix.cc" ]
- }
defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ]
@@ -124,6 +113,25 @@
}
}
+source_set("delegates") {
+ # This isn't really a standalone target; it must be linked into the
+ # mojo_system_impl component.
+ visibility = [
+ ":embedder",
+ "//mojo/edk/system",
+ ]
+
+ sources = [
+ "process_delegate.h",
+ ]
+
+ defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ]
+
+ public_deps = [
+ "//mojo/public/cpp/system",
+ ]
+}
+
source_set("embedder_unittests") {
testonly = true
diff --git a/mojo/edk/embedder/README.md b/mojo/edk/embedder/README.md
index 6def874..f976fcb 100644
--- a/mojo/edk/embedder/README.md
+++ b/mojo/edk/embedder/README.md
@@ -1,327 +1,13 @@
-# Mojo Embedder Development Kit (EDK)
+Mojo Embedder API
+=================
-The Mojo EDK is a (binary-unstable) API which enables a process to use Mojo both
-internally and for IPC to other Mojo-embedding processes.
+The Mojo Embedder API is an unstable, internal API to the Mojo system
+implementation. It should be used by code running on top of the system-level
+APIs to set up the Mojo environment (instead of directly instantiating things
+from src/mojo/edk/system).
-Using any of the API surface in `//mojo/edk/embedder` requires (somewhat
-confusingly) a direct dependency on the GN `//mojo/edk/system` target. Despite
-this fact, you should never reference any of the headers in `mojo/edk/system`
-directly, as everything there is considered to be an internal detail of the EDK.
-
-## Basic Initialization
-
-In order to use Mojo in a given process, it's necessary to call
-`mojo::edk::Init` exactly once:
-
-```
-#include "mojo/edk/embedder/embedder.h"
-
-int main(int argc, char** argv) {
- mojo::edk::Init();
-
- // Now you can create message pipes, write messages, etc
-
- return 0;
-}
-```
-
-As it happens though, Mojo is less useful without some kind of IPC support as
-well, and that's a second initialization step.
-
-## IPC Initialization
-
-You also need to provide the system with a background TaskRunner on which it can
-watch for inbound I/O from any of the various other processes you will later
-connect to it.
-
-Here we'll just create a new background thread for IPC and let Mojo use that.
-Note that in Chromium, we use the existing "IO thread" in the browser process
-and content child processes.
-
-```
-#include "base/threading/thread.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
-
-int main(int argc, char** argv) {
- mojo::edk::Init();
-
- base::Thread ipc_thread("ipc!");
- ipc_thread.StartWithOptions(
- base::Thread::Options(base::MessageLoop::TYPE_IO));
-
- // As long as this object is alive, all EDK API surface relevant to IPC
- // connections is usable and message pipes which span a process boundary will
- // continue to function.
- mojo::edk::ScopedIPCSupport ipc_support(
- ipc_thread.task_runner(),
- mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
- return 0;
-}
-```
-
-This process is now fully prepared to use Mojo IPC!
-
-Note that all existing process types in Chromium already perform this setup
-very early during startup.
-
-## Connecting Two Processes
-
-Now suppose you're running a process which has initialized Mojo IPC, and you
-want to launch another process which you know will also initialize Mojo IPC.
-You want to be able to connect Mojo interfaces between these two processes.
-Rejoice, because this section was written just for you.
-
-NOTE: For legacy reasons, some API terminology may refer to concepts of "parent"
-and "child" as a relationship between processes being connected by Mojo. This
-relationship is today completely orthogonal to any notion of process hierarchy
-in the OS, and so use of these APIs is not constrained by an adherence to any
-such hierarchy.
-
-Mojo requires you to bring your own OS pipe to the party, and it will do the
-rest. It also provides a convenient mechanism for creating such pipes, known as
-a `PlatformChannelPair`.
-
-You provide one end of this pipe to the EDK in the local process via
-`PendingProcessConnection` - which can also be used to create cross-process
-message pipes (see the next section) - and you're responsible for getting the
-other end into the remote process.
-
-```
-#include "base/process/process_handle.h"
-#include "base/threading/thread.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/pending_process_connection.h"
-#include "mojo/edk/embedder/platform_channel_pair.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
-
-// You write this. It launches a new process, passing the pipe handle
-// encapsulated by |channel| by any means possible (e.g. on Windows or POSIX
-// you may inhert the file descriptor/HANDLE at launch and pass a commandline
-// argument to indicate its numeric value). Returns the handle of the new
-// process.
-base::ProcessHandle LaunchCoolChildProcess(
- mojo::edk::ScopedPlatformHandle channel);
-
-int main(int argc, char** argv) {
- mojo::edk::Init();
-
- base::Thread ipc_thread("ipc!");
- ipc_thread.StartWithOptions(
- base::Thread::Options(base::MessageLoop::TYPE_IO));
-
- mojo::edk::ScopedIPCSupport ipc_support(
- ipc_thread.task_runner(),
- mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
- // This is essentially always an OS pipe (domain socket pair, Windows named
- // pipe, etc.)
- mojo::edk::PlatformChannelPair channel;
-
- // This is a scoper which encapsulates the intent to connect to another
- // process. It exists because process connection is inherently asynchronous,
- // things may go wrong, and the lifetime of any associated resources is bound
- // by the lifetime of this object regardless of success or failure.
- mojo::edk::PendingProcessConnection child;
-
- base::ProcessHandle child_handle =
- LaunchCoolChildProcess(channel.PassClientHandle());
-
- // At this point it's safe for |child| to go out of scope and nothing will
- // break.
- child.Connect(child_handle, channel.PassServerHandle());
-
- return 0;
-}
-```
-
-The launched process code uses `SetParentPipeHandle` to get connected, and might
-look something like:
-
-```
-#include "base/threading/thread.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
-
-// You write this. It acquires the ScopedPlatformHandle that was passed by
-// whomever launched this process (i.e. LaunchCoolChildProcess above).
-mojo::edk::ScopedPlatformHandle GetChannelHandle();
-
-int main(int argc, char** argv) {
- mojo::edk::Init();
-
- base::Thread ipc_thread("ipc!");
- ipc_thread.StartWithOptions(
- base::Thread::Options(base::MessageLoop::TYPE_IO));
-
- mojo::edk::ScopedIPCSupport ipc_support(
- ipc_thread.task_runner(),
- mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
- mojo::edk::SetParentPipeHandle(GetChannelHandle());
-
- return 0;
-}
-```
-
-Now you have IPC initialized between two processes. For some practical examples
-of how this is done, you can dig into the various multiprocess tests in the
-`mojo_system_unittests` test suite.
-
-## Bootstrapping Cross-Process Message Pipes
-
-Having internal Mojo IPC support initialized is pretty useless if you don't have
-any message pipes spanning the process boundary. Fortunately, this is made
-trivial by the EDK: `PendingProcessConnection` has a
-`CreateMessagePipe` method which synthesizes a new solitary message pipe
-endpoint for your immediate use, while also generating a magic token string that
-can be exchanged for the other end of the pipe via
-`mojo::edk::CreateChildMessagePipe`.
-
-The token exchange can be done by the same process (which is sometimes useful),
-or by the process that is eventually connected via `Connect()` on that
-`PendingProcessConnection`. This means that you can effectively pass message
-pipes on the commandline by passing a token string.
-
-We can modify our existing sample code as follows:
-
-```
-#include "base/command_line.h"
-#include "base/process/process_handle.h"
-#include "base/threading/thread.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/pending_process_connection.h"
-#include "mojo/edk/embedder/platform_channel_pair.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
-#include "mojo/public/cpp/system/message_pipe.h"
-#include "local/foo.mojom.h" // You provide this
-
-base::ProcessHandle LaunchCoolChildProcess(
- const base::CommandLine& command_line,
- mojo::edk::ScopedPlatformHandle channel);
-
-int main(int argc, char** argv) {
- mojo::edk::Init();
-
- base::Thread ipc_thread("ipc!");
- ipc_thread.StartWithOptions(
- base::Thread::Options(base::MessageLoop::TYPE_IO));
-
- mojo::edk::ScopedIPCSupport ipc_support(
- ipc_thread.task_runner(),
- mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
- mojo::edk::PlatformChannelPair channel;
-
- mojo::edk::PendingProcessConnection child;
-
- base::CommandLine command_line; // Assume this is appropriately initialized
-
- // Create a new message pipe with one end being retrievable in the new
- // process. Note that it doesn't matter whether we call CreateMessagePipe()
- // before or after Connect(), and we can create as many different pipes as
- // we like.
- std::string pipe_token;
- mojo::ScopedMessagePipeHandle my_pipe = child.CreateMessagePipe(&pipe_token);
- command_line.AppendSwitchASCII("primordial-pipe", pipe_token);
-
- base::ProcessHandle child_handle =
- LaunchCoolChildProcess(command_line, channel.PassClientHandle());
-
- child.Connect(child_handle, channel.PassServerHandle());
-
- // We can start using our end of the pipe immediately. Here we assume the
- // other end will eventually be bound to a local::mojom::Foo implementation,
- // so we can start making calls on that interface.
- //
- // Note that this could even be done before the child process is launched and
- // it would still work as expected.
- local::mojom::FooPtr foo;
- foo.Bind(local::mojom::FooPtrInfo(std::move(my_pipe), 0));
- foo->DoSomeStuff(42);
-
- return 0;
-}
-```
-
-and for the launched process:
-
-
-```
-#include "base/command_line.h"
-#include "base/run_loop/run_loop.h"
-#include "base/threading/thread.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/system/message_pipe.h"
-#include "local/foo.mojom.h" // You provide this
-
-mojo::edk::ScopedPlatformHandle GetChannelHandle();
-
-class FooImpl : local::mojom::Foo {
- public:
- explicit FooImpl(local::mojom::FooRequest request)
- : binding_(this, std::move(request)) {}
- ~FooImpl() override {}
-
- void DoSomeStuff(int32_t n) override {
- // ...
- }
-
- private:
- mojo::Binding<local::mojom::Foo> binding_;
-
- DISALLOW_COPY_AND_ASSIGN(FooImpl);
-};
-
-int main(int argc, char** argv) {
- base::CommandLine::Init(argc, argv);
-
- mojo::edk::Init();
-
- base::Thread ipc_thread("ipc!");
- ipc_thread.StartWithOptions(
- base::Thread::Options(base::MessageLoop::TYPE_IO));
-
- mojo::edk::ScopedIPCSupport ipc_support(
- ipc_thread.task_runner(),
- mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
-
- mojo::edk::SetParentPipeHandle(GetChannelHandle());
-
- mojo::ScopedMessagePipeHandle my_pipe = mojo::edk::CreateChildMessagePipe(
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- "primordial-pipe"));
-
- local::mojom::FooRequest foo_request;
- foo_request.Bind(std::move(my_pipe));
- FooImpl impl(std::move(foo_request));
-
- // Run forever!
- base::RunLoop().Run();
-
- return 0;
-}
-```
-
-Note that the above samples assume an interface definition in
-`//local/test.mojom` which would look something like:
-
-```
-module local.mojom;
-
-interface Foo {
- DoSomeStuff(int32 n);
-};
-```
-
-Once you've bootstrapped your process connection with a real mojom interface,
-you can avoid any further mucking around with EDK APIs or raw message pipe
-handles, as everything beyond this point - including the passing of other
-interface pipes - can be handled eloquently using public bindings APIs.
-
-See [additional Mojo documentation](
- https://www.chromium.org/developers/design-documents/mojo) for more
-information.
+Example uses: Mojo shell, to set up the Mojo environment for Mojo apps; Chromium
+code, to set up the Mojo IPC system for use between processes. Note that most
+code should use the Mojo Public API (under src/mojo/public) instead. The
+Embedder API should only be used to initialize the environment, set up the
+initial MessagePipe between two processes, etc.
diff --git a/mojo/edk/embedder/connection_params.cc b/mojo/edk/embedder/connection_params.cc
deleted file mode 100644
index 9b7ec54..0000000
--- a/mojo/edk/embedder/connection_params.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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/embedder/connection_params.h"
-
-#include <utility>
-
-namespace mojo {
-namespace edk {
-
-ConnectionParams::ConnectionParams(ScopedPlatformHandle channel)
- : channel_(std::move(channel)) {}
-
-ConnectionParams::ConnectionParams(ConnectionParams&& param)
- : channel_(std::move(param.channel_)) {}
-
-ConnectionParams& ConnectionParams::operator=(ConnectionParams&& param) {
- channel_ = std::move(param.channel_);
- return *this;
-}
-
-ScopedPlatformHandle ConnectionParams::TakeChannelHandle() {
- return std::move(channel_);
-}
-
-} // namespace edk
-} // namespace mojo
diff --git a/mojo/edk/embedder/connection_params.h b/mojo/edk/embedder/connection_params.h
deleted file mode 100644
index 25ffdde..0000000
--- a/mojo/edk/embedder/connection_params.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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_EDK_EMBEDDER_CONNECTION_PARAMS_H_
-#define MOJO_EDK_EMBEDDER_CONNECTION_PARAMS_H_
-
-#include "base/macros.h"
-#include "build/build_config.h"
-#include "mojo/edk/embedder/scoped_platform_handle.h"
-#include "mojo/edk/system/system_impl_export.h"
-
-namespace mojo {
-namespace edk {
-
-class MOJO_SYSTEM_IMPL_EXPORT ConnectionParams {
- public:
- explicit ConnectionParams(ScopedPlatformHandle channel);
-
- ConnectionParams(ConnectionParams&& param);
- ConnectionParams& operator=(ConnectionParams&& param);
-
- ScopedPlatformHandle TakeChannelHandle();
-
- private:
- ScopedPlatformHandle channel_;
-
- DISALLOW_COPY_AND_ASSIGN(ConnectionParams);
-};
-
-} // namespace edk
-} // namespace mojo
-
-#endif // MOJO_EDK_EMBEDDER_CONNECTION_PARAMS_H_
diff --git a/mojo/edk/embedder/embedder.cc b/mojo/edk/embedder/embedder.cc
index 0fdda5c..c90acbd 100644
--- a/mojo/edk/embedder/embedder.cc
+++ b/mojo/edk/embedder/embedder.cc
@@ -5,7 +5,6 @@
#include "mojo/edk/embedder/embedder.h"
#include <stdint.h>
-#include <utility>
#include "base/bind.h"
#include "base/location.h"
@@ -18,8 +17,8 @@
#include "mojo/edk/embedder/embedder_internal.h"
#include "mojo/edk/embedder/entrypoints.h"
#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/edk/embedder/process_delegate.h"
#include "mojo/edk/system/core.h"
-#include "mojo/edk/system/node_controller.h"
#if !defined(OS_NACL)
#include "crypto/random.h"
@@ -34,6 +33,7 @@
namespace internal {
Core* g_core;
+ProcessDelegate* g_process_delegate;
Core* GetCore() { return g_core; }
@@ -42,9 +42,30 @@
void SetMaxMessageSize(size_t bytes) {
}
+void ChildProcessLaunched(base::ProcessHandle child_process,
+ ScopedPlatformHandle server_pipe,
+ const std::string& child_token) {
+ ChildProcessLaunched(child_process, std::move(server_pipe),
+ child_token, ProcessErrorCallback());
+}
+
+void ChildProcessLaunched(base::ProcessHandle child_process,
+ ScopedPlatformHandle server_pipe,
+ const std::string& child_token,
+ const ProcessErrorCallback& process_error_callback) {
+ CHECK(internal::g_core);
+ internal::g_core->AddChild(child_process, std::move(server_pipe),
+ child_token, process_error_callback);
+}
+
+void ChildProcessLaunchFailed(const std::string& child_token) {
+ CHECK(internal::g_core);
+ internal::g_core->ChildLaunchFailed(child_token);
+}
+
void SetParentPipeHandle(ScopedPlatformHandle pipe) {
CHECK(internal::g_core);
- internal::g_core->InitChild(ConnectionParams(std::move(pipe)));
+ internal::g_core->InitChild(std::move(pipe));
}
void SetParentPipeHandleFromCommandLine() {
@@ -55,21 +76,6 @@
SetParentPipeHandle(std::move(platform_channel));
}
-ScopedMessagePipeHandle ConnectToPeerProcess(ScopedPlatformHandle pipe) {
- return ConnectToPeerProcess(std::move(pipe), GenerateRandomToken());
-}
-
-ScopedMessagePipeHandle ConnectToPeerProcess(ScopedPlatformHandle pipe,
- const std::string& peer_token) {
- DCHECK(pipe.is_valid());
- DCHECK(!peer_token.empty());
- return internal::g_core->ConnectToPeerProcess(std::move(pipe), peer_token);
-}
-
-void ClosePeerConnection(const std::string& peer_token) {
- return internal::g_core->ClosePeerConnection(peer_token);
-}
-
void Init() {
MojoSystemThunks thunks = MakeSystemThunks();
size_t expected_size = MojoEmbedderSetSystemThunks(&thunks);
@@ -78,10 +84,6 @@
internal::g_core = new Core();
}
-void SetDefaultProcessErrorCallback(const ProcessErrorCallback& callback) {
- internal::g_core->SetDefaultProcessErrorCallback(callback);
-}
-
MojoResult CreatePlatformHandleWrapper(
ScopedPlatformHandle platform_handle,
MojoHandle* platform_handle_wrapper_handle) {
@@ -113,18 +115,19 @@
mojo_handle, shared_memory_handle, num_bytes, read_only);
}
-void InitIPCSupport(scoped_refptr<base::TaskRunner> io_thread_task_runner) {
+void InitIPCSupport(ProcessDelegate* process_delegate,
+ scoped_refptr<base::TaskRunner> io_thread_task_runner) {
CHECK(internal::g_core);
internal::g_core->SetIOTaskRunner(io_thread_task_runner);
+ internal::g_process_delegate = process_delegate;
}
-scoped_refptr<base::TaskRunner> GetIOTaskRunner() {
- return internal::g_core->GetNodeController()->io_task_runner();
-}
-
-void ShutdownIPCSupport(const base::Closure& callback) {
+void ShutdownIPCSupport() {
+ CHECK(internal::g_process_delegate);
CHECK(internal::g_core);
- internal::g_core->RequestShutdown(callback);
+ internal::g_core->RequestShutdown(
+ base::Bind(&ProcessDelegate::OnShutdownComplete,
+ base::Unretained(internal::g_process_delegate)));
}
#if defined(OS_MACOSX) && !defined(OS_IOS)
@@ -134,7 +137,14 @@
}
#endif
+ScopedMessagePipeHandle CreateParentMessagePipe(
+ const std::string& token, const std::string& child_token) {
+ CHECK(internal::g_process_delegate);
+ return internal::g_core->CreateParentMessagePipe(token, child_token);
+}
+
ScopedMessagePipeHandle CreateChildMessagePipe(const std::string& token) {
+ CHECK(internal::g_process_delegate);
return internal::g_core->CreateChildMessagePipe(token);
}
diff --git a/mojo/edk/embedder/embedder.h b/mojo/edk/embedder/embedder.h
index 97258e5..9e83bbe 100644
--- a/mojo/edk/embedder/embedder.h
+++ b/mojo/edk/embedder/embedder.h
@@ -16,7 +16,6 @@
#include "base/memory/shared_memory_handle.h"
#include "base/process/process_handle.h"
#include "base/task_runner.h"
-#include "mojo/edk/embedder/pending_process_connection.h"
#include "mojo/edk/embedder/scoped_platform_handle.h"
#include "mojo/edk/system/system_impl_export.h"
#include "mojo/public/cpp/system/message_pipe.h"
@@ -28,6 +27,10 @@
namespace mojo {
namespace edk {
+class ProcessDelegate;
+
+using ProcessErrorCallback = base::Callback<void(const std::string& error)>;
+
// Basic configuration/initialization ------------------------------------------
// |Init()| sets up the basic Mojo system environment, making the |Mojo...()|
@@ -37,43 +40,40 @@
// Allows changing the default max message size. Must be called before Init.
MOJO_SYSTEM_IMPL_EXPORT void SetMaxMessageSize(size_t bytes);
-// Should be called as early as possible in a child process with a handle to the
-// other end of a pipe provided in the parent to
-// PendingProcessConnection::Connect.
+// Called in the parent process for each child process that is launched.
+MOJO_SYSTEM_IMPL_EXPORT void ChildProcessLaunched(
+ base::ProcessHandle child_process,
+ ScopedPlatformHandle server_pipe,
+ const std::string& child_token);
+
+// Called in the parent process for each child process that is launched.
+// |process_error_callback| is called if the system becomes aware of some
+// internal error related to this process, e.g., if the system is notified of a
+// bad message from this process via the |MojoNotifyBadMessage()| API.
+MOJO_SYSTEM_IMPL_EXPORT void ChildProcessLaunched(
+ base::ProcessHandle child_process,
+ ScopedPlatformHandle server_pipe,
+ const std::string& child_token,
+ const ProcessErrorCallback& error_callback);
+
+// Called in the parent process when a child process fails to launch.
+// Exactly one of ChildProcessLaunched() or ChildProcessLaunchFailed() must be
+// called per child process launch attempt.
+MOJO_SYSTEM_IMPL_EXPORT void ChildProcessLaunchFailed(
+ const std::string& child_token);
+
+// Should be called as early as possible in the child process with the handle
+// that the parent received from ChildProcessLaunched.
MOJO_SYSTEM_IMPL_EXPORT void SetParentPipeHandle(ScopedPlatformHandle pipe);
// Same as above but extracts the pipe handle from the command line. See
// PlatformChannelPair for details.
MOJO_SYSTEM_IMPL_EXPORT void SetParentPipeHandleFromCommandLine();
-// Called to connect to a peer process. This should be called only if there
-// is no common ancestor for the processes involved within this mojo system.
-// Both processes must call this function, each passing one end of a platform
-// channel. This returns one end of a message pipe to each process.
-MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle
-ConnectToPeerProcess(ScopedPlatformHandle pipe);
-
-// Called to connect to a peer process. This should be called only if there
-// is no common ancestor for the processes involved within this mojo system.
-// Both processes must call this function, each passing one end of a platform
-// channel. This returns one end of a message pipe to each process. |peer_token|
-// may be passed to ClosePeerConnection() to close the connection.
-MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle
-ConnectToPeerProcess(ScopedPlatformHandle pipe, const std::string& peer_token);
-
-// Closes a connection to a peer process created by ConnectToPeerProcess()
-// where the same |peer_token| was used.
-MOJO_SYSTEM_IMPL_EXPORT void ClosePeerConnection(const std::string& peer_token);
-
// Must be called first, or just after setting configuration parameters, to
// initialize the (global, singleton) system.
MOJO_SYSTEM_IMPL_EXPORT void Init();
-// Sets a default callback to invoke when an internal error is reported but
-// cannot be associated with a specific child process.
-MOJO_SYSTEM_IMPL_EXPORT void SetDefaultProcessErrorCallback(
- const ProcessErrorCallback& callback);
-
// Basic functions -------------------------------------------------------------
// The functions in this section are available once |Init()| has been called.
@@ -126,20 +126,22 @@
//
// This subsystem may be shut down using |ShutdownIPCSupport()|. None of the IPC
// functions may be called after this is called.
-//
-// |io_thread_task_runner| should live at least until |ShutdownIPCSupport()|'s
-// callback has been run.
-MOJO_SYSTEM_IMPL_EXPORT void InitIPCSupport(
- scoped_refptr<base::TaskRunner> io_thread_task_runner);
-// Retrieves the TaskRunner used for IPC I/O, as set by InitIPCSupport.
-MOJO_SYSTEM_IMPL_EXPORT scoped_refptr<base::TaskRunner> GetIOTaskRunner();
+// Initializes a process of the given type; to be called after |Init()|.
+// - |process_delegate| must be a process delegate of the appropriate type
+// corresponding to |process_type|; its methods will be called on the same
+// thread as Shutdown.
+// - |process_delegate|, and |io_thread_task_runner| should live at least
+// until |ShutdownIPCSupport()|'s callback has been run.
+MOJO_SYSTEM_IMPL_EXPORT void InitIPCSupport(
+ ProcessDelegate* process_delegate,
+ scoped_refptr<base::TaskRunner> io_thread_task_runner);
// Shuts down the subsystem initialized by |InitIPCSupport()|. It be called from
// any thread and will attempt to complete shutdown on the I/O thread with which
-// the system was initialized. Upon completion, |callback| is invoked on an
-// arbitrary thread.
-MOJO_SYSTEM_IMPL_EXPORT void ShutdownIPCSupport(const base::Closure& callback);
+// the system was initialized. Upon completion the ProcessDelegate's
+// |OnShutdownComplete()| method is invoked.
+MOJO_SYSTEM_IMPL_EXPORT void ShutdownIPCSupport();
#if defined(OS_MACOSX) && !defined(OS_IOS)
// Set the |base::PortProvider| for this process. Can be called on any thread,
@@ -148,14 +150,25 @@
base::PortProvider* port_provider);
#endif
-// Creates a message pipe from a token in a child process. This token must have
-// been acquired by a corresponding call to
-// PendingProcessConnection::CreateMessagePipe.
+// Creates a message pipe from a token. A child embedder must also have this
+// token and call CreateChildMessagePipe() with it in order for the pipe to get
+// connected. |child_token| identifies the child process and should be the same
+// as the token passed into ChildProcessLaunched(). If they are different, the
+// returned message pipe will not be signaled of peer closure if the child
+// process dies before establishing connection to the pipe.
+MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle
+CreateParentMessagePipe(const std::string& token,
+ const std::string& child_token);
+
+// Creates a message pipe from a token in a child process. The parent must also
+// have this token and call CreateParentMessagePipe() with it in order for the
+// pipe to get connected.
MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle
CreateChildMessagePipe(const std::string& token);
-// Generates a random ASCII token string for use with various APIs that expect
-// a globally unique token string.
+// Generates a random ASCII token string for use with CreateParentMessagePipe()
+// and CreateChildMessagePipe() above. The generated token is suitably random so
+// as to not have to worry about collisions with other generated tokens.
MOJO_SYSTEM_IMPL_EXPORT std::string GenerateRandomToken();
// Sets system properties that can be read by the MojoGetProperty() API. See the
diff --git a/mojo/edk/embedder/embedder_unittest.cc b/mojo/edk/embedder/embedder_unittest.cc
index d5a87e5..127a74f 100644
--- a/mojo/edk/embedder/embedder_unittest.cc
+++ b/mojo/edk/embedder/embedder_unittest.cc
@@ -10,24 +10,16 @@
#include <utility>
-#include "base/base_paths.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "base/memory/ptr_util.h"
#include "base/memory/shared_memory.h"
#include "base/message_loop/message_loop.h"
-#include "base/path_service.h"
#include "base/process/process_handle.h"
-#include "base/run_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/test_timeouts.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/named_platform_handle.h"
-#include "mojo/edk/embedder/named_platform_handle_utils.h"
-#include "mojo/edk/embedder/pending_process_connection.h"
#include "mojo/edk/embedder/platform_channel_pair.h"
#include "mojo/edk/embedder/test_embedder.h"
#include "mojo/edk/system/test_utils.h"
@@ -191,12 +183,13 @@
}
TEST_F(EmbedderTest, PipeSetup) {
- // Ensures that a pending process connection's message pipe can be claimed by
- // the host process itself.
- PendingProcessConnection process;
- std::string pipe_token;
- ScopedMessagePipeHandle parent_mp = process.CreateMessagePipe(&pipe_token);
- ScopedMessagePipeHandle child_mp = CreateChildMessagePipe(pipe_token);
+ std::string child_token = GenerateRandomToken();
+ std::string pipe_token = GenerateRandomToken();
+
+ ScopedMessagePipeHandle parent_mp =
+ CreateParentMessagePipe(pipe_token, child_token);
+ ScopedMessagePipeHandle child_mp =
+ CreateChildMessagePipe(pipe_token);
const std::string kHello = "hello";
WriteMessage(parent_mp.get().value(), kHello);
@@ -207,11 +200,13 @@
TEST_F(EmbedderTest, PipeSetup_LaunchDeath) {
PlatformChannelPair pair;
- PendingProcessConnection process;
- std::string pipe_token;
- ScopedMessagePipeHandle parent_mp = process.CreateMessagePipe(&pipe_token);
- process.Connect(base::GetCurrentProcessHandle(),
- ConnectionParams(pair.PassServerHandle()));
+ std::string child_token = GenerateRandomToken();
+ std::string pipe_token = GenerateRandomToken();
+
+ ScopedMessagePipeHandle parent_mp =
+ CreateParentMessagePipe(pipe_token, child_token);
+ ChildProcessLaunched(base::GetCurrentProcessHandle(), pair.PassServerHandle(),
+ child_token);
// Close the remote end, simulating child death before the child connects to
// the reserved port.
@@ -226,14 +221,13 @@
TEST_F(EmbedderTest, PipeSetup_LaunchFailure) {
PlatformChannelPair pair;
- auto process = base::MakeUnique<PendingProcessConnection>();
- std::string pipe_token;
- ScopedMessagePipeHandle parent_mp = process->CreateMessagePipe(&pipe_token);
+ std::string child_token = GenerateRandomToken();
+ std::string pipe_token = GenerateRandomToken();
- // Ensure that if a PendingProcessConnection goes away before Connect() is
- // called, any message pipes associated with it detect peer closure.
- process.reset();
+ ScopedMessagePipeHandle parent_mp =
+ CreateParentMessagePipe(pipe_token, child_token);
+ ChildProcessLaunchFailed(child_token);
EXPECT_EQ(MOJO_RESULT_OK, MojoWait(parent_mp.get().value(),
MOJO_HANDLE_SIGNAL_PEER_CLOSED,
MOJO_DEADLINE_INDEFINITE,
@@ -563,100 +557,6 @@
#endif // !defined(OS_IOS)
-NamedPlatformHandle GenerateChannelName() {
-#if defined(OS_POSIX)
- base::FilePath temp_dir;
- CHECK(base::PathService::Get(base::DIR_TEMP, &temp_dir));
- return NamedPlatformHandle(
- temp_dir.AppendASCII(GenerateRandomToken()).value());
-#else
- return NamedPlatformHandle(GenerateRandomToken());
-#endif
-}
-
-void CreateClientHandleOnIoThread(const NamedPlatformHandle& named_handle,
- ScopedPlatformHandle* output) {
- *output = CreateClientHandle(named_handle);
-}
-
-TEST_F(EmbedderTest, ClosePendingPeerConnection) {
- NamedPlatformHandle named_handle = GenerateChannelName();
- std::string peer_token = GenerateRandomToken();
- ScopedMessagePipeHandle server_pipe =
- ConnectToPeerProcess(CreateServerHandle(named_handle), peer_token);
- ClosePeerConnection(peer_token);
- EXPECT_EQ(MOJO_RESULT_OK,
- Wait(server_pipe.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
- MOJO_DEADLINE_INDEFINITE, nullptr));
- base::MessageLoop message_loop;
- base::RunLoop run_loop;
- ScopedPlatformHandle client_handle;
- // Closing the channel involves posting a task to the IO thread to do the
- // work. By the time the local message pipe has been observerd as closed,
- // that task will have been posted. Therefore, a task to create the client
- // connection should be handled after the channel is closed.
- GetIOTaskRunner()->PostTaskAndReply(
- FROM_HERE,
- base::Bind(&CreateClientHandleOnIoThread, named_handle, &client_handle),
- run_loop.QuitClosure());
- run_loop.Run();
- EXPECT_FALSE(client_handle.is_valid());
-}
-
-#if !defined(OS_IOS)
-
-TEST_F(EmbedderTest, ClosePipeToConnectedPeer) {
- set_launch_type(LaunchType::PEER);
- auto& controller = StartClient("ClosePipeToConnectedPeerClient");
- MojoHandle server_mp = controller.pipe();
- // 1. Write a message to |server_mp| (attaching nothing).
- WriteMessage(server_mp, "hello");
-
- // 2. Read a message from |server_mp|.
- EXPECT_EQ("world!", ReadMessage(server_mp));
-
- controller.ClosePeerConnection();
-
- EXPECT_EQ(MOJO_RESULT_OK, MojoWait(server_mp, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
- MOJO_DEADLINE_INDEFINITE, nullptr));
-
- EXPECT_EQ(0, controller.WaitForShutdown());
-}
-
-DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ClosePipeToConnectedPeerClient, EmbedderTest,
- client_mp) {
- // 1. Read the first message from |client_mp|.
- EXPECT_EQ("hello", ReadMessage(client_mp));
-
- // 2. Write a message to |client_mp| (attaching nothing).
- WriteMessage(client_mp, "world!");
-
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoWait(client_mp, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
- MOJO_DEADLINE_INDEFINITE, nullptr));
-}
-
-TEST_F(EmbedderTest, ClosePipeToConnectingPeer) {
- set_launch_type(LaunchType::PEER);
- auto& controller = StartClient("ClosePipeToConnectingPeerClient");
- controller.ClosePeerConnection();
-
- MojoHandle server_mp = controller.pipe();
-
- EXPECT_EQ(MOJO_RESULT_OK, MojoWait(server_mp, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
- MOJO_DEADLINE_INDEFINITE, nullptr));
-
- EXPECT_EQ(0, controller.WaitForShutdown());
-}
-
-DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ClosePipeToConnectingPeerClient, EmbedderTest,
- client_mp) {
- ASSERT_EQ(MOJO_RESULT_OK, MojoWait(client_mp, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
- MOJO_DEADLINE_INDEFINITE, nullptr));
-}
-
-#endif // !defined(OS_IOS)
-
} // namespace
} // namespace edk
} // namespace mojo
diff --git a/mojo/edk/embedder/named_platform_channel_pair.h b/mojo/edk/embedder/named_platform_channel_pair.h
deleted file mode 100644
index 5a83ae3..0000000
--- a/mojo/edk/embedder/named_platform_channel_pair.h
+++ /dev/null
@@ -1,73 +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.
-
-#ifndef MOJO_EDK_EMBEDDER_NAMED_PLATFORM_CHANNEL_PAIR_H_
-#define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_CHANNEL_PAIR_H_
-
-#include <string>
-
-#include "base/macros.h"
-#include "base/strings/string16.h"
-#include "build/build_config.h"
-#include "mojo/edk/embedder/named_platform_handle.h"
-#include "mojo/edk/embedder/scoped_platform_handle.h"
-#include "mojo/edk/system/system_impl_export.h"
-
-namespace base {
-class CommandLine;
-}
-
-namespace mojo {
-namespace edk {
-
-// This is used to create a named bidirectional pipe to connect new child
-// processes. The resulting server handle should be passed to the EDK, and the
-// child end passed as a pipe name on the command line to the child process. The
-// child process can then retrieve the pipe name from the command line and
-// resolve it into a client handle.
-class MOJO_SYSTEM_IMPL_EXPORT NamedPlatformChannelPair {
- public:
- struct Options {
-#if defined(OS_WIN)
- // 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
- };
-
- NamedPlatformChannelPair(const Options& options = {});
- ~NamedPlatformChannelPair();
-
- // Note: It is NOT acceptable to use this handle as a generic pipe channel. It
- // MUST be passed to PendingProcessConnection::Connect() only.
- ScopedPlatformHandle PassServerHandle();
-
- // To be called in the child process, after the parent process called
- // |PrepareToPassClientHandleToChildProcess()| and launched the child (using
- // the provided data), to create a client handle connected to the server
- // handle (in the parent process).
- static ScopedPlatformHandle PassClientHandleFromParentProcess(
- const base::CommandLine& command_line);
-
- // Prepares to pass the client channel to a new child process, to be launched
- // using |LaunchProcess()| (from base/launch.h). Modifies |*command_line| and
- // |*handle_passing_info| as needed.
- // Note: For Windows, this method only works on Vista and later.
- void PrepareToPassClientHandleToChildProcess(
- base::CommandLine* command_line) const;
-
- const NamedPlatformHandle& handle() const { return pipe_handle_; }
-
- private:
- NamedPlatformHandle pipe_handle_;
- ScopedPlatformHandle server_handle_;
-
- DISALLOW_COPY_AND_ASSIGN(NamedPlatformChannelPair);
-};
-
-} // namespace edk
-} // namespace mojo
-
-#endif // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_CHANNEL_PAIR_H_
diff --git a/mojo/edk/embedder/named_platform_handle.h b/mojo/edk/embedder/named_platform_handle.h
deleted file mode 100644
index 15ca656..0000000
--- a/mojo/edk/embedder/named_platform_handle.h
+++ /dev/null
@@ -1,51 +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.
-
-#ifndef MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_H_
-#define MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_H_
-
-#include <string>
-
-#include "base/strings/string16.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/utf_string_conversions.h"
-#include "build/build_config.h"
-#include "mojo/edk/system/system_impl_export.h"
-
-namespace mojo {
-namespace edk {
-
-#if defined(OS_POSIX)
-struct MOJO_SYSTEM_IMPL_EXPORT NamedPlatformHandle {
- NamedPlatformHandle() {}
- explicit NamedPlatformHandle(const base::StringPiece& name)
- : name(name.as_string()) {}
-
- bool is_valid() const { return !name.empty(); }
-
- std::string name;
-};
-#elif defined(OS_WIN)
-struct MOJO_SYSTEM_IMPL_EXPORT NamedPlatformHandle {
- NamedPlatformHandle() {}
- explicit NamedPlatformHandle(const base::StringPiece& name)
- : name(base::UTF8ToUTF16(name)) {}
-
- explicit NamedPlatformHandle(const base::StringPiece16& name)
- : name(name.as_string()) {}
-
- bool is_valid() const { return !name.empty(); }
-
- base::string16 pipe_name() const { return L"\\\\.\\pipe\\mojo." + name; }
-
- base::string16 name;
-};
-#else
-#error "Platform not yet supported."
-#endif
-
-} // namespace edk
-} // namespace mojo
-
-#endif // MOJO_EDK_EMBEDDER_NAMED_PLATFORM_HANDLE_H_
diff --git a/mojo/edk/embedder/pending_process_connection.cc b/mojo/edk/embedder/pending_process_connection.cc
deleted file mode 100644
index d6be76e..0000000
--- a/mojo/edk/embedder/pending_process_connection.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-// 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/embedder/pending_process_connection.h"
-
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/embedder_internal.h"
-#include "mojo/edk/system/core.h"
-
-namespace mojo {
-namespace edk {
-
-PendingProcessConnection::PendingProcessConnection()
- : process_token_(GenerateRandomToken()) {
- DCHECK(internal::g_core);
-}
-
-PendingProcessConnection::~PendingProcessConnection() {
- if (has_message_pipes_ && !connected_) {
- DCHECK(internal::g_core);
- internal::g_core->ChildLaunchFailed(process_token_);
- }
-}
-
-ScopedMessagePipeHandle PendingProcessConnection::CreateMessagePipe(
- std::string* token) {
- has_message_pipes_ = true;
- DCHECK(internal::g_core);
- *token = GenerateRandomToken();
- return internal::g_core->CreateParentMessagePipe(*token, process_token_);
-}
-
-void PendingProcessConnection::Connect(
- base::ProcessHandle process,
- ConnectionParams connection_params,
- const ProcessErrorCallback& error_callback) {
- // It's now safe to avoid cleanup in the destructor, as the lifetime of any
- // associated resources is effectively bound to the |channel| passed to
- // AddChild() below.
- DCHECK(!connected_);
- connected_ = true;
-
- DCHECK(internal::g_core);
- internal::g_core->AddChild(process, std::move(connection_params),
- process_token_, error_callback);
-}
-
-} // namespace edk
-} // namespace mojo
diff --git a/mojo/edk/embedder/pending_process_connection.h b/mojo/edk/embedder/pending_process_connection.h
deleted file mode 100644
index ca18227..0000000
--- a/mojo/edk/embedder/pending_process_connection.h
+++ /dev/null
@@ -1,124 +0,0 @@
-// 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_EDK_EMBEDDER_PENDING_PROCESS_CONNECTION_H_
-#define MOJO_EDK_EMBEDDER_PENDING_PROCESS_CONNECTION_H_
-
-#include <string>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/process/process_handle.h"
-#include "mojo/edk/embedder/connection_params.h"
-#include "mojo/edk/embedder/scoped_platform_handle.h"
-#include "mojo/edk/system/system_impl_export.h"
-#include "mojo/public/cpp/system/message_pipe.h"
-
-namespace mojo {
-namespace edk {
-
-using ProcessErrorCallback = base::Callback<void(const std::string& error)>;
-
-// Represents a potential connection to an external process. Use this object
-// to make other processes reachable from this one via Mojo IPC. Typical usage
-// might look something like:
-//
-// PendingProcessConnection connection;
-//
-// std::string pipe_token;
-// ScopedMessagePipeHandle pipe = connection.CreateMessagePipe(&pipe_token);
-//
-// // New pipes to the process are fully functional and can be used right
-// // away, even if the process doesn't exist yet.
-// GoDoSomethingInteresting(std::move(pipe));
-//
-// ScopedPlatformChannelPair channel;
-//
-// // Give the pipe token to the child process via command-line.
-// child_command_line.AppendSwitchASCII("yer-pipe", pipe_token);
-//
-// // Magic child process launcher which gives one end of the pipe to the
-// // new process.
-// LaunchProcess(child_command_line, channel.PassClientHandle());
-//
-// // Some time later...
-// connection.Connect(new_process, channel.PassServerHandle());
-//
-// If at any point during the above process, |connection| is destroyed before
-// Connect() can be called, |pipe| will imminently behave as if its peer has
-// been closed.
-//
-// Otherwise, if the remote process in this example eventually calls:
-//
-// mojo::edk::SetParentPipeHandle(std::move(client_channel_handle));
-//
-// std::string token = command_line.GetSwitchValueASCII("yer-pipe");
-// ScopedMessagePipeHandle pipe = mojo::edk::CreateChildMessagePipe(token);
-//
-// it will be connected to this process, and its |pipe| will be connected to
-// this process's |pipe|.
-//
-// If the remote process exits or otherwise closes its client channel handle
-// before calling CreateChildMessagePipe for a given message pipe token,
-// this process's end of the corresponding message pipe will imminently behave
-// as if its peer has been closed.
-//
-class MOJO_SYSTEM_IMPL_EXPORT PendingProcessConnection {
- public:
- PendingProcessConnection();
- ~PendingProcessConnection();
-
- // Creates a message pipe associated with a new globally unique string value
- // which will be placed in |*token|.
- //
- // The other end of the new pipe is obtainable in the remote process (or in
- // this process, to facilitate "single-process mode" in some applications) by
- // passing the new |*token| value to mojo::edk::CreateChildMessagePipe. It's
- // the caller's responsibility to communicate the value of |*token| to the
- // remote process by any means available, e.g. a command-line argument on
- // process launch, or some other out-of-band communication channel for an
- // existing process.
- //
- // NOTES: This may be called any number of times to create multiple message
- // pipes to the same remote process. This call ALWAYS succeeds, returning
- // a valid message pipe handle and populating |*token| with a new unique
- // string value.
- ScopedMessagePipeHandle CreateMessagePipe(std::string* token);
-
- // Connects to the process. This must be called at most once, with the process
- // handle in |process|.
- //
- // |connection_param| contains the platform handle of an OS pipe which can be
- // used to communicate with the connected process. The other end of that pipe
- // must ultimately be passed to mojo::edk::SetParentPipeHandle in the remote
- // process, and getting that end of the pipe into the other process is the
- // embedder's responsibility.
- //
- // If this method is not called by the time the PendingProcessConnection is
- // destroyed, it's assumed that the process is unavailable (e.g. process
- // launch failed or the process has otherwise been terminated early), and
- // any associated resources, such as remote endpoints of messages pipes
- // created by CreateMessagePipe above) will be cleaned up at that time.
- void Connect(
- base::ProcessHandle process,
- ConnectionParams connection_params,
- const ProcessErrorCallback& error_callback = ProcessErrorCallback());
-
- private:
- // A GUID representing a potential new process to be connected to this one.
- const std::string process_token_;
-
- // Indicates whether this object has been used to create new message pipes.
- bool has_message_pipes_ = false;
-
- // Indicates whether Connect() has been called yet.
- bool connected_ = false;
-
- DISALLOW_COPY_AND_ASSIGN(PendingProcessConnection);
-};
-
-} // namespace edk
-} // namespace mojo
-
-#endif // MOJO_EDK_EMBEDDER_PENDING_PROCESS_CONNECTION_H_
diff --git a/mojo/edk/embedder/platform_channel_pair.h b/mojo/edk/embedder/platform_channel_pair.h
index 9c93f76..f80de89 100644
--- a/mojo/edk/embedder/platform_channel_pair.h
+++ b/mojo/edk/embedder/platform_channel_pair.h
@@ -69,7 +69,6 @@
// |PrepareToPassClientHandleToChildProcess()| and launched the child (using
// the provided data), to create a client handle connected to the server
// handle (in the parent process).
- // TODO(jcivelli): remove the command_line param. http://crbug.com/670106
static ScopedPlatformHandle PassClientHandleFromParentProcess(
const base::CommandLine& command_line);
diff --git a/mojo/edk/embedder/platform_channel_pair_posix.cc b/mojo/edk/embedder/platform_channel_pair_posix.cc
index 4760e95..55f3ae2 100644
--- a/mojo/edk/embedder/platform_channel_pair_posix.cc
+++ b/mojo/edk/embedder/platform_channel_pair_posix.cc
@@ -35,15 +35,6 @@
namespace {
-#if defined(OS_ANDROID)
-enum {
- // Leave room for any other descriptors defined in content for example.
- // TODO(jcivelli): consider changing base::GlobalDescriptors to generate a
- // key when setting the file descriptor (http://crbug.com/676442).
- kAndroidClientHandleDescriptor =
- base::GlobalDescriptors::kBaseDescriptor + 10000,
-};
-#else
bool IsTargetDescriptorUsed(
const base::FileHandleMappingVector& file_handle_mapping,
int target_fd) {
@@ -53,7 +44,6 @@
}
return false;
}
-#endif
} // namespace
@@ -102,21 +92,13 @@
PlatformChannelPair::PassClientHandleFromParentProcessFromString(
const std::string& value) {
int client_fd = -1;
-#if defined(OS_ANDROID)
- base::GlobalDescriptors::Key key = -1;
- if (value.empty() || !base::StringToUint(value, &key)) {
- LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch;
- return ScopedPlatformHandle();
- }
- client_fd = base::GlobalDescriptors::GetInstance()->Get(key);
-#else
if (value.empty() ||
!base::StringToInt(value, &client_fd) ||
client_fd < base::GlobalDescriptors::kBaseDescriptor) {
LOG(ERROR) << "Missing or invalid --" << kMojoPlatformChannelHandleSwitch;
return ScopedPlatformHandle();
}
-#endif
+
return ScopedPlatformHandle(PlatformHandle(client_fd));
}
@@ -142,12 +124,6 @@
std::string
PlatformChannelPair::PrepareToPassClientHandleToChildProcessAsString(
HandlePassingInformation* handle_passing_info) const {
-#if defined(OS_ANDROID)
- int fd = client_handle_.get().handle;
- handle_passing_info->push_back(
- std::pair<int, int>(fd, kAndroidClientHandleDescriptor));
- return base::UintToString(kAndroidClientHandleDescriptor);
-#else
DCHECK(handle_passing_info);
// This is an arbitrary sanity check. (Note that this guarantees that the loop
// below will terminate sanely.)
@@ -165,7 +141,6 @@
handle_passing_info->push_back(
std::pair<int, int>(client_handle_.get().handle, target_fd));
return base::IntToString(target_fd);
-#endif
}
} // namespace edk
diff --git a/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc b/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc
index a3fd275..e05cd79 100644
--- a/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc
+++ b/mojo/edk/embedder/platform_channel_pair_posix_unittest.cc
@@ -153,7 +153,7 @@
for (size_t j = 1; j <= i; j++) {
base::FilePath unused;
base::ScopedFILE fp(
- base::CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &unused));
+ base::CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
ASSERT_TRUE(fp);
ASSERT_EQ(j, fwrite(std::string(j, c).data(), 1, j, fp.get()));
platform_handles->push_back(
@@ -209,7 +209,7 @@
{
base::FilePath unused;
base::ScopedFILE fp(
- base::CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &unused));
+ base::CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
ASSERT_TRUE(fp);
ASSERT_EQ(file_contents.size(),
fwrite(file_contents.data(), 1, file_contents.size(), fp.get()));
diff --git a/mojo/edk/embedder/platform_channel_utils_posix.cc b/mojo/edk/embedder/platform_channel_utils_posix.cc
index 689b6ee..3171844 100644
--- a/mojo/edk/embedder/platform_channel_utils_posix.cc
+++ b/mojo/edk/embedder/platform_channel_utils_posix.cc
@@ -8,13 +8,9 @@
#include <sys/socket.h>
#include <unistd.h>
-#include <utility>
-
-#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "build/build_config.h"
-#include "mojo/edk/embedder/scoped_platform_handle.h"
#if !defined(OS_NACL)
#include <sys/uio.h>
@@ -26,55 +22,6 @@
namespace mojo {
namespace edk {
-namespace {
-
-#if !defined(OS_NACL)
-bool IsRecoverableError() {
- return errno == ECONNABORTED || errno == EMFILE || errno == ENFILE ||
- errno == ENOMEM || errno == ENOBUFS;
-}
-
-bool GetPeerEuid(PlatformHandle handle, uid_t* peer_euid) {
- DCHECK(peer_euid);
-#if defined(OS_MACOSX) || defined(OS_OPENBSD) || defined(OS_FREEBSD)
- uid_t socket_euid;
- gid_t socket_gid;
- if (getpeereid(handle.handle, &socket_euid, &socket_gid) < 0) {
- PLOG(ERROR) << "getpeereid " << handle.handle;
- return false;
- }
- *peer_euid = socket_euid;
- return true;
-#else
- struct ucred cred;
- socklen_t cred_len = sizeof(cred);
- if (getsockopt(handle.handle, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) <
- 0) {
- PLOG(ERROR) << "getsockopt " << handle.handle;
- return false;
- }
- if (static_cast<unsigned>(cred_len) < sizeof(cred)) {
- NOTREACHED() << "Truncated ucred from SO_PEERCRED?";
- return false;
- }
- *peer_euid = cred.uid;
- return true;
-#endif
-}
-
-bool IsPeerAuthorized(PlatformHandle peer_handle) {
- uid_t peer_euid;
- if (!GetPeerEuid(peer_handle, &peer_euid))
- return false;
- if (peer_euid != geteuid()) {
- DLOG(ERROR) << "Client euid is not authorised";
- return false;
- }
- return true;
-}
-#endif // !defined(OS_NACL)
-
-} // namespace
// On Linux, |SIGPIPE| is suppressed by passing |MSG_NOSIGNAL| to
// |send()|/|sendmsg()|. (There is no way of suppressing |SIGPIPE| on
@@ -246,37 +193,5 @@
return result;
}
-bool ServerAcceptConnection(PlatformHandle server_handle,
- ScopedPlatformHandle* connection_handle,
- bool check_peer_user) {
- DCHECK(server_handle.is_valid());
- connection_handle->reset();
-#if defined(OS_NACL)
- NOTREACHED();
- return false;
-#else
- ScopedPlatformHandle accept_handle(
- PlatformHandle(HANDLE_EINTR(accept(server_handle.handle, NULL, 0))));
- if (!accept_handle.is_valid())
- return IsRecoverableError();
-
- // Verify that the IPC channel peer is running as the same user.
- if (check_peer_user && !IsPeerAuthorized(accept_handle.get())) {
- return true;
- }
-
- if (!base::SetNonBlocking(accept_handle.get().handle)) {
- PLOG(ERROR) << "base::SetNonBlocking() failed "
- << accept_handle.get().handle;
- // It's safe to keep listening on |server_handle| even if the attempt to set
- // O_NONBLOCK failed on the client fd.
- return true;
- }
-
- *connection_handle = std::move(accept_handle);
- return true;
-#endif // defined(OS_NACL)
-}
-
} // namespace edk
} // namespace mojo
diff --git a/mojo/edk/embedder/platform_channel_utils_posix.h b/mojo/edk/embedder/platform_channel_utils_posix.h
index 23cfa92..8b24bd0 100644
--- a/mojo/edk/embedder/platform_channel_utils_posix.h
+++ b/mojo/edk/embedder/platform_channel_utils_posix.h
@@ -18,7 +18,6 @@
namespace mojo {
namespace edk {
-class ScopedPlatformHandle;
// The maximum number of handles that can be sent "at once" using
// |PlatformChannelSendmsgWithHandles()|. This must be less than the Linux
@@ -71,16 +70,6 @@
std::deque<PlatformHandle>* platform_handles,
bool block = false);
-// Returns false if |server_handle| encounters an unrecoverable error.
-// Returns true if it's valid to keep listening on |server_handle|. In this
-// case, it's possible that a connection wasn't successfully established; then,
-// |connection_handle| will be invalid. If |check_peer_user| is True, the
-// connection will be rejected if the peer is running as a different user.
-MOJO_SYSTEM_IMPL_EXPORT bool ServerAcceptConnection(
- PlatformHandle server_handle,
- ScopedPlatformHandle* connection_handle,
- bool check_peer_user = true);
-
} // namespace edk
} // namespace mojo
diff --git a/mojo/edk/embedder/platform_handle.h b/mojo/edk/embedder/platform_handle.h
index 4866e75..4f76009 100644
--- a/mojo/edk/embedder/platform_handle.h
+++ b/mojo/edk/embedder/platform_handle.h
@@ -53,9 +53,6 @@
int handle = -1;
- // A POSIX handle may be a listen handle that can accept a connection.
- bool needs_connection = false;
-
#if defined(OS_MACOSX) && !defined(OS_IOS)
mach_port_t port = MACH_PORT_NULL;
#endif
diff --git a/mojo/edk/embedder/platform_handle_utils_posix.cc b/mojo/edk/embedder/platform_handle_utils_posix.cc
index 5604f96..fc2a0db 100644
--- a/mojo/edk/embedder/platform_handle_utils_posix.cc
+++ b/mojo/edk/embedder/platform_handle_utils_posix.cc
@@ -15,9 +15,7 @@
DCHECK(platform_handle.is_valid());
// Note that |dup()| returns -1 on error (which is exactly the value we use
// for invalid |PlatformHandle| FDs).
- PlatformHandle duped(dup(platform_handle.handle));
- duped.needs_connection = platform_handle.needs_connection;
- return ScopedPlatformHandle(duped);
+ return ScopedPlatformHandle(PlatformHandle(dup(platform_handle.handle)));
}
} // namespace edk
diff --git a/mojo/edk/embedder/platform_shared_buffer.cc b/mojo/edk/embedder/platform_shared_buffer.cc
index 58af44d..53d8edc 100644
--- a/mojo/edk/embedder/platform_shared_buffer.cc
+++ b/mojo/edk/embedder/platform_shared_buffer.cc
@@ -253,28 +253,21 @@
bool PlatformSharedBuffer::InitFromPlatformHandlePair(
ScopedPlatformHandle rw_platform_handle,
ScopedPlatformHandle ro_platform_handle) {
-#if defined(OS_MACOSX)
+#if defined(OS_WIN) || defined(OS_MACOSX)
NOTREACHED();
return false;
-#else // defined(OS_MACOSX)
+#else
+ DCHECK(!shared_memory_);
-#if defined(OS_WIN)
- base::SharedMemoryHandle handle(rw_platform_handle.release().handle,
- base::GetCurrentProcId());
- base::SharedMemoryHandle ro_handle(ro_platform_handle.release().handle,
- base::GetCurrentProcId());
-#else // defined(OS_WIN)
base::SharedMemoryHandle handle(rw_platform_handle.release().handle, false);
+ shared_memory_.reset(new base::SharedMemory(handle, false));
+
base::SharedMemoryHandle ro_handle(ro_platform_handle.release().handle,
false);
-#endif // defined(OS_WIN)
-
- DCHECK(!shared_memory_);
- shared_memory_.reset(new base::SharedMemory(handle, false));
ro_shared_memory_.reset(new base::SharedMemory(ro_handle, true));
- return true;
-#endif // defined(OS_MACOSX)
+ return true;
+#endif
}
void PlatformSharedBuffer::InitFromSharedMemoryHandle(
diff --git a/mojo/edk/embedder/process_delegate.h b/mojo/edk/embedder/process_delegate.h
new file mode 100644
index 0000000..144f4a3
--- /dev/null
+++ b/mojo/edk/embedder/process_delegate.h
@@ -0,0 +1,31 @@
+// 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_PROCESS_DELEGATE_H_
+#define MOJO_EDK_EMBEDDER_PROCESS_DELEGATE_H_
+
+#include "base/macros.h"
+#include "mojo/edk/system/system_impl_export.h"
+
+namespace mojo {
+namespace edk {
+
+// An interface for process delegates.
+class MOJO_SYSTEM_IMPL_EXPORT ProcessDelegate {
+ public:
+ // Called when |ShutdownIPCSupport()| has completed work on the I/O thread.
+ virtual void OnShutdownComplete() = 0;
+
+ protected:
+ ProcessDelegate() {}
+ virtual ~ProcessDelegate() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProcessDelegate);
+};
+
+} // namespace edk
+} // namespace mojo
+
+#endif // MOJO_EDK_EMBEDDER_PROCESS_DELEGATE_H_
diff --git a/mojo/edk/js/test/BUILD.gn b/mojo/edk/js/test/BUILD.gn
new file mode 100644
index 0000000..c5a78c3
--- /dev/null
+++ b/mojo/edk/js/test/BUILD.gn
@@ -0,0 +1,46 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//testing/test.gni")
+
+test("js_unittests") {
+ output_name = "mojo_js_unittests"
+
+ deps = [
+ "//base",
+ "//gin:gin_test",
+ "//mojo/edk/js",
+ "//mojo/edk/test:run_all_unittests",
+ "//mojo/edk/test:test_support",
+ "//mojo/public/cpp/system",
+ "//mojo/public/interfaces/bindings/tests:test_interfaces",
+ "//mojo/public/interfaces/bindings/tests:test_interfaces_experimental",
+ "//mojo/public/js:tests",
+ ]
+
+ sources = [
+ "//mojo/edk/js/handle_unittest.cc",
+ "run_js_tests.cc",
+ ]
+}
+
+test("js_integration_tests") {
+ output_name = "mojo_js_integration_tests"
+
+ deps = [
+ "//base",
+ "//gin:gin_test",
+ "//mojo/edk/js",
+ "//mojo/edk/js/tests:js_to_cpp_tests",
+ "//mojo/edk/test:run_all_unittests",
+ "//mojo/edk/test:test_support",
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/interfaces/bindings/tests:test_interfaces",
+ "//mojo/public/js:bindings",
+ ]
+
+ sources = [
+ "run_js_integration_tests.cc",
+ ]
+}
diff --git a/mojo/edk/js/test/hexdump.js b/mojo/edk/js/test/hexdump.js
new file mode 100644
index 0000000..b36c47f
--- /dev/null
+++ b/mojo/edk/js/test/hexdump.js
@@ -0,0 +1,34 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define(function() {
+ function hexify(value, length) {
+ var hex = value.toString(16);
+ while (hex.length < length)
+ hex = "0" + hex;
+ return hex;
+ }
+
+ function dumpArray(bytes) {
+ var dumped = "";
+ for (var i = 0; i < bytes.length; ++i) {
+ dumped += hexify(bytes[i], 2);
+
+ if (i % 16 == 15) {
+ dumped += "\n";
+ continue;
+ }
+
+ if (i % 2 == 1)
+ dumped += " ";
+ if (i % 8 == 7)
+ dumped += " ";
+ }
+ return dumped;
+ }
+
+ var exports = {};
+ exports.dumpArray = dumpArray;
+ return exports;
+});
diff --git a/mojo/edk/js/test/run_js_integration_tests.cc b/mojo/edk/js/test/run_js_integration_tests.cc
new file mode 100644
index 0000000..00d3e96
--- /dev/null
+++ b/mojo/edk/js/test/run_js_integration_tests.cc
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "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/modules/timer.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(gin::TimerModule::kName, gin::TimerModule::GetModule);
+ AddBuiltinModule(Threading::kModuleName, Threading::GetModule);
+ AddBuiltinModule(Support::kModuleName, Support::GetModule);
+ }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestRunnerDelegate);
+};
+
+void RunTest(std::string test) {
+ base::FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ path = path.AppendASCII("mojo")
+ .AppendASCII("edk")
+ .AppendASCII("js")
+ .AppendASCII("tests")
+ .AppendASCII(test);
+ TestRunnerDelegate delegate;
+ gin::RunTestFromFile(path, &delegate, false);
+}
+
+TEST(JSTest, connection) {
+ RunTest("connection_tests.js");
+}
+
+TEST(JSTest, sample_service) {
+ RunTest("sample_service_tests.js");
+}
+
+} // namespace
+} // namespace js
+} // namespace edk
+} // namespace mojo
diff --git a/mojo/edk/js/test/run_js_tests.cc b/mojo/edk/js/test/run_js_tests.cc
new file mode 100644
index 0000000..d0e8edc
--- /dev/null
+++ b/mojo/edk/js/test/run_js_tests.cc
@@ -0,0 +1,68 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "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 "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(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(test);
+ TestRunnerDelegate delegate;
+ gin::RunTestFromFile(path, &delegate, run_until_idle);
+}
+
+// TODO(abarth): Should we autogenerate these stubs from GYP?
+TEST(JSTest, core) {
+ RunTest("core_unittests.js", true);
+}
+
+TEST(JSTest, codec) {
+ RunTest("codec_unittests.js", true);
+}
+
+TEST(JSTest, struct) {
+ RunTest("struct_unittests.js", true);
+}
+
+TEST(JSTest, union) {
+ RunTest("union_unittests.js", true);
+}
+
+TEST(JSTest, validation) {
+ RunTest("validation_unittests.js", true);
+}
+
+} // namespace
+} // namespace js
+} // namespace edk
+} // namespace mojo
diff --git a/mojo/edk/js/tests/BUILD.gn b/mojo/edk/js/tests/BUILD.gn
index 41850d7..b21cdbc 100644
--- a/mojo/edk/js/tests/BUILD.gn
+++ b/mojo/edk/js/tests/BUILD.gn
@@ -2,34 +2,21 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import("//mojo/public/tools/bindings/mojom.gni")
-import("//testing/test.gni")
+import("../../../../mojo/public/tools/bindings/mojom.gni")
-# 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") {
+source_set("js_to_cpp_tests") {
testonly = true
- deps = [
- ":mojo_js_integration_tests",
- ":mojo_js_unittests",
- ]
-}
-test("mojo_js_integration_tests") {
deps = [
":js_to_cpp_bindings",
"//gin:gin_test",
"//mojo/common",
"//mojo/edk/js",
- "//mojo/edk/test:run_all_unittests",
+ "//mojo/edk/test:test_support",
"//mojo/public/cpp/bindings",
"//mojo/public/cpp/system",
- "//mojo/public/js:bindings",
+ "//mojo/public/interfaces/bindings/tests:test_interfaces",
+ "//mojo/public/interfaces/bindings/tests:test_interfaces_experimental",
]
sources = [
@@ -37,7 +24,9 @@
]
data = [
+ "connection_tests.js",
"js_to_cpp_tests.js",
+ "sample_service_tests.js",
]
configs += [ "//v8:external_startup_data" ]
@@ -48,22 +37,3 @@
"js_to_cpp.mojom",
]
}
-
-test("mojo_js_unittests") {
- deps = [
- "//base",
- "//gin:gin_test",
- "//mojo/edk/js",
- "//mojo/edk/test:run_all_unittests",
- "//mojo/edk/test:test_support",
- "//mojo/public/cpp/system",
- "//mojo/public/interfaces/bindings/tests:test_interfaces",
- "//mojo/public/interfaces/bindings/tests:test_interfaces_experimental",
- "//mojo/public/js:tests",
- ]
-
- sources = [
- "//mojo/edk/js/handle_unittest.cc",
- "run_js_unittests.cc",
- ]
-}
diff --git a/mojo/edk/js/tests/connection_tests.js b/mojo/edk/js/tests/connection_tests.js
new file mode 100644
index 0000000..0b4b213
--- /dev/null
+++ b/mojo/edk/js/tests/connection_tests.js
@@ -0,0 +1,157 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define([
+ "gin/test/expect",
+ "mojo/public/js/connection",
+ "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,
+ connection,
+ 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 = Object.create(
+ sample_service.Service.stubClass.prototype);
+
+ ServiceImpl.prototype.frobinate = function(foo, baz, port) {
+ expect(foo.name).toBe("Example name");
+ expect(baz).toBe(sample_service.Service.BazOptions.REGULAR);
+ expect(core.close(port)).toBe(core.RESULT_OK);
+
+ return Promise.resolve({result: 42});
+ };
+
+ var pipe = core.createMessagePipe();
+ var anotherPipe = core.createMessagePipe();
+ var sourcePipe = core.createMessagePipe();
+
+ var connection0 = new connection.Connection(pipe.handle0, ServiceImpl);
+
+ var connection1 = new connection.Connection(
+ pipe.handle1, undefined, sample_service.Service.proxyClass);
+
+ var foo = new sample_service.Foo();
+ foo.bar = new sample_service.Bar();
+ foo.name = "Example name";
+ foo.source = sourcePipe.handle0;
+ var promise = connection1.remote.frobinate(
+ foo, sample_service.Service.BazOptions.REGULAR, anotherPipe.handle0)
+ .then(function(response) {
+ expect(response.result).toBe(42);
+
+ connection0.close();
+ connection1.close();
+
+ return Promise.resolve();
+ });
+
+ // sourcePipe.handle1 hasn't been closed yet.
+ expect(core.close(sourcePipe.handle1)).toBe(core.RESULT_OK);
+
+ // anotherPipe.handle1 hasn't been closed yet.
+ expect(core.close(anotherPipe.handle1)).toBe(core.RESULT_OK);
+
+ return promise;
+ }
+
+ function testWriteToClosedPipe() {
+ var pipe = core.createMessagePipe();
+ var anotherPipe = core.createMessagePipe();
+
+ var connection1 = new connection.Connection(
+ pipe.handle1, function() {}, sample_service.Service.proxyClass);
+
+ // Close the other end of the pipe.
+ core.close(pipe.handle0);
+
+ // Not observed yet because we haven't pumped events yet.
+ expect(connection1.encounteredError()).toBeFalsy();
+
+ var promise = connection1.remote.frobinate(
+ null, sample_service.Service.BazOptions.REGULAR, anotherPipe.handle0)
+ .then(function(response) {
+ return Promise.reject("Unexpected response");
+ }).catch(function(e) {
+ // We should observe the closed pipe.
+ expect(connection1.encounteredError()).toBeTruthy();
+ return Promise.resolve();
+ });
+
+ // Write failures are not reported.
+ expect(connection1.encounteredError()).toBeFalsy();
+
+ return promise;
+ }
+
+ function testRequestResponse() {
+ // ProviderImpl ------------------------------------------------------------
+
+ function ProviderImpl() {
+ }
+
+ ProviderImpl.prototype =
+ Object.create(sample_interfaces.Provider.stubClass.prototype);
+
+ ProviderImpl.prototype.echoString = function(a) {
+ return Promise.resolve({a: a});
+ };
+
+ ProviderImpl.prototype.echoStrings = function(a, b) {
+ return Promise.resolve({a: a, b: b});
+ };
+
+ var pipe = core.createMessagePipe();
+
+ var connection0 = new connection.Connection(pipe.handle0, ProviderImpl);
+
+ var connection1 = new connection.Connection(
+ pipe.handle1, undefined, sample_interfaces.Provider.proxyClass);
+
+ var promise = connection1.remote.echoString("hello")
+ .then(function(response) {
+ expect(response.a).toBe("hello");
+ return connection1.remote.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 connection1.remote.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/edk/js/tests/js_to_cpp_tests.cc b/mojo/edk/js/tests/js_to_cpp_tests.cc
index e5e6bd1..6d3426d 100644
--- a/mojo/edk/js/tests/js_to_cpp_tests.cc
+++ b/mojo/edk/js/tests/js_to_cpp_tests.cc
@@ -15,15 +15,14 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/threading/thread_task_runner_handle.h"
#include "gin/array_buffer.h"
#include "gin/public/isolate_holder.h"
#include "gin/v8_initializer.h"
#include "mojo/common/data_pipe_utils.h"
#include "mojo/edk/js/mojo_runner_delegate.h"
#include "mojo/edk/js/tests/js_to_cpp.mojom.h"
+#include "mojo/edk/test/test_utils.h"
#include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/bindings/lib/validation_errors.h"
#include "mojo/public/cpp/system/core.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -105,11 +104,12 @@
args->double_val = kExpectedDoubleVal;
args->double_inf = kExpectedDoubleInf;
args->double_nan = kExpectedDoubleNan;
- args->name.emplace("coming");
- args->string_array.emplace(3);
- (*args->string_array)[0] = "one";
- (*args->string_array)[1] = "two";
- (*args->string_array)[2] = "three";
+ args->name = "coming";
+ Array<String> string_array(3);
+ string_array[0] = "one";
+ string_array[1] = "two";
+ string_array[2] = "three";
+ args->string_array = std::move(string_array);
return args;
}
@@ -128,10 +128,10 @@
EXPECT_EQ(kExpectedDoubleVal, arg->double_val);
EXPECT_EQ(kExpectedDoubleInf, arg->double_inf);
EXPECT_NAN(arg->double_nan);
- EXPECT_EQ(std::string("coming"), *arg->name);
- EXPECT_EQ(std::string("one"), (*arg->string_array)[0]);
- EXPECT_EQ(std::string("two"), (*arg->string_array)[1]);
- EXPECT_EQ(std::string("three"), (*arg->string_array)[2]);
+ EXPECT_EQ(std::string("coming"), arg->name.get());
+ EXPECT_EQ(std::string("one"), arg->string_array[0].get());
+ EXPECT_EQ(std::string("two"), arg->string_array[1].get());
+ EXPECT_EQ(std::string("three"), arg->string_array[2].get());
CheckDataPipe(std::move(arg->data_handle));
CheckMessagePipe(arg->message_handle.get());
}
@@ -146,23 +146,18 @@
// More forgiving checks are needed in the face of potentially corrupt
// messages. The values don't matter so long as all accesses are within
// bounds.
-void CheckCorruptedString(const std::string& arg) {
+void CheckCorruptedString(const String& arg) {
+ if (arg.is_null())
+ return;
for (size_t i = 0; i < arg.size(); ++i)
g_waste_accumulator += arg[i];
}
-void CheckCorruptedString(const base::Optional<std::string>& arg) {
- if (!arg)
+void CheckCorruptedStringArray(const Array<String>& string_array) {
+ if (string_array.is_null())
return;
- CheckCorruptedString(*arg);
-}
-
-void CheckCorruptedStringArray(
- const base::Optional<std::vector<std::string>>& string_array) {
- if (!string_array)
- return;
- for (size_t i = 0; i < string_array->size(); ++i)
- CheckCorruptedString((*string_array)[i]);
+ for (size_t i = 0; i < string_array.size(); ++i)
+ CheckCorruptedString(string_array[i]);
}
void CheckCorruptedDataPipe(MojoHandle data_pipe_handle) {
@@ -302,7 +297,7 @@
EXPECT_EQ(-1, special_arg->si32);
EXPECT_EQ(-1, special_arg->si16);
EXPECT_EQ(-1, special_arg->si8);
- EXPECT_EQ(std::string("going"), *special_arg->name);
+ EXPECT_EQ(std::string("going"), special_arg->name.To<std::string>());
CheckSampleEchoArgsList(list->next);
}
@@ -388,11 +383,11 @@
cpp_side->set_run_loop(&run_loop_);
js_to_cpp::JsSidePtr js_side;
- auto js_side_proxy = MakeRequest(&js_side);
+ auto js_side_proxy = GetProxy(&js_side);
cpp_side->set_js_side(js_side.get());
js_to_cpp::CppSidePtr cpp_side_ptr;
- cpp_side->Bind(MakeRequest(&cpp_side_ptr));
+ cpp_side->Bind(GetProxy(&cpp_side_ptr));
js_side->SetCppSide(std::move(cpp_side_ptr));
@@ -404,7 +399,7 @@
gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode,
gin::IsolateHolder::kStableV8Extras,
gin::ArrayBufferAllocator::SharedInstance());
- gin::IsolateHolder instance(base::ThreadTaskRunnerHandle::Get());
+ gin::IsolateHolder instance;
MojoRunnerDelegate delegate;
gin::ShellRunner runner(&delegate, instance.isolate());
delegate.Start(&runner, js_side_proxy.PassMessagePipe().release().value(),
@@ -434,18 +429,12 @@
}
TEST_F(JsToCppTest, BitFlip) {
- // These tests generate a lot of expected validation errors. Suppress logging.
- mojo::internal::ScopedSuppressValidationErrorLoggingForTests log_suppression;
-
BitFlipCppSideConnection cpp_side_connection;
RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
EXPECT_TRUE(cpp_side_connection.DidSucceed());
}
TEST_F(JsToCppTest, BackPointer) {
- // These tests generate a lot of expected validation errors. Suppress logging.
- mojo::internal::ScopedSuppressValidationErrorLoggingForTests log_suppression;
-
BackPointerCppSideConnection cpp_side_connection;
RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
EXPECT_TRUE(cpp_side_connection.DidSucceed());
diff --git a/mojo/edk/js/tests/js_to_cpp_tests.js b/mojo/edk/js/tests/js_to_cpp_tests.js
index 6b69fca..ddecc4b 100644
--- a/mojo/edk/js/tests/js_to_cpp_tests.js
+++ b/mojo/edk/js/tests/js_to_cpp_tests.js
@@ -6,9 +6,10 @@
'console',
'mojo/edk/js/tests/js_to_cpp.mojom',
'mojo/public/js/bindings',
+ 'mojo/public/js/connection',
'mojo/public/js/connector',
'mojo/public/js/core',
-], function (console, jsToCpp, bindings, connector, core) {
+], function (console, jsToCpp, bindings, connection, connector, core) {
var retainedJsSide;
var retainedJsSideStub;
var sampleData;
@@ -21,9 +22,11 @@
};
function JsSideConnection() {
- this.binding = new bindings.Binding(jsToCpp.JsSide, this);
}
+ JsSideConnection.prototype =
+ Object.create(jsToCpp.JsSide.stubClass.prototype);
+
JsSideConnection.prototype.setCppSide = function(cppSide) {
this.cppSide_ = cppSide;
this.cppSide_.startTest();
@@ -217,7 +220,9 @@
for (i = 0; i < sampleMessage.length; ++i) {
sampleMessage[i] = 255 - i;
}
+ retainedJsSideStub =
+ connection.bindHandleToStub(jsSideRequestHandle, jsToCpp.JsSide);
retainedJsSide = new JsSideConnection;
- retainedJsSide.binding.bind(jsSideRequestHandle);
+ bindings.StubBindings(retainedJsSideStub).delegate = retainedJsSide;
};
});
diff --git a/mojo/edk/js/tests/sample_service_tests.js b/mojo/edk/js/tests/sample_service_tests.js
new file mode 100644
index 0000000..2b065a8
--- /dev/null
+++ b/mojo/edk/js/tests/sample_service_tests.js
@@ -0,0 +1,176 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+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/connection",
+ "mojo/public/js/core",
+ "mojo/public/js/threading",
+ ], function(expect, sample, imported, imported2, connection, 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 = Object.create(sample.Service.stubClass.prototype);
+
+ ServiceImpl.prototype.frobinate = function(foo, baz, port) {
+ checkFoo(foo);
+ expect(baz).toBe(sample.Service.BazOptions.EXTRA);
+ expect(core.isHandle(port)).toBeTruthy();
+ return Promise.resolve({result: 1234});
+ };
+
+ var foo = makeFoo();
+ checkFoo(foo);
+
+ var sampleServicePipe = core.createMessagePipe();
+ var connection0 = new connection.Connection(sampleServicePipe.handle0,
+ ServiceImpl);
+ var connection1 = new connection.Connection(
+ sampleServicePipe.handle1, undefined, sample.Service.proxyClass);
+
+ var pipe = core.createMessagePipe();
+ var promise = connection1.remote.frobinate(
+ foo, sample.Service.BazOptions.EXTRA, pipe.handle0)
+ .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/edk/system/BUILD.gn b/mojo/edk/system/BUILD.gn
index b0acf23..84f1f30 100644
--- a/mojo/edk/system/BUILD.gn
+++ b/mojo/edk/system/BUILD.gn
@@ -20,10 +20,9 @@
"awakable_list.cc",
"awakable_list.h",
"broker.h",
- "broker_host.cc",
"broker_host.h",
+ "broker_host_posix.cc",
"broker_posix.cc",
- "broker_win.cc",
"channel.cc",
"channel.h",
"channel_posix.cc",
@@ -76,6 +75,7 @@
public_deps = [
"//mojo/edk/embedder",
+ "//mojo/edk/embedder:delegates",
"//mojo/edk/embedder:platform",
"//mojo/edk/system/ports",
"//mojo/public/c/system",
@@ -104,7 +104,7 @@
if (is_nacl && !is_nacl_nonsfi) {
sources -= [
- "broker_host.cc",
+ "broker_host_posix.cc",
"broker_posix.cc",
"channel_posix.cc",
]
@@ -154,7 +154,6 @@
test("mojo_system_unittests") {
sources = [
"awakable_list_unittest.cc",
- "channel_unittest.cc",
"core_test_base.cc",
"core_test_base.h",
"core_unittest.cc",
@@ -187,7 +186,6 @@
"//mojo/edk/system/ports:tests",
"//mojo/edk/test:run_all_unittests",
"//mojo/edk/test:test_support",
- "//testing/gmock",
"//testing/gtest",
]
diff --git a/mojo/edk/system/broker_host.cc b/mojo/edk/system/broker_host.cc
deleted file mode 100644
index 6096034..0000000
--- a/mojo/edk/system/broker_host.cc
+++ /dev/null
@@ -1,153 +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/edk/system/broker_host.h"
-
-#include <utility>
-
-#include "base/logging.h"
-#include "base/memory/ref_counted.h"
-#include "base/message_loop/message_loop.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "mojo/edk/embedder/named_platform_channel_pair.h"
-#include "mojo/edk/embedder/named_platform_handle.h"
-#include "mojo/edk/embedder/platform_handle_vector.h"
-#include "mojo/edk/embedder/platform_shared_buffer.h"
-#include "mojo/edk/system/broker_messages.h"
-
-namespace mojo {
-namespace edk {
-
-BrokerHost::BrokerHost(base::ProcessHandle client_process,
- ScopedPlatformHandle platform_handle)
-#if defined(OS_WIN)
- : client_process_(client_process)
-#endif
-{
- CHECK(platform_handle.is_valid());
-
- base::MessageLoop::current()->AddDestructionObserver(this);
-
- channel_ = Channel::Create(this, ConnectionParams(std::move(platform_handle)),
- base::ThreadTaskRunnerHandle::Get());
- channel_->Start();
-}
-
-BrokerHost::~BrokerHost() {
- // We're always destroyed on the creation thread, which is the IO thread.
- base::MessageLoop::current()->RemoveDestructionObserver(this);
-
- if (channel_)
- channel_->ShutDown();
-}
-
-bool BrokerHost::PrepareHandlesForClient(PlatformHandleVector* handles) {
-#if defined(OS_WIN)
- if (!Channel::Message::RewriteHandles(
- base::GetCurrentProcessHandle(), client_process_, handles)) {
- // NOTE: We only log an error here. We do not signal a logical error or
- // prevent any message from being sent. The client should handle unexpected
- // invalid handles appropriately.
- DLOG(ERROR) << "Failed to rewrite one or more handles to broker client.";
- return false;
- }
-#endif
- return true;
-}
-
-bool BrokerHost::SendChannel(ScopedPlatformHandle handle) {
- CHECK(handle.is_valid());
- CHECK(channel_);
-
-#if defined(OS_WIN)
- InitData* data;
- Channel::MessagePtr message =
- CreateBrokerMessage(BrokerMessageType::INIT, 1, 0, &data);
- data->pipe_name_length = 0;
-#else
- Channel::MessagePtr message =
- CreateBrokerMessage(BrokerMessageType::INIT, 1, nullptr);
-#endif
- ScopedPlatformHandleVectorPtr handles;
- handles.reset(new PlatformHandleVector(1));
- handles->at(0) = handle.release();
-
- // This may legitimately fail on Windows if the client process is in another
- // session, e.g., is an elevated process.
- if (!PrepareHandlesForClient(handles.get()))
- return false;
-
- message->SetHandles(std::move(handles));
- channel_->Write(std::move(message));
- return true;
-}
-
-#if defined(OS_WIN)
-
-void BrokerHost::SendNamedChannel(const base::StringPiece16& pipe_name) {
- InitData* data;
- base::char16* name_data;
- Channel::MessagePtr message = CreateBrokerMessage(
- BrokerMessageType::INIT, 0, sizeof(*name_data) * pipe_name.length(),
- &data, reinterpret_cast<void**>(&name_data));
- data->pipe_name_length = static_cast<uint32_t>(pipe_name.length());
- std::copy(pipe_name.begin(), pipe_name.end(), name_data);
- channel_->Write(std::move(message));
-}
-
-#endif // defined(OS_WIN)
-
-void BrokerHost::OnBufferRequest(uint32_t num_bytes) {
- scoped_refptr<PlatformSharedBuffer> read_only_buffer;
- scoped_refptr<PlatformSharedBuffer> buffer =
- PlatformSharedBuffer::Create(num_bytes);
- if (buffer)
- read_only_buffer = buffer->CreateReadOnlyDuplicate();
- if (!read_only_buffer)
- buffer = nullptr;
-
- Channel::MessagePtr message = CreateBrokerMessage(
- BrokerMessageType::BUFFER_RESPONSE, buffer ? 2 : 0, nullptr);
- if (buffer) {
- ScopedPlatformHandleVectorPtr handles;
- handles.reset(new PlatformHandleVector(2));
- handles->at(0) = buffer->PassPlatformHandle().release();
- handles->at(1) = read_only_buffer->PassPlatformHandle().release();
- PrepareHandlesForClient(handles.get());
- message->SetHandles(std::move(handles));
- }
-
- channel_->Write(std::move(message));
-}
-
-void BrokerHost::OnChannelMessage(const void* payload,
- size_t payload_size,
- ScopedPlatformHandleVectorPtr handles) {
- if (payload_size < sizeof(BrokerMessageHeader))
- return;
-
- const BrokerMessageHeader* header =
- static_cast<const BrokerMessageHeader*>(payload);
- switch (header->type) {
- case BrokerMessageType::BUFFER_REQUEST:
- if (payload_size ==
- sizeof(BrokerMessageHeader) + sizeof(BufferRequestData)) {
- const BufferRequestData* request =
- reinterpret_cast<const BufferRequestData*>(header + 1);
- OnBufferRequest(request->size);
- }
- break;
-
- default:
- LOG(ERROR) << "Unexpected broker message type: " << header->type;
- break;
- }
-}
-
-void BrokerHost::OnChannelError() { delete this; }
-
-void BrokerHost::WillDestroyCurrentMessageLoop() { delete this; }
-
-} // namespace edk
-} // namespace mojo
diff --git a/mojo/edk/system/broker_host.h b/mojo/edk/system/broker_host.h
index a7995d2..b8f68c4 100644
--- a/mojo/edk/system/broker_host.h
+++ b/mojo/edk/system/broker_host.h
@@ -5,13 +5,8 @@
#ifndef MOJO_EDK_SYSTEM_BROKER_HOST_H_
#define MOJO_EDK_SYSTEM_BROKER_HOST_H_
-#include <stdint.h>
-
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
-#include "base/process/process_handle.h"
-#include "base/strings/string_piece.h"
-#include "mojo/edk/embedder/platform_handle_vector.h"
#include "mojo/edk/embedder/scoped_platform_handle.h"
#include "mojo/edk/system/channel.h"
@@ -23,21 +18,14 @@
class BrokerHost : public Channel::Delegate,
public base::MessageLoop::DestructionObserver {
public:
- BrokerHost(base::ProcessHandle client_process, ScopedPlatformHandle handle);
+ explicit BrokerHost(ScopedPlatformHandle platform_handle);
// Send |handle| to the child, to be used to establish a NodeChannel to us.
- bool SendChannel(ScopedPlatformHandle handle);
-
-#if defined(OS_WIN)
- // Sends a named channel to the child. Like above, but for named pipes.
- void SendNamedChannel(const base::StringPiece16& pipe_name);
-#endif
+ void SendChannel(ScopedPlatformHandle handle);
private:
~BrokerHost() override;
- bool PrepareHandlesForClient(PlatformHandleVector* handles);
-
// Channel::Delegate:
void OnChannelMessage(const void* payload,
size_t payload_size,
@@ -47,11 +35,7 @@
// base::MessageLoop::DestructionObserver:
void WillDestroyCurrentMessageLoop() override;
- void OnBufferRequest(uint32_t num_bytes);
-
-#if defined(OS_WIN)
- base::ProcessHandle client_process_;
-#endif
+ void OnBufferRequest(size_t num_bytes);
scoped_refptr<Channel> channel_;
diff --git a/mojo/edk/system/broker_host_posix.cc b/mojo/edk/system/broker_host_posix.cc
new file mode 100644
index 0000000..ed8e213
--- /dev/null
+++ b/mojo/edk/system/broker_host_posix.cc
@@ -0,0 +1,129 @@
+// 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/broker_host.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/edk/embedder/embedder_internal.h"
+#include "mojo/edk/embedder/platform_channel_utils_posix.h"
+#include "mojo/edk/embedder/platform_handle_vector.h"
+#include "mojo/edk/embedder/platform_shared_buffer.h"
+#include "mojo/edk/system/broker_messages.h"
+
+namespace mojo {
+namespace edk {
+
+namespace {
+// To prevent abuse, limit the maximum size of shared memory buffers.
+// TODO(amistry): Re-consider this limit, or do something smarter.
+const size_t kMaxSharedBufferSize = 16 * 1024 * 1024;
+}
+
+BrokerHost::BrokerHost(ScopedPlatformHandle platform_handle) {
+ CHECK(platform_handle.is_valid());
+
+ base::MessageLoop::current()->AddDestructionObserver(this);
+
+ channel_ = Channel::Create(this, std::move(platform_handle),
+ base::ThreadTaskRunnerHandle::Get());
+ channel_->Start();
+}
+
+BrokerHost::~BrokerHost() {
+ // We're always destroyed on the creation thread, which is the IO thread.
+ base::MessageLoop::current()->RemoveDestructionObserver(this);
+
+ if (channel_)
+ channel_->ShutDown();
+}
+
+void BrokerHost::SendChannel(ScopedPlatformHandle handle) {
+ CHECK(handle.is_valid());
+ CHECK(channel_);
+
+ Channel::MessagePtr message =
+ CreateBrokerMessage(BrokerMessageType::INIT, 1, nullptr);
+ ScopedPlatformHandleVectorPtr handles;
+ handles.reset(new PlatformHandleVector(1));
+ handles->at(0) = handle.release();
+ message->SetHandles(std::move(handles));
+
+ channel_->Write(std::move(message));
+}
+
+void BrokerHost::OnBufferRequest(size_t num_bytes) {
+ scoped_refptr<PlatformSharedBuffer> buffer;
+ scoped_refptr<PlatformSharedBuffer> read_only_buffer;
+ if (num_bytes <= kMaxSharedBufferSize) {
+ buffer = PlatformSharedBuffer::Create(num_bytes);
+ if (buffer)
+ read_only_buffer = buffer->CreateReadOnlyDuplicate();
+ if (!read_only_buffer)
+ buffer = nullptr;
+ } else {
+ LOG(ERROR) << "Shared buffer request too large: " << num_bytes;
+ }
+
+ Channel::MessagePtr message = CreateBrokerMessage(
+ BrokerMessageType::BUFFER_RESPONSE, buffer ? 2 : 0, nullptr);
+ if (buffer) {
+ ScopedPlatformHandleVectorPtr handles;
+ handles.reset(new PlatformHandleVector(2));
+ handles->at(0) = buffer->PassPlatformHandle().release();
+ handles->at(1) = read_only_buffer->PassPlatformHandle().release();
+ message->SetHandles(std::move(handles));
+ }
+
+ channel_->Write(std::move(message));
+}
+
+void BrokerHost::OnChannelMessage(const void* payload,
+ size_t payload_size,
+ ScopedPlatformHandleVectorPtr handles) {
+ if (payload_size < sizeof(BrokerMessageHeader))
+ return;
+
+ const BrokerMessageHeader* header =
+ static_cast<const BrokerMessageHeader*>(payload);
+ switch (header->type) {
+ case BrokerMessageType::BUFFER_REQUEST:
+ if (payload_size ==
+ sizeof(BrokerMessageHeader) + sizeof(BufferRequestData)) {
+ const BufferRequestData* request =
+ reinterpret_cast<const BufferRequestData*>(header + 1);
+ OnBufferRequest(request->size);
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ LOG(ERROR) << "Unexpected broker message type: " << header->type;
+}
+
+void BrokerHost::OnChannelError() {
+ if (channel_) {
+ channel_->ShutDown();
+ channel_ = nullptr;
+ }
+
+ delete this;
+}
+
+void BrokerHost::WillDestroyCurrentMessageLoop() {
+ delete this;
+}
+
+} // namespace edk
+} // namespace mojo
diff --git a/mojo/edk/system/broker_messages.h b/mojo/edk/system/broker_messages.h
index 0f0dd9d..a67be15 100644
--- a/mojo/edk/system/broker_messages.h
+++ b/mojo/edk/system/broker_messages.h
@@ -23,41 +23,26 @@
uint32_t padding;
};
-static_assert(IsAlignedForChannelMessage(sizeof(BrokerMessageHeader)),
+static_assert(sizeof(BrokerMessageHeader) % kChannelMessageAlignment == 0,
"Invalid header size.");
struct BufferRequestData {
uint32_t size;
};
-#if defined(OS_WIN)
-struct InitData {
- // NOTE: InitData in the payload is followed by string16 data with exactly
- // |pipe_name_length| wide characters (i.e., |pipe_name_length|*2 bytes.)
- // This applies to Windows only.
- uint32_t pipe_name_length;
-};
-#endif
-
#pragma pack(pop)
template <typename T>
-inline Channel::MessagePtr CreateBrokerMessage(
- BrokerMessageType type,
- size_t num_handles,
- size_t extra_data_size,
- T** out_message_data,
- void** out_extra_data = nullptr) {
- const size_t message_size = sizeof(BrokerMessageHeader) +
- sizeof(**out_message_data) + extra_data_size;
+inline Channel::MessagePtr CreateBrokerMessage(BrokerMessageType type,
+ size_t num_handles,
+ T** out_message_data) {
+ const size_t message_size = sizeof(BrokerMessageHeader) + sizeof(T);
Channel::MessagePtr message(new Channel::Message(message_size, num_handles));
BrokerMessageHeader* header =
reinterpret_cast<BrokerMessageHeader*>(message->mutable_payload());
header->type = type;
header->padding = 0;
*out_message_data = reinterpret_cast<T*>(header + 1);
- if (out_extra_data)
- *out_extra_data = *out_message_data + 1;
return message;
}
diff --git a/mojo/edk/system/broker_posix.cc b/mojo/edk/system/broker_posix.cc
index 8742f70..62b37b3 100644
--- a/mojo/edk/system/broker_posix.cc
+++ b/mojo/edk/system/broker_posix.cc
@@ -38,7 +38,7 @@
PLOG(ERROR) << "Recvmsg error";
error = true;
} else if (static_cast<size_t>(read_result) != message->data_num_bytes()) {
- LOG(ERROR) << "Invalid node channel message";
+ LOG(ERROR) << "Invalid node channel message. Expected " << message->data_num_bytes() << " got " << read_result;
error = true;
} else if (incoming_platform_handles.size() != expected_num_handles) {
LOG(ERROR) << "Received unexpected number of handles";
@@ -94,7 +94,7 @@
BufferRequestData* buffer_request;
Channel::MessagePtr out_message = CreateBrokerMessage(
- BrokerMessageType::BUFFER_REQUEST, 0, 0, &buffer_request);
+ BrokerMessageType::BUFFER_REQUEST, 0, &buffer_request);
buffer_request->size = num_bytes;
ssize_t write_result = PlatformChannelWrite(
sync_channel_.get(), out_message->data(), out_message->data_num_bytes());
diff --git a/mojo/edk/system/channel.cc b/mojo/edk/system/channel.cc
index 8a44d36..56509b5 100644
--- a/mojo/edk/system/channel.cc
+++ b/mojo/edk/system/channel.cc
@@ -4,7 +4,6 @@
#include "mojo/edk/system/channel.h"
-#include <stddef.h>
#include <string.h>
#include <algorithm>
@@ -27,47 +26,27 @@
namespace {
-static_assert(
- IsAlignedForChannelMessage(sizeof(Channel::Message::LegacyHeader)),
- "Invalid LegacyHeader size.");
+static_assert(sizeof(Channel::Message::Header) % kChannelMessageAlignment == 0,
+ "Invalid Header size.");
-static_assert(IsAlignedForChannelMessage(sizeof(Channel::Message::Header)),
- "Invalid Header size.");
-
-static_assert(sizeof(Channel::Message::LegacyHeader) == 8,
- "LegacyHeader must be 8 bytes on ChromeOS and Android");
-
-static_assert(offsetof(Channel::Message::LegacyHeader, num_bytes) ==
- offsetof(Channel::Message::Header, num_bytes),
- "num_bytes should be at the same offset in both Header structs.");
-static_assert(offsetof(Channel::Message::LegacyHeader, message_type) ==
- offsetof(Channel::Message::Header, message_type),
- "message_type should be at the same offset in both Header "
- "structs.");
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+static_assert(sizeof(Channel::Message::Header) == 8,
+ "Header must be 8 bytes on ChromeOS and Android");
+#endif
} // namespace
const size_t kReadBufferSize = 4096;
-const size_t kMaxUnusedReadBufferCapacity = 4096;
+const size_t kMaxUnusedReadBufferCapacity = 64 * 1024;
const size_t kMaxChannelMessageSize = 256 * 1024 * 1024;
const size_t kMaxAttachedHandles = 128;
-Channel::Message::Message(size_t payload_size, size_t max_handles)
-#if defined(MOJO_EDK_LEGACY_PROTOCOL)
- : Message(payload_size, max_handles, MessageType::NORMAL_LEGACY) {
-}
-#else
- : Message(payload_size, max_handles, MessageType::NORMAL) {
-}
-#endif
-
Channel::Message::Message(size_t payload_size,
size_t max_handles,
- MessageType message_type)
+ Header::MessageType message_type)
: max_handles_(max_handles) {
DCHECK_LE(max_handles_, kMaxAttachedHandles);
- const bool is_legacy_message = (message_type == MessageType::NORMAL_LEGACY);
size_t extra_header_size = 0;
#if defined(OS_WIN)
// On Windows we serialize HANDLEs into the extra header space.
@@ -83,16 +62,16 @@
}
#endif
// Pad extra header data to be aliged to |kChannelMessageAlignment| bytes.
- if (!IsAlignedForChannelMessage(extra_header_size)) {
+ if (extra_header_size % kChannelMessageAlignment) {
extra_header_size += kChannelMessageAlignment -
(extra_header_size % kChannelMessageAlignment);
}
- DCHECK(IsAlignedForChannelMessage(extra_header_size));
- const size_t header_size =
- is_legacy_message ? sizeof(LegacyHeader) : sizeof(Header);
- DCHECK(extra_header_size == 0 || !is_legacy_message);
+ DCHECK_EQ(0u, extra_header_size % kChannelMessageAlignment);
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+ DCHECK_EQ(0u, extra_header_size);
+#endif
- size_ = header_size + extra_header_size + payload_size;
+ size_ = sizeof(Header) + extra_header_size + payload_size;
data_ = static_cast<char*>(base::AlignedAlloc(size_,
kChannelMessageAlignment));
// Only zero out the header and not the payload. Since the payload is going to
@@ -100,21 +79,21 @@
// performance issue when dealing with large messages. Any sanitizer errors
// complaining about an uninitialized read in the payload area should be
// treated as an error and fixed.
- memset(data_, 0, header_size + extra_header_size);
+ memset(data_, 0, sizeof(Header) + extra_header_size);
+ header_ = reinterpret_cast<Header*>(data_);
DCHECK_LE(size_, std::numeric_limits<uint32_t>::max());
- legacy_header()->num_bytes = static_cast<uint32_t>(size_);
+ header_->num_bytes = static_cast<uint32_t>(size_);
- DCHECK_LE(header_size + extra_header_size,
+ DCHECK_LE(sizeof(Header) + extra_header_size,
std::numeric_limits<uint16_t>::max());
- legacy_header()->message_type = message_type;
-
- if (is_legacy_message) {
- legacy_header()->num_handles = static_cast<uint16_t>(max_handles);
- } else {
- header()->num_header_bytes =
- static_cast<uint16_t>(header_size + extra_header_size);
- }
+ header_->message_type = message_type;
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+ header_->num_handles = static_cast<uint16_t>(max_handles);
+#else
+ header_->num_header_bytes =
+ static_cast<uint16_t>(sizeof(Header) + extra_header_size);
+#endif
if (max_handles_ > 0) {
#if defined(OS_WIN)
@@ -142,93 +121,79 @@
// static
Channel::MessagePtr Channel::Message::Deserialize(const void* data,
size_t data_num_bytes) {
- if (data_num_bytes < sizeof(LegacyHeader))
+ if (data_num_bytes < sizeof(Header))
return nullptr;
- const LegacyHeader* legacy_header =
- reinterpret_cast<const LegacyHeader*>(data);
- if (legacy_header->num_bytes != data_num_bytes) {
- DLOG(ERROR) << "Decoding invalid message: " << legacy_header->num_bytes
+ const Header* header = reinterpret_cast<const Header*>(data);
+ if (header->num_bytes != data_num_bytes) {
+ DLOG(ERROR) << "Decoding invalid message: " << header->num_bytes
<< " != " << data_num_bytes;
return nullptr;
}
- const Header* header = nullptr;
- if (legacy_header->message_type == MessageType::NORMAL)
- header = reinterpret_cast<const Header*>(data);
-
- uint32_t extra_header_size = 0;
- size_t payload_size = 0;
- const char* payload = nullptr;
- if (!header) {
- payload_size = data_num_bytes - sizeof(LegacyHeader);
- payload = static_cast<const char*>(data) + sizeof(LegacyHeader);
- } else {
- if (header->num_bytes < header->num_header_bytes ||
- header->num_header_bytes < sizeof(Header)) {
- DLOG(ERROR) << "Decoding invalid message: " << header->num_bytes << " < "
- << header->num_header_bytes;
- return nullptr;
- }
- extra_header_size = header->num_header_bytes - sizeof(Header);
- payload_size = data_num_bytes - header->num_header_bytes;
- payload = static_cast<const char*>(data) + header->num_header_bytes;
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+ size_t payload_size = data_num_bytes - sizeof(Header);
+ const char* payload = static_cast<const char*>(data) + sizeof(Header);
+#else
+ if (header->num_bytes < header->num_header_bytes ||
+ header->num_header_bytes < sizeof(Header)) {
+ DLOG(ERROR) << "Decoding invalid message: " << header->num_bytes << " < "
+ << header->num_header_bytes;
+ return nullptr;
}
+ uint32_t extra_header_size = header->num_header_bytes - sizeof(Header);
+ size_t payload_size = data_num_bytes - header->num_header_bytes;
+ const char* payload =
+ static_cast<const char*>(data) + header->num_header_bytes;
+#endif // defined(MOJO_EDK_LEGACY_PROTOCOL)
+
#if defined(OS_WIN)
uint32_t max_handles = extra_header_size / sizeof(HandleEntry);
#elif defined(OS_MACOSX) && !defined(OS_IOS)
- if (extra_header_size > 0 &&
- extra_header_size < sizeof(MachPortsExtraHeader)) {
+ if (extra_header_size < sizeof(MachPortsExtraHeader)) {
DLOG(ERROR) << "Decoding invalid message: " << extra_header_size << " < "
<< sizeof(MachPortsExtraHeader);
return nullptr;
}
- uint32_t max_handles =
- extra_header_size == 0
- ? 0
- : (extra_header_size - sizeof(MachPortsExtraHeader)) /
- sizeof(MachPortsEntry);
+ uint32_t max_handles = (extra_header_size - sizeof(MachPortsExtraHeader)) /
+ sizeof(MachPortsEntry);
#else
const uint32_t max_handles = 0;
#endif // defined(OS_WIN)
- const uint16_t num_handles =
- header ? header->num_handles : legacy_header->num_handles;
- if (num_handles > max_handles || max_handles > kMaxAttachedHandles) {
- DLOG(ERROR) << "Decoding invalid message: " << num_handles << " > "
- << max_handles;
+ if (header->num_handles > max_handles || max_handles > kMaxAttachedHandles) {
+ DLOG(ERROR) << "Decoding invalid message:" << header->num_handles
+ << " > " << max_handles;
return nullptr;
}
- MessagePtr message(
- new Message(payload_size, max_handles, legacy_header->message_type));
+ MessagePtr message(new Message(payload_size, max_handles));
DCHECK_EQ(message->data_num_bytes(), data_num_bytes);
// Copy all payload bytes.
if (payload_size)
memcpy(message->mutable_payload(), payload, payload_size);
- if (header) {
- DCHECK_EQ(message->extra_header_size(), extra_header_size);
- DCHECK_EQ(message->header()->num_header_bytes, header->num_header_bytes);
+#if !defined(MOJO_EDK_LEGACY_PROTOCOL)
+ DCHECK_EQ(message->extra_header_size(), extra_header_size);
+ DCHECK_EQ(message->header_->num_header_bytes, header->num_header_bytes);
- if (message->extra_header_size()) {
- // Copy extra header bytes.
- memcpy(message->mutable_extra_header(),
- static_cast<const char*>(data) + sizeof(Header),
- message->extra_header_size());
- }
- message->header()->num_handles = header->num_handles;
- } else {
- message->legacy_header()->num_handles = legacy_header->num_handles;
+ if (message->extra_header_size()) {
+ // Copy extra header bytes.
+ memcpy(message->mutable_extra_header(),
+ static_cast<const char*>(data) + sizeof(Header),
+ message->extra_header_size());
}
+#endif
+ message->header_->num_handles = header->num_handles;
#if defined(OS_WIN)
- ScopedPlatformHandleVectorPtr handles(new PlatformHandleVector(num_handles));
- for (size_t i = 0; i < num_handles; i++) {
- (*handles)[i].handle =
- base::win::Uint32ToHandle(message->handles_[i].handle);
+ ScopedPlatformHandleVectorPtr handles(
+ new PlatformHandleVector(header->num_handles));
+ for (size_t i = 0; i < header->num_handles; i++) {
+ (*handles)[i].handle = reinterpret_cast<HANDLE>(
+ static_cast<uintptr_t>(message->handles_[i].handle));
}
message->SetHandles(std::move(handles));
#endif
@@ -236,46 +201,12 @@
return message;
}
-const void* Channel::Message::extra_header() const {
- DCHECK(!is_legacy_message());
- return data_ + sizeof(Header);
-}
-
-void* Channel::Message::mutable_extra_header() {
- DCHECK(!is_legacy_message());
- return data_ + sizeof(Header);
-}
-
-size_t Channel::Message::extra_header_size() const {
- return header()->num_header_bytes - sizeof(Header);
-}
-
-void* Channel::Message::mutable_payload() {
- if (is_legacy_message())
- return static_cast<void*>(legacy_header() + 1);
- return data_ + header()->num_header_bytes;
-}
-
-const void* Channel::Message::payload() const {
- if (is_legacy_message())
- return static_cast<const void*>(legacy_header() + 1);
- return data_ + header()->num_header_bytes;
-}
-
size_t Channel::Message::payload_size() const {
- if (is_legacy_message())
- return legacy_header()->num_bytes - sizeof(LegacyHeader);
- return size_ - header()->num_header_bytes;
-}
-
-size_t Channel::Message::num_handles() const {
- return is_legacy_message() ? legacy_header()->num_handles
- : header()->num_handles;
-}
-
-bool Channel::Message::has_handles() const {
- return (is_legacy_message() ? legacy_header()->num_handles
- : header()->num_handles) > 0;
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+ return header_->num_bytes - sizeof(Header);
+#else
+ return size_ - header_->num_header_bytes;
+#endif
}
#if defined(OS_MACOSX) && !defined(OS_IOS)
@@ -293,44 +224,31 @@
}
#endif
-bool Channel::Message::is_legacy_message() const {
- return legacy_header()->message_type == MessageType::NORMAL_LEGACY;
-}
-
-Channel::Message::LegacyHeader* Channel::Message::legacy_header() const {
- return reinterpret_cast<LegacyHeader*>(data_);
-}
-
-Channel::Message::Header* Channel::Message::header() const {
- DCHECK(!is_legacy_message());
- return reinterpret_cast<Header*>(data_);
-}
-
void Channel::Message::SetHandles(ScopedPlatformHandleVectorPtr new_handles) {
- if (is_legacy_message()) {
- // Old semantics for ChromeOS and Android
- if (legacy_header()->num_handles == 0) {
- CHECK(!new_handles || new_handles->size() == 0);
- return;
- }
- CHECK(new_handles && new_handles->size() == legacy_header()->num_handles);
- std::swap(handle_vector_, new_handles);
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+ // Old semantics for ChromeOS and Android
+ if (header_->num_handles == 0) {
+ CHECK(!new_handles || new_handles->size() == 0);
return;
}
+ CHECK(new_handles && new_handles->size() == header_->num_handles);
+ std::swap(handle_vector_, new_handles);
+#else
if (max_handles_ == 0) {
CHECK(!new_handles || new_handles->size() == 0);
return;
}
CHECK(new_handles && new_handles->size() <= max_handles_);
- header()->num_handles = static_cast<uint16_t>(new_handles->size());
+ header_->num_handles = static_cast<uint16_t>(new_handles->size());
std::swap(handle_vector_, new_handles);
#if defined(OS_WIN)
memset(handles_, 0, extra_header_size());
for (size_t i = 0; i < handle_vector_->size(); i++)
handles_[i].handle = base::win::HandleToUint32((*handle_vector_)[i].handle);
#endif // defined(OS_WIN)
+#endif // defined(MOJO_EDK_LEGACY_PROTOCOL)
#if defined(OS_MACOSX) && !defined(OS_IOS)
size_t mach_port_index = 0;
@@ -362,12 +280,12 @@
}
mach_ports_header_->num_ports = 0;
}
-#endif
- if (is_legacy_message())
- legacy_header()->num_handles = 0;
- else
- header()->num_handles = 0;
+ header_->num_handles = 0;
return std::move(handle_vector_);
+#else
+ header_->num_handles = 0;
+ return std::move(handle_vector_);
+#endif
}
ScopedPlatformHandleVectorPtr Channel::Message::TakeHandlesForTransport() {
@@ -573,71 +491,58 @@
bool Channel::OnReadComplete(size_t bytes_read, size_t *next_read_size_hint) {
bool did_dispatch_message = false;
read_buffer_->Claim(bytes_read);
- while (read_buffer_->num_occupied_bytes() >= sizeof(Message::LegacyHeader)) {
+ while (read_buffer_->num_occupied_bytes() >= sizeof(Message::Header)) {
// Ensure the occupied data is properly aligned. If it isn't, a SIGBUS could
// happen on architectures that don't allow misaligned words access (i.e.
// anything other than x86). Only re-align when necessary to avoid copies.
- if (!IsAlignedForChannelMessage(
- reinterpret_cast<uintptr_t>(read_buffer_->occupied_bytes()))) {
+ if (reinterpret_cast<uintptr_t>(read_buffer_->occupied_bytes()) %
+ kChannelMessageAlignment != 0)
read_buffer_->Realign();
- }
- // We have at least enough data available for a LegacyHeader.
- const Message::LegacyHeader* legacy_header =
- reinterpret_cast<const Message::LegacyHeader*>(
- read_buffer_->occupied_bytes());
-
- if (legacy_header->num_bytes < sizeof(Message::LegacyHeader) ||
- legacy_header->num_bytes > kMaxChannelMessageSize) {
- LOG(ERROR) << "Invalid message size: " << legacy_header->num_bytes;
+ // We have at least enough data available for a MessageHeader.
+ const Message::Header* header = reinterpret_cast<const Message::Header*>(
+ read_buffer_->occupied_bytes());
+ if (header->num_bytes < sizeof(Message::Header) ||
+ header->num_bytes > kMaxChannelMessageSize) {
+ LOG(ERROR) << "Invalid message size: " << header->num_bytes;
return false;
}
- if (read_buffer_->num_occupied_bytes() < legacy_header->num_bytes) {
+ if (read_buffer_->num_occupied_bytes() < header->num_bytes) {
// Not enough data available to read the full message. Hint to the
// implementation that it should try reading the full size of the message.
*next_read_size_hint =
- legacy_header->num_bytes - read_buffer_->num_occupied_bytes();
+ header->num_bytes - read_buffer_->num_occupied_bytes();
return true;
}
- const Message::Header* header = nullptr;
- if (legacy_header->message_type != Message::MessageType::NORMAL_LEGACY) {
- header = reinterpret_cast<const Message::Header*>(legacy_header);
- }
-
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
size_t extra_header_size = 0;
const void* extra_header = nullptr;
- size_t payload_size = 0;
- void* payload = nullptr;
- if (header) {
- if (header->num_header_bytes < sizeof(Message::Header) ||
- header->num_header_bytes > header->num_bytes) {
- LOG(ERROR) << "Invalid message header size: "
- << header->num_header_bytes;
- return false;
- }
- extra_header_size = header->num_header_bytes - sizeof(Message::Header);
- extra_header = extra_header_size ? header + 1 : nullptr;
- payload_size = header->num_bytes - header->num_header_bytes;
- payload = payload_size
- ? reinterpret_cast<Message::Header*>(
- const_cast<char*>(read_buffer_->occupied_bytes()) +
- header->num_header_bytes)
- : nullptr;
- } else {
- payload_size = legacy_header->num_bytes - sizeof(Message::LegacyHeader);
- payload = payload_size
- ? const_cast<Message::LegacyHeader*>(&legacy_header[1])
- : nullptr;
+ size_t payload_size = header->num_bytes - sizeof(Message::Header);
+ void* payload = payload_size ? const_cast<Message::Header*>(&header[1])
+ : nullptr;
+#else
+ if (header->num_header_bytes < sizeof(Message::Header) ||
+ header->num_header_bytes > header->num_bytes) {
+ LOG(ERROR) << "Invalid message header size: " << header->num_header_bytes;
+ return false;
}
+ size_t extra_header_size =
+ header->num_header_bytes - sizeof(Message::Header);
+ const void* extra_header = extra_header_size ? header + 1 : nullptr;
+ size_t payload_size = header->num_bytes - header->num_header_bytes;
+ void* payload =
+ payload_size ? reinterpret_cast<Message::Header*>(
+ const_cast<char*>(read_buffer_->occupied_bytes()) +
+ header->num_header_bytes)
+ : nullptr;
+#endif // defined(MOJO_EDK_LEGACY_PROTOCOL)
- const uint16_t num_handles =
- header ? header->num_handles : legacy_header->num_handles;
ScopedPlatformHandleVectorPtr handles;
- if (num_handles > 0) {
- if (!GetReadPlatformHandles(num_handles, extra_header, extra_header_size,
- &handles)) {
+ if (header->num_handles > 0) {
+ if (!GetReadPlatformHandles(header->num_handles, extra_header,
+ extra_header_size, &handles)) {
return false;
}
@@ -648,9 +553,8 @@
}
// We've got a complete message! Dispatch it and try another.
- if (legacy_header->message_type != Message::MessageType::NORMAL_LEGACY &&
- legacy_header->message_type != Message::MessageType::NORMAL) {
- if (!OnControlMessage(legacy_header->message_type, payload, payload_size,
+ if (header->message_type != Message::Header::MessageType::NORMAL) {
+ if (!OnControlMessage(header->message_type, payload, payload_size,
std::move(handles))) {
return false;
}
@@ -660,7 +564,7 @@
did_dispatch_message = true;
}
- read_buffer_->Discard(legacy_header->num_bytes);
+ read_buffer_->Discard(header->num_bytes);
}
*next_read_size_hint = did_dispatch_message ? 0 : kReadBufferSize;
@@ -672,7 +576,7 @@
delegate_->OnChannelError();
}
-bool Channel::OnControlMessage(Message::MessageType message_type,
+bool Channel::OnControlMessage(Message::Header::MessageType message_type,
const void* payload,
size_t payload_size,
ScopedPlatformHandleVectorPtr handles) {
diff --git a/mojo/edk/system/channel.h b/mojo/edk/system/channel.h
index 33a510c..aa6d70c 100644
--- a/mojo/edk/system/channel.h
+++ b/mojo/edk/system/channel.h
@@ -10,7 +10,6 @@
#include "base/memory/ref_counted.h"
#include "base/process/process_handle.h"
#include "base/task_runner.h"
-#include "mojo/edk/embedder/connection_params.h"
#include "mojo/edk/embedder/platform_handle_vector.h"
#include "mojo/edk/embedder/scoped_platform_handle.h"
@@ -19,70 +18,53 @@
const size_t kChannelMessageAlignment = 8;
-constexpr bool IsAlignedForChannelMessage(size_t n) {
- return n % kChannelMessageAlignment == 0;
-}
-
// Channel provides a thread-safe interface to read and write arbitrary
// delimited messages over an underlying I/O channel, optionally transferring
// one or more platform handles in the process.
-class MOJO_SYSTEM_IMPL_EXPORT Channel
- : public base::RefCountedThreadSafe<Channel> {
+class Channel : public base::RefCountedThreadSafe<Channel> {
public:
struct Message;
using MessagePtr = std::unique_ptr<Message>;
// A message to be written to a channel.
- struct MOJO_SYSTEM_IMPL_EXPORT Message {
- enum class MessageType : uint16_t {
- // An old format normal message, that uses the LegacyHeader.
- // Only used on Android and ChromeOS.
- // TODO(jcivelli): remove legacy support when Arc++ has updated to Mojo
- // with normal versioned messages. crbug.com/695645
- NORMAL_LEGACY = 0,
-#if defined(OS_MACOSX)
- // A control message containing handles to echo back.
- HANDLES_SENT,
- // A control message containing handles that can now be closed.
- HANDLES_SENT_ACK,
-#endif
- // A normal message that uses Header and can contain extra header values.
- NORMAL,
- };
-
+ struct Message {
#pragma pack(push, 1)
- // Old message wire format for ChromeOS and Android, used by NORMAL_LEGACY
- // messages.
- struct LegacyHeader {
+ struct Header {
+ enum class MessageType : uint16_t {
+ // A normal message.
+ NORMAL = 0,
+#if defined(OS_MACOSX)
+ // A control message containing handles to echo back.
+ HANDLES_SENT,
+ // A control message containing handles that can now be closed.
+ HANDLES_SENT_ACK,
+#endif
+ };
+
// Message size in bytes, including the header.
uint32_t num_bytes;
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+ // Old message wire format for ChromeOS and Android.
// Number of attached handles.
uint16_t num_handles;
MessageType message_type;
- };
-
- // Header used by NORMAL messages.
- // To preserve backward compatibility with LegacyHeader, the num_bytes and
- // message_type field must be at the same offset as in LegacyHeader.
- struct Header {
- // Message size in bytes, including the header.
- uint32_t num_bytes;
-
+#else
// Total size of header, including extra header data (i.e. HANDLEs on
// windows).
uint16_t num_header_bytes;
- MessageType message_type;
-
// Number of attached handles. May be less than the reserved handle
// storage size in this message on platforms that serialise handles as
// data (i.e. HANDLEs on Windows, Mach ports on OSX).
uint16_t num_handles;
+ MessageType message_type;
+
char padding[6];
+#endif // defined(MOJO_EDK_LEGACY_PROTOCOL)
};
#if defined(OS_MACOSX) && !defined(OS_IOS)
@@ -103,7 +85,7 @@
// Actual number of Mach ports encoded in the extra header.
uint16_t num_ports;
- // Array of encoded Mach ports. If |num_ports| > 0, |entries[0]| through
+ // Array of encoded Mach ports. If |num_ports| > 0, |entires[0]| through
// to |entries[num_ports-1]| inclusive are valid.
MachPortsEntry entries[0];
};
@@ -122,8 +104,10 @@
// Allocates and owns a buffer for message data with enough capacity for
// |payload_size| bytes plus a header, plus |max_handles| platform handles.
- Message(size_t payload_size, size_t max_handles);
- Message(size_t payload_size, size_t max_handles, MessageType message_type);
+ Message(size_t payload_size,
+ size_t max_handles,
+ Header::MessageType message_type = Header::MessageType::NORMAL);
+
~Message();
// Constructs a Message from serialized message data.
@@ -132,24 +116,30 @@
const void* data() const { return data_; }
size_t data_num_bytes() const { return size_; }
- const void* extra_header() const;
- void* mutable_extra_header();
- size_t extra_header_size() const;
-
- void* mutable_payload();
- const void* payload() const;
+#if defined(MOJO_EDK_LEGACY_PROTOCOL)
+ void* mutable_payload() { return static_cast<void*>(header_ + 1); }
+ const void* payload() const {
+ return static_cast<const void*>(header_ + 1);
+ }
size_t payload_size() const;
+#else
+ const void* extra_header() const { return data_ + sizeof(Header); }
+ void* mutable_extra_header() { return data_ + sizeof(Header); }
+ size_t extra_header_size() const {
+ return header_->num_header_bytes - sizeof(Header);
+ }
- size_t num_handles() const;
- bool has_handles() const;
+ void* mutable_payload() { return data_ + header_->num_header_bytes; }
+ const void* payload() const { return data_ + header_->num_header_bytes; }
+ size_t payload_size() const;
+#endif // defined(MOJO_EDK_LEGACY_PROTOCOL)
+
+ size_t num_handles() const { return header_->num_handles; }
+ bool has_handles() const { return header_->num_handles > 0; }
#if defined(OS_MACOSX) && !defined(OS_IOS)
bool has_mach_ports() const;
#endif
- bool is_legacy_message() const;
- LegacyHeader* legacy_header() const;
- Header* header() const;
-
// Note: SetHandles() and TakeHandles() invalidate any previous value of
// handles().
void SetHandles(ScopedPlatformHandleVectorPtr new_handles);
@@ -171,12 +161,11 @@
PlatformHandleVector* handles);
#endif
- void SetVersionForTest(uint16_t version_number);
-
private:
- size_t size_ = 0;
- size_t max_handles_ = 0;
- char* data_ = nullptr;
+ size_t size_;
+ size_t max_handles_;
+ char* data_;
+ Header* header_;
ScopedPlatformHandleVectorPtr handle_vector_;
@@ -214,7 +203,7 @@
// |delegate| is destroyed.
static scoped_refptr<Channel> Create(
Delegate* delegate,
- ConnectionParams connection_params,
+ ScopedPlatformHandle platform_handle,
scoped_refptr<base::TaskRunner> io_task_runner);
// Request that the channel be shut down. This should always be called before
@@ -281,7 +270,7 @@
// Handles a received control message. Returns |true| if the message is
// accepted, or |false| otherwise.
- virtual bool OnControlMessage(Message::MessageType message_type,
+ virtual bool OnControlMessage(Message::Header::MessageType message_type,
const void* payload,
size_t payload_size,
ScopedPlatformHandleVectorPtr handles);
diff --git a/mojo/edk/system/channel_posix.cc b/mojo/edk/system/channel_posix.cc
index 8b4ca7f..16a9304 100644
--- a/mojo/edk/system/channel_posix.cc
+++ b/mojo/edk/system/channel_posix.cc
@@ -88,18 +88,17 @@
public base::MessageLoopForIO::Watcher {
public:
ChannelPosix(Delegate* delegate,
- ConnectionParams connection_params,
+ ScopedPlatformHandle handle,
scoped_refptr<base::TaskRunner> io_task_runner)
: Channel(delegate),
self_(this),
- handle_(connection_params.TakeChannelHandle()),
+ handle_(std::move(handle)),
io_task_runner_(io_task_runner)
#if defined(OS_MACOSX)
,
handles_to_close_(new PlatformHandleVector)
#endif
{
- CHECK(handle_.is_valid());
}
void Start() override {
@@ -211,22 +210,12 @@
void StartOnIOThread() {
DCHECK(!read_watcher_);
DCHECK(!write_watcher_);
- read_watcher_.reset(
- new base::MessageLoopForIO::FileDescriptorWatcher(FROM_HERE));
+ read_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher);
+ write_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher);
+ base::MessageLoopForIO::current()->WatchFileDescriptor(
+ handle_.get().handle, true /* persistent */,
+ base::MessageLoopForIO::WATCH_READ, read_watcher_.get(), this);
base::MessageLoop::current()->AddDestructionObserver(this);
- if (handle_.get().needs_connection) {
- base::MessageLoopForIO::current()->WatchFileDescriptor(
- handle_.get().handle, false /* persistent */,
- base::MessageLoopForIO::WATCH_READ, read_watcher_.get(), this);
- } else {
- write_watcher_.reset(
- new base::MessageLoopForIO::FileDescriptorWatcher(FROM_HERE));
- base::MessageLoopForIO::current()->WatchFileDescriptor(
- handle_.get().handle, true /* persistent */,
- base::MessageLoopForIO::WATCH_READ, read_watcher_.get(), this);
- base::AutoLock lock(write_lock_);
- FlushOutgoingMessagesNoLock();
- }
}
void WaitForWriteOnIOThread() {
@@ -276,24 +265,6 @@
// base::MessageLoopForIO::Watcher:
void OnFileCanReadWithoutBlocking(int fd) override {
CHECK_EQ(fd, handle_.get().handle);
- if (handle_.get().needs_connection) {
-#if !defined(OS_NACL)
- read_watcher_.reset();
- base::MessageLoop::current()->RemoveDestructionObserver(this);
-
- ScopedPlatformHandle accept_fd;
- ServerAcceptConnection(handle_.get(), &accept_fd);
- if (!accept_fd.is_valid()) {
- OnError();
- return;
- }
- handle_ = std::move(accept_fd);
- StartOnIOThread();
-#else
- NOTREACHED();
-#endif
- return;
- }
bool read_error = false;
size_t next_read_size = 0;
@@ -350,10 +321,6 @@
// cannot be written, it's queued and a wait is initiated to write the message
// ASAP on the I/O thread.
bool WriteNoLock(MessageView message_view) {
- if (handle_.get().needs_connection) {
- outgoing_messages_.emplace_front(std::move(message_view));
- return true;
- }
size_t bytes_written = 0;
do {
message_view.advance_data_offset(bytes_written);
@@ -388,7 +355,7 @@
}
MessagePtr fds_message(
new Channel::Message(sizeof(fds[0]) * fds.size(), 0,
- Message::MessageType::HANDLES_SENT));
+ Message::Header::MessageType::HANDLES_SENT));
memcpy(fds_message->mutable_payload(), fds.data(),
sizeof(fds[0]) * fds.size());
outgoing_messages_.emplace_back(std::move(fds_message), 0);
@@ -403,26 +370,8 @@
}
if (result < 0) {
- if (errno != EAGAIN && errno != EWOULDBLOCK
-#if defined(OS_MACOSX)
- // On OS X if sendmsg() is trying to send fds between processes and
- // there isn't enough room in the output buffer to send the fd
- // structure over atomically then EMSGSIZE is returned.
- //
- // EMSGSIZE presents a problem since the system APIs can only call
- // us when there's room in the socket buffer and not when there is
- // "enough" room.
- //
- // The current behavior is to return to the event loop when EMSGSIZE
- // is received and hopefull service another FD. This is however
- // still technically a busy wait since the event loop will call us
- // right back until the receiver has read enough data to allow
- // passing the FD over atomically.
- && errno != EMSGSIZE
-#endif
- ) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK)
return false;
- }
message_view.SetHandles(std::move(handles));
outgoing_messages_.emplace_front(std::move(message_view));
WaitForWriteOnIOThreadNoLock();
@@ -463,22 +412,22 @@
}
#if defined(OS_MACOSX)
- bool OnControlMessage(Message::MessageType message_type,
+ bool OnControlMessage(Message::Header::MessageType message_type,
const void* payload,
size_t payload_size,
ScopedPlatformHandleVectorPtr handles) override {
switch (message_type) {
- case Message::MessageType::HANDLES_SENT: {
+ case Message::Header::MessageType::HANDLES_SENT: {
if (payload_size == 0)
break;
MessagePtr message(new Channel::Message(
- payload_size, 0, Message::MessageType::HANDLES_SENT_ACK));
+ payload_size, 0, Message::Header::MessageType::HANDLES_SENT_ACK));
memcpy(message->mutable_payload(), payload, payload_size);
Write(std::move(message));
return true;
}
- case Message::MessageType::HANDLES_SENT_ACK: {
+ case Message::Header::MessageType::HANDLES_SENT_ACK: {
size_t num_fds = payload_size / sizeof(int);
if (num_fds == 0 || payload_size % sizeof(int) != 0)
break;
@@ -562,10 +511,9 @@
// static
scoped_refptr<Channel> Channel::Create(
Delegate* delegate,
- ConnectionParams connection_params,
+ ScopedPlatformHandle platform_handle,
scoped_refptr<base::TaskRunner> io_task_runner) {
- return new ChannelPosix(delegate, std::move(connection_params),
- io_task_runner);
+ return new ChannelPosix(delegate, std::move(platform_handle), io_task_runner);
}
} // namespace edk
diff --git a/mojo/edk/system/channel_win.cc b/mojo/edk/system/channel_win.cc
index c15df16..c989344 100644
--- a/mojo/edk/system/channel_win.cc
+++ b/mojo/edk/system/channel_win.cc
@@ -19,7 +19,6 @@
#include "base/message_loop/message_loop.h"
#include "base/synchronization/lock.h"
#include "base/task_runner.h"
-#include "base/win/win_util.h"
#include "mojo/edk/embedder/platform_handle_vector.h"
namespace mojo {
@@ -138,8 +137,8 @@
const HandleEntry* extra_header_handles =
reinterpret_cast<const HandleEntry*>(extra_header);
for (size_t i = 0; i < num_handles; i++) {
- (*handles)->at(i).handle =
- base::win::Uint32ToHandle(extra_header_handles[i].handle);
+ (*handles)->at(i).handle = reinterpret_cast<HANDLE>(
+ static_cast<uintptr_t>(extra_header_handles[i].handle));
}
return true;
}
@@ -350,10 +349,9 @@
// static
scoped_refptr<Channel> Channel::Create(
Delegate* delegate,
- ConnectionParams connection_params,
+ ScopedPlatformHandle platform_handle,
scoped_refptr<base::TaskRunner> io_task_runner) {
- return new ChannelWin(delegate, connection_params.TakeChannelHandle(),
- io_task_runner);
+ return new ChannelWin(delegate, std::move(platform_handle), io_task_runner);
}
} // namespace edk
diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc
index 1e0bf4e..ada3753 100644
--- a/mojo/edk/system/core.cc
+++ b/mojo/edk/system/core.cc
@@ -44,8 +44,8 @@
// This is an unnecessarily large limit that is relatively easy to enforce.
const uint32_t kMaxHandlesPerMessage = 1024 * 1024;
-// TODO(rockot): Maybe we could negotiate a debugging pipe ID for cross-process
-// pipes too; for now we just use a constant. This only affects bootstrap pipes.
+// TODO: Maybe we could negotiate a debugging pipe ID for cross-process pipes
+// too; for now we just use a constant. This only affects bootstrap pipes.
const uint64_t kUnknownPipeIdForDebug = 0x7f7f7f7f7f7f7f7fUL;
void CallWatchCallback(MojoWatchCallback callback,
@@ -166,17 +166,13 @@
return handles_.GetDispatcher(handle);
}
-void Core::SetDefaultProcessErrorCallback(
- const ProcessErrorCallback& callback) {
- default_process_error_callback_ = callback;
-}
-
void Core::AddChild(base::ProcessHandle process_handle,
- ConnectionParams connection_params,
+ ScopedPlatformHandle platform_handle,
const std::string& child_token,
const ProcessErrorCallback& process_error_callback) {
GetNodeController()->ConnectToChild(process_handle,
- std::move(connection_params), child_token,
+ std::move(platform_handle),
+ child_token,
process_error_callback);
}
@@ -185,26 +181,8 @@
GetNodeController()->CloseChildPorts(child_token);
}
-ScopedMessagePipeHandle Core::ConnectToPeerProcess(
- ScopedPlatformHandle pipe_handle,
- const std::string& peer_token) {
- RequestContext request_context;
- ports::PortRef port0, port1;
- GetNodeController()->node()->CreatePortPair(&port0, &port1);
- MojoHandle handle = AddDispatcher(new MessagePipeDispatcher(
- GetNodeController(), port0, kUnknownPipeIdForDebug, 0));
- ConnectionParams connection_params(std::move(pipe_handle));
- GetNodeController()->ConnectToPeer(std::move(connection_params), port1,
- peer_token);
- return ScopedMessagePipeHandle(MessagePipeHandle(handle));
-}
-
-void Core::ClosePeerConnection(const std::string& peer_token) {
- GetNodeController()->ClosePeerConnection(peer_token);
-}
-
-void Core::InitChild(ConnectionParams connection_params) {
- GetNodeController()->ConnectToParent(std::move(connection_params));
+void Core::InitChild(ScopedPlatformHandle platform_handle) {
+ GetNodeController()->ConnectToParent(std::move(platform_handle));
}
void Core::SetMachPortProvider(base::PortProvider* port_provider) {
@@ -332,7 +310,15 @@
}
void Core::RequestShutdown(const base::Closure& callback) {
- GetNodeController()->RequestShutdown(callback);
+ base::Closure on_shutdown;
+ if (base::ThreadTaskRunnerHandle::IsSet()) {
+ on_shutdown = base::Bind(base::IgnoreResult(&base::TaskRunner::PostTask),
+ base::ThreadTaskRunnerHandle::Get(),
+ FROM_HERE, callback);
+ } else {
+ on_shutdown = callback;
+ }
+ GetNodeController()->RequestShutdown(on_shutdown);
}
ScopedMessagePipeHandle Core::CreateParentMessagePipe(
@@ -768,8 +754,6 @@
reinterpret_cast<MessageForTransit*>(message)->ports_message();
if (ports_message.source_node() == ports::kInvalidNodeName) {
DVLOG(1) << "Received invalid message from unknown node.";
- if (!default_process_error_callback_.is_null())
- default_process_error_callback_.Run(std::string(error, error_num_bytes));
return MOJO_RESULT_OK;
}
@@ -790,12 +774,12 @@
create_options.struct_size = sizeof(MojoCreateDataPipeOptions);
create_options.flags = options ? options->flags : 0;
create_options.element_num_bytes = options ? options->element_num_bytes : 1;
- // TODO(rockot): Use Configuration to get default data pipe capacity.
+ // TODO: Use Configuration to get default data pipe capacity.
create_options.capacity_num_bytes =
options && options->capacity_num_bytes ? options->capacity_num_bytes
: 64 * 1024;
- // TODO(rockot): Broker through the parent when necessary.
+ // TODO: Broker through the parent when necessary.
scoped_refptr<PlatformSharedBuffer> ring_buffer =
GetNodeController()->CreateSharedBuffer(
create_options.capacity_num_bytes);
@@ -1101,7 +1085,7 @@
const MojoHandleSignals* signals,
uint32_t num_handles,
MojoDeadline deadline,
- uint32_t* result_index,
+ uint32_t *result_index,
HandleSignalsState* signals_states) {
CHECK(handles);
CHECK(signals);
diff --git a/mojo/edk/system/core.h b/mojo/edk/system/core.h
index 1e20a87..64298f9 100644
--- a/mojo/edk/system/core.h
+++ b/mojo/edk/system/core.h
@@ -6,7 +6,6 @@
#define MOJO_EDK_SYSTEM_CORE_H_
#include <memory>
-#include <string>
#include <vector>
#include "base/callback.h"
@@ -40,7 +39,7 @@
// are thread-safe.
class MOJO_SYSTEM_IMPL_EXPORT Core {
public:
- Core();
+ explicit Core();
virtual ~Core();
// Called exactly once, shortly after construction, and before any other
@@ -52,27 +51,17 @@
scoped_refptr<Dispatcher> GetDispatcher(MojoHandle handle);
- void SetDefaultProcessErrorCallback(const ProcessErrorCallback& callback);
-
// Called in the parent process any time a new child is launched.
void AddChild(base::ProcessHandle process_handle,
- ConnectionParams connection_params,
+ ScopedPlatformHandle platform_handle,
const std::string& child_token,
const ProcessErrorCallback& process_error_callback);
// Called in the parent process when a child process fails to launch.
void ChildLaunchFailed(const std::string& child_token);
- // Called to connect to a peer process. This should be called only if there
- // is no common ancestor for the processes involved within this mojo system.
- // Both processes must call this function, each passing one end of a platform
- // channel. This returns one end of a message pipe to each process.
- ScopedMessagePipeHandle ConnectToPeerProcess(ScopedPlatformHandle pipe_handle,
- const std::string& peer_token);
- void ClosePeerConnection(const std::string& peer_token);
-
// Called in a child process exactly once during early initialization.
- void InitChild(ConnectionParams connection_params);
+ void InitChild(ScopedPlatformHandle platform_handle);
// Creates a message pipe endpoint associated with |token|, which a child
// holding the token can later locate and connect to.
@@ -273,7 +262,7 @@
const MojoHandleSignals* signals,
uint32_t num_handles,
MojoDeadline deadline,
- uint32_t* result_index,
+ uint32_t *result_index,
HandleSignalsState* signals_states);
// Used to pass ownership of our NodeController over to the IO thread in the
@@ -295,10 +284,6 @@
// to access it.
std::unique_ptr<NodeController> node_controller_;
- // The default callback to invoke, if any, when a process error is reported
- // but cannot be associated with a specific process.
- ProcessErrorCallback default_process_error_callback_;
-
base::Lock handles_lock_;
HandleTable handles_;
diff --git a/mojo/edk/system/core_unittest.cc b/mojo/edk/system/core_unittest.cc
index 814ce4b..33a7068 100644
--- a/mojo/edk/system/core_unittest.cc
+++ b/mojo/edk/system/core_unittest.cc
@@ -846,16 +846,14 @@
MOJO_RESULT_FAILED_PRECONDITION,
core()->Wait(ch, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss));
ASSERT_EQ(0u, hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
hss = kFullMojoHandleSignalsState;
ASSERT_EQ(
MOJO_RESULT_DEADLINE_EXCEEDED,
core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
ASSERT_EQ(0u, hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Write.
@@ -874,10 +872,8 @@
hss = kEmptyMojoHandleSignalsState;
ASSERT_EQ(MOJO_RESULT_OK, core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0,
&hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Peek one character.
@@ -998,9 +994,8 @@
ASSERT_EQ(
MOJO_RESULT_DEADLINE_EXCEEDED,
core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
- EXPECT_EQ(0u, hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(0u, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// TODO(vtl): More.
@@ -1086,10 +1081,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
core()->Wait(ch_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000,
&hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
num_bytes = kBufferSize;
ASSERT_EQ(MOJO_RESULT_OK,
@@ -1140,10 +1133,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
core()->Wait(ch_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000,
&hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
num_bytes = kBufferSize;
ASSERT_EQ(MOJO_RESULT_OK,
@@ -1196,10 +1187,8 @@
hss = kEmptyMojoHandleSignalsState;
ASSERT_EQ(MOJO_RESULT_OK, core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE,
1000000000, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Make sure that |ch| can't be sent if it's in a two-phase read.
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.cc b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
index c908e3a..23cb2e0 100644
--- a/mojo/edk/system/data_pipe_consumer_dispatcher.cc
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.cc
@@ -124,8 +124,6 @@
uint32_t* num_bytes,
MojoReadDataFlags flags) {
base::AutoLock lock(lock_);
- new_data_available_ = false;
-
if (!shared_ring_buffer_ || in_transit_)
return MOJO_RESULT_INVALID_ARGUMENT;
@@ -206,7 +204,6 @@
uint32_t* buffer_num_bytes,
MojoReadDataFlags flags) {
base::AutoLock lock(lock_);
- new_data_available_ = false;
if (!shared_ring_buffer_ || in_transit_)
return MOJO_RESULT_INVALID_ARGUMENT;
@@ -422,10 +419,8 @@
base::AutoLock lock(dispatcher->lock_);
dispatcher->read_offset_ = state->read_offset;
dispatcher->bytes_available_ = state->bytes_available;
- dispatcher->new_data_available_ = state->bytes_available > 0;
dispatcher->peer_closed_ = state->flags & kFlagPeerClosed;
dispatcher->InitializeNoLock();
- dispatcher->UpdateSignalsStateNoLock();
}
return dispatcher;
@@ -478,25 +473,16 @@
HandleSignalsState rv;
if (shared_ring_buffer_ && bytes_available_) {
- if (!in_two_phase_read_) {
+ if (!in_two_phase_read_)
rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_READABLE;
- if (new_data_available_)
- rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE;
- }
rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE;
} else if (!peer_closed_ && shared_ring_buffer_) {
rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_READABLE;
}
- if (shared_ring_buffer_) {
- if (new_data_available_ || !peer_closed_)
- rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE;
- }
-
if (peer_closed_)
rv.satisfied_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED;
rv.satisfiable_signals |= MOJO_HANDLE_SIGNAL_PEER_CLOSED;
-
return rv;
}
@@ -540,8 +526,8 @@
} else if (rv == ports::OK && port_status.has_messages && !in_transit_) {
ports::ScopedMessage message;
do {
- int rv = node_controller_->node()->GetMessage(
- control_port_, &message, nullptr);
+ int rv = node_controller_->node()->GetMessageIf(control_port_, nullptr,
+ &message);
if (rv != ports::OK)
peer_closed_ = true;
if (message) {
@@ -576,11 +562,8 @@
} while (message);
}
- bool has_new_data = bytes_available_ != previous_bytes_available;
- if (has_new_data)
- new_data_available_ = true;
-
- if (peer_closed_ != was_peer_closed || has_new_data) {
+ if (peer_closed_ != was_peer_closed ||
+ bytes_available_ != previous_bytes_available) {
awakable_list_.AwakeForStateChange(GetHandleSignalsStateNoLock());
}
}
diff --git a/mojo/edk/system/data_pipe_consumer_dispatcher.h b/mojo/edk/system/data_pipe_consumer_dispatcher.h
index b323c16..6a7fb1c 100644
--- a/mojo/edk/system/data_pipe_consumer_dispatcher.h
+++ b/mojo/edk/system/data_pipe_consumer_dispatcher.h
@@ -24,6 +24,7 @@
namespace mojo {
namespace edk {
+struct DataPipeControlMessage;
class NodeController;
// This is the Dispatcher implementation for the consumer handle for data
@@ -117,9 +118,6 @@
uint32_t read_offset_ = 0;
uint32_t bytes_available_ = 0;
- // Indicates whether any new data is available since the last read attempt.
- bool new_data_available_ = false;
-
DISALLOW_COPY_AND_ASSIGN(DataPipeConsumerDispatcher);
};
diff --git a/mojo/edk/system/data_pipe_control_message.h b/mojo/edk/system/data_pipe_control_message.h
index ec84ea3..82ee594 100644
--- a/mojo/edk/system/data_pipe_control_message.h
+++ b/mojo/edk/system/data_pipe_control_message.h
@@ -17,6 +17,7 @@
namespace edk {
class NodeController;
+class PortsMessage;
enum DataPipeCommand : uint32_t {
// Signal to the consumer that new data is available.
diff --git a/mojo/edk/system/data_pipe_producer_dispatcher.cc b/mojo/edk/system/data_pipe_producer_dispatcher.cc
index 8c1993a..d056e7d 100644
--- a/mojo/edk/system/data_pipe_producer_dispatcher.cc
+++ b/mojo/edk/system/data_pipe_producer_dispatcher.cc
@@ -137,8 +137,9 @@
if (*num_bytes == 0)
return MOJO_RESULT_OK; // Nothing to do.
- if ((flags & MOJO_WRITE_DATA_FLAG_ALL_OR_NONE) &&
- (*num_bytes > available_capacity_)) {
+ bool all_or_none = flags & MOJO_WRITE_DATA_FLAG_ALL_OR_NONE;
+ uint32_t min_num_bytes_to_write = all_or_none ? *num_bytes : 0;
+ if (min_num_bytes_to_write > options_.capacity_num_bytes) {
// Don't return "should wait" since you can't wait for a specified amount of
// data.
return MOJO_RESULT_OUT_OF_RANGE;
@@ -402,7 +403,6 @@
dispatcher->available_capacity_ = state->available_capacity;
dispatcher->peer_closed_ = state->flags & kFlagPeerClosed;
dispatcher->InitializeNoLock();
- dispatcher->UpdateSignalsStateNoLock();
}
return dispatcher;
@@ -504,8 +504,8 @@
} else if (rv == ports::OK && port_status.has_messages && !in_transit_) {
ports::ScopedMessage message;
do {
- int rv = node_controller_->node()->GetMessage(
- control_port_, &message, nullptr);
+ int rv = node_controller_->node()->GetMessageIf(control_port_, nullptr,
+ &message);
if (rv != ports::OK)
peer_closed_ = true;
if (message) {
diff --git a/mojo/edk/system/data_pipe_unittest.cc b/mojo/edk/system/data_pipe_unittest.cc
index 610aeac..526444c 100644
--- a/mojo/edk/system/data_pipe_unittest.cc
+++ b/mojo/edk/system/data_pipe_unittest.cc
@@ -12,7 +12,6 @@
#include "base/logging.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/edk/embedder/platform_channel_pair.h"
#include "mojo/edk/system/test_utils.h"
@@ -21,7 +20,6 @@
#include "mojo/public/c/system/data_pipe.h"
#include "mojo/public/c/system/functions.h"
#include "mojo/public/c/system/message_pipe.h"
-#include "mojo/public/cpp/system/watcher.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
@@ -42,9 +40,6 @@
const char kMultiprocessTestData[] = "hello i'm a string that is 36 bytes";
const int kMultiprocessMaxIter = 5;
-// TODO(rockot): There are many uses of ASSERT where EXPECT would be more
-// appropriate. Fix this.
-
class DataPipeTest : public test::MojoTestBase {
public:
DataPipeTest() : producer_(MOJO_HANDLE_INVALID),
@@ -164,8 +159,7 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &state));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- state.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, state.satisfied_signals);
elements[0] = -1;
elements[1] = -1;
@@ -251,10 +245,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Query.
@@ -360,10 +352,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Peek one element.
@@ -487,9 +477,8 @@
ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_WRITABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(0u, hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(0u, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Write two elements.
@@ -502,10 +491,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Discard one element.
@@ -518,9 +505,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Peek one element.
@@ -536,9 +522,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Read one element.
@@ -561,10 +546,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Close the producer.
@@ -575,10 +558,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_TRUE(hss.satisfied_signals & (MOJO_HANDLE_SIGNAL_READABLE |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE) != 0);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Wait for the peer closed signal.
@@ -586,11 +567,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE |
- MOJO_HANDLE_SIGNAL_PEER_CLOSED,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_PEER_CLOSED) != 0);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Read one element.
@@ -611,64 +589,6 @@
ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
}
-TEST_F(DataPipeTest, ConsumerNewDataReadable) {
- const MojoCreateDataPipeOptions options = {
- kSizeOfOptions, // |struct_size|.
- MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, // |flags|.
- static_cast<uint32_t>(sizeof(int32_t)), // |element_num_bytes|.
- 1000 * sizeof(int32_t) // |capacity_num_bytes|.
- };
- EXPECT_EQ(MOJO_RESULT_OK, Create(&options));
-
- int32_t elements[2] = {123, 456};
- uint32_t num_bytes = static_cast<uint32_t>(2u * sizeof(elements[0]));
- EXPECT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true));
-
- // The consumer handle should appear to be readable and have new data.
- EXPECT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
- MOJO_DEADLINE_INDEFINITE, nullptr));
- EXPECT_EQ(MOJO_RESULT_OK,
- MojoWait(consumer_, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- MOJO_DEADLINE_INDEFINITE, nullptr));
-
- // Now try to read a minimum of 6 elements.
- int32_t read_elements[6];
- uint32_t num_read_bytes = sizeof(read_elements);
- EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
- MojoReadData(consumer_, read_elements, &num_read_bytes,
- MOJO_READ_DATA_FLAG_ALL_OR_NONE));
-
- // The consumer should still appear to be readable, but not with new data.
- EXPECT_EQ(MOJO_RESULT_OK,
- MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, nullptr));
- EXPECT_EQ(
- MOJO_RESULT_DEADLINE_EXCEEDED,
- MojoWait(consumer_, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, 0, nullptr));
-
- // Write four more elements.
- EXPECT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true));
- EXPECT_EQ(MOJO_RESULT_OK, WriteData(elements, &num_bytes, true));
-
- // The consumer handle should once again appear to be readable with new data.
- EXPECT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
- MOJO_DEADLINE_INDEFINITE, nullptr));
- EXPECT_EQ(MOJO_RESULT_OK,
- MojoWait(consumer_, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- MOJO_DEADLINE_INDEFINITE, nullptr));
-
- // Read should succeed this time.
- EXPECT_EQ(MOJO_RESULT_OK,
- MojoReadData(consumer_, read_elements, &num_read_bytes,
- MOJO_READ_DATA_FLAG_ALL_OR_NONE));
-
- // And once again the consumer is unreadable.
- EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
- MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, nullptr));
- EXPECT_EQ(
- MOJO_RESULT_DEADLINE_EXCEEDED,
- MojoWait(consumer_, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, 0, nullptr));
-}
-
// Test with two-phase APIs and also closing the producer with an active
// consumer waiter.
TEST_F(DataPipeTest, ConsumerWaitingTwoPhase) {
@@ -686,7 +606,7 @@
void* buffer = nullptr;
// Request room for three (but we'll only write two).
uint32_t num_bytes = static_cast<uint32_t>(3u * sizeof(elements[0]));
- ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&buffer, &num_bytes, false));
+ ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&buffer, &num_bytes, true));
EXPECT_TRUE(buffer);
EXPECT_GE(num_bytes, static_cast<uint32_t>(3u * sizeof(elements[0])));
elements = static_cast<int32_t*>(buffer);
@@ -699,10 +619,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Read one element.
@@ -721,9 +639,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Read one element.
@@ -787,8 +704,7 @@
ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
ASSERT_EQ(0u, hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
static_cast<int32_t*>(write_ptr)[0] = 123;
@@ -807,10 +723,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Start another two-phase write and check that it's readable even in the
@@ -826,10 +740,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// End the two-phase write without writing anything.
@@ -855,8 +767,7 @@
ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
ASSERT_EQ(0u, hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// End the two-phase read without reading anything.
@@ -867,8 +778,7 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss));
ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
}
@@ -887,7 +797,7 @@
ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
MojoHandleSignalsState hss;
- // Try writing more than the total capacity of the pipe.
+ // Try writing way too much.
uint32_t num_bytes = 20u * sizeof(int32_t);
int32_t buffer[100];
Seq(0, arraysize(buffer), buffer);
@@ -913,10 +823,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE |
- MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Half full.
@@ -924,11 +832,12 @@
ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
ASSERT_EQ(5u * sizeof(int32_t), num_bytes);
- // Try writing more than the available capacity of the pipe, but less than the
- // total capacity.
+ /* TODO(jam): enable if we end up observing max capacity
+ // Too much.
num_bytes = 6u * sizeof(int32_t);
Seq(200, arraysize(buffer), buffer);
ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE, WriteData(buffer, &num_bytes, true));
+ */
// Try reading too much.
num_bytes = 11u * sizeof(int32_t);
@@ -1004,9 +913,9 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Try reading too much; "failed precondition" since the producer is closed.
@@ -1069,9 +978,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_TRUE(hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE) != 0);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Read 10 bytes.
@@ -1233,10 +1141,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Start two-phase read.
@@ -1325,11 +1231,9 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Peek that data.
@@ -1386,10 +1290,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Begin a two-phase read.
@@ -1413,9 +1315,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Read the two phase memory to check it's still valid.
@@ -1501,10 +1402,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// One element available.
@@ -1571,10 +1470,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Check the data.
@@ -1614,10 +1511,8 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_DEADLINE_INDEFINITE, &hss));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- hss.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
hss.satisfiable_signals);
// Check the second write.
@@ -1655,11 +1550,9 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
MOJO_DEADLINE_INDEFINITE, &state));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
state.satisfied_signals);
- ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
state.satisfiable_signals);
// Now send the consumer over a MP so that it's serialized.
@@ -1682,11 +1575,9 @@
ASSERT_EQ(MOJO_RESULT_OK,
MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
MOJO_DEADLINE_INDEFINITE, &state));
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
state.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
state.satisfiable_signals);
int32_t read_data;
@@ -1758,8 +1649,8 @@
MOJO_DEADLINE_INDEFINITE, &hss));
// Peer could have become closed while we're still waiting for data.
EXPECT_TRUE(MOJO_HANDLE_SIGNAL_READABLE & hss.satisfied_signals);
- EXPECT_TRUE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE);
- EXPECT_TRUE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_PEER_CLOSED);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+ hss.satisfiable_signals);
}
return num_bytes == 0;
@@ -2007,90 +1898,6 @@
END_CHILD()
}
-DEFINE_TEST_CLIENT_TEST_WITH_PIPE(DataPipeStatusChangeInTransitClient,
- DataPipeTest, parent) {
- // This test verifies that peer closure is detectable through various
- // mechanisms when it races with handle transfer.
-
- MojoHandle handles[6];
- EXPECT_EQ("o_O", ReadMessageWithHandles(parent, handles, 6));
- MojoHandle* producers = &handles[0];
- MojoHandle* consumers = &handles[3];
-
- // Wait on producer 0 using MojoWait.
- EXPECT_EQ(MOJO_RESULT_OK,
- MojoWait(producers[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED,
- MOJO_DEADLINE_INDEFINITE, nullptr));
-
- // Wait on consumer 0 using MojoWait.
- EXPECT_EQ(MOJO_RESULT_OK,
- MojoWait(consumers[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED,
- MOJO_DEADLINE_INDEFINITE, nullptr));
-
- base::MessageLoop message_loop;
-
- // Wait on producer 1 and consumer 1 using Watchers.
- {
- base::RunLoop run_loop;
- int count = 0;
- auto callback = base::Bind(
- [] (base::RunLoop* loop, int* count, MojoResult result) {
- EXPECT_EQ(MOJO_RESULT_OK, result);
- if (++*count == 2)
- loop->Quit();
- },
- &run_loop, &count);
- Watcher producer_watcher(FROM_HERE), consumer_watcher(FROM_HERE);
- producer_watcher.Start(
- Handle(producers[1]), MOJO_HANDLE_SIGNAL_PEER_CLOSED, callback);
- consumer_watcher.Start(
- Handle(consumers[1]), MOJO_HANDLE_SIGNAL_PEER_CLOSED, callback);
- run_loop.Run();
- }
-
- // Wait on producer 2 by polling with MojoWriteData.
- MojoResult result;
- do {
- uint32_t num_bytes = 0;
- result = MojoWriteData(
- producers[2], nullptr, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE);
- } while (result == MOJO_RESULT_OK);
- EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
-
- // Wait on consumer 2 by polling with MojoReadData.
- do {
- char byte;
- uint32_t num_bytes = 1;
- result = MojoReadData(
- consumers[2], &byte, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
- } while (result == MOJO_RESULT_SHOULD_WAIT);
- EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
-
- for (size_t i = 0; i < 6; ++i)
- CloseHandle(handles[i]);
-}
-
-TEST_F(DataPipeTest, StatusChangeInTransit) {
- MojoHandle producers[6];
- MojoHandle consumers[6];
- for (size_t i = 0; i < 6; ++i)
- CreateDataPipe(&producers[i], &consumers[i], 1);
-
- RUN_CHILD_ON_PIPE(DataPipeStatusChangeInTransitClient, child)
- MojoHandle handles[] = { producers[0], producers[1], producers[2],
- consumers[3], consumers[4], consumers[5] };
-
- // Send 3 producers and 3 consumers, and let their transfer race with their
- // peers' closure.
- WriteMessageWithHandles(child, "o_O", handles, 6);
-
- for (size_t i = 0; i < 3; ++i)
- CloseHandle(consumers[i]);
- for (size_t i = 3; i < 6; ++i)
- CloseHandle(producers[i]);
- END_CHILD()
-}
-
#endif // !defined(OS_IOS)
} // namespace
diff --git a/mojo/edk/system/handle_table.cc b/mojo/edk/system/handle_table.cc
index b570793..e41817d 100644
--- a/mojo/edk/system/handle_table.cc
+++ b/mojo/edk/system/handle_table.cc
@@ -21,8 +21,7 @@
return MOJO_HANDLE_INVALID;
MojoHandle handle = next_available_handle_++;
- auto result =
- handles_.insert(std::make_pair(handle, Entry(std::move(dispatcher))));
+ auto result = handles_.insert(std::make_pair(handle, Entry(dispatcher)));
DCHECK(result.second);
return handle;
@@ -67,7 +66,7 @@
if (it->second.busy)
return MOJO_RESULT_BUSY;
- *dispatcher = std::move(it->second.dispatcher);
+ *dispatcher = it->second.dispatcher;
handles_.erase(it);
return MOJO_RESULT_OK;
}
@@ -125,7 +124,7 @@
HandleTable::Entry::Entry() {}
HandleTable::Entry::Entry(scoped_refptr<Dispatcher> dispatcher)
- : dispatcher(std::move(dispatcher)) {}
+ : dispatcher(dispatcher) {}
HandleTable::Entry::Entry(const Entry& other) = default;
diff --git a/mojo/edk/system/message_pipe_dispatcher.cc b/mojo/edk/system/message_pipe_dispatcher.cc
index f27336b..f06054d 100644
--- a/mojo/edk/system/message_pipe_dispatcher.cc
+++ b/mojo/edk/system/message_pipe_dispatcher.cc
@@ -14,7 +14,6 @@
#include "mojo/edk/system/core.h"
#include "mojo/edk/system/message_for_transit.h"
#include "mojo/edk/system/node_controller.h"
-#include "mojo/edk/system/ports/message_filter.h"
#include "mojo/edk/system/ports_message.h"
#include "mojo/edk/system/request_context.h"
@@ -60,103 +59,6 @@
DISALLOW_COPY_AND_ASSIGN(PortObserverThunk);
};
-// A MessageFilter used by ReadMessage to determine whether a message should
-// actually be consumed yet.
-class ReadMessageFilter : public ports::MessageFilter {
- public:
- // Creates a new ReadMessageFilter which captures and potentially modifies
- // various (unowned) local state within MessagePipeDispatcher::ReadMessage.
- ReadMessageFilter(bool read_any_size,
- bool may_discard,
- uint32_t* num_bytes,
- uint32_t* num_handles,
- bool* no_space,
- bool* invalid_message)
- : read_any_size_(read_any_size),
- may_discard_(may_discard),
- num_bytes_(num_bytes),
- num_handles_(num_handles),
- no_space_(no_space),
- invalid_message_(invalid_message) {}
-
- ~ReadMessageFilter() override {}
-
- // ports::MessageFilter:
- bool Match(const ports::Message& m) override {
- const PortsMessage& message = static_cast<const PortsMessage&>(m);
- if (message.num_payload_bytes() < sizeof(MessageHeader)) {
- *invalid_message_ = true;
- return true;
- }
-
- const MessageHeader* header =
- static_cast<const MessageHeader*>(message.payload_bytes());
- if (header->header_size > message.num_payload_bytes()) {
- *invalid_message_ = true;
- return true;
- }
-
- uint32_t bytes_to_read = 0;
- uint32_t bytes_available =
- static_cast<uint32_t>(message.num_payload_bytes()) -
- header->header_size;
- if (num_bytes_) {
- bytes_to_read = std::min(*num_bytes_, bytes_available);
- *num_bytes_ = bytes_available;
- }
-
- uint32_t handles_to_read = 0;
- uint32_t handles_available = header->num_dispatchers;
- if (num_handles_) {
- handles_to_read = std::min(*num_handles_, handles_available);
- *num_handles_ = handles_available;
- }
-
- if (handles_to_read < handles_available ||
- (!read_any_size_ && bytes_to_read < bytes_available)) {
- *no_space_ = true;
- return may_discard_;
- }
-
- return true;
- }
-
- private:
- const bool read_any_size_;
- const bool may_discard_;
- uint32_t* const num_bytes_;
- uint32_t* const num_handles_;
- bool* const no_space_;
- bool* const invalid_message_;
-
- DISALLOW_COPY_AND_ASSIGN(ReadMessageFilter);
-};
-
-#if DCHECK_IS_ON()
-
-// A MessageFilter which never matches a message. Used to peek at the size of
-// the next available message on a port, for debug logging only.
-class PeekSizeMessageFilter : public ports::MessageFilter {
- public:
- PeekSizeMessageFilter() {}
- ~PeekSizeMessageFilter() override {}
-
- // ports::MessageFilter:
- bool Match(const ports::Message& message) override {
- message_size_ = message.num_payload_bytes();
- return false;
- }
-
- size_t message_size() const { return message_size_; }
-
- private:
- size_t message_size_ = 0;
-
- DISALLOW_COPY_AND_ASSIGN(PeekSizeMessageFilter);
-};
-
-#endif // DCHECK_IS_ON()
-
MessagePipeDispatcher::MessagePipeDispatcher(NodeController* node_controller,
const ports::PortRef& port,
uint64_t pipe_id,
@@ -239,7 +141,7 @@
size_t num_bytes = message->num_bytes();
int rv = node_controller_->SendMessage(port_, message->TakePortsMessage());
- DVLOG(4) << "Sent message on pipe " << pipe_id_ << " endpoint " << endpoint_
+ DVLOG(2) << "Sent message on pipe " << pipe_id_ << " endpoint " << endpoint_
<< " [port=" << port_.name() << "; rv=" << rv
<< "; num_bytes=" << num_bytes << "]";
@@ -249,6 +151,8 @@
rv == ports::ERROR_PORT_CANNOT_SEND_PEER) {
return MOJO_RESULT_INVALID_ARGUMENT;
} else if (rv == ports::ERROR_PORT_PEER_CLOSED) {
+ base::AutoLock lock(signal_lock_);
+ awakables_.AwakeForStateChange(GetHandleSignalsStateNoLock());
return MOJO_RESULT_FAILED_PRECONDITION;
}
@@ -282,9 +186,50 @@
// This flag exists to support both new and old API behavior.
ports::ScopedMessage ports_message;
- ReadMessageFilter filter(read_any_size, may_discard, num_bytes, num_handles,
- &no_space, &invalid_message);
- int rv = node_controller_->node()->GetMessage(port_, &ports_message, &filter);
+ int rv = node_controller_->node()->GetMessageIf(
+ port_,
+ [read_any_size, num_bytes, num_handles, &no_space, &may_discard,
+ &invalid_message](
+ const ports::Message& next_message) {
+ const PortsMessage& message =
+ static_cast<const PortsMessage&>(next_message);
+ if (message.num_payload_bytes() < sizeof(MessageHeader)) {
+ invalid_message = true;
+ return true;
+ }
+
+ const MessageHeader* header =
+ static_cast<const MessageHeader*>(message.payload_bytes());
+ if (header->header_size > message.num_payload_bytes()) {
+ invalid_message = true;
+ return true;
+ }
+
+ uint32_t bytes_to_read = 0;
+ uint32_t bytes_available =
+ static_cast<uint32_t>(message.num_payload_bytes()) -
+ header->header_size;
+ if (num_bytes) {
+ bytes_to_read = std::min(*num_bytes, bytes_available);
+ *num_bytes = bytes_available;
+ }
+
+ uint32_t handles_to_read = 0;
+ uint32_t handles_available = header->num_dispatchers;
+ if (num_handles) {
+ handles_to_read = std::min(*num_handles, handles_available);
+ *num_handles = handles_available;
+ }
+
+ if (handles_to_read < handles_available ||
+ (!read_any_size && bytes_to_read < bytes_available)) {
+ no_space = true;
+ return may_discard;
+ }
+
+ return true;
+ },
+ &ports_message);
if (invalid_message)
return MOJO_RESULT_UNKNOWN;
@@ -313,6 +258,8 @@
// Peer is closed and there are no more messages to read.
DCHECK_EQ(rv, ports::ERROR_PORT_PEER_CLOSED);
+ base::AutoLock lock(signal_lock_);
+ awakables_.AwakeForStateChange(GetHandleSignalsStateNoLock());
return MOJO_RESULT_FAILED_PRECONDITION;
}
@@ -583,11 +530,15 @@
if (node_controller_->node()->GetStatus(port_, &port_status) == ports::OK) {
if (port_status.has_messages) {
ports::ScopedMessage unused;
- PeekSizeMessageFilter filter;
- node_controller_->node()->GetMessage(port_, &unused, &filter);
- DVLOG(4) << "New message detected on message pipe " << pipe_id_
+ size_t message_size = 0;
+ node_controller_->node()->GetMessageIf(
+ port_, [&message_size](const ports::Message& message) {
+ message_size = message.num_payload_bytes();
+ return false;
+ }, &unused);
+ DVLOG(2) << "New message detected on message pipe " << pipe_id_
<< " endpoint " << endpoint_ << " [port=" << port_.name()
- << "; size=" << filter.message_size() << "]";
+ << "; size=" << message_size << "]";
}
if (port_status.peer_closed) {
DVLOG(2) << "Peer closure detected on message pipe " << pipe_id_
diff --git a/mojo/edk/system/message_pipe_dispatcher.h b/mojo/edk/system/message_pipe_dispatcher.h
index 6743222..fddd0fd 100644
--- a/mojo/edk/system/message_pipe_dispatcher.h
+++ b/mojo/edk/system/message_pipe_dispatcher.h
@@ -21,6 +21,7 @@
namespace edk {
class NodeController;
+class PortsMessage;
class MessagePipeDispatcher : public Dispatcher {
public:
diff --git a/mojo/edk/system/message_pipe_unittest.cc b/mojo/edk/system/message_pipe_unittest.cc
index 8f48950..fcfaeca 100644
--- a/mojo/edk/system/message_pipe_unittest.cc
+++ b/mojo/edk/system/message_pipe_unittest.cc
@@ -706,17 +706,6 @@
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d));
}
-TEST_F(MessagePipeTest, ClosePipesStressTest) {
- // Stress test to exercise https://crbug.com/665869.
- const size_t kNumPipes = 100000;
- for (size_t i = 0; i < kNumPipes; ++i) {
- MojoHandle a, b;
- CreateMessagePipe(&a, &b);
- MojoClose(a);
- MojoClose(b);
- }
-}
-
} // namespace
} // namespace edk
} // namespace mojo
diff --git a/mojo/edk/system/multiprocess_message_pipe_unittest.cc b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
index 498980c..6e2c6f1 100644
--- a/mojo/edk/system/multiprocess_message_pipe_unittest.cc
+++ b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
@@ -18,8 +18,6 @@
#include "base/files/scoped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
#include "base/strings/string_split.h"
#include "build/build_config.h"
#include "mojo/edk/embedder/platform_channel_pair.h"
@@ -31,7 +29,6 @@
#include "mojo/public/c/system/buffer.h"
#include "mojo/public/c/system/functions.h"
#include "mojo/public/c/system/types.h"
-#include "mojo/public/cpp/system/watcher.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -71,16 +68,6 @@
};
};
-class MultiprocessMessagePipeTestWithPeerSupport
- : public MultiprocessMessagePipeTest,
- public testing::WithParamInterface<test::MojoTestBase::LaunchType> {
- protected:
- void SetUp() override {
- test::MojoTestBase::SetUp();
- set_launch_type(GetParam());
- }
-};
-
// For each message received, sends a reply message with the same contents
// repeated twice, until the other end is closed or it receives "quitquitquit"
// (which it doesn't reply to). It'll return the number of messages received,
@@ -129,7 +116,7 @@
return rv;
}
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, Basic) {
+TEST_F(MultiprocessMessagePipeTest, Basic) {
RUN_CHILD_ON_PIPE(EchoEcho, h)
std::string hello("hello");
ASSERT_EQ(MOJO_RESULT_OK,
@@ -165,7 +152,7 @@
END_CHILD_AND_EXPECT_EXIT_CODE(1 % 100);
}
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, QueueMessages) {
+TEST_F(MultiprocessMessagePipeTest, QueueMessages) {
static const size_t kNumMessages = 1001;
RUN_CHILD_ON_PIPE(EchoEcho, h)
for (size_t i = 0; i < kNumMessages; i++) {
@@ -428,7 +415,7 @@
for (size_t i = 0; i < pipe_count; ++i) {
base::FilePath unused;
base::ScopedFILE fp(
- CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &unused));
+ CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
const std::string world("world");
CHECK_EQ(fwrite(&world[0], 1, world.size(), fp.get()), world.size());
fflush(fp.get());
@@ -443,8 +430,7 @@
}
char message[128];
- snprintf(message, sizeof(message), "hello %d",
- static_cast<int>(pipe_count));
+ sprintf(message, "hello %d", static_cast<int>(pipe_count));
ASSERT_EQ(MOJO_RESULT_OK,
MojoWriteMessage(h, message,
static_cast<uint32_t>(strlen(message)),
@@ -466,9 +452,9 @@
#if !defined(OS_ANDROID)
INSTANTIATE_TEST_CASE_P(PipeCount,
MultiprocessMessagePipeTestWithPipeCount,
- // TODO(rockot): Re-enable the 140-pipe case when
- // ChannelPosix has support for sending lots of handles.
- testing::Values(1u, 128u /*, 140u*/));
+ // TODO: Re-enable the 140-pipe case when ChannelPosix
+ // has support for sending lots of handles.
+ testing::Values(1u, 128u/*, 140u*/));
#endif
DEFINE_TEST_CLIENT_WITH_PIPE(CheckMessagePipe, MultiprocessMessagePipeTest, h) {
@@ -523,7 +509,7 @@
return 0;
}
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, MessagePipePassing) {
+TEST_F(MultiprocessMessagePipeTest, MessagePipePassing) {
RUN_CHILD_ON_PIPE(CheckMessagePipe, h)
MojoCreateSharedBufferOptions options;
options.struct_size = sizeof(options);
@@ -565,7 +551,7 @@
END_CHILD()
}
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, MessagePipeTwoPassing) {
+TEST_F(MultiprocessMessagePipeTest, MessagePipeTwoPassing) {
RUN_CHILD_ON_PIPE(CheckMessagePipe, h)
MojoHandle mp1, mp2;
ASSERT_EQ(MOJO_RESULT_OK,
@@ -695,9 +681,11 @@
END_CHILD();
}
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, CreateMessagePipe) {
+TEST_F(MultiprocessMessagePipeTest, CreateMessagePipe) {
MojoHandle p0, p1;
CreateMessagePipe(&p0, &p1);
+ VerifyTransmission(p0, p1, "hey man");
+ VerifyTransmission(p1, p0, "slow down");
VerifyTransmission(p0, p1, std::string(10 * 1024 * 1024, 'a'));
VerifyTransmission(p1, p0, std::string(10 * 1024 * 1024, 'e'));
@@ -705,7 +693,7 @@
CloseHandle(p1);
}
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, PassMessagePipeLocal) {
+TEST_F(MultiprocessMessagePipeTest, PassMessagePipeLocal) {
MojoHandle p0, p1;
CreateMessagePipe(&p0, &p1);
VerifyTransmission(p0, p1, "testing testing");
@@ -745,7 +733,7 @@
return 0;
}
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, MultiprocessChannelPipe) {
+TEST_F(MultiprocessMessagePipeTest, MultiprocessChannelPipe) {
RUN_CHILD_ON_PIPE(ChannelEchoClient, h)
VerifyEcho(h, "in an interstellar burst");
VerifyEcho(h, "i am back to save the universe");
@@ -770,8 +758,7 @@
return 0;
}
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport,
- PassMessagePipeCrossProcess) {
+TEST_F(MultiprocessMessagePipeTest, PassMessagePipeCrossProcess) {
MojoHandle p0, p1;
CreateMessagePipe(&p0, &p1);
RUN_CHILD_ON_PIPE(EchoServiceClient, h)
@@ -828,8 +815,7 @@
return 0;
}
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport,
- PassMoarMessagePipesCrossProcess) {
+TEST_F(MultiprocessMessagePipeTest, PassMoarMessagePipesCrossProcess) {
MojoHandle echo_factory_proxy, echo_factory_request;
CreateMessagePipe(&echo_factory_proxy, &echo_factory_request);
@@ -874,8 +860,7 @@
CloseHandle(echo_proxy_c);
}
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport,
- ChannelPipesWithMultipleChildren) {
+TEST_F(MultiprocessMessagePipeTest, ChannelPipesWithMultipleChildren) {
RUN_CHILD_ON_PIPE(ChannelEchoClient, a)
RUN_CHILD_ON_PIPE(ChannelEchoClient, b)
VerifyEcho(a, "hello child 0");
@@ -905,7 +890,7 @@
EXPECT_EQ("quit", ReadMessage(h));
}
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, PingPongPipe) {
+TEST_F(MultiprocessMessagePipeTest, PingPongPipe) {
MojoHandle p0, p1;
CreateMessagePipe(&p0, &p1);
@@ -998,7 +983,7 @@
}
}
- for (auto& pipe : named_pipes)
+ for (auto& pipe: named_pipes)
CloseHandle(pipe.second);
return 0;
@@ -1017,8 +1002,8 @@
b.SendHandle("y", p1);
// Make sure they can talk.
- a.Send("say:x:hello");
- b.Send("hear:y:hello");
+ a.Send("say:x:hello sir");
+ b.Send("hear:y:hello sir");
b.Send("say:y:i love multiprocess pipes!");
a.Send("hear:x:i love multiprocess pipes!");
@@ -1111,12 +1096,11 @@
MojoHandle p;
EXPECT_EQ("foo", ReadMessageWithHandles(h, &p, 1));
- auto result = MojoWait(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
- MOJO_DEADLINE_INDEFINITE, nullptr);
- EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(MOJO_RESULT_OK, MojoWait(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+ MOJO_DEADLINE_INDEFINITE, nullptr));
}
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, SendPipeThenClosePeer) {
+TEST_F(MultiprocessMessagePipeTest, SendPipeThenClosePeer) {
RUN_CHILD_ON_PIPE(ReceivePipeWithClosedPeer, h)
MojoHandle a, b;
CreateMessagePipe(&a, &b);
@@ -1192,7 +1176,8 @@
END_CHILD()
}
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, SendClosePeerSend) {
+
+TEST_F(MultiprocessMessagePipeTest, SendClosePeerSend) {
MojoHandle a, b;
CreateMessagePipe(&a, &b);
@@ -1235,7 +1220,7 @@
EXPECT_EQ("quit", ReadMessage(h));
}
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, WriteCloseSendPeer) {
+TEST_F(MultiprocessMessagePipeTest, WriteCloseSendPeer) {
MojoHandle pipe[2];
CreateMessagePipe(&pipe[0], &pipe[1]);
@@ -1258,61 +1243,41 @@
END_CHILD()
}
-DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MessagePipeStatusChangeInTransitClient,
+DEFINE_TEST_CLIENT_TEST_WITH_PIPE(BootstrapMessagePipeAsyncClient,
MultiprocessMessagePipeTest, parent) {
- // This test verifies that peer closure is detectable through various
- // mechanisms when it races with handle transfer.
- MojoHandle handles[4];
- EXPECT_EQ("o_O", ReadMessageWithHandles(parent, handles, 4));
+ // Receive one end of a platform channel from the parent.
+ MojoHandle channel_handle;
+ EXPECT_EQ("hi", ReadMessageWithHandles(parent, &channel_handle, 1));
+ ScopedPlatformHandle channel;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ edk::PassWrappedPlatformHandle(channel_handle, &channel));
+ ASSERT_TRUE(channel.is_valid());
- // Wait on handle 0 using MojoWait.
- EXPECT_EQ(MOJO_RESULT_OK, MojoWait(handles[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED,
- MOJO_DEADLINE_INDEFINITE, nullptr));
+ // Create a new pipe using our end of the channel.
+ ScopedMessagePipeHandle pipe = edk::CreateMessagePipe(std::move(channel));
- base::MessageLoop message_loop;
-
- // Wait on handle 1 using a Watcher.
- {
- base::RunLoop run_loop;
- Watcher watcher(FROM_HERE);
- watcher.Start(Handle(handles[1]), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
- base::Bind([] (base::RunLoop* loop, MojoResult result) {
- EXPECT_EQ(MOJO_RESULT_OK, result);
- loop->Quit();
- }, &run_loop));
- run_loop.Run();
- }
-
- // Wait on handle 2 by polling with MojoReadMessage.
- MojoResult result;
- do {
- result = MojoReadMessage(handles[2], nullptr, nullptr, nullptr, nullptr,
- MOJO_READ_MESSAGE_FLAG_NONE);
- } while (result == MOJO_RESULT_SHOULD_WAIT);
- EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
-
- // Wait on handle 3 by polling with MojoWriteMessage.
- do {
- result = MojoWriteMessage(handles[3], nullptr, 0, nullptr, 0,
- MOJO_WRITE_MESSAGE_FLAG_NONE);
- } while (result == MOJO_RESULT_OK);
- EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
-
- for (size_t i = 0; i < 4; ++i)
- CloseHandle(handles[i]);
+ // Ensure that we can read and write on the new pipe.
+ VerifyEcho(pipe.get().value(), "goodbye");
}
-TEST_F(MultiprocessMessagePipeTest, MessagePipeStatusChangeInTransit) {
- MojoHandle local_handles[4];
- MojoHandle sent_handles[4];
- for (size_t i = 0; i < 4; ++i)
- CreateMessagePipe(&local_handles[i], &sent_handles[i]);
+TEST_F(MultiprocessMessagePipeTest, BootstrapMessagePipeAsync) {
+ // Tests that new cross-process message pipes can be created synchronously
+ // using asynchronous negotiation over an arbitrary platform channel.
+ RUN_CHILD_ON_PIPE(BootstrapMessagePipeAsyncClient, child)
+ // Pass one end of a platform channel to the child.
+ PlatformChannelPair platform_channel;
+ MojoHandle client_channel_handle;
+ EXPECT_EQ(MOJO_RESULT_OK,
+ CreatePlatformHandleWrapper(platform_channel.PassClientHandle(),
+ &client_channel_handle));
+ WriteMessageWithHandles(child, "hi", &client_channel_handle, 1);
- RUN_CHILD_ON_PIPE(MessagePipeStatusChangeInTransitClient, child)
- // Send 4 handles and let their transfer race with their peers' closure.
- WriteMessageWithHandles(child, "o_O", sent_handles, 4);
- for (size_t i = 0; i < 4; ++i)
- CloseHandle(local_handles[i]);
+ // Create a new pipe using our end of the channel.
+ ScopedMessagePipeHandle pipe =
+ edk::CreateMessagePipe(platform_channel.PassServerHandle());
+
+ // Ensure that we can read and write on the new pipe.
+ VerifyEcho(pipe.get().value(), "goodbye");
END_CHILD()
}
@@ -1380,13 +1345,7 @@
EXPECT_NE(std::string::npos, first_process_error.find(kFirstErrorMessage));
EXPECT_NE(std::string::npos, second_process_error.find(kSecondErrorMessage));
}
-INSTANTIATE_TEST_CASE_P(
- ,
- MultiprocessMessagePipeTestWithPeerSupport,
- testing::Values(test::MojoTestBase::LaunchType::CHILD,
- test::MojoTestBase::LaunchType::PEER,
- test::MojoTestBase::LaunchType::NAMED_CHILD,
- test::MojoTestBase::LaunchType::NAMED_PEER));
+
} // namespace
} // namespace edk
} // namespace mojo
diff --git a/mojo/edk/system/node_channel.cc b/mojo/edk/system/node_channel.cc
index b0f770d..ce094c1 100644
--- a/mojo/edk/system/node_channel.cc
+++ b/mojo/edk/system/node_channel.cc
@@ -47,7 +47,6 @@
#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
PORTS_MESSAGE_FROM_RELAY,
#endif
- ACCEPT_PEER,
};
struct Header {
@@ -55,8 +54,8 @@
uint32_t padding;
};
-static_assert(IsAlignedForChannelMessage(sizeof(Header)),
- "Invalid header size.");
+static_assert(sizeof(Header) % kChannelMessageAlignment == 0,
+ "Invalid header size.");
struct AcceptChildData {
ports::NodeName parent_name;
@@ -68,12 +67,6 @@
ports::NodeName child_name;
};
-struct AcceptPeerData {
- ports::NodeName token;
- ports::NodeName peer_name;
- ports::PortName port_name;
-};
-
// This message may include a process handle on plaforms that require it.
struct AddBrokerClientData {
ports::NodeName client_name;
@@ -160,14 +153,14 @@
// static
scoped_refptr<NodeChannel> NodeChannel::Create(
Delegate* delegate,
- ConnectionParams connection_params,
+ ScopedPlatformHandle platform_handle,
scoped_refptr<base::TaskRunner> io_task_runner,
const ProcessErrorCallback& process_error_callback) {
#if defined(OS_NACL_SFI)
LOG(FATAL) << "Multi-process not yet supported on NaCl-SFI";
return nullptr;
#else
- return new NodeChannel(delegate, std::move(connection_params), io_task_runner,
+ return new NodeChannel(delegate, std::move(platform_handle), io_task_runner,
process_error_callback);
#endif
}
@@ -289,18 +282,6 @@
WriteChannelMessage(std::move(message));
}
-void NodeChannel::AcceptPeer(const ports::NodeName& sender_name,
- const ports::NodeName& token,
- const ports::PortName& port_name) {
- AcceptPeerData* data;
- Channel::MessagePtr message =
- CreateMessage(MessageType::ACCEPT_PEER, sizeof(AcceptPeerData), 0, &data);
- data->token = token;
- data->peer_name = sender_name;
- data->port_name = port_name;
- WriteChannelMessage(std::move(message));
-}
-
void NodeChannel::AddBrokerClient(const ports::NodeName& client_name,
base::ProcessHandle process_handle) {
AddBrokerClientData* data;
@@ -454,18 +435,17 @@
#endif // defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
NodeChannel::NodeChannel(Delegate* delegate,
- ConnectionParams connection_params,
+ ScopedPlatformHandle platform_handle,
scoped_refptr<base::TaskRunner> io_task_runner,
const ProcessErrorCallback& process_error_callback)
: delegate_(delegate),
io_task_runner_(io_task_runner),
process_error_callback_(process_error_callback)
#if !defined(OS_NACL_SFI)
- ,
- channel_(
- Channel::Create(this, std::move(connection_params), io_task_runner_))
+ , channel_(
+ Channel::Create(this, std::move(platform_handle), io_task_runner_))
#endif
-{
+ {
}
NodeChannel::~NodeChannel() {
@@ -748,16 +728,6 @@
#endif // defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS))
- case MessageType::ACCEPT_PEER: {
- const AcceptPeerData* data;
- if (GetMessagePayload(payload, payload_size, &data)) {
- delegate_->OnAcceptPeer(remote_node_name_, data->token, data->peer_name,
- data->port_name);
- return;
- }
- break;
- }
-
default:
break;
}
@@ -801,6 +771,7 @@
pending_writes.swap(pending_write_messages_);
pending_relays.swap(pending_relay_messages_);
}
+ DCHECK(pending_writes.empty() && pending_relays.empty());
while (!pending_writes.empty()) {
Channel::MessagePtr message = std::move(pending_writes.front());
diff --git a/mojo/edk/system/node_channel.h b/mojo/edk/system/node_channel.h
index 95dc341..5a5fc8a 100644
--- a/mojo/edk/system/node_channel.h
+++ b/mojo/edk/system/node_channel.h
@@ -16,7 +16,6 @@
#include "base/synchronization/lock.h"
#include "base/task_runner.h"
#include "build/build_config.h"
-#include "mojo/edk/embedder/connection_params.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/edk/embedder/platform_handle_vector.h"
#include "mojo/edk/embedder/scoped_platform_handle.h"
@@ -77,10 +76,7 @@
const ports::NodeName& source_node,
Channel::MessagePtr message) = 0;
#endif
- virtual void OnAcceptPeer(const ports::NodeName& from_node,
- const ports::NodeName& token,
- const ports::NodeName& peer_name,
- const ports::PortName& port_name) = 0;
+
virtual void OnChannelError(const ports::NodeName& node,
NodeChannel* channel) = 0;
@@ -91,7 +87,7 @@
static scoped_refptr<NodeChannel> Create(
Delegate* delegate,
- ConnectionParams connection_params,
+ ScopedPlatformHandle platform_handle,
scoped_refptr<base::TaskRunner> io_task_runner,
const ProcessErrorCallback& process_error_callback);
@@ -128,9 +124,6 @@
const ports::NodeName& token);
void AcceptParent(const ports::NodeName& token,
const ports::NodeName& child_name);
- void AcceptPeer(const ports::NodeName& sender_name,
- const ports::NodeName& token,
- const ports::PortName& port_name);
void AddBrokerClient(const ports::NodeName& client_name,
base::ProcessHandle process_handle);
void BrokerClientAdded(const ports::NodeName& client_name,
@@ -168,7 +161,7 @@
std::queue<std::pair<ports::NodeName, Channel::MessagePtr>>;
NodeChannel(Delegate* delegate,
- ConnectionParams connection_params,
+ ScopedPlatformHandle platform_handle,
scoped_refptr<base::TaskRunner> io_task_runner,
const ProcessErrorCallback& process_error_callback);
~NodeChannel() override;
diff --git a/mojo/edk/system/node_controller.cc b/mojo/edk/system/node_controller.cc
index 7bdb571..63291a5 100644
--- a/mojo/edk/system/node_controller.cc
+++ b/mojo/edk/system/node_controller.cc
@@ -18,8 +18,6 @@
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "mojo/edk/embedder/embedder_internal.h"
-#include "mojo/edk/embedder/named_platform_channel_pair.h"
-#include "mojo/edk/embedder/named_platform_handle.h"
#include "mojo/edk/embedder/platform_channel_pair.h"
#include "mojo/edk/system/broker.h"
#include "mojo/edk/system/broker_host.h"
@@ -60,7 +58,7 @@
// 8k is the maximum number of file descriptors allowed in Chrome.
UMA_HISTOGRAM_CUSTOM_COUNTS("Mojo.System.Node.ConnectedPeers",
static_cast<int32_t>(count),
- 1 /* min */,
+ 0 /* min */,
8000 /* max */,
50 /* bucket count */);
}
@@ -71,7 +69,7 @@
// 8k is the maximum number of file descriptors allowed in Chrome.
UMA_HISTOGRAM_CUSTOM_COUNTS("Mojo.System.Node.PendingChildren",
static_cast<int32_t>(count),
- 1 /* min */,
+ 0 /* min */,
8000 /* max */,
50 /* bucket count */);
}
@@ -165,7 +163,7 @@
void NodeController::ConnectToChild(
base::ProcessHandle process_handle,
- ConnectionParams connection_params,
+ ScopedPlatformHandle platform_handle,
const std::string& child_token,
const ProcessErrorCallback& process_error_callback) {
// Generate the temporary remote node name here so that it can be associated
@@ -196,10 +194,13 @@
#endif
io_task_runner_->PostTask(
- FROM_HERE, base::Bind(&NodeController::ConnectToChildOnIOThread,
- base::Unretained(this), process_handle,
- base::Passed(&connection_params), node_name,
- process_error_callback));
+ FROM_HERE,
+ base::Bind(&NodeController::ConnectToChildOnIOThread,
+ base::Unretained(this),
+ process_handle,
+ base::Passed(&platform_handle),
+ node_name,
+ process_error_callback));
}
void NodeController::CloseChildPorts(const std::string& child_token) {
@@ -226,19 +227,14 @@
AcceptIncomingMessages();
}
-void NodeController::ClosePeerConnection(const std::string& peer_token) {
- io_task_runner_->PostTask(
- FROM_HERE, base::Bind(&NodeController::ClosePeerConnectionOnIOThread,
- base::Unretained(this), peer_token));
-}
-
-void NodeController::ConnectToParent(ConnectionParams connection_params) {
-#if !defined(OS_MACOSX) && !defined(OS_NACL_SFI)
- // Use the bootstrap channel for the broker and receive the node's channel
- // synchronously as the first message from the broker.
+void NodeController::ConnectToParent(ScopedPlatformHandle platform_handle) {
+// TODO(amistry): Consider the need for a broker on Windows.
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_NACL_SFI)
+ // On posix, use the bootstrap channel for the broker and receive the node's
+ // channel synchronously as the first message from the broker.
base::ElapsedTimer timer;
- broker_.reset(new Broker(connection_params.TakeChannelHandle()));
- ScopedPlatformHandle platform_handle = broker_->GetParentPlatformHandle();
+ broker_.reset(new Broker(std::move(platform_handle)));
+ platform_handle = broker_->GetParentPlatformHandle();
UMA_HISTOGRAM_TIMES("Mojo.System.GetParentPlatformHandleSyncTime",
timer.Elapsed());
@@ -247,33 +243,21 @@
// the broker was unable to negotiate a NodeChannel pipe. In this case we
// can cancel parent connection.
DVLOG(1) << "Cannot connect to invalid parent channel.";
- CancelPendingPortMerges();
return;
}
- connection_params = ConnectionParams(std::move(platform_handle));
#endif
io_task_runner_->PostTask(
FROM_HERE,
base::Bind(&NodeController::ConnectToParentOnIOThread,
- base::Unretained(this), base::Passed(&connection_params)));
+ base::Unretained(this),
+ base::Passed(&platform_handle)));
}
-void NodeController::ConnectToPeer(ConnectionParams connection_params,
- const ports::PortRef& port,
- const std::string& peer_token) {
- ports::NodeName node_name;
- GenerateRandomName(&node_name);
- io_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&NodeController::ConnectToPeerOnIOThread,
- base::Unretained(this), base::Passed(&connection_params),
- node_name, port, peer_token));
-}
-
-void NodeController::SetPortObserver(const ports::PortRef& port,
- scoped_refptr<PortObserver> observer) {
- node_->SetUserData(port, std::move(observer));
+void NodeController::SetPortObserver(
+ const ports::PortRef& port,
+ const scoped_refptr<PortObserver>& observer) {
+ node_->SetUserData(port, observer);
}
void NodeController::ClosePort(const ports::PortRef& port) {
@@ -361,7 +345,7 @@
scoped_refptr<PlatformSharedBuffer> NodeController::CreateSharedBuffer(
size_t num_bytes) {
-#if !defined(OS_MACOSX) && !defined(OS_NACL_SFI)
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_NACL_SFI)
// Shared buffer creation failure is fatal, so always use the broker when we
// have one. This does mean that a non-root process that has children will use
// the broker for shared buffer creation even though that process is
@@ -392,40 +376,24 @@
void NodeController::ConnectToChildOnIOThread(
base::ProcessHandle process_handle,
- ConnectionParams connection_params,
+ ScopedPlatformHandle platform_handle,
ports::NodeName token,
const ProcessErrorCallback& process_error_callback) {
DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
-#if !defined(OS_MACOSX) && !defined(OS_NACL)
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_NACL)
PlatformChannelPair node_channel;
- ScopedPlatformHandle server_handle = node_channel.PassServerHandle();
// BrokerHost owns itself.
- BrokerHost* broker_host =
- new BrokerHost(process_handle, connection_params.TakeChannelHandle());
- bool channel_ok = broker_host->SendChannel(node_channel.PassClientHandle());
-
-#if defined(OS_WIN)
- if (!channel_ok) {
- // On Windows the above operation may fail if the channel is crossing a
- // session boundary. In that case we fall back to a named pipe.
- NamedPlatformChannelPair named_channel;
- server_handle = named_channel.PassServerHandle();
- broker_host->SendNamedChannel(named_channel.handle().name);
- }
+ BrokerHost* broker_host = new BrokerHost(std::move(platform_handle));
+ broker_host->SendChannel(node_channel.PassClientHandle());
+ scoped_refptr<NodeChannel> channel = NodeChannel::Create(
+ this, node_channel.PassServerHandle(), io_task_runner_,
+ process_error_callback);
#else
- CHECK(channel_ok);
-#endif // defined(OS_WIN)
-
scoped_refptr<NodeChannel> channel =
- NodeChannel::Create(this, ConnectionParams(std::move(server_handle)),
- io_task_runner_, process_error_callback);
-
-#else // !defined(OS_MACOSX) && !defined(OS_NACL)
- scoped_refptr<NodeChannel> channel =
- NodeChannel::Create(this, std::move(connection_params), io_task_runner_,
+ NodeChannel::Create(this, std::move(platform_handle), io_task_runner_,
process_error_callback);
-#endif // !defined(OS_MACOSX) && !defined(OS_NACL)
+#endif
// We set up the child channel with a temporary name so it can be identified
// as a pending child if it writes any messages to the channel. We may start
@@ -443,7 +411,7 @@
}
void NodeController::ConnectToParentOnIOThread(
- ConnectionParams connection_params) {
+ ScopedPlatformHandle platform_handle) {
DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
{
@@ -454,7 +422,7 @@
// into our |peers_| map. That will happen as soon as we receive an
// AcceptChild message from them.
bootstrap_parent_channel_ =
- NodeChannel::Create(this, std::move(connection_params), io_task_runner_,
+ NodeChannel::Create(this, std::move(platform_handle), io_task_runner_,
ProcessErrorCallback());
// Prevent the parent pipe handle from being closed on shutdown. Pipe
// closure is used by the parent to detect the child process has exited.
@@ -466,37 +434,6 @@
bootstrap_parent_channel_->Start();
}
-void NodeController::ConnectToPeerOnIOThread(ConnectionParams connection_params,
- ports::NodeName token,
- ports::PortRef port,
- const std::string& peer_token) {
- DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
-
- scoped_refptr<NodeChannel> channel = NodeChannel::Create(
- this, std::move(connection_params), io_task_runner_, {});
- peer_connections_.insert(
- {token, PeerConnection{channel, port, peer_token}});
- peers_by_token_.insert({peer_token, token});
-
- channel->SetRemoteNodeName(token);
- channel->Start();
-
- channel->AcceptPeer(name_, token, port.name());
-}
-
-void NodeController::ClosePeerConnectionOnIOThread(
- const std::string& peer_token) {
- RequestContext request_context(RequestContext::Source::SYSTEM);
- auto peer = peers_by_token_.find(peer_token);
- // The connection may already be closed.
- if (peer == peers_by_token_.end())
- return;
-
- // |peer| may be removed so make a copy of |name|.
- ports::NodeName name = peer->second;
- DropPeer(name, nullptr);
-}
-
scoped_refptr<NodeChannel> NodeController::GetPeerChannel(
const ports::NodeName& name) {
base::AutoLock lock(peers_lock_);
@@ -622,18 +559,16 @@
base::AutoLock lock(parent_lock_);
is_parent = (name == parent_name_ || channel == bootstrap_parent_channel_);
}
-
// If the error comes from the parent channel, we also need to cancel any
// port merge requests, so that errors can be propagated to the message
// pipes.
- if (is_parent)
- CancelPendingPortMerges();
+ if (is_parent) {
+ base::AutoLock lock(pending_port_merges_lock_);
+ reject_pending_merges_ = true;
- auto peer = peer_connections_.find(name);
- if (peer != peer_connections_.end()) {
- peers_by_token_.erase(peer->second.peer_token);
- ports_to_close.push_back(peer->second.local_port);
- peer_connections_.erase(peer);
+ for (const auto& port : pending_port_merges_)
+ ports_to_close.push_back(port.second);
+ pending_port_merges_.clear();
}
for (const auto& port : ports_to_close)
@@ -717,50 +652,20 @@
}
void NodeController::AcceptIncomingMessages() {
- // This is an impactically large value which should never be reached in
- // practice. See the CHECK below for usage.
- constexpr size_t kMaxAcceptedMessages = 1000000;
+ {
+ base::AutoLock lock(messages_lock_);
+ if (!incoming_messages_.empty()) {
+ // libstdc++'s deque creates an internal buffer on construction, even when
+ // the size is 0. So avoid creating it until it is necessary.
+ std::queue<ports::ScopedMessage> messages;
+ std::swap(messages, incoming_messages_);
+ base::AutoUnlock unlock(messages_lock_);
- size_t num_messages_accepted = 0;
- while (incoming_messages_flag_) {
- // TODO: We may need to be more careful to avoid starving the rest of the
- // thread here. Revisit this if it turns out to be a problem. One
- // alternative would be to schedule a task to continue pumping messages
- // after flushing once.
-
- messages_lock_.Acquire();
- if (incoming_messages_.empty()) {
- messages_lock_.Release();
- break;
+ while (!messages.empty()) {
+ node_->AcceptMessage(std::move(messages.front()));
+ messages.pop();
+ }
}
-
- // libstdc++'s deque creates an internal buffer on construction, even when
- // the size is 0. So avoid creating it until it is necessary.
- std::queue<ports::ScopedMessage> messages;
- std::swap(messages, incoming_messages_);
- incoming_messages_flag_.Set(false);
- messages_lock_.Release();
-
- num_messages_accepted += messages.size();
- while (!messages.empty()) {
- node_->AcceptMessage(std::move(messages.front()));
- messages.pop();
- }
-
- // This is effectively a safeguard against potential bugs which might lead
- // to runaway message cycles. If any such cycles arise, we'll start seeing
- // crash reports from this location.
- CHECK_LE(num_messages_accepted, kMaxAcceptedMessages);
- }
-
- if (num_messages_accepted >= 4) {
- // Note: We avoid logging this histogram for the vast majority of cases.
- // See https://crbug.com/685763 for more context.
- UMA_HISTOGRAM_CUSTOM_COUNTS("Mojo.System.MessagesAcceptedPerEvent",
- static_cast<int32_t>(num_messages_accepted),
- 1 /* min */,
- 500 /* max */,
- 50 /* bucket count */);
}
AttemptShutdownIfRequested();
@@ -807,7 +712,6 @@
peers_.clear();
pending_children_.clear();
pending_peer_messages_.clear();
- peer_connections_.clear();
}
for (const auto& peer : all_peers)
@@ -842,7 +746,6 @@
!incoming_messages_task_posted_;
incoming_messages_task_posted_ |= schedule_pump_task;
incoming_messages_.emplace(std::move(message));
- incoming_messages_flag_.Set(true);
} else {
SendPeerMessage(node, std::move(message));
}
@@ -993,10 +896,9 @@
}
PlatformChannelPair broker_channel;
- ConnectionParams connection_params(broker_channel.PassServerHandle());
- scoped_refptr<NodeChannel> client =
- NodeChannel::Create(this, std::move(connection_params), io_task_runner_,
- ProcessErrorCallback());
+ scoped_refptr<NodeChannel> client = NodeChannel::Create(
+ this, broker_channel.PassServerHandle(), io_task_runner_,
+ ProcessErrorCallback());
#if defined(OS_WIN)
// The broker must have a working handle to the client process in order to
@@ -1072,9 +974,8 @@
broker = parent;
} else {
DCHECK(broker_channel.is_valid());
- broker =
- NodeChannel::Create(this, ConnectionParams(std::move(broker_channel)),
- io_task_runner_, ProcessErrorCallback());
+ broker = NodeChannel::Create(this, std::move(broker_channel),
+ io_task_runner_, ProcessErrorCallback());
AddPeer(broker_name, broker, true /* start_channel */);
}
@@ -1195,15 +1096,15 @@
if (!channel_handle.is_valid()) {
node_->LostConnectionToNode(name);
- DVLOG(1) << "Could not be introduced to peer " << name;
+ DLOG(ERROR) << "Could not be introduced to peer " << name;
base::AutoLock lock(peers_lock_);
pending_peer_messages_.erase(name);
return;
}
scoped_refptr<NodeChannel> channel =
- NodeChannel::Create(this, ConnectionParams(std::move(channel_handle)),
- io_task_runner_, ProcessErrorCallback());
+ NodeChannel::Create(this, std::move(channel_handle), io_task_runner_,
+ ProcessErrorCallback());
DVLOG(1) << "Adding new peer " << name << " via parent introduction.";
AddPeer(name, channel, true /* start_channel */);
@@ -1318,46 +1219,6 @@
}
#endif
-void NodeController::OnAcceptPeer(const ports::NodeName& from_node,
- const ports::NodeName& token,
- const ports::NodeName& peer_name,
- const ports::PortName& port_name) {
- DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
-
- auto it = peer_connections_.find(from_node);
- if (it == peer_connections_.end()) {
- DLOG(ERROR) << "Received unexpected AcceptPeer message from " << from_node;
- DropPeer(from_node, nullptr);
- return;
- }
-
- scoped_refptr<NodeChannel> channel = std::move(it->second.channel);
- ports::PortRef local_port = it->second.local_port;
- std::string peer_token = std::move(it->second.peer_token);
- peer_connections_.erase(it);
- DCHECK(channel);
-
- // If the peer connection is a self connection (which is used in tests),
- // drop the channel to it and skip straight to merging the ports.
- if (name_ == peer_name) {
- peers_by_token_.erase(peer_token);
- } else {
- peers_by_token_[peer_token] = peer_name;
- peer_connections_.insert(
- {peer_name, PeerConnection{nullptr, local_port, peer_token}});
- DVLOG(1) << "Node " << name_ << " accepted peer " << peer_name;
-
- AddPeer(peer_name, channel, false /* start_channel */);
- }
-
- // We need to choose one side to initiate the port merge. It doesn't matter
- // who does it as long as they don't both try. Simple solution: pick the one
- // with the "smaller" port name.
- if (local_port.name() < port_name) {
- node()->MergePorts(local_port, peer_name, port_name);
- }
-}
-
void NodeController::OnChannelError(const ports::NodeName& from_node,
NodeChannel* channel) {
if (io_task_runner_->RunsTasksOnCurrentThread()) {
@@ -1387,21 +1248,6 @@
}
#endif
-void NodeController::CancelPendingPortMerges() {
- std::vector<ports::PortRef> ports_to_close;
-
- {
- base::AutoLock lock(pending_port_merges_lock_);
- reject_pending_merges_ = true;
- for (const auto& port : pending_port_merges_)
- ports_to_close.push_back(port.second);
- pending_port_merges_.clear();
- }
-
- for (const auto& port : ports_to_close)
- node_->ClosePort(port);
-}
-
void NodeController::DestroyOnIOThreadShutdown() {
destroy_on_io_thread_shutdown_ = true;
}
@@ -1415,8 +1261,7 @@
base::AutoLock lock(shutdown_lock_);
if (shutdown_callback_.is_null())
return;
- if (!node_->CanShutdownCleanly(
- ports::Node::ShutdownPolicy::ALLOW_LOCAL_PORTS)) {
+ if (!node_->CanShutdownCleanly(true /* allow_local_ports */)) {
DVLOG(2) << "Unable to cleanly shut down node " << name_;
return;
}
@@ -1431,29 +1276,5 @@
callback.Run();
}
-NodeController::PeerConnection::PeerConnection() = default;
-
-NodeController::PeerConnection::PeerConnection(
- const PeerConnection& other) = default;
-
-NodeController::PeerConnection::PeerConnection(
- PeerConnection&& other) = default;
-
-NodeController::PeerConnection::PeerConnection(
- scoped_refptr<NodeChannel> channel,
- const ports::PortRef& local_port,
- const std::string& peer_token)
- : channel(std::move(channel)),
- local_port(local_port),
- peer_token(peer_token) {}
-
-NodeController::PeerConnection::~PeerConnection() = default;
-
-NodeController::PeerConnection& NodeController::PeerConnection::
-operator=(const PeerConnection& other) = default;
-
-NodeController::PeerConnection& NodeController::PeerConnection::
-operator=(PeerConnection&& other) = default;
-
} // namespace edk
} // namespace mojo
diff --git a/mojo/edk/system/node_controller.h b/mojo/edk/system/node_controller.h
index 46a2d61..11d5f19 100644
--- a/mojo/edk/system/node_controller.h
+++ b/mojo/edk/system/node_controller.h
@@ -73,7 +73,7 @@
// Connects this node to a child node. This node will initiate a handshake.
void ConnectToChild(base::ProcessHandle process_handle,
- ConnectionParams connection_params,
+ ScopedPlatformHandle platform_handle,
const std::string& child_token,
const ProcessErrorCallback& process_error_callback);
@@ -81,23 +81,14 @@
// |child_token|.
void CloseChildPorts(const std::string& child_token);
- // Close a connection to a peer associated with |peer_token|.
- void ClosePeerConnection(const std::string& peer_token);
-
// Connects this node to a parent node. The parent node will initiate a
// handshake.
- void ConnectToParent(ConnectionParams connection_params);
-
- // Connects this node to a peer node. On success, |port| will be merged with
- // the corresponding port in the peer node.
- void ConnectToPeer(ConnectionParams connection_params,
- const ports::PortRef& port,
- const std::string& peer_token);
+ void ConnectToParent(ScopedPlatformHandle platform_handle);
// Sets a port's observer. If |observer| is null the port's current observer
// is removed.
void SetPortObserver(const ports::PortRef& port,
- scoped_refptr<PortObserver> observer);
+ const scoped_refptr<PortObserver>& observer);
// Closes a port. Use this in lieu of calling Node::ClosePort() directly, as
// it ensures the port's observer has also been removed.
@@ -148,36 +139,12 @@
const std::string child_token;
};
- struct PeerConnection {
- PeerConnection();
- PeerConnection(const PeerConnection& other);
- PeerConnection(PeerConnection&& other);
- PeerConnection(scoped_refptr<NodeChannel> channel,
- const ports::PortRef& local_port,
- const std::string& peer_token);
- ~PeerConnection();
-
- PeerConnection& operator=(const PeerConnection& other);
- PeerConnection& operator=(PeerConnection&& other);
-
-
- scoped_refptr<NodeChannel> channel;
- ports::PortRef local_port;
- std::string peer_token;
- };
-
void ConnectToChildOnIOThread(
base::ProcessHandle process_handle,
- ConnectionParams connection_params,
+ ScopedPlatformHandle platform_handle,
ports::NodeName token,
const ProcessErrorCallback& process_error_callback);
- void ConnectToParentOnIOThread(ConnectionParams connection_params);
-
- void ConnectToPeerOnIOThread(ConnectionParams connection_params,
- ports::NodeName token,
- ports::PortRef port,
- const std::string& peer_token);
- void ClosePeerConnectionOnIOThread(const std::string& node_name);
+ void ConnectToParentOnIOThread(ScopedPlatformHandle platform_handle);
scoped_refptr<NodeChannel> GetPeerChannel(const ports::NodeName& name);
scoped_refptr<NodeChannel> GetParentChannel();
@@ -239,21 +206,12 @@
const ports::NodeName& source_node,
Channel::MessagePtr message) override;
#endif
- void OnAcceptPeer(const ports::NodeName& from_node,
- const ports::NodeName& token,
- const ports::NodeName& peer_name,
- const ports::PortName& port_name) override;
void OnChannelError(const ports::NodeName& from_node,
NodeChannel* channel) override;
#if defined(OS_MACOSX) && !defined(OS_IOS)
MachPortRelay* GetMachPortRelay() override;
#endif
- // Cancels all pending port merges. These are merges which are supposed to
- // be requested from the parent ASAP, and they may be cancelled if the
- // connection to the parent is broken or never established.
- void CancelPendingPortMerges();
-
// Marks this NodeController for destruction when the IO thread shuts down.
// This is used in case Core is torn down before the IO thread. Must only be
// called on the IO thread.
@@ -328,8 +286,6 @@
// Ensures that there is only one incoming messages task posted to the IO
// thread.
bool incoming_messages_task_posted_ = false;
- // Flag to fast-path checking |incoming_messages_|.
- AtomicFlag incoming_messages_flag_;
// Guards |shutdown_callback_|.
base::Lock shutdown_lock_;
@@ -347,19 +303,12 @@
// Channels to children during handshake.
NodeMap pending_children_;
- using PeerNodeMap =
- std::unordered_map<ports::NodeName, PeerConnection>;
- PeerNodeMap peer_connections_;
-
- // Maps from peer token to node name, pending or not.
- std::unordered_map<std::string, ports::NodeName> peers_by_token_;
-
// Indicates whether this object should delete itself on IO thread shutdown.
// Must only be accessed from the IO thread.
bool destroy_on_io_thread_shutdown_ = false;
-#if !defined(OS_MACOSX) && !defined(OS_NACL_SFI)
- // Broker for sync shared buffer creation in children.
+#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_NACL_SFI)
+ // Broker for sync shared buffer creation (non-Mac posix-only) in children.
std::unique_ptr<Broker> broker_;
#endif
diff --git a/mojo/edk/system/platform_handle_dispatcher_unittest.cc b/mojo/edk/system/platform_handle_dispatcher_unittest.cc
index 7a94262..ad6dc38 100644
--- a/mojo/edk/system/platform_handle_dispatcher_unittest.cc
+++ b/mojo/edk/system/platform_handle_dispatcher_unittest.cc
@@ -28,7 +28,7 @@
base::FilePath unused;
base::ScopedFILE fp(
- CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &unused));
+ CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
ASSERT_TRUE(fp);
EXPECT_EQ(sizeof(kHelloWorld),
fwrite(kHelloWorld, 1, sizeof(kHelloWorld), fp.get()));
@@ -70,7 +70,7 @@
base::FilePath unused;
base::ScopedFILE fp(
- CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &unused));
+ CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
EXPECT_EQ(sizeof(kFooBar), fwrite(kFooBar, 1, sizeof(kFooBar), fp.get()));
scoped_refptr<PlatformHandleDispatcher> dispatcher =
diff --git a/mojo/edk/system/ports/BUILD.gn b/mojo/edk/system/ports/BUILD.gn
index 37b2548..239b3a4 100644
--- a/mojo/edk/system/ports/BUILD.gn
+++ b/mojo/edk/system/ports/BUILD.gn
@@ -10,7 +10,6 @@
"event.h",
"message.cc",
"message.h",
- "message_filter.h",
"message_queue.cc",
"message_queue.h",
"name.cc",
diff --git a/mojo/edk/system/ports/message_filter.h b/mojo/edk/system/ports/message_filter.h
deleted file mode 100644
index bf8fa21..0000000
--- a/mojo/edk/system/ports/message_filter.h
+++ /dev/null
@@ -1,29 +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.
-
-#ifndef MOJO_EDK_SYSTEM_PORTS_MESSAGE_FILTER_H_
-#define MOJO_EDK_SYSTEM_PORTS_MESSAGE_FILTER_H_
-
-namespace mojo {
-namespace edk {
-namespace ports {
-
-class Message;
-
-// An interface which can be implemented to filter port messages according to
-// arbitrary policy.
-class MessageFilter {
- public:
- virtual ~MessageFilter() {}
-
- // Returns true of |message| should be accepted by whomever is applying this
- // filter. See MessageQueue::GetNextMessage(), for example.
- virtual bool Match(const Message& message) = 0;
-};
-
-} // namespace ports
-} // namespace edk
-} // namespace mojo
-
-#endif // MOJO_EDK_SYSTEM_PORTS_MESSAGE_FILTER_H_
diff --git a/mojo/edk/system/ports/message_queue.cc b/mojo/edk/system/ports/message_queue.cc
index defb1b6..ef2e940 100644
--- a/mojo/edk/system/ports/message_queue.cc
+++ b/mojo/edk/system/ports/message_queue.cc
@@ -8,7 +8,6 @@
#include "base/logging.h"
#include "mojo/edk/system/ports/event.h"
-#include "mojo/edk/system/ports/message_filter.h"
namespace mojo {
namespace edk {
@@ -45,9 +44,10 @@
return !heap_.empty() && GetSequenceNum(heap_[0]) == next_sequence_num_;
}
-void MessageQueue::GetNextMessage(ScopedMessage* message,
- MessageFilter* filter) {
- if (!HasNextMessage() || (filter && !filter->Match(*heap_[0].get()))) {
+void MessageQueue::GetNextMessageIf(
+ std::function<bool(const Message&)> selector,
+ ScopedMessage* message) {
+ if (!HasNextMessage() || (selector && !selector(*heap_[0].get()))) {
message->reset();
return;
}
diff --git a/mojo/edk/system/ports/message_queue.h b/mojo/edk/system/ports/message_queue.h
index d9a47ed..d90ac1a 100644
--- a/mojo/edk/system/ports/message_queue.h
+++ b/mojo/edk/system/ports/message_queue.h
@@ -22,8 +22,6 @@
const uint64_t kInitialSequenceNum = 1;
const uint64_t kInvalidSequenceNum = std::numeric_limits<uint64_t>::max();
-class MessageFilter;
-
// An incoming message queue for a port. MessageQueue keeps track of the highest
// known sequence number and can indicate whether the next sequential message is
// available. Thus the queue enforces message ordering for the consumer without
@@ -40,9 +38,9 @@
bool HasNextMessage() const;
- // Gives ownership of the message. If |filter| is non-null, the next message
- // will only be retrieved if the filter successfully matches it.
- void GetNextMessage(ScopedMessage* message, MessageFilter* filter);
+ // Gives ownership of the message. The selector may be null.
+ void GetNextMessageIf(std::function<bool(const Message&)> selector,
+ ScopedMessage* message);
// Takes ownership of the message. Note: Messages are ordered, so while we
// have added a message to the queue, we may still be waiting on a message
diff --git a/mojo/edk/system/ports/name.cc b/mojo/edk/system/ports/name.cc
index ea17698..7088cbf 100644
--- a/mojo/edk/system/ports/name.cc
+++ b/mojo/edk/system/ports/name.cc
@@ -8,10 +8,6 @@
namespace edk {
namespace ports {
-extern const PortName kInvalidPortName = {0, 0};
-
-extern const NodeName kInvalidNodeName = {0, 0};
-
std::ostream& operator<<(std::ostream& stream, const Name& name) {
std::ios::fmtflags flags(stream.flags());
stream << std::hex << std::uppercase << name.v1;
diff --git a/mojo/edk/system/ports/name.h b/mojo/edk/system/ports/name.h
index 72e41b9..1082719 100644
--- a/mojo/edk/system/ports/name.h
+++ b/mojo/edk/system/ports/name.h
@@ -40,14 +40,14 @@
PortName(uint64_t v1, uint64_t v2) : Name(v1, v2) {}
};
-extern const PortName kInvalidPortName;
+const PortName kInvalidPortName = {0, 0};
struct NodeName : Name {
NodeName() : Name(0, 0) {}
NodeName(uint64_t v1, uint64_t v2) : Name(v1, v2) {}
};
-extern const NodeName kInvalidNodeName;
+const NodeName kInvalidNodeName = {0, 0};
} // namespace ports
} // namespace edk
diff --git a/mojo/edk/system/ports/node.cc b/mojo/edk/system/ports/node.cc
index 9c6f1fd..128ecdf 100644
--- a/mojo/edk/system/ports/node.cc
+++ b/mojo/edk/system/ports/node.cc
@@ -8,7 +8,6 @@
#include <utility>
-#include "base/atomicops.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h"
@@ -65,10 +64,10 @@
DLOG(WARNING) << "Unclean shutdown for node " << name_;
}
-bool Node::CanShutdownCleanly(ShutdownPolicy policy) {
+bool Node::CanShutdownCleanly(bool allow_local_ports) {
base::AutoLock ports_lock(ports_lock_);
- if (policy == ShutdownPolicy::DONT_ALLOW_LOCAL_PORTS) {
+ if (!allow_local_ports) {
#if DCHECK_IS_ON()
for (auto entry : ports_) {
DVLOG(2) << "Port " << entry.first << " referencing node "
@@ -79,8 +78,6 @@
return ports_.empty();
}
- DCHECK_EQ(policy, ShutdownPolicy::ALLOW_LOCAL_PORTS);
-
// NOTE: This is not efficient, though it probably doesn't need to be since
// relatively few ports should be open during shutdown and shutdown doesn't
// need to be blazingly fast.
@@ -117,7 +114,8 @@
PortName port_name;
delegate_->GenerateRandomPortName(&port_name);
- scoped_refptr<Port> port(new Port(kInitialSequenceNum, kInitialSequenceNum));
+ scoped_refptr<Port> port = make_scoped_refptr(new Port(kInitialSequenceNum,
+ kInitialSequenceNum));
int rv = AddPortWithName(port_name, port);
if (rv != OK)
return rv;
@@ -169,7 +167,7 @@
}
int Node::SetUserData(const PortRef& port_ref,
- scoped_refptr<UserData> user_data) {
+ const scoped_refptr<UserData>& user_data) {
Port* port = port_ref.port();
base::AutoLock lock(port->lock);
@@ -263,12 +261,16 @@
return OK;
}
-int Node::GetMessage(const PortRef& port_ref,
- ScopedMessage* message,
- MessageFilter* filter) {
+int Node::GetMessage(const PortRef& port_ref, ScopedMessage* message) {
+ return GetMessageIf(port_ref, nullptr, message);
+}
+
+int Node::GetMessageIf(const PortRef& port_ref,
+ std::function<bool(const Message&)> selector,
+ ScopedMessage* message) {
*message = nullptr;
- DVLOG(4) << "GetMessage for " << port_ref.name() << "@" << name_;
+ DVLOG(2) << "GetMessageIf for " << port_ref.name() << "@" << name_;
Port* port = port_ref.port();
{
@@ -284,7 +286,7 @@
if (!CanAcceptMoreMessages(port))
return ERROR_PORT_PEER_CLOSED;
- port->message_queue.GetNextMessage(message, filter);
+ port->message_queue.GetNextMessageIf(std::move(selector), message);
}
// Allow referenced ports to trigger PortStatusChanged calls.
@@ -424,7 +426,7 @@
ports_buf << message->ports()[i];
}
- DVLOG(4) << "AcceptMessage " << event->sequence_num
+ DVLOG(2) << "AcceptMessage " << event->sequence_num
<< " [ports=" << ports_buf.str() << "] at "
<< port_name << "@" << name_;
#endif
@@ -504,7 +506,7 @@
<< " pointing to "
<< port->peer_port_name << "@" << port->peer_node_name;
- return BeginProxying(PortRef(port_name, std::move(port)));
+ return BeginProxying(PortRef(port_name, port));
}
int Node::OnObserveProxy(const PortName& port_name,
@@ -529,6 +531,17 @@
scoped_refptr<Port> port = GetPort(port_name);
if (!port) {
DVLOG(1) << "ObserveProxy: " << port_name << "@" << name_ << " not found";
+
+ if (port_name != event.proxy_port_name &&
+ port_name != event.proxy_to_port_name) {
+ // The receiving port may have been removed while this message was in
+ // transit. In this case, we restart the ObserveProxy circulation from
+ // the referenced proxy port to avoid leaking the proxy.
+ delegate_->ForwardMessage(
+ event.proxy_node_name,
+ NewInternalMessage(
+ event.proxy_port_name, EventType::kObserveProxy, event));
+ }
return OK;
}
@@ -619,7 +632,7 @@
port->remove_proxy_on_last_message = true;
port->last_sequence_num_to_receive = last_sequence_num;
}
- TryRemoveProxy(PortRef(port_name, std::move(port)));
+ TryRemoveProxy(PortRef(port_name, port));
return OK;
}
@@ -696,7 +709,7 @@
forwarded_data));
if (notify_delegate) {
- PortRef port_ref(port_name, std::move(port));
+ PortRef port_ref(port_name, port);
delegate_->PortStatusChanged(port_ref);
}
return OK;
@@ -771,10 +784,11 @@
return ERROR_PORT_STATE_UNEXPECTED;
}
-int Node::AddPortWithName(const PortName& port_name, scoped_refptr<Port> port) {
+int Node::AddPortWithName(const PortName& port_name,
+ const scoped_refptr<Port>& port) {
base::AutoLock lock(ports_lock_);
- if (!ports_.insert(std::make_pair(port_name, std::move(port))).second)
+ if (!ports_.insert(std::make_pair(port_name, port)).second)
return OOPS(ERROR_PORT_EXISTS); // Suggests a bad UUID generator.
DVLOG(2) << "Created port " << port_name << "@" << name_;
@@ -803,11 +817,6 @@
if (iter == ports_.end())
return nullptr;
-#if defined(OS_ANDROID) && defined(ARCH_CPU_ARM64)
- // Workaround for https://crbug.com/665869.
- base::subtle::MemoryBarrier();
-#endif
-
return iter->second;
}
@@ -994,10 +1003,10 @@
<< port->last_sequence_num_to_receive << "]";
// A newly accepted port is not signalable until the message referencing the
- // new port finds its way to the consumer (see GetMessage).
+ // new port finds its way to the consumer (see GetMessageIf).
port->message_queue.set_signalable(false);
- int rv = AddPortWithName(port_name, std::move(port));
+ int rv = AddPortWithName(port_name, port);
if (rv != OK)
return rv;
@@ -1078,7 +1087,7 @@
}
#if DCHECK_IS_ON()
- DVLOG(4) << "Sending message "
+ DVLOG(2) << "Sending message "
<< GetEventData<UserEventData>(*message)->sequence_num
<< " [ports=" << ports_buf.str() << "]"
<< " from " << port_name << "@" << name_
@@ -1176,7 +1185,7 @@
for (;;) {
ScopedMessage message;
- port->message_queue.GetNextMessage(&message, nullptr);
+ port->message_queue.GetNextMessageIf(nullptr, &message);
if (!message)
break;
diff --git a/mojo/edk/system/ports/node.h b/mojo/edk/system/ports/node.h
index 55b8d27..3aeadca 100644
--- a/mojo/edk/system/ports/node.h
+++ b/mojo/edk/system/ports/node.h
@@ -44,16 +44,10 @@
bool peer_closed;
};
-class MessageFilter;
class NodeDelegate;
class Node {
public:
- enum class ShutdownPolicy {
- DONT_ALLOW_LOCAL_PORTS,
- ALLOW_LOCAL_PORTS,
- };
-
// Does not take ownership of the delegate.
Node(const NodeName& name, NodeDelegate* delegate);
~Node();
@@ -65,11 +59,9 @@
// method may be called again after AcceptMessage to check if the Node is now
// ready to be destroyed.
//
- // If |policy| is set to |ShutdownPolicy::ALLOW_LOCAL_PORTS|, this will return
- // |true| even if some ports remain alive, as long as none of them are proxies
- // to another node.
- bool CanShutdownCleanly(
- ShutdownPolicy policy = ShutdownPolicy::DONT_ALLOW_LOCAL_PORTS);
+ // If |allow_local_ports| is |true|, this will only return |false| when there
+ // are transient ports referring to other nodes.
+ bool CanShutdownCleanly(bool allow_local_ports);
// Lookup the named port.
int GetPort(const PortName& port_name, PortRef* port_ref);
@@ -90,7 +82,8 @@
int CreatePortPair(PortRef* port0_ref, PortRef* port1_ref);
// User data associated with the port.
- int SetUserData(const PortRef& port_ref, scoped_refptr<UserData> user_data);
+ int SetUserData(const PortRef& port_ref,
+ const scoped_refptr<UserData>& user_data);
int GetUserData(const PortRef& port_ref,
scoped_refptr<UserData>* user_data);
@@ -107,15 +100,15 @@
// indicate that this port's peer has closed. In such cases GetMessage may
// be called until it yields a null message, indicating that no more messages
// may be read from the port.
- //
- // If |filter| is non-null, the next available message is returned only if it
- // is matched by the filter. If the provided filter does not match the next
- // available message, GetMessage() behaves as if there is no message
- // available. Ownership of |filter| is not taken, and it must outlive the
- // extent of this call.
- int GetMessage(const PortRef& port_ref,
- ScopedMessage* message,
- MessageFilter* filter);
+ int GetMessage(const PortRef& port_ref, ScopedMessage* message);
+
+ // Like GetMessage, but the caller may optionally supply a selector function
+ // that decides whether or not to return the message. If |selector| is a
+ // nullptr, then GetMessageIf acts just like GetMessage. The |selector| may
+ // not call any Node methods.
+ int GetMessageIf(const PortRef& port_ref,
+ std::function<bool(const Message&)> selector,
+ ScopedMessage* message);
// Sends a message from the specified port to its peer. Note that the message
// notification may arrive synchronously (via PortStatusChanged() on the
@@ -163,7 +156,8 @@
int OnObserveClosure(const PortName& port_name, uint64_t last_sequence_num);
int OnMergePort(const PortName& port_name, const MergePortEventData& event);
- int AddPortWithName(const PortName& port_name, scoped_refptr<Port> port);
+ int AddPortWithName(const PortName& port_name,
+ const scoped_refptr<Port>& port);
void ErasePort(const PortName& port_name);
void ErasePort_Locked(const PortName& port_name);
scoped_refptr<Port> GetPort(const PortName& port_name);
diff --git a/mojo/edk/system/ports/port_ref.cc b/mojo/edk/system/ports/port_ref.cc
index 675754d..bd59629 100644
--- a/mojo/edk/system/ports/port_ref.cc
+++ b/mojo/edk/system/ports/port_ref.cc
@@ -16,8 +16,9 @@
PortRef::PortRef() {
}
-PortRef::PortRef(const PortName& name, scoped_refptr<Port> port)
- : name_(name), port_(std::move(port)) {}
+PortRef::PortRef(const PortName& name, const scoped_refptr<Port>& port)
+ : name_(name), port_(port) {
+}
PortRef::PortRef(const PortRef& other)
: name_(other.name_), port_(other.port_) {
diff --git a/mojo/edk/system/ports/port_ref.h b/mojo/edk/system/ports/port_ref.h
index 59036c3..9af4a8d 100644
--- a/mojo/edk/system/ports/port_ref.h
+++ b/mojo/edk/system/ports/port_ref.h
@@ -19,7 +19,7 @@
public:
~PortRef();
PortRef();
- PortRef(const PortName& name, scoped_refptr<Port> port);
+ PortRef(const PortName& name, const scoped_refptr<Port>& port);
PortRef(const PortRef& other);
PortRef& operator=(const PortRef& other);
diff --git a/mojo/edk/system/ports/ports_unittest.cc b/mojo/edk/system/ports/ports_unittest.cc
index cb48b3e..200e72b 100644
--- a/mojo/edk/system/ports/ports_unittest.cc
+++ b/mojo/edk/system/ports/ports_unittest.cc
@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -10,18 +9,9 @@
#include <map>
#include <queue>
#include <sstream>
-#include <utility>
-#include "base/bind.h"
-#include "base/callback.h"
#include "base/logging.h"
-#include "base/memory/ref_counted.h"
#include "base/rand_util.h"
-#include "base/strings/string_piece.h"
-#include "base/strings/stringprintf.h"
-#include "base/synchronization/lock.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/threading/thread.h"
#include "mojo/edk/system/ports/event.h"
#include "mojo/edk/system/ports/node.h"
#include "mojo/edk/system/ports/node_delegate.h"
@@ -34,8 +24,24 @@
namespace {
-bool MessageEquals(const ScopedMessage& message, const base::StringPiece& s) {
- return !strcmp(static_cast<const char*>(message->payload_bytes()), s.data());
+void LogMessage(const Message* message) {
+ std::stringstream ports;
+ for (size_t i = 0; i < message->num_ports(); ++i) {
+ if (i > 0)
+ ports << ",";
+ ports << message->ports()[i];
+ }
+ DVLOG(1) << "message: \""
+ << static_cast<const char*>(message->payload_bytes())
+ << "\" ports=[" << ports.str() << "]";
+}
+
+void ClosePortsInMessage(Node* node, Message* message) {
+ for (size_t i = 0; i < message->num_ports(); ++i) {
+ PortRef port;
+ ASSERT_EQ(OK, node->GetPort(message->ports()[i], &port));
+ EXPECT_EQ(OK, node->ClosePort(port));
+ }
}
class TestMessage : public Message {
@@ -65,140 +71,132 @@
}
};
-class TestNode;
+struct Task {
+ Task(NodeName node_name, ScopedMessage message)
+ : node_name(node_name),
+ message(std::move(message)),
+ priority(base::RandUint64()) {
+ }
-class MessageRouter {
- public:
- virtual ~MessageRouter() {}
-
- virtual void GeneratePortName(PortName* name) = 0;
- virtual void ForwardMessage(TestNode* from_node,
- const NodeName& node_name,
- ScopedMessage message) = 0;
- virtual void BroadcastMessage(TestNode* from_node, ScopedMessage message) = 0;
+ NodeName node_name;
+ ScopedMessage message;
+ uint64_t priority;
};
-class TestNode : public NodeDelegate {
+struct TaskComparator {
+ bool operator()(const Task* a, const Task* b) {
+ return a->priority < b->priority;
+ }
+};
+
+const size_t kMaxNodes = 3;
+
+std::priority_queue<Task*, std::vector<Task*>, TaskComparator> task_queue;
+Node* node_map[kMaxNodes];
+
+Node* GetNode(const NodeName& name) {
+ return node_map[name.v1];
+}
+
+void SetNode(const NodeName& name, Node* node) {
+ node_map[name.v1] = node;
+}
+
+void PumpTasks() {
+ while (!task_queue.empty()) {
+ Task* task = task_queue.top();
+ task_queue.pop();
+
+ Node* node = GetNode(task->node_name);
+ if (node)
+ node->AcceptMessage(std::move(task->message));
+
+ delete task;
+ }
+}
+
+void PumpUntilTask(EventType type) {
+ while (!task_queue.empty()) {
+ Task* task = task_queue.top();
+
+ const EventHeader* header = GetEventHeader(*task->message);
+ if (header->type == type)
+ return;
+
+ task_queue.pop();
+
+ Node* node = GetNode(task->node_name);
+ if (node)
+ node->AcceptMessage(std::move(task->message));
+
+ delete task;
+ }
+}
+
+void DiscardPendingTasks() {
+ while (!task_queue.empty()) {
+ Task* task = task_queue.top();
+ task_queue.pop();
+ delete task;
+ }
+}
+
+int SendStringMessage(Node* node, const PortRef& port, const std::string& s) {
+ size_t size = s.size() + 1;
+ ScopedMessage message = TestMessage::NewUserMessage(size, 0);
+ memcpy(message->mutable_payload_bytes(), s.data(), size);
+ return node->SendMessage(port, std::move(message));
+}
+
+int SendStringMessageWithPort(Node* node,
+ const PortRef& port,
+ const std::string& s,
+ const PortName& sent_port_name) {
+ size_t size = s.size() + 1;
+ ScopedMessage message = TestMessage::NewUserMessage(size, 1);
+ memcpy(message->mutable_payload_bytes(), s.data(), size);
+ message->mutable_ports()[0] = sent_port_name;
+ return node->SendMessage(port, std::move(message));
+}
+
+int SendStringMessageWithPort(Node* node,
+ const PortRef& port,
+ const std::string& s,
+ const PortRef& sent_port) {
+ return SendStringMessageWithPort(node, port, s, sent_port.name());
+}
+
+const char* ToString(const ScopedMessage& message) {
+ return static_cast<const char*>(message->payload_bytes());
+}
+
+class TestNodeDelegate : public NodeDelegate {
public:
- explicit TestNode(uint64_t id)
- : node_name_(id, 1),
- node_(node_name_, this),
- node_thread_(base::StringPrintf("Node %" PRIu64 " thread", id)),
- messages_available_event_(
- base::WaitableEvent::ResetPolicy::AUTOMATIC,
- base::WaitableEvent::InitialState::NOT_SIGNALED),
- idle_event_(
- base::WaitableEvent::ResetPolicy::MANUAL,
- base::WaitableEvent::InitialState::SIGNALED) {
+ explicit TestNodeDelegate(const NodeName& node_name)
+ : node_name_(node_name),
+ drop_messages_(false),
+ read_messages_(true),
+ save_messages_(false) {
}
- ~TestNode() override {
- StopWhenIdle();
- node_thread_.Stop();
- }
-
- const NodeName& name() const { return node_name_; }
-
- // NOTE: Node is thread-safe.
- Node& node() { return node_; }
-
- base::WaitableEvent& idle_event() { return idle_event_; }
-
- bool IsIdle() {
- base::AutoLock lock(lock_);
- return started_ && !dispatching_ &&
- (incoming_messages_.empty() || (block_on_event_ && blocked_));
- }
-
- void BlockOnEvent(EventType type) {
- base::AutoLock lock(lock_);
- blocked_event_type_ = type;
- block_on_event_ = true;
- }
-
- void Unblock() {
- base::AutoLock lock(lock_);
- block_on_event_ = false;
- messages_available_event_.Signal();
- }
-
- void Start(MessageRouter* router) {
- router_ = router;
- node_thread_.Start();
- node_thread_.task_runner()->PostTask(
- FROM_HERE,
- base::Bind(&TestNode::ProcessMessages, base::Unretained(this)));
- }
-
- void StopWhenIdle() {
- base::AutoLock lock(lock_);
- should_quit_ = true;
- messages_available_event_.Signal();
- }
-
- void WakeUp() { messages_available_event_.Signal(); }
-
- int SendStringMessage(const PortRef& port, const std::string& s) {
- size_t size = s.size() + 1;
- ScopedMessage message = TestMessage::NewUserMessage(size, 0);
- memcpy(message->mutable_payload_bytes(), s.data(), size);
- return node_.SendMessage(port, std::move(message));
- }
-
- int SendStringMessageWithPort(const PortRef& port,
- const std::string& s,
- const PortName& sent_port_name) {
- size_t size = s.size() + 1;
- ScopedMessage message = TestMessage::NewUserMessage(size, 1);
- memcpy(message->mutable_payload_bytes(), s.data(), size);
- message->mutable_ports()[0] = sent_port_name;
- return node_.SendMessage(port, std::move(message));
- }
-
- int SendStringMessageWithPort(const PortRef& port,
- const std::string& s,
- const PortRef& sent_port) {
- return SendStringMessageWithPort(port, s, sent_port.name());
- }
-
- void set_drop_messages(bool value) {
- base::AutoLock lock(lock_);
- drop_messages_ = value;
- }
-
- void set_save_messages(bool value) {
- base::AutoLock lock(lock_);
- save_messages_ = value;
- }
-
- bool ReadMessage(const PortRef& port, ScopedMessage* message) {
- return node_.GetMessage(port, message, nullptr) == OK && *message;
- }
+ void set_drop_messages(bool value) { drop_messages_ = value; }
+ void set_read_messages(bool value) { read_messages_ = value; }
+ void set_save_messages(bool value) { save_messages_ = value; }
bool GetSavedMessage(ScopedMessage* message) {
- base::AutoLock lock(lock_);
if (saved_messages_.empty()) {
message->reset();
return false;
}
- std::swap(*message, saved_messages_.front());
+ *message = std::move(saved_messages_.front());
saved_messages_.pop();
return true;
}
- void EnqueueMessage(ScopedMessage message) {
- idle_event_.Reset();
-
- // NOTE: This may be called from ForwardMessage and thus must not reenter
- // |node_|.
- base::AutoLock lock(lock_);
- incoming_messages_.emplace(std::move(message));
- messages_available_event_.Signal();
- }
-
void GenerateRandomPortName(PortName* port_name) override {
- DCHECK(router_);
- router_->GeneratePortName(port_name);
+ static uint64_t next_port_name = 1;
+ port_name->v1 = next_port_name++;
+ port_name->v2 = 0;
}
void AllocMessage(size_t num_header_bytes, ScopedMessage* message) override {
@@ -207,570 +205,489 @@
void ForwardMessage(const NodeName& node_name,
ScopedMessage message) override {
- {
- base::AutoLock lock(lock_);
- if (drop_messages_) {
- DVLOG(1) << "Dropping ForwardMessage from node "
- << node_name_ << " to " << node_name;
-
- base::AutoUnlock unlock(lock_);
- ClosePortsInMessage(message.get());
- return;
- }
+ if (drop_messages_) {
+ DVLOG(1) << "Dropping ForwardMessage from node "
+ << node_name_ << " to " << node_name;
+ ClosePortsInMessage(GetNode(node_name), message.get());
+ return;
}
-
- DCHECK(router_);
DVLOG(1) << "ForwardMessage from node "
<< node_name_ << " to " << node_name;
- router_->ForwardMessage(this, node_name, std::move(message));
+ task_queue.push(new Task(node_name, std::move(message)));
}
void BroadcastMessage(ScopedMessage message) override {
- router_->BroadcastMessage(this, std::move(message));
+ for (size_t i = 0; i < kMaxNodes; ++i) {
+ Node* node = node_map[i];
+ // Broadcast doesn't deliver to the local node.
+ if (node && node != GetNode(node_name_)) {
+ // NOTE: We only need to support broadcast of events, which have no
+ // payload or ports bytes.
+ ScopedMessage new_message(
+ new TestMessage(message->num_header_bytes(), 0, 0));
+ memcpy(new_message->mutable_header_bytes(), message->header_bytes(),
+ message->num_header_bytes());
+ node->AcceptMessage(std::move(new_message));
+ }
+ }
}
void PortStatusChanged(const PortRef& port) override {
- // The port may be closed, in which case we ignore the notification.
- base::AutoLock lock(lock_);
- if (!save_messages_)
+ DVLOG(1) << "PortStatusChanged for " << port.name() << "@" << node_name_;
+ if (!read_messages_)
return;
-
+ Node* node = GetNode(node_name_);
for (;;) {
ScopedMessage message;
- {
- base::AutoUnlock unlock(lock_);
- if (!ReadMessage(port, &message))
- break;
+ int rv = node->GetMessage(port, &message);
+ EXPECT_TRUE(rv == OK || rv == ERROR_PORT_PEER_CLOSED);
+ if (rv == ERROR_PORT_PEER_CLOSED || !message)
+ break;
+ if (save_messages_) {
+ SaveMessage(std::move(message));
+ } else {
+ LogMessage(message.get());
+ for (size_t i = 0; i < message->num_ports(); ++i) {
+ std::stringstream buf;
+ buf << "got port: " << message->ports()[i];
+
+ PortRef received_port;
+ node->GetPort(message->ports()[i], &received_port);
+
+ SendStringMessage(node, received_port, buf.str());
+
+ // Avoid leaking these ports.
+ node->ClosePort(received_port);
+ }
}
-
- saved_messages_.emplace(std::move(message));
- }
- }
-
- void ClosePortsInMessage(Message* message) {
- for (size_t i = 0; i < message->num_ports(); ++i) {
- PortRef port;
- ASSERT_EQ(OK, node_.GetPort(message->ports()[i], &port));
- EXPECT_EQ(OK, node_.ClosePort(port));
}
}
private:
- void ProcessMessages() {
- for (;;) {
- messages_available_event_.Wait();
-
- base::AutoLock lock(lock_);
-
- if (should_quit_)
- return;
-
- dispatching_ = true;
- while (!incoming_messages_.empty()) {
- if (block_on_event_ &&
- GetEventHeader(*incoming_messages_.front())->type ==
- blocked_event_type_) {
- blocked_ = true;
- // Go idle if we hit a blocked event type.
- break;
- } else {
- blocked_ = false;
- }
- ScopedMessage message = std::move(incoming_messages_.front());
- incoming_messages_.pop();
-
- // NOTE: AcceptMessage() can re-enter this object to call any of the
- // NodeDelegate interface methods.
- base::AutoUnlock unlock(lock_);
- node_.AcceptMessage(std::move(message));
- }
-
- dispatching_ = false;
- started_ = true;
- idle_event_.Signal();
- };
+ void SaveMessage(ScopedMessage message) {
+ saved_messages_.emplace(std::move(message));
}
- const NodeName node_name_;
- Node node_;
- MessageRouter* router_ = nullptr;
-
- base::Thread node_thread_;
- base::WaitableEvent messages_available_event_;
- base::WaitableEvent idle_event_;
-
- // Guards fields below.
- base::Lock lock_;
- bool started_ = false;
- bool dispatching_ = false;
- bool should_quit_ = false;
- bool drop_messages_ = false;
- bool save_messages_ = false;
- bool blocked_ = false;
- bool block_on_event_ = false;
- EventType blocked_event_type_;
- std::queue<ScopedMessage> incoming_messages_;
std::queue<ScopedMessage> saved_messages_;
+ NodeName node_name_;
+ bool drop_messages_;
+ bool read_messages_;
+ bool save_messages_;
};
-class PortsTest : public testing::Test, public MessageRouter {
+class PortsTest : public testing::Test {
public:
- void AddNode(TestNode* node) {
- {
- base::AutoLock lock(lock_);
- nodes_[node->name()] = node;
- }
- node->Start(this);
+ void SetUp() override {
+ DiscardPendingTasks();
+ SetNode(NodeName(0, 1), nullptr);
+ SetNode(NodeName(1, 1), nullptr);
+ SetNode(NodeName(2, 1), nullptr);
}
-
- void RemoveNode(TestNode* node) {
- {
- base::AutoLock lock(lock_);
- nodes_.erase(node->name());
- }
-
- for (const auto& entry : nodes_)
- entry.second->node().LostConnectionToNode(node->name());
- }
-
- // Waits until all known Nodes are idle. Message forwarding and processing
- // is handled in such a way that idleness is a stable state: once all nodes in
- // the system are idle, they will remain idle until the test explicitly
- // initiates some further event (e.g. sending a message, closing a port, or
- // removing a Node).
- void WaitForIdle() {
- for (;;) {
- base::AutoLock global_lock(global_lock_);
- bool all_nodes_idle = true;
- for (const auto& entry : nodes_) {
- if (!entry.second->IsIdle())
- all_nodes_idle = false;
- entry.second->WakeUp();
- }
- if (all_nodes_idle)
- return;
-
- // Wait for any Node to signal that it's idle.
- base::AutoUnlock global_unlock(global_lock_);
- std::vector<base::WaitableEvent*> events;
- for (const auto& entry : nodes_)
- events.push_back(&entry.second->idle_event());
- base::WaitableEvent::WaitMany(events.data(), events.size());
- }
- }
-
- void CreatePortPair(TestNode* node0,
- PortRef* port0,
- TestNode* node1,
- PortRef* port1) {
- if (node0 == node1) {
- EXPECT_EQ(OK, node0->node().CreatePortPair(port0, port1));
- } else {
- EXPECT_EQ(OK, node0->node().CreateUninitializedPort(port0));
- EXPECT_EQ(OK, node1->node().CreateUninitializedPort(port1));
- EXPECT_EQ(OK, node0->node().InitializePort(*port0, node1->name(),
- port1->name()));
- EXPECT_EQ(OK, node1->node().InitializePort(*port1, node0->name(),
- port0->name()));
- }
- }
-
- private:
- // MessageRouter:
- void GeneratePortName(PortName* name) override {
- base::AutoLock lock(lock_);
- name->v1 = next_port_id_++;
- name->v2 = 0;
- }
-
- void ForwardMessage(TestNode* from_node,
- const NodeName& node_name,
- ScopedMessage message) override {
- base::AutoLock global_lock(global_lock_);
- base::AutoLock lock(lock_);
- // Drop messages from nodes that have been removed.
- if (nodes_.find(from_node->name()) == nodes_.end()) {
- from_node->ClosePortsInMessage(message.get());
- return;
- }
-
- auto it = nodes_.find(node_name);
- if (it == nodes_.end()) {
- DVLOG(1) << "Node not found: " << node_name;
- return;
- }
-
- it->second->EnqueueMessage(std::move(message));
- }
-
- void BroadcastMessage(TestNode* from_node, ScopedMessage message) override {
- base::AutoLock global_lock(global_lock_);
- base::AutoLock lock(lock_);
-
- // Drop messages from nodes that have been removed.
- if (nodes_.find(from_node->name()) == nodes_.end())
- return;
-
- for (const auto& entry : nodes_) {
- TestNode* node = entry.second;
- // Broadcast doesn't deliver to the local node.
- if (node == from_node)
- continue;
-
- // NOTE: We only need to support broadcast of events. Events have no
- // payload or ports bytes.
- ScopedMessage new_message(
- new TestMessage(message->num_header_bytes(), 0, 0));
- memcpy(new_message->mutable_header_bytes(), message->header_bytes(),
- message->num_header_bytes());
- node->EnqueueMessage(std::move(new_message));
- }
- }
-
- base::MessageLoop message_loop_;
-
- // Acquired before any operation which makes a Node busy, and before testing
- // if all nodes are idle.
- base::Lock global_lock_;
-
- base::Lock lock_;
- uint64_t next_port_id_ = 1;
- std::map<NodeName, TestNode*> nodes_;
};
} // namespace
TEST_F(PortsTest, Basic1) {
- TestNode node0(0);
- AddNode(&node0);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ SetNode(node0_name, &node0);
- TestNode node1(1);
- AddNode(&node1);
+ NodeName node1_name(1, 1);
+ TestNodeDelegate node1_delegate(node1_name);
+ Node node1(node1_name, &node1_delegate);
+ SetNode(node1_name, &node1);
+ // Setup pipe between node0 and node1.
PortRef x0, x1;
- CreatePortPair(&node0, &x0, &node1, &x1);
+ EXPECT_EQ(OK, node0.CreateUninitializedPort(&x0));
+ EXPECT_EQ(OK, node1.CreateUninitializedPort(&x1));
+ EXPECT_EQ(OK, node0.InitializePort(x0, node1_name, x1.name()));
+ EXPECT_EQ(OK, node1.InitializePort(x1, node0_name, x0.name()));
+ // Transfer a port from node0 to node1.
PortRef a0, a1;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&a0, &a1));
- EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "hello", a1));
- EXPECT_EQ(OK, node0.node().ClosePort(a0));
+ EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "hello", a1));
- EXPECT_EQ(OK, node0.node().ClosePort(x0));
- EXPECT_EQ(OK, node1.node().ClosePort(x1));
+ EXPECT_EQ(OK, node0.ClosePort(a0));
- WaitForIdle();
+ EXPECT_EQ(OK, node0.ClosePort(x0));
+ EXPECT_EQ(OK, node1.ClosePort(x1));
- EXPECT_TRUE(node0.node().CanShutdownCleanly());
- EXPECT_TRUE(node1.node().CanShutdownCleanly());
+ PumpTasks();
+
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
+ EXPECT_TRUE(node1.CanShutdownCleanly(false));
}
TEST_F(PortsTest, Basic2) {
- TestNode node0(0);
- AddNode(&node0);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ SetNode(node0_name, &node0);
- TestNode node1(1);
- AddNode(&node1);
+ NodeName node1_name(1, 1);
+ TestNodeDelegate node1_delegate(node1_name);
+ Node node1(node1_name, &node1_delegate);
+ SetNode(node1_name, &node1);
+ // Setup pipe between node0 and node1.
PortRef x0, x1;
- CreatePortPair(&node0, &x0, &node1, &x1);
+ EXPECT_EQ(OK, node0.CreateUninitializedPort(&x0));
+ EXPECT_EQ(OK, node1.CreateUninitializedPort(&x1));
+ EXPECT_EQ(OK, node0.InitializePort(x0, node1_name, x1.name()));
+ EXPECT_EQ(OK, node1.InitializePort(x1, node0_name, x0.name()));
PortRef b0, b1;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&b0, &b1));
- EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "hello", b1));
- EXPECT_EQ(OK, node0.SendStringMessage(b0, "hello again"));
+ EXPECT_EQ(OK, node0.CreatePortPair(&b0, &b1));
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "hello", b1));
+ EXPECT_EQ(OK, SendStringMessage(&node0, b0, "hello again"));
- EXPECT_EQ(OK, node0.node().ClosePort(b0));
+ // This may cause a SendMessage(b1) failure.
+ EXPECT_EQ(OK, node0.ClosePort(b0));
- EXPECT_EQ(OK, node0.node().ClosePort(x0));
- EXPECT_EQ(OK, node1.node().ClosePort(x1));
+ EXPECT_EQ(OK, node0.ClosePort(x0));
+ EXPECT_EQ(OK, node1.ClosePort(x1));
- WaitForIdle();
+ PumpTasks();
- EXPECT_TRUE(node0.node().CanShutdownCleanly());
- EXPECT_TRUE(node1.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
+ EXPECT_TRUE(node1.CanShutdownCleanly(false));
}
TEST_F(PortsTest, Basic3) {
- TestNode node0(0);
- AddNode(&node0);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ SetNode(node0_name, &node0);
- TestNode node1(1);
- AddNode(&node1);
+ NodeName node1_name(1, 1);
+ TestNodeDelegate node1_delegate(node1_name);
+ Node node1(node1_name, &node1_delegate);
+ SetNode(node1_name, &node1);
+ // Setup pipe between node0 and node1.
PortRef x0, x1;
- CreatePortPair(&node0, &x0, &node1, &x1);
+ EXPECT_EQ(OK, node0.CreateUninitializedPort(&x0));
+ EXPECT_EQ(OK, node1.CreateUninitializedPort(&x1));
+ EXPECT_EQ(OK, node0.InitializePort(x0, node1_name, x1.name()));
+ EXPECT_EQ(OK, node1.InitializePort(x1, node0_name, x0.name()));
+ // Transfer a port from node0 to node1.
PortRef a0, a1;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&a0, &a1));
+ EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "hello", a1));
+ EXPECT_EQ(OK, SendStringMessage(&node0, a0, "hello again"));
- EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "hello", a1));
- EXPECT_EQ(OK, node0.SendStringMessage(a0, "hello again"));
-
- EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "foo", a0));
+ // Transfer a0 as well.
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "foo", a0));
PortRef b0, b1;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&b0, &b1));
- EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "bar", b1));
- EXPECT_EQ(OK, node0.SendStringMessage(b0, "baz"));
+ EXPECT_EQ(OK, node0.CreatePortPair(&b0, &b1));
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "bar", b1));
+ EXPECT_EQ(OK, SendStringMessage(&node0, b0, "baz"));
- EXPECT_EQ(OK, node0.node().ClosePort(b0));
+ // This may cause a SendMessage(b1) failure.
+ EXPECT_EQ(OK, node0.ClosePort(b0));
- EXPECT_EQ(OK, node0.node().ClosePort(x0));
- EXPECT_EQ(OK, node1.node().ClosePort(x1));
+ EXPECT_EQ(OK, node0.ClosePort(x0));
+ EXPECT_EQ(OK, node1.ClosePort(x1));
- WaitForIdle();
+ PumpTasks();
- EXPECT_TRUE(node0.node().CanShutdownCleanly());
- EXPECT_TRUE(node1.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
+ EXPECT_TRUE(node1.CanShutdownCleanly(false));
}
TEST_F(PortsTest, LostConnectionToNode1) {
- TestNode node0(0);
- AddNode(&node0);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ SetNode(node0_name, &node0);
- TestNode node1(1);
- AddNode(&node1);
- node1.set_drop_messages(true);
+ NodeName node1_name(1, 1);
+ TestNodeDelegate node1_delegate(node1_name);
+ Node node1(node1_name, &node1_delegate);
+ SetNode(node1_name, &node1);
+ // Setup pipe between node0 and node1.
PortRef x0, x1;
- CreatePortPair(&node0, &x0, &node1, &x1);
+ EXPECT_EQ(OK, node0.CreateUninitializedPort(&x0));
+ EXPECT_EQ(OK, node1.CreateUninitializedPort(&x1));
+ EXPECT_EQ(OK, node0.InitializePort(x0, node1_name, x1.name()));
+ EXPECT_EQ(OK, node1.InitializePort(x1, node0_name, x0.name()));
- // Transfer a port to node1 and simulate a lost connection to node1.
+ // Transfer port to node1 and simulate a lost connection to node1. Dropping
+ // events from node1 is how we simulate the lost connection.
+
+ node1_delegate.set_drop_messages(true);
PortRef a0, a1;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&a0, &a1));
- EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "foo", a1));
+ EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "foo", a1));
- WaitForIdle();
+ PumpTasks();
- RemoveNode(&node1);
+ EXPECT_EQ(OK, node0.LostConnectionToNode(node1_name));
- WaitForIdle();
+ PumpTasks();
- EXPECT_EQ(OK, node0.node().ClosePort(a0));
- EXPECT_EQ(OK, node0.node().ClosePort(x0));
- EXPECT_EQ(OK, node1.node().ClosePort(x1));
+ EXPECT_EQ(OK, node0.ClosePort(a0));
+ EXPECT_EQ(OK, node0.ClosePort(x0));
+ EXPECT_EQ(OK, node1.ClosePort(x1));
- WaitForIdle();
+ PumpTasks();
- EXPECT_TRUE(node0.node().CanShutdownCleanly());
- EXPECT_TRUE(node1.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
+ EXPECT_TRUE(node1.CanShutdownCleanly(false));
}
TEST_F(PortsTest, LostConnectionToNode2) {
- TestNode node0(0);
- AddNode(&node0);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
- TestNode node1(1);
- AddNode(&node1);
+ NodeName node1_name(1, 1);
+ TestNodeDelegate node1_delegate(node1_name);
+ Node node1(node1_name, &node1_delegate);
+ node_map[1] = &node1;
+ // Setup pipe between node0 and node1.
PortRef x0, x1;
- CreatePortPair(&node0, &x0, &node1, &x1);
+ EXPECT_EQ(OK, node0.CreateUninitializedPort(&x0));
+ EXPECT_EQ(OK, node1.CreateUninitializedPort(&x1));
+ EXPECT_EQ(OK, node0.InitializePort(x0, node1_name, x1.name()));
+ EXPECT_EQ(OK, node1.InitializePort(x1, node0_name, x0.name()));
+
+ node1_delegate.set_read_messages(false);
PortRef a0, a1;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&a0, &a1));
- EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "take a1", a1));
+ EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "take a1", a1));
- WaitForIdle();
+ PumpTasks();
- node1.set_drop_messages(true);
+ node1_delegate.set_drop_messages(true);
- RemoveNode(&node1);
+ EXPECT_EQ(OK, node0.LostConnectionToNode(node1_name));
- WaitForIdle();
+ PumpTasks();
- // a0 should have eventually detected peer closure after node loss.
ScopedMessage message;
- EXPECT_EQ(ERROR_PORT_PEER_CLOSED,
- node0.node().GetMessage(a0, &message, nullptr));
+ EXPECT_EQ(ERROR_PORT_PEER_CLOSED, node0.GetMessage(a0, &message));
EXPECT_FALSE(message);
- EXPECT_EQ(OK, node0.node().ClosePort(a0));
+ EXPECT_EQ(OK, node0.ClosePort(a0));
- EXPECT_EQ(OK, node0.node().ClosePort(x0));
+ EXPECT_EQ(OK, node0.ClosePort(x0));
- EXPECT_EQ(OK, node1.node().GetMessage(x1, &message, nullptr));
+ EXPECT_EQ(OK, node1.GetMessage(x1, &message));
EXPECT_TRUE(message);
- node1.ClosePortsInMessage(message.get());
+ ClosePortsInMessage(&node1, message.get());
- EXPECT_EQ(OK, node1.node().ClosePort(x1));
+ EXPECT_EQ(OK, node1.ClosePort(x1));
- WaitForIdle();
+ PumpTasks();
- EXPECT_TRUE(node0.node().CanShutdownCleanly());
- EXPECT_TRUE(node1.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
+ EXPECT_TRUE(node1.CanShutdownCleanly(false));
}
TEST_F(PortsTest, LostConnectionToNodeWithSecondaryProxy) {
// Tests that a proxy gets cleaned up when its indirect peer lives on a lost
// node.
- TestNode node0(0);
- AddNode(&node0);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
- TestNode node1(1);
- AddNode(&node1);
+ NodeName node1_name(1, 1);
+ TestNodeDelegate node1_delegate(node1_name);
+ Node node1(node1_name, &node1_delegate);
+ node_map[1] = &node1;
- TestNode node2(2);
- AddNode(&node2);
+ NodeName node2_name(2, 1);
+ TestNodeDelegate node2_delegate(node2_name);
+ Node node2(node2_name, &node2_delegate);
+ node_map[2] = &node2;
+
+ node1_delegate.set_save_messages(true);
// Create A-B spanning nodes 0 and 1 and C-D spanning 1 and 2.
PortRef A, B, C, D;
- CreatePortPair(&node0, &A, &node1, &B);
- CreatePortPair(&node1, &C, &node2, &D);
+ EXPECT_EQ(OK, node0.CreateUninitializedPort(&A));
+ EXPECT_EQ(OK, node1.CreateUninitializedPort(&B));
+ EXPECT_EQ(OK, node0.InitializePort(A, node1_name, B.name()));
+ EXPECT_EQ(OK, node1.InitializePort(B, node0_name, A.name()));
+ EXPECT_EQ(OK, node1.CreateUninitializedPort(&C));
+ EXPECT_EQ(OK, node2.CreateUninitializedPort(&D));
+ EXPECT_EQ(OK, node1.InitializePort(C, node2_name, D.name()));
+ EXPECT_EQ(OK, node2.InitializePort(D, node1_name, C.name()));
// Create E-F and send F over A to node 1.
PortRef E, F;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&E, &F));
- EXPECT_EQ(OK, node0.SendStringMessageWithPort(A, ".", F));
+ EXPECT_EQ(OK, node0.CreatePortPair(&E, &F));
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, ".", F));
- WaitForIdle();
+ PumpTasks();
ScopedMessage message;
- ASSERT_TRUE(node1.ReadMessage(B, &message));
+ ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
ASSERT_EQ(1u, message->num_ports());
- EXPECT_EQ(OK, node1.node().GetPort(message->ports()[0], &F));
+ EXPECT_EQ(OK, node1.GetPort(message->ports()[0], &F));
// Send F over C to node 2 and then simulate node 2 loss from node 1. Node 1
// will trivially become aware of the loss, and this test verifies that the
// port A on node 0 will eventually also become aware of it.
- // Make sure node2 stops processing events when it encounters an ObserveProxy.
- node2.BlockOnEvent(EventType::kObserveProxy);
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node1, C, ".", F));
- EXPECT_EQ(OK, node1.SendStringMessageWithPort(C, ".", F));
- WaitForIdle();
+ node_map[2] = nullptr;
+ EXPECT_EQ(OK, node1.LostConnectionToNode(node2_name));
- // Simulate node 1 and 2 disconnecting.
- EXPECT_EQ(OK, node1.node().LostConnectionToNode(node2.name()));
-
- // Let node2 continue processing events and wait for everyone to go idle.
- node2.Unblock();
- WaitForIdle();
+ PumpTasks();
// Port F should be gone.
- EXPECT_EQ(ERROR_PORT_UNKNOWN, node1.node().GetPort(F.name(), &F));
+ EXPECT_EQ(ERROR_PORT_UNKNOWN, node1.GetPort(F.name(), &F));
// Port E should have detected peer closure despite the fact that there is
// no longer a continuous route from F to E over which the event could travel.
PortStatus status;
- EXPECT_EQ(OK, node0.node().GetStatus(E, &status));
+ EXPECT_EQ(OK, node0.GetStatus(E, &status));
EXPECT_TRUE(status.peer_closed);
- EXPECT_EQ(OK, node0.node().ClosePort(A));
- EXPECT_EQ(OK, node1.node().ClosePort(B));
- EXPECT_EQ(OK, node1.node().ClosePort(C));
- EXPECT_EQ(OK, node0.node().ClosePort(E));
+ EXPECT_EQ(OK, node0.ClosePort(A));
+ EXPECT_EQ(OK, node1.ClosePort(B));
+ EXPECT_EQ(OK, node1.ClosePort(C));
+ EXPECT_EQ(OK, node0.ClosePort(E));
- WaitForIdle();
-
- EXPECT_TRUE(node0.node().CanShutdownCleanly());
- EXPECT_TRUE(node1.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
+ EXPECT_TRUE(node1.CanShutdownCleanly(false));
}
TEST_F(PortsTest, LostConnectionToNodeWithLocalProxy) {
// Tests that a proxy gets cleaned up when its direct peer lives on a lost
// node and it's predecessor lives on the same node.
- TestNode node0(0);
- AddNode(&node0);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
- TestNode node1(1);
- AddNode(&node1);
+ NodeName node1_name(1, 1);
+ TestNodeDelegate node1_delegate(node1_name);
+ Node node1(node1_name, &node1_delegate);
+ node_map[1] = &node1;
+ node1_delegate.set_save_messages(true);
+
+ // Create A-B spanning nodes 0 and 1.
PortRef A, B;
- CreatePortPair(&node0, &A, &node1, &B);
+ EXPECT_EQ(OK, node0.CreateUninitializedPort(&A));
+ EXPECT_EQ(OK, node1.CreateUninitializedPort(&B));
+ EXPECT_EQ(OK, node0.InitializePort(A, node1_name, B.name()));
+ EXPECT_EQ(OK, node1.InitializePort(B, node0_name, A.name()));
+ // Create C-D and send D over A to node 1.
PortRef C, D;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&C, &D));
+ EXPECT_EQ(OK, node0.CreatePortPair(&C, &D));
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, ".", D));
- // Send D but block node0 on an ObserveProxy event.
- node0.BlockOnEvent(EventType::kObserveProxy);
- EXPECT_EQ(OK, node0.SendStringMessageWithPort(A, ".", D));
-
- // node0 won't collapse the proxy but node1 will receive the message before
- // going idle.
- WaitForIdle();
+ // Pump tasks until the start of port collapse for port D, which should become
+ // a proxy.
+ PumpUntilTask(EventType::kObserveProxy);
ScopedMessage message;
- ASSERT_TRUE(node1.ReadMessage(B, &message));
+ ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
ASSERT_EQ(1u, message->num_ports());
+
PortRef E;
- EXPECT_EQ(OK, node1.node().GetPort(message->ports()[0], &E));
+ EXPECT_EQ(OK, node1.GetPort(message->ports()[0], &E));
- RemoveNode(&node1);
-
- node0.Unblock();
- WaitForIdle();
+ EXPECT_EQ(OK, node0.LostConnectionToNode(node1_name));
+ PumpTasks();
// Port C should have detected peer closure.
PortStatus status;
- EXPECT_EQ(OK, node0.node().GetStatus(C, &status));
+ EXPECT_EQ(OK, node0.GetStatus(C, &status));
EXPECT_TRUE(status.peer_closed);
- EXPECT_EQ(OK, node0.node().ClosePort(A));
- EXPECT_EQ(OK, node1.node().ClosePort(B));
- EXPECT_EQ(OK, node0.node().ClosePort(C));
- EXPECT_EQ(OK, node1.node().ClosePort(E));
+ EXPECT_EQ(OK, node0.ClosePort(A));
+ EXPECT_EQ(OK, node1.ClosePort(B));
+ EXPECT_EQ(OK, node0.ClosePort(C));
+ EXPECT_EQ(OK, node1.ClosePort(E));
- EXPECT_TRUE(node0.node().CanShutdownCleanly());
- EXPECT_TRUE(node1.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
+ EXPECT_TRUE(node1.CanShutdownCleanly(false));
}
TEST_F(PortsTest, GetMessage1) {
- TestNode node(0);
- AddNode(&node);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
PortRef a0, a1;
- EXPECT_EQ(OK, node.node().CreatePortPair(&a0, &a1));
+ EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
ScopedMessage message;
- EXPECT_EQ(OK, node.node().GetMessage(a0, &message, nullptr));
+ EXPECT_EQ(OK, node0.GetMessage(a0, &message));
EXPECT_FALSE(message);
- EXPECT_EQ(OK, node.node().ClosePort(a1));
+ EXPECT_EQ(OK, node0.ClosePort(a1));
- WaitForIdle();
-
- EXPECT_EQ(ERROR_PORT_PEER_CLOSED,
- node.node().GetMessage(a0, &message, nullptr));
+ EXPECT_EQ(OK, node0.GetMessage(a0, &message));
EXPECT_FALSE(message);
- EXPECT_EQ(OK, node.node().ClosePort(a0));
+ PumpTasks();
- WaitForIdle();
+ EXPECT_EQ(ERROR_PORT_PEER_CLOSED, node0.GetMessage(a0, &message));
+ EXPECT_FALSE(message);
- EXPECT_TRUE(node.node().CanShutdownCleanly());
+ EXPECT_EQ(OK, node0.ClosePort(a0));
+
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
}
TEST_F(PortsTest, GetMessage2) {
- TestNode node(0);
- AddNode(&node);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
+
+ node0_delegate.set_read_messages(false);
PortRef a0, a1;
- EXPECT_EQ(OK, node.node().CreatePortPair(&a0, &a1));
+ EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
- EXPECT_EQ(OK, node.SendStringMessage(a1, "1"));
+ EXPECT_EQ(OK, SendStringMessage(&node0, a1, "1"));
ScopedMessage message;
- EXPECT_EQ(OK, node.node().GetMessage(a0, &message, nullptr));
+ EXPECT_EQ(OK, node0.GetMessage(a0, &message));
ASSERT_TRUE(message);
- EXPECT_TRUE(MessageEquals(message, "1"));
+ EXPECT_EQ(0, strcmp("1", ToString(message)));
- EXPECT_EQ(OK, node.node().ClosePort(a0));
- EXPECT_EQ(OK, node.node().ClosePort(a1));
+ EXPECT_EQ(OK, node0.ClosePort(a0));
+ EXPECT_EQ(OK, node0.ClosePort(a1));
- EXPECT_TRUE(node.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
}
TEST_F(PortsTest, GetMessage3) {
- TestNode node(0);
- AddNode(&node);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
+
+ node0_delegate.set_read_messages(false);
PortRef a0, a1;
- EXPECT_EQ(OK, node.node().CreatePortPair(&a0, &a1));
+ EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
const char* kStrings[] = {
"1",
@@ -779,305 +696,371 @@
};
for (size_t i = 0; i < sizeof(kStrings)/sizeof(kStrings[0]); ++i)
- EXPECT_EQ(OK, node.SendStringMessage(a1, kStrings[i]));
+ EXPECT_EQ(OK, SendStringMessage(&node0, a1, kStrings[i]));
ScopedMessage message;
for (size_t i = 0; i < sizeof(kStrings)/sizeof(kStrings[0]); ++i) {
- EXPECT_EQ(OK, node.node().GetMessage(a0, &message, nullptr));
+ EXPECT_EQ(OK, node0.GetMessage(a0, &message));
ASSERT_TRUE(message);
- EXPECT_TRUE(MessageEquals(message, kStrings[i]));
+ EXPECT_EQ(0, strcmp(kStrings[i], ToString(message)));
+ DVLOG(1) << "got " << kStrings[i];
}
- EXPECT_EQ(OK, node.node().ClosePort(a0));
- EXPECT_EQ(OK, node.node().ClosePort(a1));
+ EXPECT_EQ(OK, node0.ClosePort(a0));
+ EXPECT_EQ(OK, node0.ClosePort(a1));
- EXPECT_TRUE(node.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
}
TEST_F(PortsTest, Delegation1) {
- TestNode node0(0);
- AddNode(&node0);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ SetNode(node0_name, &node0);
- TestNode node1(1);
- AddNode(&node1);
+ NodeName node1_name(1, 1);
+ TestNodeDelegate node1_delegate(node1_name);
+ Node node1(node1_name, &node1_delegate);
+ node_map[1] = &node1;
+ node0_delegate.set_save_messages(true);
+ node1_delegate.set_save_messages(true);
+
+ // Setup pipe between node0 and node1.
PortRef x0, x1;
- CreatePortPair(&node0, &x0, &node1, &x1);
+ EXPECT_EQ(OK, node0.CreateUninitializedPort(&x0));
+ EXPECT_EQ(OK, node1.CreateUninitializedPort(&x1));
+ EXPECT_EQ(OK, node0.InitializePort(x0, node1_name, x1.name()));
+ EXPECT_EQ(OK, node1.InitializePort(x1, node0_name, x0.name()));
// In this test, we send a message to a port that has been moved.
PortRef a0, a1;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&a0, &a1));
- EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "a1", a1));
- WaitForIdle();
+ EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1));
+
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "a1", a1));
+
+ PumpTasks();
ScopedMessage message;
- ASSERT_TRUE(node1.ReadMessage(x1, &message));
+ ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
+
ASSERT_EQ(1u, message->num_ports());
- EXPECT_TRUE(MessageEquals(message, "a1"));
// This is "a1" from the point of view of node1.
PortName a2_name = message->ports()[0];
- EXPECT_EQ(OK, node1.SendStringMessageWithPort(x1, "a2", a2_name));
- EXPECT_EQ(OK, node0.SendStringMessage(a0, "hello"));
- WaitForIdle();
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node1, x1, "a2", a2_name));
- ASSERT_TRUE(node0.ReadMessage(x0, &message));
+ PumpTasks();
+
+ EXPECT_EQ(OK, SendStringMessage(&node0, a0, "hello"));
+
+ PumpTasks();
+
+ ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+
ASSERT_EQ(1u, message->num_ports());
- EXPECT_TRUE(MessageEquals(message, "a2"));
// This is "a2" from the point of view of node1.
PortName a3_name = message->ports()[0];
PortRef a3;
- EXPECT_EQ(OK, node0.node().GetPort(a3_name, &a3));
+ EXPECT_EQ(OK, node0.GetPort(a3_name, &a3));
- ASSERT_TRUE(node0.ReadMessage(a3, &message));
+ EXPECT_EQ(0, strcmp("a2", ToString(message)));
+
+ ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+
EXPECT_EQ(0u, message->num_ports());
- EXPECT_TRUE(MessageEquals(message, "hello"));
+ EXPECT_EQ(0, strcmp("hello", ToString(message)));
- EXPECT_EQ(OK, node0.node().ClosePort(a0));
- EXPECT_EQ(OK, node0.node().ClosePort(a3));
+ EXPECT_EQ(OK, node0.ClosePort(a0));
+ EXPECT_EQ(OK, node0.ClosePort(a3));
- EXPECT_EQ(OK, node0.node().ClosePort(x0));
- EXPECT_EQ(OK, node1.node().ClosePort(x1));
+ EXPECT_EQ(OK, node0.ClosePort(x0));
+ EXPECT_EQ(OK, node1.ClosePort(x1));
- EXPECT_TRUE(node0.node().CanShutdownCleanly());
- EXPECT_TRUE(node1.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
+ EXPECT_TRUE(node1.CanShutdownCleanly(false));
}
TEST_F(PortsTest, Delegation2) {
- TestNode node0(0);
- AddNode(&node0);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ SetNode(node0_name, &node0);
- TestNode node1(1);
- AddNode(&node1);
+ NodeName node1_name(1, 1);
+ TestNodeDelegate node1_delegate(node1_name);
+ Node node1(node1_name, &node1_delegate);
+ node_map[1] = &node1;
- for (int i = 0; i < 100; ++i) {
+ node0_delegate.set_save_messages(true);
+ node1_delegate.set_save_messages(true);
+
+ for (int i = 0; i < 10; ++i) {
// Setup pipe a<->b between node0 and node1.
PortRef A, B;
- CreatePortPair(&node0, &A, &node1, &B);
+ EXPECT_EQ(OK, node0.CreateUninitializedPort(&A));
+ EXPECT_EQ(OK, node1.CreateUninitializedPort(&B));
+ EXPECT_EQ(OK, node0.InitializePort(A, node1_name, B.name()));
+ EXPECT_EQ(OK, node1.InitializePort(B, node0_name, A.name()));
PortRef C, D;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&C, &D));
+ EXPECT_EQ(OK, node0.CreatePortPair(&C, &D));
PortRef E, F;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&E, &F));
-
- node1.set_save_messages(true);
+ EXPECT_EQ(OK, node0.CreatePortPair(&E, &F));
// Pass D over A to B.
- EXPECT_EQ(OK, node0.SendStringMessageWithPort(A, "1", D));
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, "1", D));
// Pass F over C to D.
- EXPECT_EQ(OK, node0.SendStringMessageWithPort(C, "1", F));
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, C, "1", F));
// This message should find its way to node1.
- EXPECT_EQ(OK, node0.SendStringMessage(E, "hello"));
+ EXPECT_EQ(OK, SendStringMessage(&node0, E, "hello"));
- WaitForIdle();
+ PumpTasks();
- EXPECT_EQ(OK, node0.node().ClosePort(C));
- EXPECT_EQ(OK, node0.node().ClosePort(E));
+ EXPECT_EQ(OK, node0.ClosePort(C));
+ EXPECT_EQ(OK, node0.ClosePort(E));
- EXPECT_EQ(OK, node0.node().ClosePort(A));
- EXPECT_EQ(OK, node1.node().ClosePort(B));
+ EXPECT_EQ(OK, node0.ClosePort(A));
+ EXPECT_EQ(OK, node1.ClosePort(B));
- bool got_hello = false;
- ScopedMessage message;
- while (node1.GetSavedMessage(&message)) {
- node1.ClosePortsInMessage(message.get());
- if (MessageEquals(message, "hello")) {
- got_hello = true;
+ for (;;) {
+ ScopedMessage message;
+ if (node1_delegate.GetSavedMessage(&message)) {
+ ClosePortsInMessage(&node1, message.get());
+ if (strcmp("hello", ToString(message)) == 0)
+ break;
+ } else {
+ ASSERT_TRUE(false); // "hello" message not delivered!
break;
}
}
- EXPECT_TRUE(got_hello);
-
- WaitForIdle(); // Because closing ports may have generated tasks.
+ PumpTasks(); // Because ClosePort may have generated tasks.
}
- EXPECT_TRUE(node0.node().CanShutdownCleanly());
- EXPECT_TRUE(node1.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
+ EXPECT_TRUE(node1.CanShutdownCleanly(false));
}
TEST_F(PortsTest, SendUninitialized) {
- TestNode node(0);
- AddNode(&node);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
PortRef x0;
- EXPECT_EQ(OK, node.node().CreateUninitializedPort(&x0));
- EXPECT_EQ(ERROR_PORT_STATE_UNEXPECTED, node.SendStringMessage(x0, "oops"));
- EXPECT_EQ(OK, node.node().ClosePort(x0));
- EXPECT_TRUE(node.node().CanShutdownCleanly());
+ EXPECT_EQ(OK, node0.CreateUninitializedPort(&x0));
+ EXPECT_EQ(ERROR_PORT_STATE_UNEXPECTED,
+ SendStringMessage(&node0, x0, "oops"));
+ EXPECT_EQ(OK, node0.ClosePort(x0));
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
}
TEST_F(PortsTest, SendFailure) {
- TestNode node(0);
- AddNode(&node);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
- node.set_save_messages(true);
+ node0_delegate.set_save_messages(true);
PortRef A, B;
- EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B));
+ EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
// Try to send A over itself.
EXPECT_EQ(ERROR_PORT_CANNOT_SEND_SELF,
- node.SendStringMessageWithPort(A, "oops", A));
+ SendStringMessageWithPort(&node0, A, "oops", A));
// Try to send B over A.
EXPECT_EQ(ERROR_PORT_CANNOT_SEND_PEER,
- node.SendStringMessageWithPort(A, "nope", B));
+ SendStringMessageWithPort(&node0, A, "nope", B));
// B should be closed immediately.
- EXPECT_EQ(ERROR_PORT_UNKNOWN, node.node().GetPort(B.name(), &B));
+ EXPECT_EQ(ERROR_PORT_UNKNOWN, node0.GetPort(B.name(), &B));
- WaitForIdle();
+ PumpTasks();
// There should have been no messages accepted.
ScopedMessage message;
- EXPECT_FALSE(node.GetSavedMessage(&message));
+ EXPECT_FALSE(node0_delegate.GetSavedMessage(&message));
- EXPECT_EQ(OK, node.node().ClosePort(A));
+ EXPECT_EQ(OK, node0.ClosePort(A));
- WaitForIdle();
+ PumpTasks();
- EXPECT_TRUE(node.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
}
TEST_F(PortsTest, DontLeakUnreceivedPorts) {
- TestNode node(0);
- AddNode(&node);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
- PortRef A, B, C, D;
- EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B));
- EXPECT_EQ(OK, node.node().CreatePortPair(&C, &D));
+ node0_delegate.set_read_messages(false);
- EXPECT_EQ(OK, node.SendStringMessageWithPort(A, "foo", D));
+ PortRef A, B;
+ EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
- EXPECT_EQ(OK, node.node().ClosePort(C));
- EXPECT_EQ(OK, node.node().ClosePort(A));
- EXPECT_EQ(OK, node.node().ClosePort(B));
+ PortRef C, D;
+ EXPECT_EQ(OK, node0.CreatePortPair(&C, &D));
- WaitForIdle();
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, "foo", D));
- EXPECT_TRUE(node.node().CanShutdownCleanly());
+ PumpTasks();
+
+ EXPECT_EQ(OK, node0.ClosePort(C));
+
+ EXPECT_EQ(OK, node0.ClosePort(A));
+ EXPECT_EQ(OK, node0.ClosePort(B));
+
+ PumpTasks();
+
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
}
TEST_F(PortsTest, AllowShutdownWithLocalPortsOpen) {
- TestNode node(0);
- AddNode(&node);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
- PortRef A, B, C, D;
- EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B));
- EXPECT_EQ(OK, node.node().CreatePortPair(&C, &D));
+ node0_delegate.set_save_messages(true);
- EXPECT_EQ(OK, node.SendStringMessageWithPort(A, "foo", D));
+ PortRef A, B;
+ EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+
+ PortRef C, D;
+ EXPECT_EQ(OK, node0.CreatePortPair(&C, &D));
+
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, "foo", D));
ScopedMessage message;
- EXPECT_TRUE(node.ReadMessage(B, &message));
+ EXPECT_TRUE(node0_delegate.GetSavedMessage(&message));
ASSERT_EQ(1u, message->num_ports());
- EXPECT_TRUE(MessageEquals(message, "foo"));
+
PortRef E;
- ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &E));
+ ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &E));
- EXPECT_TRUE(
- node.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS));
+ EXPECT_TRUE(node0.CanShutdownCleanly(true));
- WaitForIdle();
+ PumpTasks();
- EXPECT_TRUE(
- node.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS));
- EXPECT_FALSE(node.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(true));
+ EXPECT_FALSE(node0.CanShutdownCleanly(false));
- EXPECT_EQ(OK, node.node().ClosePort(A));
- EXPECT_EQ(OK, node.node().ClosePort(B));
- EXPECT_EQ(OK, node.node().ClosePort(C));
- EXPECT_EQ(OK, node.node().ClosePort(E));
+ EXPECT_EQ(OK, node0.ClosePort(A));
+ EXPECT_EQ(OK, node0.ClosePort(B));
+ EXPECT_EQ(OK, node0.ClosePort(C));
+ EXPECT_EQ(OK, node0.ClosePort(E));
- WaitForIdle();
+ PumpTasks();
- EXPECT_TRUE(node.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
}
TEST_F(PortsTest, ProxyCollapse1) {
- TestNode node(0);
- AddNode(&node);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
+
+ node0_delegate.set_save_messages(true);
PortRef A, B;
- EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B));
+ EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
PortRef X, Y;
- EXPECT_EQ(OK, node.node().CreatePortPair(&X, &Y));
+ EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y));
ScopedMessage message;
// Send B and receive it as C.
- EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", B));
- ASSERT_TRUE(node.ReadMessage(Y, &message));
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", B));
+ ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
ASSERT_EQ(1u, message->num_ports());
PortRef C;
- ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &C));
+ ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &C));
// Send C and receive it as D.
- EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", C));
- ASSERT_TRUE(node.ReadMessage(Y, &message));
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", C));
+ ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
ASSERT_EQ(1u, message->num_ports());
PortRef D;
- ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &D));
+ ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &D));
// Send D and receive it as E.
- EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", D));
- ASSERT_TRUE(node.ReadMessage(Y, &message));
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", D));
+ ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
ASSERT_EQ(1u, message->num_ports());
PortRef E;
- ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &E));
+ ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &E));
- EXPECT_EQ(OK, node.node().ClosePort(X));
- EXPECT_EQ(OK, node.node().ClosePort(Y));
+ EXPECT_EQ(OK, node0.ClosePort(X));
+ EXPECT_EQ(OK, node0.ClosePort(Y));
- EXPECT_EQ(OK, node.node().ClosePort(A));
- EXPECT_EQ(OK, node.node().ClosePort(E));
+ EXPECT_EQ(OK, node0.ClosePort(A));
+ EXPECT_EQ(OK, node0.ClosePort(E));
- // The node should not idle until all proxies are collapsed.
- WaitForIdle();
+ PumpTasks();
- EXPECT_TRUE(node.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
}
TEST_F(PortsTest, ProxyCollapse2) {
- TestNode node(0);
- AddNode(&node);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
+
+ node0_delegate.set_save_messages(true);
PortRef A, B;
- EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B));
+ EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
PortRef X, Y;
- EXPECT_EQ(OK, node.node().CreatePortPair(&X, &Y));
+ EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y));
ScopedMessage message;
- // Send B and A to create proxies in each direction.
- EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", B));
- EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", A));
+ // Send B and receive it as C.
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", B));
+ ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+ ASSERT_EQ(1u, message->num_ports());
+ PortRef C;
+ ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &C));
- EXPECT_EQ(OK, node.node().ClosePort(X));
- EXPECT_EQ(OK, node.node().ClosePort(Y));
+ // Send A and receive it as D.
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", A));
+ ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+ ASSERT_EQ(1u, message->num_ports());
+ PortRef D;
+ ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &D));
// At this point we have a scenario with:
//
// D -> [B] -> C -> [A]
//
- // Ensure that the proxies can collapse. The sent ports will be closed
- // eventually as a result of Y's closure.
+ // Ensure that the proxies can collapse.
- WaitForIdle();
+ EXPECT_EQ(OK, node0.ClosePort(X));
+ EXPECT_EQ(OK, node0.ClosePort(Y));
- EXPECT_TRUE(node.node().CanShutdownCleanly());
+ EXPECT_EQ(OK, node0.ClosePort(C));
+ EXPECT_EQ(OK, node0.ClosePort(D));
+
+ PumpTasks();
+
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
}
TEST_F(PortsTest, SendWithClosedPeer) {
@@ -1085,47 +1068,56 @@
// closed, the newly created port will be aware of that peer closure, and the
// proxy will eventually collapse.
- TestNode node(0);
- AddNode(&node);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
+
+ node0_delegate.set_read_messages(false);
// Send a message from A to B, then close A.
PortRef A, B;
- EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B));
- EXPECT_EQ(OK, node.SendStringMessage(A, "hey"));
- EXPECT_EQ(OK, node.node().ClosePort(A));
+ EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+ EXPECT_EQ(OK, SendStringMessage(&node0, A, "hey"));
+ EXPECT_EQ(OK, node0.ClosePort(A));
+
+ PumpTasks();
// Now send B over X-Y as new port C.
PortRef X, Y;
- EXPECT_EQ(OK, node.node().CreatePortPair(&X, &Y));
- EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", B));
+ EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y));
+
+ node0_delegate.set_read_messages(true);
+ node0_delegate.set_save_messages(true);
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", B));
+
+ EXPECT_EQ(OK, node0.ClosePort(X));
+ EXPECT_EQ(OK, node0.ClosePort(Y));
+
ScopedMessage message;
- ASSERT_TRUE(node.ReadMessage(Y, &message));
+ ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
ASSERT_EQ(1u, message->num_ports());
+
PortRef C;
- ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &C));
+ ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &C));
- EXPECT_EQ(OK, node.node().ClosePort(X));
- EXPECT_EQ(OK, node.node().ClosePort(Y));
+ PumpTasks();
- WaitForIdle();
+ // C should receive the message originally sent to B, and it should also be
+ // aware of A's closure.
- // C should have received the message originally sent to B, and it should also
- // be aware of A's closure.
-
- ASSERT_TRUE(node.ReadMessage(C, &message));
- EXPECT_TRUE(MessageEquals(message, "hey"));
+ ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+ EXPECT_EQ(0, strcmp("hey", ToString(message)));
PortStatus status;
- EXPECT_EQ(OK, node.node().GetStatus(C, &status));
+ EXPECT_EQ(OK, node0.GetStatus(C, &status));
EXPECT_FALSE(status.receiving_messages);
EXPECT_FALSE(status.has_messages);
EXPECT_TRUE(status.peer_closed);
- node.node().ClosePort(C);
+ node0.ClosePort(C);
- WaitForIdle();
-
- EXPECT_TRUE(node.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
}
TEST_F(PortsTest, SendWithClosedPeerSent) {
@@ -1134,342 +1126,391 @@
// eventually notified of the closure, and the dead-end proxies will
// eventually be removed.
- TestNode node(0);
- AddNode(&node);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
+
+ node0_delegate.set_save_messages(true);
PortRef X, Y;
- EXPECT_EQ(OK, node.node().CreatePortPair(&X, &Y));
+ EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y));
PortRef A, B;
- EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B));
+ EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
ScopedMessage message;
// Send A as new port C.
- EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", A));
-
- ASSERT_TRUE(node.ReadMessage(Y, &message));
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", A));
+ ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
ASSERT_EQ(1u, message->num_ports());
PortRef C;
- ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &C));
+ ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &C));
// Send C as new port D.
- EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", C));
-
- ASSERT_TRUE(node.ReadMessage(Y, &message));
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", C));
+ ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
ASSERT_EQ(1u, message->num_ports());
PortRef D;
- ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &D));
+ ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &D));
+
+ node0_delegate.set_read_messages(false);
// Send a message to B through D, then close D.
- EXPECT_EQ(OK, node.SendStringMessage(D, "hey"));
- EXPECT_EQ(OK, node.node().ClosePort(D));
+ EXPECT_EQ(OK, SendStringMessage(&node0, D, "hey"));
+ EXPECT_EQ(OK, node0.ClosePort(D));
+
+ PumpTasks();
// Now send B as new port E.
- EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", B));
- EXPECT_EQ(OK, node.node().ClosePort(X));
+ node0_delegate.set_read_messages(true);
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", B));
- ASSERT_TRUE(node.ReadMessage(Y, &message));
+ EXPECT_EQ(OK, node0.ClosePort(X));
+ EXPECT_EQ(OK, node0.ClosePort(Y));
+
+ ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
ASSERT_EQ(1u, message->num_ports());
+
PortRef E;
- ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &E));
+ ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &E));
- EXPECT_EQ(OK, node.node().ClosePort(Y));
-
- WaitForIdle();
+ PumpTasks();
// E should receive the message originally sent to B, and it should also be
// aware of D's closure.
- ASSERT_TRUE(node.ReadMessage(E, &message));
- EXPECT_TRUE(MessageEquals(message, "hey"));
+ ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+ EXPECT_EQ(0, strcmp("hey", ToString(message)));
PortStatus status;
- EXPECT_EQ(OK, node.node().GetStatus(E, &status));
+ EXPECT_EQ(OK, node0.GetStatus(E, &status));
EXPECT_FALSE(status.receiving_messages);
EXPECT_FALSE(status.has_messages);
EXPECT_TRUE(status.peer_closed);
- EXPECT_EQ(OK, node.node().ClosePort(E));
+ node0.ClosePort(E);
- WaitForIdle();
+ PumpTasks();
- EXPECT_TRUE(node.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
}
TEST_F(PortsTest, MergePorts) {
- TestNode node0(0);
- AddNode(&node0);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
- TestNode node1(1);
- AddNode(&node1);
+ NodeName node1_name(1, 1);
+ TestNodeDelegate node1_delegate(node1_name);
+ Node node1(node1_name, &node1_delegate);
+ node_map[1] = &node1;
// Setup two independent port pairs, A-B on node0 and C-D on node1.
PortRef A, B, C, D;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B));
- EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D));
+ EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+ EXPECT_EQ(OK, node1.CreatePortPair(&C, &D));
+
+ node0_delegate.set_read_messages(false);
+ node1_delegate.set_save_messages(true);
// Write a message on A.
- EXPECT_EQ(OK, node0.SendStringMessage(A, "hey"));
+ EXPECT_EQ(OK, SendStringMessage(&node0, A, "hey"));
+
+ PumpTasks();
// Initiate a merge between B and C.
- EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name()));
+ EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name()));
- WaitForIdle();
+ PumpTasks();
- // Expect all proxies to be gone once idle.
- EXPECT_TRUE(
- node0.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS));
- EXPECT_TRUE(
- node1.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS));
+ // Expect only two receiving ports to be left after pumping tasks.
+ EXPECT_TRUE(node0.CanShutdownCleanly(true));
+ EXPECT_TRUE(node1.CanShutdownCleanly(true));
// Expect D to have received the message sent on A.
ScopedMessage message;
- ASSERT_TRUE(node1.ReadMessage(D, &message));
- EXPECT_TRUE(MessageEquals(message, "hey"));
+ ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
+ EXPECT_EQ(0, strcmp("hey", ToString(message)));
- EXPECT_EQ(OK, node0.node().ClosePort(A));
- EXPECT_EQ(OK, node1.node().ClosePort(D));
+ EXPECT_EQ(OK, node0.ClosePort(A));
+ EXPECT_EQ(OK, node1.ClosePort(D));
// No more ports should be open.
- EXPECT_TRUE(node0.node().CanShutdownCleanly());
- EXPECT_TRUE(node1.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
+ EXPECT_TRUE(node1.CanShutdownCleanly(false));
}
TEST_F(PortsTest, MergePortWithClosedPeer1) {
// This tests that the right thing happens when initiating a merge on a port
// whose peer has already been closed.
- TestNode node0(0);
- AddNode(&node0);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
- TestNode node1(1);
- AddNode(&node1);
+ NodeName node1_name(1, 1);
+ TestNodeDelegate node1_delegate(node1_name);
+ Node node1(node1_name, &node1_delegate);
+ node_map[1] = &node1;
// Setup two independent port pairs, A-B on node0 and C-D on node1.
PortRef A, B, C, D;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B));
- EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D));
+ EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+ EXPECT_EQ(OK, node1.CreatePortPair(&C, &D));
+
+ node0_delegate.set_read_messages(false);
+ node1_delegate.set_save_messages(true);
// Write a message on A.
- EXPECT_EQ(OK, node0.SendStringMessage(A, "hey"));
+ EXPECT_EQ(OK, SendStringMessage(&node0, A, "hey"));
+
+ PumpTasks();
// Close A.
- EXPECT_EQ(OK, node0.node().ClosePort(A));
+ EXPECT_EQ(OK, node0.ClosePort(A));
// Initiate a merge between B and C.
- EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name()));
+ EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name()));
- WaitForIdle();
+ PumpTasks();
- // Expect all proxies to be gone once idle. node0 should have no ports since
- // A was explicitly closed.
- EXPECT_TRUE(node0.node().CanShutdownCleanly());
- EXPECT_TRUE(
- node1.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS));
+ // Expect only one receiving port to be left after pumping tasks.
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
+ EXPECT_TRUE(node1.CanShutdownCleanly(true));
// Expect D to have received the message sent on A.
ScopedMessage message;
- ASSERT_TRUE(node1.ReadMessage(D, &message));
- EXPECT_TRUE(MessageEquals(message, "hey"));
+ ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
+ EXPECT_EQ(0, strcmp("hey", ToString(message)));
- EXPECT_EQ(OK, node1.node().ClosePort(D));
+ EXPECT_EQ(OK, node1.ClosePort(D));
// No more ports should be open.
- EXPECT_TRUE(node0.node().CanShutdownCleanly());
- EXPECT_TRUE(node1.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
+ EXPECT_TRUE(node1.CanShutdownCleanly(false));
}
TEST_F(PortsTest, MergePortWithClosedPeer2) {
// This tests that the right thing happens when merging into a port whose peer
// has already been closed.
- TestNode node0(0);
- AddNode(&node0);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
- TestNode node1(1);
- AddNode(&node1);
+ NodeName node1_name(1, 1);
+ TestNodeDelegate node1_delegate(node1_name);
+ Node node1(node1_name, &node1_delegate);
+ node_map[1] = &node1;
// Setup two independent port pairs, A-B on node0 and C-D on node1.
PortRef A, B, C, D;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B));
- EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D));
+ EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+ EXPECT_EQ(OK, node1.CreatePortPair(&C, &D));
- // Write a message on D and close it.
- EXPECT_EQ(OK, node0.SendStringMessage(D, "hey"));
- EXPECT_EQ(OK, node1.node().ClosePort(D));
+ node0_delegate.set_save_messages(true);
+ node1_delegate.set_read_messages(false);
+
+ // Write a message on D.
+ EXPECT_EQ(OK, SendStringMessage(&node0, D, "hey"));
+
+ PumpTasks();
+
+ // Close D.
+ EXPECT_EQ(OK, node1.ClosePort(D));
// Initiate a merge between B and C.
- EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name()));
+ EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name()));
- WaitForIdle();
+ PumpTasks();
- // Expect all proxies to be gone once idle. node1 should have no ports since
- // D was explicitly closed.
- EXPECT_TRUE(
- node0.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS));
- EXPECT_TRUE(node1.node().CanShutdownCleanly());
+ // Expect only one receiving port to be left after pumping tasks.
+ EXPECT_TRUE(node0.CanShutdownCleanly(true));
+ EXPECT_TRUE(node1.CanShutdownCleanly(false));
// Expect A to have received the message sent on D.
ScopedMessage message;
- ASSERT_TRUE(node0.ReadMessage(A, &message));
- EXPECT_TRUE(MessageEquals(message, "hey"));
+ ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+ EXPECT_EQ(0, strcmp("hey", ToString(message)));
- EXPECT_EQ(OK, node0.node().ClosePort(A));
+ EXPECT_EQ(OK, node0.ClosePort(A));
// No more ports should be open.
- EXPECT_TRUE(node0.node().CanShutdownCleanly());
- EXPECT_TRUE(node1.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
+ EXPECT_TRUE(node1.CanShutdownCleanly(false));
}
TEST_F(PortsTest, MergePortsWithClosedPeers) {
// This tests that no residual ports are left behind if two ports are merged
// when both of their peers have been closed.
- TestNode node0(0);
- AddNode(&node0);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
- TestNode node1(1);
- AddNode(&node1);
+ NodeName node1_name(1, 1);
+ TestNodeDelegate node1_delegate(node1_name);
+ Node node1(node1_name, &node1_delegate);
+ node_map[1] = &node1;
// Setup two independent port pairs, A-B on node0 and C-D on node1.
PortRef A, B, C, D;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B));
- EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D));
+ EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+ EXPECT_EQ(OK, node1.CreatePortPair(&C, &D));
+
+ node0_delegate.set_save_messages(true);
+ node1_delegate.set_read_messages(false);
// Close A and D.
- EXPECT_EQ(OK, node0.node().ClosePort(A));
- EXPECT_EQ(OK, node1.node().ClosePort(D));
+ EXPECT_EQ(OK, node0.ClosePort(A));
+ EXPECT_EQ(OK, node1.ClosePort(D));
- WaitForIdle();
+ PumpTasks();
// Initiate a merge between B and C.
- EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name()));
+ EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name()));
- WaitForIdle();
+ PumpTasks();
// Expect everything to have gone away.
- EXPECT_TRUE(node0.node().CanShutdownCleanly());
- EXPECT_TRUE(node1.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
+ EXPECT_TRUE(node1.CanShutdownCleanly(false));
}
TEST_F(PortsTest, MergePortsWithMovedPeers) {
- // This tests that ports can be merged successfully even if their peers are
- // moved around.
+ // This tests that no ports can be merged successfully even if their peers
+ // are moved around.
- TestNode node0(0);
- AddNode(&node0);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
- TestNode node1(1);
- AddNode(&node1);
+ NodeName node1_name(1, 1);
+ TestNodeDelegate node1_delegate(node1_name);
+ Node node1(node1_name, &node1_delegate);
+ node_map[1] = &node1;
+
+ node0_delegate.set_save_messages(true);
+ node1_delegate.set_read_messages(false);
// Setup two independent port pairs, A-B on node0 and C-D on node1.
PortRef A, B, C, D;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B));
- EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D));
+ EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+ EXPECT_EQ(OK, node1.CreatePortPair(&C, &D));
// Set up another pair X-Y for moving ports on node0.
PortRef X, Y;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&X, &Y));
+ EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y));
ScopedMessage message;
// Move A to new port E.
- EXPECT_EQ(OK, node0.SendStringMessageWithPort(X, "foo", A));
- ASSERT_TRUE(node0.ReadMessage(Y, &message));
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", A));
+ ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
ASSERT_EQ(1u, message->num_ports());
PortRef E;
- ASSERT_EQ(OK, node0.node().GetPort(message->ports()[0], &E));
+ ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &E));
- EXPECT_EQ(OK, node0.node().ClosePort(X));
- EXPECT_EQ(OK, node0.node().ClosePort(Y));
+ EXPECT_EQ(OK, node0.ClosePort(X));
+ EXPECT_EQ(OK, node0.ClosePort(Y));
+
+ node0_delegate.set_read_messages(false);
// Write messages on E and D.
- EXPECT_EQ(OK, node0.SendStringMessage(E, "hey"));
- EXPECT_EQ(OK, node1.SendStringMessage(D, "hi"));
+ EXPECT_EQ(OK, SendStringMessage(&node0, E, "hey"));
+ EXPECT_EQ(OK, SendStringMessage(&node1, D, "hi"));
// Initiate a merge between B and C.
- EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name()));
+ EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name()));
- WaitForIdle();
+ node0_delegate.set_read_messages(true);
+ node1_delegate.set_read_messages(true);
+ node1_delegate.set_save_messages(true);
+
+ PumpTasks();
// Expect to receive D's message on E and E's message on D.
- ASSERT_TRUE(node0.ReadMessage(E, &message));
- EXPECT_TRUE(MessageEquals(message, "hi"));
- ASSERT_TRUE(node1.ReadMessage(D, &message));
- EXPECT_TRUE(MessageEquals(message, "hey"));
+ ASSERT_TRUE(node0_delegate.GetSavedMessage(&message));
+ EXPECT_EQ(0, strcmp("hi", ToString(message)));
+ ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
+ EXPECT_EQ(0, strcmp("hey", ToString(message)));
// Close E and D.
- EXPECT_EQ(OK, node0.node().ClosePort(E));
- EXPECT_EQ(OK, node1.node().ClosePort(D));
+ EXPECT_EQ(OK, node0.ClosePort(E));
+ EXPECT_EQ(OK, node1.ClosePort(D));
- WaitForIdle();
+ PumpTasks();
// Expect everything to have gone away.
- EXPECT_TRUE(node0.node().CanShutdownCleanly());
- EXPECT_TRUE(node1.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
+ EXPECT_TRUE(node1.CanShutdownCleanly(false));
}
TEST_F(PortsTest, MergePortsFailsGracefully) {
// This tests that the system remains in a well-defined state if something
// goes wrong during port merge.
- TestNode node0(0);
- AddNode(&node0);
+ NodeName node0_name(0, 1);
+ TestNodeDelegate node0_delegate(node0_name);
+ Node node0(node0_name, &node0_delegate);
+ node_map[0] = &node0;
- TestNode node1(1);
- AddNode(&node1);
+ NodeName node1_name(1, 1);
+ TestNodeDelegate node1_delegate(node1_name);
+ Node node1(node1_name, &node1_delegate);
+ node_map[1] = &node1;
// Setup two independent port pairs, A-B on node0 and C-D on node1.
PortRef A, B, C, D;
- EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B));
- EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D));
+ EXPECT_EQ(OK, node0.CreatePortPair(&A, &B));
+ EXPECT_EQ(OK, node1.CreatePortPair(&C, &D));
+ PumpTasks();
+
+ // Initiate a merge between B and C.
+ EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name()));
+
+ // Move C to a new port E. This is dumb and nobody should do it, but it's
+ // possible. MergePorts will fail as a result because C won't be in a
+ // receiving state when the event arrives at node1, so B should be closed.
ScopedMessage message;
PortRef X, Y;
- EXPECT_EQ(OK, node1.node().CreatePortPair(&X, &Y));
-
- // Block the merge from proceeding until we can do something stupid with port
- // C. This avoids the test logic racing with async merge logic.
- node1.BlockOnEvent(EventType::kMergePort);
-
- // Initiate the merge between B and C.
- EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name()));
-
- // Move C to a new port E. This is not a sane use of Node's public API but
- // is still hypothetically possible. It allows us to force a merge failure
- // because C will be in an invalid state by the term the merge is processed.
- // As a result, B should be closed.
- EXPECT_EQ(OK, node1.SendStringMessageWithPort(X, "foo", C));
-
- node1.Unblock();
-
- ASSERT_TRUE(node1.ReadMessage(Y, &message));
+ EXPECT_EQ(OK, node1.CreatePortPair(&X, &Y));
+ node1_delegate.set_save_messages(true);
+ EXPECT_EQ(OK, SendStringMessageWithPort(&node1, X, "foo", C));
+ ASSERT_TRUE(node1_delegate.GetSavedMessage(&message));
ASSERT_EQ(1u, message->num_ports());
PortRef E;
- ASSERT_EQ(OK, node1.node().GetPort(message->ports()[0], &E));
+ ASSERT_EQ(OK, node1.GetPort(message->ports()[0], &E));
+ EXPECT_EQ(OK, node1.ClosePort(X));
+ EXPECT_EQ(OK, node1.ClosePort(Y));
- EXPECT_EQ(OK, node1.node().ClosePort(X));
- EXPECT_EQ(OK, node1.node().ClosePort(Y));
+ // C goes away as a result of normal proxy removal.
+ PumpTasks();
- WaitForIdle();
+ EXPECT_EQ(ERROR_PORT_UNKNOWN, node1.GetPort(C.name(), &C));
- // C goes away as a result of normal proxy removal. B should have been closed
- // cleanly by the failed MergePorts.
- EXPECT_EQ(ERROR_PORT_UNKNOWN, node1.node().GetPort(C.name(), &C));
- EXPECT_EQ(ERROR_PORT_UNKNOWN, node0.node().GetPort(B.name(), &B));
+ // B should have been closed cleanly.
+ EXPECT_EQ(ERROR_PORT_UNKNOWN, node0.GetPort(B.name(), &B));
// Close A, D, and E.
- EXPECT_EQ(OK, node0.node().ClosePort(A));
- EXPECT_EQ(OK, node1.node().ClosePort(D));
- EXPECT_EQ(OK, node1.node().ClosePort(E));
+ EXPECT_EQ(OK, node0.ClosePort(A));
+ EXPECT_EQ(OK, node1.ClosePort(D));
+ EXPECT_EQ(OK, node1.ClosePort(E));
- WaitForIdle();
+ PumpTasks();
// Expect everything to have gone away.
- EXPECT_TRUE(node0.node().CanShutdownCleanly());
- EXPECT_TRUE(node1.node().CanShutdownCleanly());
+ EXPECT_TRUE(node0.CanShutdownCleanly(false));
+ EXPECT_TRUE(node1.CanShutdownCleanly(false));
}
} // namespace test
diff --git a/mojo/edk/system/request_context.cc b/mojo/edk/system/request_context.cc
index 6370ab1..276e39f 100644
--- a/mojo/edk/system/request_context.cc
+++ b/mojo/edk/system/request_context.cc
@@ -77,12 +77,12 @@
const HandleSignalsState& state) {
DCHECK(IsCurrent());
watch_notify_finalizers_->push_back(
- WatchNotifyFinalizer(std::move(watcher), result, state));
+ WatchNotifyFinalizer(watcher, result, state));
}
void RequestContext::AddWatchCancelFinalizer(scoped_refptr<Watcher> watcher) {
DCHECK(IsCurrent());
- watch_cancel_finalizers_->push_back(std::move(watcher));
+ watch_cancel_finalizers_->push_back(watcher);
}
bool RequestContext::IsCurrent() const {
@@ -93,7 +93,8 @@
scoped_refptr<Watcher> watcher,
MojoResult result,
const HandleSignalsState& state)
- : watcher(std::move(watcher)), result(result), state(state) {}
+ : watcher(watcher), result(result), state(state) {
+}
RequestContext::WatchNotifyFinalizer::WatchNotifyFinalizer(
const WatchNotifyFinalizer& other) = default;
diff --git a/mojo/edk/system/shared_buffer_dispatcher.h b/mojo/edk/system/shared_buffer_dispatcher.h
index 6015595..1648dd2 100644
--- a/mojo/edk/system/shared_buffer_dispatcher.h
+++ b/mojo/edk/system/shared_buffer_dispatcher.h
@@ -21,6 +21,7 @@
namespace edk {
class NodeController;
+class PlatformSupport;
class MOJO_SYSTEM_IMPL_EXPORT SharedBufferDispatcher final : public Dispatcher {
public:
diff --git a/mojo/edk/test/BUILD.gn b/mojo/edk/test/BUILD.gn
index a15456a..ebacc64 100644
--- a/mojo/edk/test/BUILD.gn
+++ b/mojo/edk/test/BUILD.gn
@@ -9,6 +9,8 @@
sources = [
"mojo_test_base.cc",
"mojo_test_base.h",
+ "scoped_ipc_support.cc",
+ "scoped_ipc_support.h",
"test_utils.h",
"test_utils_posix.cc",
"test_utils_win.cc",
diff --git a/mojo/edk/test/mojo_test_base.cc b/mojo/edk/test/mojo_test_base.cc
index f1032d7..5d77a11 100644
--- a/mojo/edk/test/mojo_test_base.cc
+++ b/mojo/edk/test/mojo_test_base.cc
@@ -44,15 +44,14 @@
MojoTestBase::ClientController& MojoTestBase::StartClient(
const std::string& client_name) {
clients_.push_back(base::MakeUnique<ClientController>(
- client_name, this, process_error_callback_, launch_type_));
+ client_name, this, process_error_callback_));
return *clients_.back();
}
MojoTestBase::ClientController::ClientController(
const std::string& client_name,
MojoTestBase* test,
- const ProcessErrorCallback& process_error_callback,
- LaunchType launch_type) {
+ const ProcessErrorCallback& process_error_callback) {
#if !defined(OS_IOS)
#if defined(OS_MACOSX)
// This lock needs to be held while launching the child because the Mach port
@@ -64,7 +63,7 @@
base::AutoLock lock(g_mach_broker->GetLock());
#endif
helper_.set_process_error_callback(process_error_callback);
- pipe_ = helper_.StartChild(client_name, launch_type);
+ pipe_ = helper_.StartChild(client_name);
#if defined(OS_MACOSX)
g_mach_broker->AddPlaceholderForPid(helper_.test_child().Handle());
#endif
@@ -76,12 +75,6 @@
<< "Test clients should be waited on explicitly with WaitForShutdown().";
}
-void MojoTestBase::ClientController::ClosePeerConnection() {
-#if !defined(OS_IOS)
- helper_.ClosePeerConnection();
-#endif
-}
-
int MojoTestBase::ClientController::WaitForShutdown() {
was_shutdown_ = true;
#if !defined(OS_IOS)
diff --git a/mojo/edk/test/mojo_test_base.h b/mojo/edk/test/mojo_test_base.h
index fa5b64c..4f55145 100644
--- a/mojo/edk/test/mojo_test_base.h
+++ b/mojo/edk/test/mojo_test_base.h
@@ -29,8 +29,6 @@
MojoTestBase();
~MojoTestBase() override;
- using LaunchType = MultiprocessTestHelper::LaunchType;
-
protected:
using HandlerCallback = base::Callback<void(ScopedMessagePipeHandle)>;
@@ -38,13 +36,11 @@
public:
ClientController(const std::string& client_name,
MojoTestBase* test,
- const ProcessErrorCallback& process_error_callback,
- LaunchType launch_type);
+ const ProcessErrorCallback& process_error_callback_);
~ClientController();
MojoHandle pipe() const { return pipe_.get().value(); }
- void ClosePeerConnection();
int WaitForShutdown();
private:
@@ -152,8 +148,6 @@
// Reads data from a data pipe.
static std::string ReadData(MojoHandle consumer, size_t size);
- void set_launch_type(LaunchType launch_type) { launch_type_ = launch_type; }
-
private:
friend class ClientController;
@@ -161,8 +155,6 @@
ProcessErrorCallback process_error_callback_;
- LaunchType launch_type_ = LaunchType::CHILD;
-
DISALLOW_COPY_AND_ASSIGN(MojoTestBase);
};
diff --git a/mojo/edk/test/multiprocess_test_helper.cc b/mojo/edk/test/multiprocess_test_helper.cc
index de6e2d9..82a82c8 100644
--- a/mojo/edk/test/multiprocess_test_helper.cc
+++ b/mojo/edk/test/multiprocess_test_helper.cc
@@ -8,14 +8,12 @@
#include <set>
#include <utility>
-#include "base/base_paths.h"
#include "base/base_switches.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
-#include "base/path_service.h"
#include "base/process/kill.h"
#include "base/process/process_handle.h"
#include "base/run_loop.h"
@@ -24,9 +22,6 @@
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/named_platform_handle.h"
-#include "mojo/edk/embedder/named_platform_handle_utils.h"
-#include "mojo/edk/embedder/pending_process_connection.h"
#include "mojo/edk/embedder/platform_channel_pair.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -43,13 +38,11 @@
namespace {
const char kMojoPrimordialPipeToken[] = "mojo-primordial-pipe-token";
-const char kMojoNamedPipeName[] = "mojo-named-pipe-name";
-template <typename Func>
-int RunClientFunction(Func handler) {
- CHECK(MultiprocessTestHelper::primordial_pipe.is_valid());
- ScopedMessagePipeHandle pipe =
- std::move(MultiprocessTestHelper::primordial_pipe);
+int RunClientFunction(std::function<int(MojoHandle)> handler) {
+ CHECK(!MultiprocessTestHelper::primordial_pipe_token.empty());
+ ScopedMessagePipeHandle pipe = CreateChildMessagePipe(
+ MultiprocessTestHelper::primordial_pipe_token);
return handler(pipe.get().value());
}
@@ -62,17 +55,15 @@
}
ScopedMessagePipeHandle MultiprocessTestHelper::StartChild(
- const std::string& test_child_name,
- LaunchType launch_type) {
- return StartChildWithExtraSwitch(test_child_name, std::string(),
- std::string(), launch_type);
+ const std::string& test_child_name) {
+ return StartChildWithExtraSwitch(
+ test_child_name, std::string(), std::string());
}
ScopedMessagePipeHandle MultiprocessTestHelper::StartChildWithExtraSwitch(
const std::string& test_child_name,
const std::string& switch_string,
- const std::string& switch_value,
- LaunchType launch_type) {
+ const std::string& switch_value) {
CHECK(!test_child_name.empty());
CHECK(!test_child_.IsValid());
@@ -97,23 +88,12 @@
}
PlatformChannelPair channel;
- NamedPlatformHandle named_pipe;
HandlePassingInformation handle_passing_info;
- if (launch_type == LaunchType::CHILD || launch_type == LaunchType::PEER) {
- channel.PrepareToPassClientHandleToChildProcess(&command_line,
- &handle_passing_info);
- } else if (launch_type == LaunchType::NAMED_CHILD ||
- launch_type == LaunchType::NAMED_PEER) {
-#if defined(OS_POSIX)
- base::FilePath temp_dir;
- CHECK(base::PathService::Get(base::DIR_TEMP, &temp_dir));
- named_pipe = NamedPlatformHandle(
- temp_dir.AppendASCII(GenerateRandomToken()).value());
-#else
- named_pipe = NamedPlatformHandle(GenerateRandomToken());
-#endif
- command_line.AppendSwitchNative(kMojoNamedPipeName, named_pipe.name);
- }
+ channel.PrepareToPassClientHandleToChildProcess(&command_line,
+ &handle_passing_info);
+
+ std::string pipe_token = mojo::edk::GenerateRandomToken();
+ command_line.AppendSwitchASCII(kMojoPrimordialPipeToken, pipe_token);
if (!switch_string.empty()) {
CHECK(!command_line.HasSwitch(switch_string));
@@ -136,44 +116,18 @@
#error "Not supported yet."
#endif
- // NOTE: In the case of named pipes, it's important that the server handle be
- // created before the child process is launched; otherwise the server binding
- // the pipe path can race with child's connection to the pipe.
- ScopedPlatformHandle server_handle;
- if (launch_type == LaunchType::CHILD || launch_type == LaunchType::PEER) {
- server_handle = channel.PassServerHandle();
- } else if (launch_type == LaunchType::NAMED_CHILD ||
- launch_type == LaunchType::NAMED_PEER) {
- server_handle = CreateServerHandle(named_pipe);
- }
-
- PendingProcessConnection process;
- ScopedMessagePipeHandle pipe;
- if (launch_type == LaunchType::CHILD ||
- launch_type == LaunchType::NAMED_CHILD) {
- std::string pipe_token;
- pipe = process.CreateMessagePipe(&pipe_token);
- command_line.AppendSwitchASCII(kMojoPrimordialPipeToken, pipe_token);
- } else if (launch_type == LaunchType::PEER ||
- launch_type == LaunchType::NAMED_PEER) {
- peer_token_ = mojo::edk::GenerateRandomToken();
- pipe = ConnectToPeerProcess(std::move(server_handle), peer_token_);
- }
+ std::string child_token = mojo::edk::GenerateRandomToken();
+ ScopedMessagePipeHandle pipe = CreateParentMessagePipe(pipe_token,
+ child_token);
test_child_ =
base::SpawnMultiProcessTestChild(test_child_main, command_line, options);
- if (launch_type == LaunchType::CHILD || launch_type == LaunchType::PEER)
- channel.ChildProcessLaunched();
+ channel.ChildProcessLaunched();
- if (launch_type == LaunchType::CHILD ||
- launch_type == LaunchType::NAMED_CHILD) {
- DCHECK(server_handle.is_valid());
- process.Connect(test_child_.Handle(),
- ConnectionParams(std::move(server_handle)),
- process_error_callback_);
- }
-
+ ChildProcessLaunched(test_child_.Handle(), channel.PassServerHandle(),
+ child_token, process_error_callback_);
CHECK(test_child_.IsValid());
+
return pipe;
}
@@ -181,18 +135,18 @@
CHECK(test_child_.IsValid());
int rv = -1;
- WaitForMultiprocessTestChildExit(test_child_, TestTimeouts::action_timeout(),
- &rv);
+#if defined(OS_ANDROID)
+ // On Android, we need to use a special function to wait for the child.
+ CHECK(AndroidWaitForChildExitWithTimeout(
+ test_child_, TestTimeouts::action_timeout(), &rv));
+#else
+ CHECK(
+ test_child_.WaitForExitWithTimeout(TestTimeouts::action_timeout(), &rv));
+#endif
test_child_.Close();
return rv;
}
-void MultiprocessTestHelper::ClosePeerConnection() {
- DCHECK(!peer_token_.empty());
- ::mojo::edk::ClosePeerConnection(peer_token_);
- peer_token_.clear();
-}
-
bool MultiprocessTestHelper::WaitForChildTestShutdown() {
return WaitForChildShutdown() == 0;
}
@@ -201,33 +155,17 @@
void MultiprocessTestHelper::ChildSetup() {
CHECK(base::CommandLine::InitializedForCurrentProcess());
- std::string primordial_pipe_token =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- kMojoPrimordialPipeToken);
- NamedPlatformHandle named_pipe(
- base::CommandLine::ForCurrentProcess()->GetSwitchValueNative(
- kMojoNamedPipeName));
- if (!primordial_pipe_token.empty()) {
- primordial_pipe = CreateChildMessagePipe(primordial_pipe_token);
+ primordial_pipe_token = base::CommandLine::ForCurrentProcess()
+ ->GetSwitchValueASCII(kMojoPrimordialPipeToken);
+ CHECK(!primordial_pipe_token.empty());
+
#if defined(OS_MACOSX) && !defined(OS_IOS)
- CHECK(base::MachPortBroker::ChildSendTaskPortToParent("mojo_test"));
+ CHECK(base::MachPortBroker::ChildSendTaskPortToParent("mojo_test"));
#endif
- if (named_pipe.is_valid()) {
- SetParentPipeHandle(CreateClientHandle(named_pipe));
- } else {
- SetParentPipeHandle(
- PlatformChannelPair::PassClientHandleFromParentProcess(
- *base::CommandLine::ForCurrentProcess()));
- }
- } else {
- if (named_pipe.is_valid()) {
- primordial_pipe = ConnectToPeerProcess(CreateClientHandle(named_pipe));
- } else {
- primordial_pipe = ConnectToPeerProcess(
- PlatformChannelPair::PassClientHandleFromParentProcess(
- *base::CommandLine::ForCurrentProcess()));
- }
- }
+
+ SetParentPipeHandle(
+ PlatformChannelPair::PassClientHandleFromParentProcess(
+ *base::CommandLine::ForCurrentProcess()));
}
// static
@@ -249,7 +187,7 @@
}
// static
-mojo::ScopedMessagePipeHandle MultiprocessTestHelper::primordial_pipe;
+std::string MultiprocessTestHelper::primordial_pipe_token;
} // namespace test
} // namespace edk
diff --git a/mojo/edk/test/multiprocess_test_helper.h b/mojo/edk/test/multiprocess_test_helper.h
index dd9bd6a..203eb11 100644
--- a/mojo/edk/test/multiprocess_test_helper.h
+++ b/mojo/edk/test/multiprocess_test_helper.h
@@ -19,6 +19,7 @@
namespace mojo {
namespace edk {
+class PlatformChannelPair;
namespace test {
@@ -26,31 +27,13 @@
public:
using HandlerCallback = base::Callback<void(ScopedMessagePipeHandle)>;
- enum class LaunchType {
- // Launch the child process as a child in the mojo system.
- CHILD,
-
- // Launch the child process as an unrelated peer process in the mojo system.
- PEER,
-
- // Launch the child process as a child in the mojo system, using a named
- // pipe.
- NAMED_CHILD,
-
- // Launch the child process as an unrelated peer process in the mojo
- // system, using a named pipe.
- NAMED_PEER,
- };
-
MultiprocessTestHelper();
~MultiprocessTestHelper();
// Start a child process and run the "main" function "named" |test_child_name|
// declared using |MOJO_MULTIPROCESS_TEST_CHILD_MAIN()| or
// |MOJO_MULTIPROCESS_TEST_CHILD_TEST()| (below).
- ScopedMessagePipeHandle StartChild(
- const std::string& test_child_name,
- LaunchType launch_type = LaunchType::CHILD);
+ ScopedMessagePipeHandle StartChild(const std::string& test_child_name);
// Like |StartChild()|, but appends an extra switch (with ASCII value) to the
// command line. (The switch must not already be present in the default
@@ -58,15 +41,12 @@
ScopedMessagePipeHandle StartChildWithExtraSwitch(
const std::string& test_child_name,
const std::string& switch_string,
- const std::string& switch_value,
- LaunchType launch_type);
+ const std::string& switch_value);
void set_process_error_callback(const ProcessErrorCallback& callback) {
process_error_callback_ = callback;
}
- void ClosePeerConnection();
-
// Wait for the child process to terminate.
// Returns the exit code of the child process. Note that, though it's declared
// to be an |int|, the exit code is subject to mangling by the OS. E.g., we
@@ -89,7 +69,7 @@
static int RunClientTestMain(const base::Callback<void(MojoHandle)>& main);
// For use (and only valid) in the child process:
- static mojo::ScopedMessagePipeHandle primordial_pipe;
+ static std::string primordial_pipe_token;
private:
// Valid after |StartChild()| and before |WaitForChildShutdown()|.
@@ -97,8 +77,6 @@
ProcessErrorCallback process_error_callback_;
- std::string peer_token_;
-
DISALLOW_COPY_AND_ASSIGN(MultiprocessTestHelper);
};
diff --git a/mojo/edk/test/run_all_perftests.cc b/mojo/edk/test/run_all_perftests.cc
index 3ce3b47..50c41d7 100644
--- a/mojo/edk/test/run_all_perftests.cc
+++ b/mojo/edk/test/run_all_perftests.cc
@@ -7,19 +7,24 @@
#include "base/test/perf_test_suite.h"
#include "base/test/test_io_thread.h"
#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
#include "mojo/edk/test/multiprocess_test_helper.h"
+#include "mojo/edk/test/scoped_ipc_support.h"
#include "mojo/edk/test/test_support_impl.h"
#include "mojo/public/tests/test_support_private.h"
int main(int argc, char** argv) {
+#if defined(OS_ANDROID)
+ base::InitAndroidMultiProcessTestHelper(main);
+#endif
+
base::PerfTestSuite test(argc, argv);
mojo::edk::Init();
base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart);
- mojo::edk::ScopedIPCSupport ipc_support(
- test_io_thread.task_runner(),
- mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
+ // Leak this because its destructor calls mojo::edk::ShutdownIPCSupport which
+ // really does nothing in the new EDK but does depend on the current message
+ // loop, which is destructed inside base::LaunchUnitTests.
+ new mojo::edk::test::ScopedIPCSupport(test_io_thread.task_runner());
mojo::test::TestSupport::Init(new mojo::edk::test::TestSupportImpl());
return test.Run();
diff --git a/mojo/edk/test/run_all_unittests.cc b/mojo/edk/test/run_all_unittests.cc
index a057825..05aed91 100644
--- a/mojo/edk/test/run_all_unittests.cc
+++ b/mojo/edk/test/run_all_unittests.cc
@@ -11,8 +11,8 @@
#include "base/test/test_io_thread.h"
#include "base/test/test_suite.h"
#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/scoped_ipc_support.h"
#include "mojo/edk/test/multiprocess_test_helper.h"
+#include "mojo/edk/test/scoped_ipc_support.h"
#include "mojo/edk/test/test_support_impl.h"
#include "mojo/public/tests/test_support_private.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -26,6 +26,10 @@
testing::GTEST_FLAG(death_test_style) = "threadsafe";
#endif
#if defined(OS_ANDROID)
+ // Enable the alternate test child implementation. This is needed because Mojo
+ // tests need to spawn test children after initialising the Mojo system.
+ base::InitAndroidMultiProcessTestHelper(main);
+
// On android, the test framework has a signal handler that will print a
// [ CRASH ] line when the application crashes. This breaks death test has the
// test runner will consider the death of the child process a test failure.
@@ -39,10 +43,11 @@
mojo::test::TestSupport::Init(new mojo::edk::test::TestSupportImpl());
base::TestIOThread test_io_thread(base::TestIOThread::kAutoStart);
+ // Leak this because its destructor calls mojo::edk::ShutdownIPCSupport which
+ // really does nothing in the new EDK but does depend on the current message
+ // loop, which is destructed inside base::LaunchUnitTests.
+ new mojo::edk::test::ScopedIPCSupport(test_io_thread.task_runner());
- mojo::edk::ScopedIPCSupport ipc_support(
- test_io_thread.task_runner(),
- mojo::edk::ScopedIPCSupport::ShutdownPolicy::CLEAN);
return base::LaunchUnitTests(
argc, argv,
base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
diff --git a/mojo/edk/test/scoped_ipc_support.cc b/mojo/edk/test/scoped_ipc_support.cc
new file mode 100644
index 0000000..7dc7c99
--- /dev/null
+++ b/mojo/edk/test/scoped_ipc_support.cc
@@ -0,0 +1,62 @@
+// 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/test/scoped_ipc_support.h"
+
+#include <utility>
+
+#include "base/message_loop/message_loop.h"
+#include "mojo/edk/embedder/embedder.h"
+
+namespace mojo {
+namespace edk {
+namespace test {
+
+namespace {
+base::TaskRunner* g_io_task_runner = nullptr;
+}
+
+base::TaskRunner* GetIoTaskRunner() {
+ return g_io_task_runner;
+}
+
+namespace internal {
+
+ScopedIPCSupportHelper::ScopedIPCSupportHelper() {
+}
+
+ScopedIPCSupportHelper::~ScopedIPCSupportHelper() {
+ ShutdownIPCSupport();
+ run_loop_.Run();
+}
+
+void ScopedIPCSupportHelper::Init(
+ ProcessDelegate* process_delegate,
+ scoped_refptr<base::TaskRunner> io_thread_task_runner) {
+ io_thread_task_runner_ = io_thread_task_runner;
+ InitIPCSupport(process_delegate, io_thread_task_runner_);
+}
+
+void ScopedIPCSupportHelper::OnShutdownCompleteImpl() {
+ run_loop_.Quit();
+}
+
+} // namespace internal
+
+ScopedIPCSupport::ScopedIPCSupport(
+ scoped_refptr<base::TaskRunner> io_thread_task_runner) {
+ g_io_task_runner = io_thread_task_runner.get();
+ helper_.Init(this, std::move(io_thread_task_runner));
+}
+
+ScopedIPCSupport::~ScopedIPCSupport() {
+}
+
+void ScopedIPCSupport::OnShutdownComplete() {
+ helper_.OnShutdownCompleteImpl();
+}
+
+} // namespace test
+} // namespace edk
+} // namespace mojo
diff --git a/mojo/edk/test/scoped_ipc_support.h b/mojo/edk/test/scoped_ipc_support.h
new file mode 100644
index 0000000..04173d3
--- /dev/null
+++ b/mojo/edk/test/scoped_ipc_support.h
@@ -0,0 +1,65 @@
+// 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_TEST_SCOPED_IPC_SUPPORT_H_
+#define MOJO_EDK_TEST_SCOPED_IPC_SUPPORT_H_
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "base/task_runner.h"
+#include "mojo/edk/embedder/process_delegate.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+
+namespace mojo {
+namespace edk {
+namespace test {
+
+base::TaskRunner* GetIoTaskRunner();
+
+namespace internal {
+
+class ScopedIPCSupportHelper {
+ public:
+ ScopedIPCSupportHelper();
+ ~ScopedIPCSupportHelper();
+
+ void Init(ProcessDelegate* process_delegate,
+ scoped_refptr<base::TaskRunner> io_thread_task_runner);
+
+ void OnShutdownCompleteImpl();
+
+ private:
+ scoped_refptr<base::TaskRunner> io_thread_task_runner_;
+
+ base::RunLoop run_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedIPCSupportHelper);
+};
+
+} // namespace internal
+
+// A simple class that calls |InitIPCSupport()| on construction and
+// |ShutdownIPCSupport()| on destruction.
+class ScopedIPCSupport : public ProcessDelegate {
+ public:
+ explicit ScopedIPCSupport(
+ scoped_refptr<base::TaskRunner> io_thread_task_runner);
+ ~ScopedIPCSupport() override;
+
+ private:
+ // |ProcessDelegate| implementation:
+ // Note: Executed on the I/O thread.
+ void OnShutdownComplete() override;
+
+ internal::ScopedIPCSupportHelper helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedIPCSupport);
+};
+
+} // namespace test
+} // namespace edk
+} // namespace mojo
+
+#endif // MOJO_EDK_TEST_SCOPED_IPC_SUPPORT_H_
diff --git a/mojo/gpu/BUILD.gn b/mojo/gpu/BUILD.gn
new file mode 100644
index 0000000..ad37238
--- /dev/null
+++ b/mojo/gpu/BUILD.gn
@@ -0,0 +1,18 @@
+# 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.
+
+source_set("mojo_gles2_implementation") {
+ sources = [
+ "mojo_gles2_impl_autogen.cc",
+ "mojo_gles2_impl_autogen.h",
+ ]
+
+ configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+
+ deps = [
+ "//base",
+ "//gpu/command_buffer/client:gles2_interface",
+ "//mojo/public/c/gles2",
+ ]
+}
diff --git a/mojo/gpu/DEPS b/mojo/gpu/DEPS
new file mode 100644
index 0000000..241389e
--- /dev/null
+++ b/mojo/gpu/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+gpu/command_buffer/client",
+ "+third_party/khronos/GLES2",
+]
diff --git a/mojo/gpu/mojo_gles2_impl_autogen.cc b/mojo/gpu/mojo_gles2_impl_autogen.cc
new file mode 100644
index 0000000..93377a5
--- /dev/null
+++ b/mojo/gpu/mojo_gles2_impl_autogen.cc
@@ -0,0 +1,1899 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is auto-generated from
+// gpu/command_buffer/build_gles2_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+// clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+#include "mojo/gpu/mojo_gles2_impl_autogen.h"
+
+#include "base/logging.h"
+#include "mojo/public/c/gles2/chromium_extension.h"
+#include "mojo/public/c/gles2/gles2.h"
+
+namespace mojo {
+
+void MojoGLES2Impl::ActiveTexture(GLenum texture) {
+ MojoGLES2MakeCurrent(context_);
+ glActiveTexture(texture);
+}
+void MojoGLES2Impl::AttachShader(GLuint program, GLuint shader) {
+ MojoGLES2MakeCurrent(context_);
+ glAttachShader(program, shader);
+}
+void MojoGLES2Impl::BindAttribLocation(GLuint program,
+ GLuint index,
+ const char* name) {
+ MojoGLES2MakeCurrent(context_);
+ glBindAttribLocation(program, index, name);
+}
+void MojoGLES2Impl::BindBuffer(GLenum target, GLuint buffer) {
+ MojoGLES2MakeCurrent(context_);
+ glBindBuffer(target, buffer);
+}
+void MojoGLES2Impl::BindBufferBase(GLenum target, GLuint index, GLuint buffer) {
+ NOTREACHED() << "Unimplemented BindBufferBase.";
+}
+void MojoGLES2Impl::BindBufferRange(GLenum target,
+ GLuint index,
+ GLuint buffer,
+ GLintptr offset,
+ GLsizeiptr size) {
+ NOTREACHED() << "Unimplemented BindBufferRange.";
+}
+void MojoGLES2Impl::BindFramebuffer(GLenum target, GLuint framebuffer) {
+ MojoGLES2MakeCurrent(context_);
+ glBindFramebuffer(target, framebuffer);
+}
+void MojoGLES2Impl::BindRenderbuffer(GLenum target, GLuint renderbuffer) {
+ MojoGLES2MakeCurrent(context_);
+ glBindRenderbuffer(target, renderbuffer);
+}
+void MojoGLES2Impl::BindSampler(GLuint unit, GLuint sampler) {
+ NOTREACHED() << "Unimplemented BindSampler.";
+}
+void MojoGLES2Impl::BindTexture(GLenum target, GLuint texture) {
+ MojoGLES2MakeCurrent(context_);
+ glBindTexture(target, texture);
+}
+void MojoGLES2Impl::BindTransformFeedback(GLenum target,
+ GLuint transformfeedback) {
+ NOTREACHED() << "Unimplemented BindTransformFeedback.";
+}
+void MojoGLES2Impl::BlendColor(GLclampf red,
+ GLclampf green,
+ GLclampf blue,
+ GLclampf alpha) {
+ MojoGLES2MakeCurrent(context_);
+ glBlendColor(red, green, blue, alpha);
+}
+void MojoGLES2Impl::BlendEquation(GLenum mode) {
+ MojoGLES2MakeCurrent(context_);
+ glBlendEquation(mode);
+}
+void MojoGLES2Impl::BlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) {
+ MojoGLES2MakeCurrent(context_);
+ glBlendEquationSeparate(modeRGB, modeAlpha);
+}
+void MojoGLES2Impl::BlendFunc(GLenum sfactor, GLenum dfactor) {
+ MojoGLES2MakeCurrent(context_);
+ glBlendFunc(sfactor, dfactor);
+}
+void MojoGLES2Impl::BlendFuncSeparate(GLenum srcRGB,
+ GLenum dstRGB,
+ GLenum srcAlpha,
+ GLenum dstAlpha) {
+ MojoGLES2MakeCurrent(context_);
+ glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);
+}
+void MojoGLES2Impl::BufferData(GLenum target,
+ GLsizeiptr size,
+ const void* data,
+ GLenum usage) {
+ MojoGLES2MakeCurrent(context_);
+ glBufferData(target, size, data, usage);
+}
+void MojoGLES2Impl::BufferSubData(GLenum target,
+ GLintptr offset,
+ GLsizeiptr size,
+ const void* data) {
+ MojoGLES2MakeCurrent(context_);
+ glBufferSubData(target, offset, size, data);
+}
+GLenum MojoGLES2Impl::CheckFramebufferStatus(GLenum target) {
+ MojoGLES2MakeCurrent(context_);
+ return glCheckFramebufferStatus(target);
+}
+void MojoGLES2Impl::Clear(GLbitfield mask) {
+ MojoGLES2MakeCurrent(context_);
+ glClear(mask);
+}
+void MojoGLES2Impl::ClearBufferfi(GLenum buffer,
+ GLint drawbuffers,
+ GLfloat depth,
+ GLint stencil) {
+ NOTREACHED() << "Unimplemented ClearBufferfi.";
+}
+void MojoGLES2Impl::ClearBufferfv(GLenum buffer,
+ GLint drawbuffers,
+ const GLfloat* value) {
+ NOTREACHED() << "Unimplemented ClearBufferfv.";
+}
+void MojoGLES2Impl::ClearBufferiv(GLenum buffer,
+ GLint drawbuffers,
+ const GLint* value) {
+ NOTREACHED() << "Unimplemented ClearBufferiv.";
+}
+void MojoGLES2Impl::ClearBufferuiv(GLenum buffer,
+ GLint drawbuffers,
+ const GLuint* value) {
+ NOTREACHED() << "Unimplemented ClearBufferuiv.";
+}
+void MojoGLES2Impl::ClearColor(GLclampf red,
+ GLclampf green,
+ GLclampf blue,
+ GLclampf alpha) {
+ MojoGLES2MakeCurrent(context_);
+ glClearColor(red, green, blue, alpha);
+}
+void MojoGLES2Impl::ClearDepthf(GLclampf depth) {
+ MojoGLES2MakeCurrent(context_);
+ glClearDepthf(depth);
+}
+void MojoGLES2Impl::ClearStencil(GLint s) {
+ MojoGLES2MakeCurrent(context_);
+ glClearStencil(s);
+}
+GLenum MojoGLES2Impl::ClientWaitSync(GLsync sync,
+ GLbitfield flags,
+ GLuint64 timeout) {
+ NOTREACHED() << "Unimplemented ClientWaitSync.";
+ return 0;
+}
+void MojoGLES2Impl::ColorMask(GLboolean red,
+ GLboolean green,
+ GLboolean blue,
+ GLboolean alpha) {
+ MojoGLES2MakeCurrent(context_);
+ glColorMask(red, green, blue, alpha);
+}
+void MojoGLES2Impl::CompileShader(GLuint shader) {
+ MojoGLES2MakeCurrent(context_);
+ glCompileShader(shader);
+}
+void MojoGLES2Impl::CompressedTexImage2D(GLenum target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLsizei imageSize,
+ const void* data) {
+ MojoGLES2MakeCurrent(context_);
+ glCompressedTexImage2D(target, level, internalformat, width, height, border,
+ imageSize, data);
+}
+void MojoGLES2Impl::CompressedTexSubImage2D(GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLsizei imageSize,
+ const void* data) {
+ MojoGLES2MakeCurrent(context_);
+ glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height,
+ format, imageSize, data);
+}
+void MojoGLES2Impl::CompressedTexImage3D(GLenum target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLsizei imageSize,
+ const void* data) {
+ NOTREACHED() << "Unimplemented CompressedTexImage3D.";
+}
+void MojoGLES2Impl::CompressedTexSubImage3D(GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLenum format,
+ GLsizei imageSize,
+ const void* data) {
+ NOTREACHED() << "Unimplemented CompressedTexSubImage3D.";
+}
+void MojoGLES2Impl::CopyBufferSubData(GLenum readtarget,
+ GLenum writetarget,
+ GLintptr readoffset,
+ GLintptr writeoffset,
+ GLsizeiptr size) {
+ NOTREACHED() << "Unimplemented CopyBufferSubData.";
+}
+void MojoGLES2Impl::CopyTexImage2D(GLenum target,
+ GLint level,
+ GLenum internalformat,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLint border) {
+ MojoGLES2MakeCurrent(context_);
+ glCopyTexImage2D(target, level, internalformat, x, y, width, height, border);
+}
+void MojoGLES2Impl::CopyTexSubImage2D(GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height) {
+ MojoGLES2MakeCurrent(context_);
+ glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
+}
+void MojoGLES2Impl::CopyTexSubImage3D(GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height) {
+ NOTREACHED() << "Unimplemented CopyTexSubImage3D.";
+}
+GLuint MojoGLES2Impl::CreateProgram() {
+ MojoGLES2MakeCurrent(context_);
+ return glCreateProgram();
+}
+GLuint MojoGLES2Impl::CreateShader(GLenum type) {
+ MojoGLES2MakeCurrent(context_);
+ return glCreateShader(type);
+}
+void MojoGLES2Impl::CullFace(GLenum mode) {
+ MojoGLES2MakeCurrent(context_);
+ glCullFace(mode);
+}
+void MojoGLES2Impl::DeleteBuffers(GLsizei n, const GLuint* buffers) {
+ MojoGLES2MakeCurrent(context_);
+ glDeleteBuffers(n, buffers);
+}
+void MojoGLES2Impl::DeleteFramebuffers(GLsizei n, const GLuint* framebuffers) {
+ MojoGLES2MakeCurrent(context_);
+ glDeleteFramebuffers(n, framebuffers);
+}
+void MojoGLES2Impl::DeleteProgram(GLuint program) {
+ MojoGLES2MakeCurrent(context_);
+ glDeleteProgram(program);
+}
+void MojoGLES2Impl::DeleteRenderbuffers(GLsizei n,
+ const GLuint* renderbuffers) {
+ MojoGLES2MakeCurrent(context_);
+ glDeleteRenderbuffers(n, renderbuffers);
+}
+void MojoGLES2Impl::DeleteSamplers(GLsizei n, const GLuint* samplers) {
+ NOTREACHED() << "Unimplemented DeleteSamplers.";
+}
+void MojoGLES2Impl::DeleteSync(GLsync sync) {
+ NOTREACHED() << "Unimplemented DeleteSync.";
+}
+void MojoGLES2Impl::DeleteShader(GLuint shader) {
+ MojoGLES2MakeCurrent(context_);
+ glDeleteShader(shader);
+}
+void MojoGLES2Impl::DeleteTextures(GLsizei n, const GLuint* textures) {
+ MojoGLES2MakeCurrent(context_);
+ glDeleteTextures(n, textures);
+}
+void MojoGLES2Impl::DeleteTransformFeedbacks(GLsizei n, const GLuint* ids) {
+ NOTREACHED() << "Unimplemented DeleteTransformFeedbacks.";
+}
+void MojoGLES2Impl::DepthFunc(GLenum func) {
+ MojoGLES2MakeCurrent(context_);
+ glDepthFunc(func);
+}
+void MojoGLES2Impl::DepthMask(GLboolean flag) {
+ MojoGLES2MakeCurrent(context_);
+ glDepthMask(flag);
+}
+void MojoGLES2Impl::DepthRangef(GLclampf zNear, GLclampf zFar) {
+ MojoGLES2MakeCurrent(context_);
+ glDepthRangef(zNear, zFar);
+}
+void MojoGLES2Impl::DetachShader(GLuint program, GLuint shader) {
+ MojoGLES2MakeCurrent(context_);
+ glDetachShader(program, shader);
+}
+void MojoGLES2Impl::Disable(GLenum cap) {
+ MojoGLES2MakeCurrent(context_);
+ glDisable(cap);
+}
+void MojoGLES2Impl::DisableVertexAttribArray(GLuint index) {
+ MojoGLES2MakeCurrent(context_);
+ glDisableVertexAttribArray(index);
+}
+void MojoGLES2Impl::DrawArrays(GLenum mode, GLint first, GLsizei count) {
+ MojoGLES2MakeCurrent(context_);
+ glDrawArrays(mode, first, count);
+}
+void MojoGLES2Impl::DrawElements(GLenum mode,
+ GLsizei count,
+ GLenum type,
+ const void* indices) {
+ MojoGLES2MakeCurrent(context_);
+ glDrawElements(mode, count, type, indices);
+}
+void MojoGLES2Impl::DrawRangeElements(GLenum mode,
+ GLuint start,
+ GLuint end,
+ GLsizei count,
+ GLenum type,
+ const void* indices) {
+ NOTREACHED() << "Unimplemented DrawRangeElements.";
+}
+void MojoGLES2Impl::Enable(GLenum cap) {
+ MojoGLES2MakeCurrent(context_);
+ glEnable(cap);
+}
+void MojoGLES2Impl::EnableVertexAttribArray(GLuint index) {
+ MojoGLES2MakeCurrent(context_);
+ glEnableVertexAttribArray(index);
+}
+GLsync MojoGLES2Impl::FenceSync(GLenum condition, GLbitfield flags) {
+ NOTREACHED() << "Unimplemented FenceSync.";
+ return 0;
+}
+void MojoGLES2Impl::Finish() {
+ MojoGLES2MakeCurrent(context_);
+ glFinish();
+}
+void MojoGLES2Impl::Flush() {
+ MojoGLES2MakeCurrent(context_);
+ glFlush();
+}
+void MojoGLES2Impl::FramebufferRenderbuffer(GLenum target,
+ GLenum attachment,
+ GLenum renderbuffertarget,
+ GLuint renderbuffer) {
+ MojoGLES2MakeCurrent(context_);
+ glFramebufferRenderbuffer(target, attachment, renderbuffertarget,
+ renderbuffer);
+}
+void MojoGLES2Impl::FramebufferTexture2D(GLenum target,
+ GLenum attachment,
+ GLenum textarget,
+ GLuint texture,
+ GLint level) {
+ MojoGLES2MakeCurrent(context_);
+ glFramebufferTexture2D(target, attachment, textarget, texture, level);
+}
+void MojoGLES2Impl::FramebufferTextureLayer(GLenum target,
+ GLenum attachment,
+ GLuint texture,
+ GLint level,
+ GLint layer) {
+ NOTREACHED() << "Unimplemented FramebufferTextureLayer.";
+}
+void MojoGLES2Impl::FrontFace(GLenum mode) {
+ MojoGLES2MakeCurrent(context_);
+ glFrontFace(mode);
+}
+void MojoGLES2Impl::GenBuffers(GLsizei n, GLuint* buffers) {
+ MojoGLES2MakeCurrent(context_);
+ glGenBuffers(n, buffers);
+}
+void MojoGLES2Impl::GenerateMipmap(GLenum target) {
+ MojoGLES2MakeCurrent(context_);
+ glGenerateMipmap(target);
+}
+void MojoGLES2Impl::GenFramebuffers(GLsizei n, GLuint* framebuffers) {
+ MojoGLES2MakeCurrent(context_);
+ glGenFramebuffers(n, framebuffers);
+}
+void MojoGLES2Impl::GenRenderbuffers(GLsizei n, GLuint* renderbuffers) {
+ MojoGLES2MakeCurrent(context_);
+ glGenRenderbuffers(n, renderbuffers);
+}
+void MojoGLES2Impl::GenSamplers(GLsizei n, GLuint* samplers) {
+ NOTREACHED() << "Unimplemented GenSamplers.";
+}
+void MojoGLES2Impl::GenTextures(GLsizei n, GLuint* textures) {
+ MojoGLES2MakeCurrent(context_);
+ glGenTextures(n, textures);
+}
+void MojoGLES2Impl::GenTransformFeedbacks(GLsizei n, GLuint* ids) {
+ NOTREACHED() << "Unimplemented GenTransformFeedbacks.";
+}
+void MojoGLES2Impl::GetActiveAttrib(GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei* length,
+ GLint* size,
+ GLenum* type,
+ char* name) {
+ MojoGLES2MakeCurrent(context_);
+ glGetActiveAttrib(program, index, bufsize, length, size, type, name);
+}
+void MojoGLES2Impl::GetActiveUniform(GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei* length,
+ GLint* size,
+ GLenum* type,
+ char* name) {
+ MojoGLES2MakeCurrent(context_);
+ glGetActiveUniform(program, index, bufsize, length, size, type, name);
+}
+void MojoGLES2Impl::GetActiveUniformBlockiv(GLuint program,
+ GLuint index,
+ GLenum pname,
+ GLint* params) {
+ NOTREACHED() << "Unimplemented GetActiveUniformBlockiv.";
+}
+void MojoGLES2Impl::GetActiveUniformBlockName(GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei* length,
+ char* name) {
+ NOTREACHED() << "Unimplemented GetActiveUniformBlockName.";
+}
+void MojoGLES2Impl::GetActiveUniformsiv(GLuint program,
+ GLsizei count,
+ const GLuint* indices,
+ GLenum pname,
+ GLint* params) {
+ NOTREACHED() << "Unimplemented GetActiveUniformsiv.";
+}
+void MojoGLES2Impl::GetAttachedShaders(GLuint program,
+ GLsizei maxcount,
+ GLsizei* count,
+ GLuint* shaders) {
+ MojoGLES2MakeCurrent(context_);
+ glGetAttachedShaders(program, maxcount, count, shaders);
+}
+GLint MojoGLES2Impl::GetAttribLocation(GLuint program, const char* name) {
+ MojoGLES2MakeCurrent(context_);
+ return glGetAttribLocation(program, name);
+}
+void MojoGLES2Impl::GetBooleanv(GLenum pname, GLboolean* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetBooleanv(pname, params);
+}
+void MojoGLES2Impl::GetBufferParameteri64v(GLenum target,
+ GLenum pname,
+ GLint64* params) {
+ NOTREACHED() << "Unimplemented GetBufferParameteri64v.";
+}
+void MojoGLES2Impl::GetBufferParameteriv(GLenum target,
+ GLenum pname,
+ GLint* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetBufferParameteriv(target, pname, params);
+}
+GLenum MojoGLES2Impl::GetError() {
+ MojoGLES2MakeCurrent(context_);
+ return glGetError();
+}
+void MojoGLES2Impl::GetFloatv(GLenum pname, GLfloat* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetFloatv(pname, params);
+}
+GLint MojoGLES2Impl::GetFragDataLocation(GLuint program, const char* name) {
+ NOTREACHED() << "Unimplemented GetFragDataLocation.";
+ return 0;
+}
+void MojoGLES2Impl::GetFramebufferAttachmentParameteriv(GLenum target,
+ GLenum attachment,
+ GLenum pname,
+ GLint* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetFramebufferAttachmentParameteriv(target, attachment, pname, params);
+}
+void MojoGLES2Impl::GetInteger64v(GLenum pname, GLint64* params) {
+ NOTREACHED() << "Unimplemented GetInteger64v.";
+}
+void MojoGLES2Impl::GetIntegeri_v(GLenum pname, GLuint index, GLint* data) {
+ NOTREACHED() << "Unimplemented GetIntegeri_v.";
+}
+void MojoGLES2Impl::GetInteger64i_v(GLenum pname, GLuint index, GLint64* data) {
+ NOTREACHED() << "Unimplemented GetInteger64i_v.";
+}
+void MojoGLES2Impl::GetIntegerv(GLenum pname, GLint* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetIntegerv(pname, params);
+}
+void MojoGLES2Impl::GetInternalformativ(GLenum target,
+ GLenum format,
+ GLenum pname,
+ GLsizei bufSize,
+ GLint* params) {
+ NOTREACHED() << "Unimplemented GetInternalformativ.";
+}
+void MojoGLES2Impl::GetProgramiv(GLuint program, GLenum pname, GLint* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetProgramiv(program, pname, params);
+}
+void MojoGLES2Impl::GetProgramInfoLog(GLuint program,
+ GLsizei bufsize,
+ GLsizei* length,
+ char* infolog) {
+ MojoGLES2MakeCurrent(context_);
+ glGetProgramInfoLog(program, bufsize, length, infolog);
+}
+void MojoGLES2Impl::GetRenderbufferParameteriv(GLenum target,
+ GLenum pname,
+ GLint* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetRenderbufferParameteriv(target, pname, params);
+}
+void MojoGLES2Impl::GetSamplerParameterfv(GLuint sampler,
+ GLenum pname,
+ GLfloat* params) {
+ NOTREACHED() << "Unimplemented GetSamplerParameterfv.";
+}
+void MojoGLES2Impl::GetSamplerParameteriv(GLuint sampler,
+ GLenum pname,
+ GLint* params) {
+ NOTREACHED() << "Unimplemented GetSamplerParameteriv.";
+}
+void MojoGLES2Impl::GetShaderiv(GLuint shader, GLenum pname, GLint* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetShaderiv(shader, pname, params);
+}
+void MojoGLES2Impl::GetShaderInfoLog(GLuint shader,
+ GLsizei bufsize,
+ GLsizei* length,
+ char* infolog) {
+ MojoGLES2MakeCurrent(context_);
+ glGetShaderInfoLog(shader, bufsize, length, infolog);
+}
+void MojoGLES2Impl::GetShaderPrecisionFormat(GLenum shadertype,
+ GLenum precisiontype,
+ GLint* range,
+ GLint* precision) {
+ MojoGLES2MakeCurrent(context_);
+ glGetShaderPrecisionFormat(shadertype, precisiontype, range, precision);
+}
+void MojoGLES2Impl::GetShaderSource(GLuint shader,
+ GLsizei bufsize,
+ GLsizei* length,
+ char* source) {
+ MojoGLES2MakeCurrent(context_);
+ glGetShaderSource(shader, bufsize, length, source);
+}
+const GLubyte* MojoGLES2Impl::GetString(GLenum name) {
+ MojoGLES2MakeCurrent(context_);
+ return glGetString(name);
+}
+const GLubyte* MojoGLES2Impl::GetStringi(GLenum name, GLuint index) {
+ NOTREACHED() << "Unimplemented GetStringi.";
+ return 0;
+}
+void MojoGLES2Impl::GetSynciv(GLsync sync,
+ GLenum pname,
+ GLsizei bufsize,
+ GLsizei* length,
+ GLint* values) {
+ NOTREACHED() << "Unimplemented GetSynciv.";
+}
+void MojoGLES2Impl::GetTexParameterfv(GLenum target,
+ GLenum pname,
+ GLfloat* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetTexParameterfv(target, pname, params);
+}
+void MojoGLES2Impl::GetTexParameteriv(GLenum target,
+ GLenum pname,
+ GLint* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetTexParameteriv(target, pname, params);
+}
+void MojoGLES2Impl::GetTransformFeedbackVarying(GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei* length,
+ GLsizei* size,
+ GLenum* type,
+ char* name) {
+ NOTREACHED() << "Unimplemented GetTransformFeedbackVarying.";
+}
+GLuint MojoGLES2Impl::GetUniformBlockIndex(GLuint program, const char* name) {
+ NOTREACHED() << "Unimplemented GetUniformBlockIndex.";
+ return 0;
+}
+void MojoGLES2Impl::GetUniformfv(GLuint program,
+ GLint location,
+ GLfloat* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetUniformfv(program, location, params);
+}
+void MojoGLES2Impl::GetUniformiv(GLuint program,
+ GLint location,
+ GLint* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetUniformiv(program, location, params);
+}
+void MojoGLES2Impl::GetUniformuiv(GLuint program,
+ GLint location,
+ GLuint* params) {
+ NOTREACHED() << "Unimplemented GetUniformuiv.";
+}
+void MojoGLES2Impl::GetUniformIndices(GLuint program,
+ GLsizei count,
+ const char* const* names,
+ GLuint* indices) {
+ NOTREACHED() << "Unimplemented GetUniformIndices.";
+}
+GLint MojoGLES2Impl::GetUniformLocation(GLuint program, const char* name) {
+ MojoGLES2MakeCurrent(context_);
+ return glGetUniformLocation(program, name);
+}
+void MojoGLES2Impl::GetVertexAttribfv(GLuint index,
+ GLenum pname,
+ GLfloat* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetVertexAttribfv(index, pname, params);
+}
+void MojoGLES2Impl::GetVertexAttribiv(GLuint index,
+ GLenum pname,
+ GLint* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetVertexAttribiv(index, pname, params);
+}
+void MojoGLES2Impl::GetVertexAttribIiv(GLuint index,
+ GLenum pname,
+ GLint* params) {
+ NOTREACHED() << "Unimplemented GetVertexAttribIiv.";
+}
+void MojoGLES2Impl::GetVertexAttribIuiv(GLuint index,
+ GLenum pname,
+ GLuint* params) {
+ NOTREACHED() << "Unimplemented GetVertexAttribIuiv.";
+}
+void MojoGLES2Impl::GetVertexAttribPointerv(GLuint index,
+ GLenum pname,
+ void** pointer) {
+ MojoGLES2MakeCurrent(context_);
+ glGetVertexAttribPointerv(index, pname, pointer);
+}
+void MojoGLES2Impl::Hint(GLenum target, GLenum mode) {
+ MojoGLES2MakeCurrent(context_);
+ glHint(target, mode);
+}
+void MojoGLES2Impl::InvalidateFramebuffer(GLenum target,
+ GLsizei count,
+ const GLenum* attachments) {
+ NOTREACHED() << "Unimplemented InvalidateFramebuffer.";
+}
+void MojoGLES2Impl::InvalidateSubFramebuffer(GLenum target,
+ GLsizei count,
+ const GLenum* attachments,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height) {
+ NOTREACHED() << "Unimplemented InvalidateSubFramebuffer.";
+}
+GLboolean MojoGLES2Impl::IsBuffer(GLuint buffer) {
+ MojoGLES2MakeCurrent(context_);
+ return glIsBuffer(buffer);
+}
+GLboolean MojoGLES2Impl::IsEnabled(GLenum cap) {
+ MojoGLES2MakeCurrent(context_);
+ return glIsEnabled(cap);
+}
+GLboolean MojoGLES2Impl::IsFramebuffer(GLuint framebuffer) {
+ MojoGLES2MakeCurrent(context_);
+ return glIsFramebuffer(framebuffer);
+}
+GLboolean MojoGLES2Impl::IsProgram(GLuint program) {
+ MojoGLES2MakeCurrent(context_);
+ return glIsProgram(program);
+}
+GLboolean MojoGLES2Impl::IsRenderbuffer(GLuint renderbuffer) {
+ MojoGLES2MakeCurrent(context_);
+ return glIsRenderbuffer(renderbuffer);
+}
+GLboolean MojoGLES2Impl::IsSampler(GLuint sampler) {
+ NOTREACHED() << "Unimplemented IsSampler.";
+ return 0;
+}
+GLboolean MojoGLES2Impl::IsShader(GLuint shader) {
+ MojoGLES2MakeCurrent(context_);
+ return glIsShader(shader);
+}
+GLboolean MojoGLES2Impl::IsSync(GLsync sync) {
+ NOTREACHED() << "Unimplemented IsSync.";
+ return 0;
+}
+GLboolean MojoGLES2Impl::IsTexture(GLuint texture) {
+ MojoGLES2MakeCurrent(context_);
+ return glIsTexture(texture);
+}
+GLboolean MojoGLES2Impl::IsTransformFeedback(GLuint transformfeedback) {
+ NOTREACHED() << "Unimplemented IsTransformFeedback.";
+ return 0;
+}
+void MojoGLES2Impl::LineWidth(GLfloat width) {
+ MojoGLES2MakeCurrent(context_);
+ glLineWidth(width);
+}
+void MojoGLES2Impl::LinkProgram(GLuint program) {
+ MojoGLES2MakeCurrent(context_);
+ glLinkProgram(program);
+}
+void MojoGLES2Impl::PauseTransformFeedback() {
+ NOTREACHED() << "Unimplemented PauseTransformFeedback.";
+}
+void MojoGLES2Impl::PixelStorei(GLenum pname, GLint param) {
+ MojoGLES2MakeCurrent(context_);
+ glPixelStorei(pname, param);
+}
+void MojoGLES2Impl::PolygonOffset(GLfloat factor, GLfloat units) {
+ MojoGLES2MakeCurrent(context_);
+ glPolygonOffset(factor, units);
+}
+void MojoGLES2Impl::ReadBuffer(GLenum src) {
+ NOTREACHED() << "Unimplemented ReadBuffer.";
+}
+void MojoGLES2Impl::ReadPixels(GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ void* pixels) {
+ MojoGLES2MakeCurrent(context_);
+ glReadPixels(x, y, width, height, format, type, pixels);
+}
+void MojoGLES2Impl::ReleaseShaderCompiler() {
+ MojoGLES2MakeCurrent(context_);
+ glReleaseShaderCompiler();
+}
+void MojoGLES2Impl::RenderbufferStorage(GLenum target,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height) {
+ MojoGLES2MakeCurrent(context_);
+ glRenderbufferStorage(target, internalformat, width, height);
+}
+void MojoGLES2Impl::ResumeTransformFeedback() {
+ NOTREACHED() << "Unimplemented ResumeTransformFeedback.";
+}
+void MojoGLES2Impl::SampleCoverage(GLclampf value, GLboolean invert) {
+ MojoGLES2MakeCurrent(context_);
+ glSampleCoverage(value, invert);
+}
+void MojoGLES2Impl::SamplerParameterf(GLuint sampler,
+ GLenum pname,
+ GLfloat param) {
+ NOTREACHED() << "Unimplemented SamplerParameterf.";
+}
+void MojoGLES2Impl::SamplerParameterfv(GLuint sampler,
+ GLenum pname,
+ const GLfloat* params) {
+ NOTREACHED() << "Unimplemented SamplerParameterfv.";
+}
+void MojoGLES2Impl::SamplerParameteri(GLuint sampler,
+ GLenum pname,
+ GLint param) {
+ NOTREACHED() << "Unimplemented SamplerParameteri.";
+}
+void MojoGLES2Impl::SamplerParameteriv(GLuint sampler,
+ GLenum pname,
+ const GLint* params) {
+ NOTREACHED() << "Unimplemented SamplerParameteriv.";
+}
+void MojoGLES2Impl::Scissor(GLint x, GLint y, GLsizei width, GLsizei height) {
+ MojoGLES2MakeCurrent(context_);
+ glScissor(x, y, width, height);
+}
+void MojoGLES2Impl::ShaderBinary(GLsizei n,
+ const GLuint* shaders,
+ GLenum binaryformat,
+ const void* binary,
+ GLsizei length) {
+ MojoGLES2MakeCurrent(context_);
+ glShaderBinary(n, shaders, binaryformat, binary, length);
+}
+void MojoGLES2Impl::ShaderSource(GLuint shader,
+ GLsizei count,
+ const GLchar* const* str,
+ const GLint* length) {
+ MojoGLES2MakeCurrent(context_);
+ glShaderSource(shader, count, str, length);
+}
+void MojoGLES2Impl::ShallowFinishCHROMIUM() {
+ MojoGLES2MakeCurrent(context_);
+ glShallowFinishCHROMIUM();
+}
+void MojoGLES2Impl::ShallowFlushCHROMIUM() {
+ MojoGLES2MakeCurrent(context_);
+ glShallowFlushCHROMIUM();
+}
+void MojoGLES2Impl::OrderingBarrierCHROMIUM() {
+ MojoGLES2MakeCurrent(context_);
+ glOrderingBarrierCHROMIUM();
+}
+void MojoGLES2Impl::StencilFunc(GLenum func, GLint ref, GLuint mask) {
+ MojoGLES2MakeCurrent(context_);
+ glStencilFunc(func, ref, mask);
+}
+void MojoGLES2Impl::StencilFuncSeparate(GLenum face,
+ GLenum func,
+ GLint ref,
+ GLuint mask) {
+ MojoGLES2MakeCurrent(context_);
+ glStencilFuncSeparate(face, func, ref, mask);
+}
+void MojoGLES2Impl::StencilMask(GLuint mask) {
+ MojoGLES2MakeCurrent(context_);
+ glStencilMask(mask);
+}
+void MojoGLES2Impl::StencilMaskSeparate(GLenum face, GLuint mask) {
+ MojoGLES2MakeCurrent(context_);
+ glStencilMaskSeparate(face, mask);
+}
+void MojoGLES2Impl::StencilOp(GLenum fail, GLenum zfail, GLenum zpass) {
+ MojoGLES2MakeCurrent(context_);
+ glStencilOp(fail, zfail, zpass);
+}
+void MojoGLES2Impl::StencilOpSeparate(GLenum face,
+ GLenum fail,
+ GLenum zfail,
+ GLenum zpass) {
+ MojoGLES2MakeCurrent(context_);
+ glStencilOpSeparate(face, fail, zfail, zpass);
+}
+void MojoGLES2Impl::TexImage2D(GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const void* pixels) {
+ MojoGLES2MakeCurrent(context_);
+ glTexImage2D(target, level, internalformat, width, height, border, format,
+ type, pixels);
+}
+void MojoGLES2Impl::TexImage3D(GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const void* pixels) {
+ NOTREACHED() << "Unimplemented TexImage3D.";
+}
+void MojoGLES2Impl::TexParameterf(GLenum target, GLenum pname, GLfloat param) {
+ MojoGLES2MakeCurrent(context_);
+ glTexParameterf(target, pname, param);
+}
+void MojoGLES2Impl::TexParameterfv(GLenum target,
+ GLenum pname,
+ const GLfloat* params) {
+ MojoGLES2MakeCurrent(context_);
+ glTexParameterfv(target, pname, params);
+}
+void MojoGLES2Impl::TexParameteri(GLenum target, GLenum pname, GLint param) {
+ MojoGLES2MakeCurrent(context_);
+ glTexParameteri(target, pname, param);
+}
+void MojoGLES2Impl::TexParameteriv(GLenum target,
+ GLenum pname,
+ const GLint* params) {
+ MojoGLES2MakeCurrent(context_);
+ glTexParameteriv(target, pname, params);
+}
+void MojoGLES2Impl::TexStorage3D(GLenum target,
+ GLsizei levels,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth) {
+ NOTREACHED() << "Unimplemented TexStorage3D.";
+}
+void MojoGLES2Impl::TexSubImage2D(GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ const void* pixels) {
+ MojoGLES2MakeCurrent(context_);
+ glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type,
+ pixels);
+}
+void MojoGLES2Impl::TexSubImage3D(GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLenum format,
+ GLenum type,
+ const void* pixels) {
+ NOTREACHED() << "Unimplemented TexSubImage3D.";
+}
+void MojoGLES2Impl::TransformFeedbackVaryings(GLuint program,
+ GLsizei count,
+ const char* const* varyings,
+ GLenum buffermode) {
+ NOTREACHED() << "Unimplemented TransformFeedbackVaryings.";
+}
+void MojoGLES2Impl::Uniform1f(GLint location, GLfloat x) {
+ MojoGLES2MakeCurrent(context_);
+ glUniform1f(location, x);
+}
+void MojoGLES2Impl::Uniform1fv(GLint location,
+ GLsizei count,
+ const GLfloat* v) {
+ MojoGLES2MakeCurrent(context_);
+ glUniform1fv(location, count, v);
+}
+void MojoGLES2Impl::Uniform1i(GLint location, GLint x) {
+ MojoGLES2MakeCurrent(context_);
+ glUniform1i(location, x);
+}
+void MojoGLES2Impl::Uniform1iv(GLint location, GLsizei count, const GLint* v) {
+ MojoGLES2MakeCurrent(context_);
+ glUniform1iv(location, count, v);
+}
+void MojoGLES2Impl::Uniform1ui(GLint location, GLuint x) {
+ NOTREACHED() << "Unimplemented Uniform1ui.";
+}
+void MojoGLES2Impl::Uniform1uiv(GLint location,
+ GLsizei count,
+ const GLuint* v) {
+ NOTREACHED() << "Unimplemented Uniform1uiv.";
+}
+void MojoGLES2Impl::Uniform2f(GLint location, GLfloat x, GLfloat y) {
+ MojoGLES2MakeCurrent(context_);
+ glUniform2f(location, x, y);
+}
+void MojoGLES2Impl::Uniform2fv(GLint location,
+ GLsizei count,
+ const GLfloat* v) {
+ MojoGLES2MakeCurrent(context_);
+ glUniform2fv(location, count, v);
+}
+void MojoGLES2Impl::Uniform2i(GLint location, GLint x, GLint y) {
+ MojoGLES2MakeCurrent(context_);
+ glUniform2i(location, x, y);
+}
+void MojoGLES2Impl::Uniform2iv(GLint location, GLsizei count, const GLint* v) {
+ MojoGLES2MakeCurrent(context_);
+ glUniform2iv(location, count, v);
+}
+void MojoGLES2Impl::Uniform2ui(GLint location, GLuint x, GLuint y) {
+ NOTREACHED() << "Unimplemented Uniform2ui.";
+}
+void MojoGLES2Impl::Uniform2uiv(GLint location,
+ GLsizei count,
+ const GLuint* v) {
+ NOTREACHED() << "Unimplemented Uniform2uiv.";
+}
+void MojoGLES2Impl::Uniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z) {
+ MojoGLES2MakeCurrent(context_);
+ glUniform3f(location, x, y, z);
+}
+void MojoGLES2Impl::Uniform3fv(GLint location,
+ GLsizei count,
+ const GLfloat* v) {
+ MojoGLES2MakeCurrent(context_);
+ glUniform3fv(location, count, v);
+}
+void MojoGLES2Impl::Uniform3i(GLint location, GLint x, GLint y, GLint z) {
+ MojoGLES2MakeCurrent(context_);
+ glUniform3i(location, x, y, z);
+}
+void MojoGLES2Impl::Uniform3iv(GLint location, GLsizei count, const GLint* v) {
+ MojoGLES2MakeCurrent(context_);
+ glUniform3iv(location, count, v);
+}
+void MojoGLES2Impl::Uniform3ui(GLint location, GLuint x, GLuint y, GLuint z) {
+ NOTREACHED() << "Unimplemented Uniform3ui.";
+}
+void MojoGLES2Impl::Uniform3uiv(GLint location,
+ GLsizei count,
+ const GLuint* v) {
+ NOTREACHED() << "Unimplemented Uniform3uiv.";
+}
+void MojoGLES2Impl::Uniform4f(GLint location,
+ GLfloat x,
+ GLfloat y,
+ GLfloat z,
+ GLfloat w) {
+ MojoGLES2MakeCurrent(context_);
+ glUniform4f(location, x, y, z, w);
+}
+void MojoGLES2Impl::Uniform4fv(GLint location,
+ GLsizei count,
+ const GLfloat* v) {
+ MojoGLES2MakeCurrent(context_);
+ glUniform4fv(location, count, v);
+}
+void MojoGLES2Impl::Uniform4i(GLint location,
+ GLint x,
+ GLint y,
+ GLint z,
+ GLint w) {
+ MojoGLES2MakeCurrent(context_);
+ glUniform4i(location, x, y, z, w);
+}
+void MojoGLES2Impl::Uniform4iv(GLint location, GLsizei count, const GLint* v) {
+ MojoGLES2MakeCurrent(context_);
+ glUniform4iv(location, count, v);
+}
+void MojoGLES2Impl::Uniform4ui(GLint location,
+ GLuint x,
+ GLuint y,
+ GLuint z,
+ GLuint w) {
+ NOTREACHED() << "Unimplemented Uniform4ui.";
+}
+void MojoGLES2Impl::Uniform4uiv(GLint location,
+ GLsizei count,
+ const GLuint* v) {
+ NOTREACHED() << "Unimplemented Uniform4uiv.";
+}
+void MojoGLES2Impl::UniformBlockBinding(GLuint program,
+ GLuint index,
+ GLuint binding) {
+ NOTREACHED() << "Unimplemented UniformBlockBinding.";
+}
+void MojoGLES2Impl::UniformMatrix2fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) {
+ MojoGLES2MakeCurrent(context_);
+ glUniformMatrix2fv(location, count, transpose, value);
+}
+void MojoGLES2Impl::UniformMatrix2x3fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) {
+ NOTREACHED() << "Unimplemented UniformMatrix2x3fv.";
+}
+void MojoGLES2Impl::UniformMatrix2x4fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) {
+ NOTREACHED() << "Unimplemented UniformMatrix2x4fv.";
+}
+void MojoGLES2Impl::UniformMatrix3fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) {
+ MojoGLES2MakeCurrent(context_);
+ glUniformMatrix3fv(location, count, transpose, value);
+}
+void MojoGLES2Impl::UniformMatrix3x2fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) {
+ NOTREACHED() << "Unimplemented UniformMatrix3x2fv.";
+}
+void MojoGLES2Impl::UniformMatrix3x4fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) {
+ NOTREACHED() << "Unimplemented UniformMatrix3x4fv.";
+}
+void MojoGLES2Impl::UniformMatrix4fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) {
+ MojoGLES2MakeCurrent(context_);
+ glUniformMatrix4fv(location, count, transpose, value);
+}
+void MojoGLES2Impl::UniformMatrix4x2fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) {
+ NOTREACHED() << "Unimplemented UniformMatrix4x2fv.";
+}
+void MojoGLES2Impl::UniformMatrix4x3fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) {
+ NOTREACHED() << "Unimplemented UniformMatrix4x3fv.";
+}
+void MojoGLES2Impl::UseProgram(GLuint program) {
+ MojoGLES2MakeCurrent(context_);
+ glUseProgram(program);
+}
+void MojoGLES2Impl::ValidateProgram(GLuint program) {
+ MojoGLES2MakeCurrent(context_);
+ glValidateProgram(program);
+}
+void MojoGLES2Impl::VertexAttrib1f(GLuint indx, GLfloat x) {
+ MojoGLES2MakeCurrent(context_);
+ glVertexAttrib1f(indx, x);
+}
+void MojoGLES2Impl::VertexAttrib1fv(GLuint indx, const GLfloat* values) {
+ MojoGLES2MakeCurrent(context_);
+ glVertexAttrib1fv(indx, values);
+}
+void MojoGLES2Impl::VertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) {
+ MojoGLES2MakeCurrent(context_);
+ glVertexAttrib2f(indx, x, y);
+}
+void MojoGLES2Impl::VertexAttrib2fv(GLuint indx, const GLfloat* values) {
+ MojoGLES2MakeCurrent(context_);
+ glVertexAttrib2fv(indx, values);
+}
+void MojoGLES2Impl::VertexAttrib3f(GLuint indx,
+ GLfloat x,
+ GLfloat y,
+ GLfloat z) {
+ MojoGLES2MakeCurrent(context_);
+ glVertexAttrib3f(indx, x, y, z);
+}
+void MojoGLES2Impl::VertexAttrib3fv(GLuint indx, const GLfloat* values) {
+ MojoGLES2MakeCurrent(context_);
+ glVertexAttrib3fv(indx, values);
+}
+void MojoGLES2Impl::VertexAttrib4f(GLuint indx,
+ GLfloat x,
+ GLfloat y,
+ GLfloat z,
+ GLfloat w) {
+ MojoGLES2MakeCurrent(context_);
+ glVertexAttrib4f(indx, x, y, z, w);
+}
+void MojoGLES2Impl::VertexAttrib4fv(GLuint indx, const GLfloat* values) {
+ MojoGLES2MakeCurrent(context_);
+ glVertexAttrib4fv(indx, values);
+}
+void MojoGLES2Impl::VertexAttribI4i(GLuint indx,
+ GLint x,
+ GLint y,
+ GLint z,
+ GLint w) {
+ NOTREACHED() << "Unimplemented VertexAttribI4i.";
+}
+void MojoGLES2Impl::VertexAttribI4iv(GLuint indx, const GLint* values) {
+ NOTREACHED() << "Unimplemented VertexAttribI4iv.";
+}
+void MojoGLES2Impl::VertexAttribI4ui(GLuint indx,
+ GLuint x,
+ GLuint y,
+ GLuint z,
+ GLuint w) {
+ NOTREACHED() << "Unimplemented VertexAttribI4ui.";
+}
+void MojoGLES2Impl::VertexAttribI4uiv(GLuint indx, const GLuint* values) {
+ NOTREACHED() << "Unimplemented VertexAttribI4uiv.";
+}
+void MojoGLES2Impl::VertexAttribIPointer(GLuint indx,
+ GLint size,
+ GLenum type,
+ GLsizei stride,
+ const void* ptr) {
+ NOTREACHED() << "Unimplemented VertexAttribIPointer.";
+}
+void MojoGLES2Impl::VertexAttribPointer(GLuint indx,
+ GLint size,
+ GLenum type,
+ GLboolean normalized,
+ GLsizei stride,
+ const void* ptr) {
+ MojoGLES2MakeCurrent(context_);
+ glVertexAttribPointer(indx, size, type, normalized, stride, ptr);
+}
+void MojoGLES2Impl::Viewport(GLint x, GLint y, GLsizei width, GLsizei height) {
+ MojoGLES2MakeCurrent(context_);
+ glViewport(x, y, width, height);
+}
+void MojoGLES2Impl::WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
+ NOTREACHED() << "Unimplemented WaitSync.";
+}
+void MojoGLES2Impl::BlitFramebufferCHROMIUM(GLint srcX0,
+ GLint srcY0,
+ GLint srcX1,
+ GLint srcY1,
+ GLint dstX0,
+ GLint dstY0,
+ GLint dstX1,
+ GLint dstY1,
+ GLbitfield mask,
+ GLenum filter) {
+ MojoGLES2MakeCurrent(context_);
+ glBlitFramebufferCHROMIUM(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1,
+ dstY1, mask, filter);
+}
+void MojoGLES2Impl::RenderbufferStorageMultisampleCHROMIUM(
+ GLenum target,
+ GLsizei samples,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height) {
+ MojoGLES2MakeCurrent(context_);
+ glRenderbufferStorageMultisampleCHROMIUM(target, samples, internalformat,
+ width, height);
+}
+void MojoGLES2Impl::RenderbufferStorageMultisampleEXT(GLenum target,
+ GLsizei samples,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height) {
+ MojoGLES2MakeCurrent(context_);
+ glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width,
+ height);
+}
+void MojoGLES2Impl::FramebufferTexture2DMultisampleEXT(GLenum target,
+ GLenum attachment,
+ GLenum textarget,
+ GLuint texture,
+ GLint level,
+ GLsizei samples) {
+ MojoGLES2MakeCurrent(context_);
+ glFramebufferTexture2DMultisampleEXT(target, attachment, textarget, texture,
+ level, samples);
+}
+void MojoGLES2Impl::TexStorage2DEXT(GLenum target,
+ GLsizei levels,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height) {
+ MojoGLES2MakeCurrent(context_);
+ glTexStorage2DEXT(target, levels, internalFormat, width, height);
+}
+void MojoGLES2Impl::GenQueriesEXT(GLsizei n, GLuint* queries) {
+ MojoGLES2MakeCurrent(context_);
+ glGenQueriesEXT(n, queries);
+}
+void MojoGLES2Impl::DeleteQueriesEXT(GLsizei n, const GLuint* queries) {
+ MojoGLES2MakeCurrent(context_);
+ glDeleteQueriesEXT(n, queries);
+}
+void MojoGLES2Impl::QueryCounterEXT(GLuint id, GLenum target) {
+ MojoGLES2MakeCurrent(context_);
+ glQueryCounterEXT(id, target);
+}
+GLboolean MojoGLES2Impl::IsQueryEXT(GLuint id) {
+ MojoGLES2MakeCurrent(context_);
+ return glIsQueryEXT(id);
+}
+void MojoGLES2Impl::BeginQueryEXT(GLenum target, GLuint id) {
+ MojoGLES2MakeCurrent(context_);
+ glBeginQueryEXT(target, id);
+}
+void MojoGLES2Impl::BeginTransformFeedback(GLenum primitivemode) {
+ NOTREACHED() << "Unimplemented BeginTransformFeedback.";
+}
+void MojoGLES2Impl::EndQueryEXT(GLenum target) {
+ MojoGLES2MakeCurrent(context_);
+ glEndQueryEXT(target);
+}
+void MojoGLES2Impl::EndTransformFeedback() {
+ NOTREACHED() << "Unimplemented EndTransformFeedback.";
+}
+void MojoGLES2Impl::GetQueryivEXT(GLenum target, GLenum pname, GLint* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetQueryivEXT(target, pname, params);
+}
+void MojoGLES2Impl::GetQueryObjectivEXT(GLuint id,
+ GLenum pname,
+ GLint* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetQueryObjectivEXT(id, pname, params);
+}
+void MojoGLES2Impl::GetQueryObjectuivEXT(GLuint id,
+ GLenum pname,
+ GLuint* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetQueryObjectuivEXT(id, pname, params);
+}
+void MojoGLES2Impl::GetQueryObjecti64vEXT(GLuint id,
+ GLenum pname,
+ GLint64* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetQueryObjecti64vEXT(id, pname, params);
+}
+void MojoGLES2Impl::GetQueryObjectui64vEXT(GLuint id,
+ GLenum pname,
+ GLuint64* params) {
+ MojoGLES2MakeCurrent(context_);
+ glGetQueryObjectui64vEXT(id, pname, params);
+}
+void MojoGLES2Impl::SetDisjointValueSyncCHROMIUM() {
+ MojoGLES2MakeCurrent(context_);
+ glSetDisjointValueSyncCHROMIUM();
+}
+void MojoGLES2Impl::InsertEventMarkerEXT(GLsizei length, const GLchar* marker) {
+ MojoGLES2MakeCurrent(context_);
+ glInsertEventMarkerEXT(length, marker);
+}
+void MojoGLES2Impl::PushGroupMarkerEXT(GLsizei length, const GLchar* marker) {
+ MojoGLES2MakeCurrent(context_);
+ glPushGroupMarkerEXT(length, marker);
+}
+void MojoGLES2Impl::PopGroupMarkerEXT() {
+ MojoGLES2MakeCurrent(context_);
+ glPopGroupMarkerEXT();
+}
+void MojoGLES2Impl::GenVertexArraysOES(GLsizei n, GLuint* arrays) {
+ MojoGLES2MakeCurrent(context_);
+ glGenVertexArraysOES(n, arrays);
+}
+void MojoGLES2Impl::DeleteVertexArraysOES(GLsizei n, const GLuint* arrays) {
+ MojoGLES2MakeCurrent(context_);
+ glDeleteVertexArraysOES(n, arrays);
+}
+GLboolean MojoGLES2Impl::IsVertexArrayOES(GLuint array) {
+ MojoGLES2MakeCurrent(context_);
+ return glIsVertexArrayOES(array);
+}
+void MojoGLES2Impl::BindVertexArrayOES(GLuint array) {
+ MojoGLES2MakeCurrent(context_);
+ glBindVertexArrayOES(array);
+}
+void MojoGLES2Impl::SwapBuffers() {
+ MojoGLES2MakeCurrent(context_);
+ glSwapBuffers();
+}
+GLuint MojoGLES2Impl::GetMaxValueInBufferCHROMIUM(GLuint buffer_id,
+ GLsizei count,
+ GLenum type,
+ GLuint offset) {
+ MojoGLES2MakeCurrent(context_);
+ return glGetMaxValueInBufferCHROMIUM(buffer_id, count, type, offset);
+}
+GLboolean MojoGLES2Impl::EnableFeatureCHROMIUM(const char* feature) {
+ MojoGLES2MakeCurrent(context_);
+ return glEnableFeatureCHROMIUM(feature);
+}
+void* MojoGLES2Impl::MapBufferCHROMIUM(GLuint target, GLenum access) {
+ MojoGLES2MakeCurrent(context_);
+ return glMapBufferCHROMIUM(target, access);
+}
+GLboolean MojoGLES2Impl::UnmapBufferCHROMIUM(GLuint target) {
+ MojoGLES2MakeCurrent(context_);
+ return glUnmapBufferCHROMIUM(target);
+}
+void* MojoGLES2Impl::MapBufferSubDataCHROMIUM(GLuint target,
+ GLintptr offset,
+ GLsizeiptr size,
+ GLenum access) {
+ MojoGLES2MakeCurrent(context_);
+ return glMapBufferSubDataCHROMIUM(target, offset, size, access);
+}
+void MojoGLES2Impl::UnmapBufferSubDataCHROMIUM(const void* mem) {
+ MojoGLES2MakeCurrent(context_);
+ glUnmapBufferSubDataCHROMIUM(mem);
+}
+void* MojoGLES2Impl::MapBufferRange(GLenum target,
+ GLintptr offset,
+ GLsizeiptr size,
+ GLbitfield access) {
+ NOTREACHED() << "Unimplemented MapBufferRange.";
+ return 0;
+}
+GLboolean MojoGLES2Impl::UnmapBuffer(GLenum target) {
+ NOTREACHED() << "Unimplemented UnmapBuffer.";
+ return 0;
+}
+void* MojoGLES2Impl::MapTexSubImage2DCHROMIUM(GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ GLenum access) {
+ MojoGLES2MakeCurrent(context_);
+ return glMapTexSubImage2DCHROMIUM(target, level, xoffset, yoffset, width,
+ height, format, type, access);
+}
+void MojoGLES2Impl::UnmapTexSubImage2DCHROMIUM(const void* mem) {
+ MojoGLES2MakeCurrent(context_);
+ glUnmapTexSubImage2DCHROMIUM(mem);
+}
+void MojoGLES2Impl::ResizeCHROMIUM(GLuint width,
+ GLuint height,
+ GLfloat scale_factor,
+ GLboolean alpha) {
+ MojoGLES2MakeCurrent(context_);
+ glResizeCHROMIUM(width, height, scale_factor, alpha);
+}
+const GLchar* MojoGLES2Impl::GetRequestableExtensionsCHROMIUM() {
+ MojoGLES2MakeCurrent(context_);
+ return glGetRequestableExtensionsCHROMIUM();
+}
+void MojoGLES2Impl::RequestExtensionCHROMIUM(const char* extension) {
+ MojoGLES2MakeCurrent(context_);
+ glRequestExtensionCHROMIUM(extension);
+}
+void MojoGLES2Impl::GetProgramInfoCHROMIUM(GLuint program,
+ GLsizei bufsize,
+ GLsizei* size,
+ void* info) {
+ MojoGLES2MakeCurrent(context_);
+ glGetProgramInfoCHROMIUM(program, bufsize, size, info);
+}
+void MojoGLES2Impl::GetUniformBlocksCHROMIUM(GLuint program,
+ GLsizei bufsize,
+ GLsizei* size,
+ void* info) {
+ NOTREACHED() << "Unimplemented GetUniformBlocksCHROMIUM.";
+}
+void MojoGLES2Impl::GetTransformFeedbackVaryingsCHROMIUM(GLuint program,
+ GLsizei bufsize,
+ GLsizei* size,
+ void* info) {
+ NOTREACHED() << "Unimplemented GetTransformFeedbackVaryingsCHROMIUM.";
+}
+void MojoGLES2Impl::GetUniformsES3CHROMIUM(GLuint program,
+ GLsizei bufsize,
+ GLsizei* size,
+ void* info) {
+ NOTREACHED() << "Unimplemented GetUniformsES3CHROMIUM.";
+}
+GLuint MojoGLES2Impl::CreateImageCHROMIUM(ClientBuffer buffer,
+ GLsizei width,
+ GLsizei height,
+ GLenum internalformat) {
+ MojoGLES2MakeCurrent(context_);
+ return glCreateImageCHROMIUM(buffer, width, height, internalformat);
+}
+void MojoGLES2Impl::DestroyImageCHROMIUM(GLuint image_id) {
+ MojoGLES2MakeCurrent(context_);
+ glDestroyImageCHROMIUM(image_id);
+}
+GLuint MojoGLES2Impl::CreateGpuMemoryBufferImageCHROMIUM(GLsizei width,
+ GLsizei height,
+ GLenum internalformat,
+ GLenum usage) {
+ MojoGLES2MakeCurrent(context_);
+ return glCreateGpuMemoryBufferImageCHROMIUM(width, height, internalformat,
+ usage);
+}
+void MojoGLES2Impl::GetTranslatedShaderSourceANGLE(GLuint shader,
+ GLsizei bufsize,
+ GLsizei* length,
+ char* source) {
+ MojoGLES2MakeCurrent(context_);
+ glGetTranslatedShaderSourceANGLE(shader, bufsize, length, source);
+}
+void MojoGLES2Impl::PostSubBufferCHROMIUM(GLint x,
+ GLint y,
+ GLint width,
+ GLint height) {
+ MojoGLES2MakeCurrent(context_);
+ glPostSubBufferCHROMIUM(x, y, width, height);
+}
+void MojoGLES2Impl::CopyTextureCHROMIUM(GLenum source_id,
+ GLenum dest_id,
+ GLint internalformat,
+ GLenum dest_type,
+ GLboolean unpack_flip_y,
+ GLboolean unpack_premultiply_alpha,
+ GLboolean unpack_unmultiply_alpha) {
+ MojoGLES2MakeCurrent(context_);
+ glCopyTextureCHROMIUM(source_id, dest_id, internalformat, dest_type,
+ unpack_flip_y, unpack_premultiply_alpha,
+ unpack_unmultiply_alpha);
+}
+void MojoGLES2Impl::CopySubTextureCHROMIUM(GLenum source_id,
+ GLenum dest_id,
+ GLint xoffset,
+ GLint yoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLboolean unpack_flip_y,
+ GLboolean unpack_premultiply_alpha,
+ GLboolean unpack_unmultiply_alpha) {
+ MojoGLES2MakeCurrent(context_);
+ glCopySubTextureCHROMIUM(source_id, dest_id, xoffset, yoffset, x, y, width,
+ height, unpack_flip_y, unpack_premultiply_alpha,
+ unpack_unmultiply_alpha);
+}
+void MojoGLES2Impl::CompressedCopyTextureCHROMIUM(GLenum source_id,
+ GLenum dest_id) {
+ MojoGLES2MakeCurrent(context_);
+ glCompressedCopyTextureCHROMIUM(source_id, dest_id);
+}
+void MojoGLES2Impl::DrawArraysInstancedANGLE(GLenum mode,
+ GLint first,
+ GLsizei count,
+ GLsizei primcount) {
+ MojoGLES2MakeCurrent(context_);
+ glDrawArraysInstancedANGLE(mode, first, count, primcount);
+}
+void MojoGLES2Impl::DrawElementsInstancedANGLE(GLenum mode,
+ GLsizei count,
+ GLenum type,
+ const void* indices,
+ GLsizei primcount) {
+ MojoGLES2MakeCurrent(context_);
+ glDrawElementsInstancedANGLE(mode, count, type, indices, primcount);
+}
+void MojoGLES2Impl::VertexAttribDivisorANGLE(GLuint index, GLuint divisor) {
+ MojoGLES2MakeCurrent(context_);
+ glVertexAttribDivisorANGLE(index, divisor);
+}
+void MojoGLES2Impl::GenMailboxCHROMIUM(GLbyte* mailbox) {
+ MojoGLES2MakeCurrent(context_);
+ glGenMailboxCHROMIUM(mailbox);
+}
+void MojoGLES2Impl::ProduceTextureCHROMIUM(GLenum target,
+ const GLbyte* mailbox) {
+ MojoGLES2MakeCurrent(context_);
+ glProduceTextureCHROMIUM(target, mailbox);
+}
+void MojoGLES2Impl::ProduceTextureDirectCHROMIUM(GLuint texture,
+ GLenum target,
+ const GLbyte* mailbox) {
+ MojoGLES2MakeCurrent(context_);
+ glProduceTextureDirectCHROMIUM(texture, target, mailbox);
+}
+void MojoGLES2Impl::ConsumeTextureCHROMIUM(GLenum target,
+ const GLbyte* mailbox) {
+ MojoGLES2MakeCurrent(context_);
+ glConsumeTextureCHROMIUM(target, mailbox);
+}
+GLuint MojoGLES2Impl::CreateAndConsumeTextureCHROMIUM(GLenum target,
+ const GLbyte* mailbox) {
+ MojoGLES2MakeCurrent(context_);
+ return glCreateAndConsumeTextureCHROMIUM(target, mailbox);
+}
+void MojoGLES2Impl::BindUniformLocationCHROMIUM(GLuint program,
+ GLint location,
+ const char* name) {
+ MojoGLES2MakeCurrent(context_);
+ glBindUniformLocationCHROMIUM(program, location, name);
+}
+void MojoGLES2Impl::BindTexImage2DCHROMIUM(GLenum target, GLint imageId) {
+ MojoGLES2MakeCurrent(context_);
+ glBindTexImage2DCHROMIUM(target, imageId);
+}
+void MojoGLES2Impl::ReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId) {
+ MojoGLES2MakeCurrent(context_);
+ glReleaseTexImage2DCHROMIUM(target, imageId);
+}
+void MojoGLES2Impl::TraceBeginCHROMIUM(const char* category_name,
+ const char* trace_name) {
+ MojoGLES2MakeCurrent(context_);
+ glTraceBeginCHROMIUM(category_name, trace_name);
+}
+void MojoGLES2Impl::TraceEndCHROMIUM() {
+ MojoGLES2MakeCurrent(context_);
+ glTraceEndCHROMIUM();
+}
+void MojoGLES2Impl::DiscardFramebufferEXT(GLenum target,
+ GLsizei count,
+ const GLenum* attachments) {
+ MojoGLES2MakeCurrent(context_);
+ glDiscardFramebufferEXT(target, count, attachments);
+}
+void MojoGLES2Impl::LoseContextCHROMIUM(GLenum current, GLenum other) {
+ MojoGLES2MakeCurrent(context_);
+ glLoseContextCHROMIUM(current, other);
+}
+GLuint64 MojoGLES2Impl::InsertFenceSyncCHROMIUM() {
+ MojoGLES2MakeCurrent(context_);
+ return glInsertFenceSyncCHROMIUM();
+}
+void MojoGLES2Impl::GenSyncTokenCHROMIUM(GLuint64 fence_sync,
+ GLbyte* sync_token) {
+ MojoGLES2MakeCurrent(context_);
+ glGenSyncTokenCHROMIUM(fence_sync, sync_token);
+}
+void MojoGLES2Impl::GenUnverifiedSyncTokenCHROMIUM(GLuint64 fence_sync,
+ GLbyte* sync_token) {
+ MojoGLES2MakeCurrent(context_);
+ glGenUnverifiedSyncTokenCHROMIUM(fence_sync, sync_token);
+}
+void MojoGLES2Impl::VerifySyncTokensCHROMIUM(GLbyte** sync_tokens,
+ GLsizei count) {
+ MojoGLES2MakeCurrent(context_);
+ glVerifySyncTokensCHROMIUM(sync_tokens, count);
+}
+void MojoGLES2Impl::WaitSyncTokenCHROMIUM(const GLbyte* sync_token) {
+ MojoGLES2MakeCurrent(context_);
+ glWaitSyncTokenCHROMIUM(sync_token);
+}
+void MojoGLES2Impl::DrawBuffersEXT(GLsizei count, const GLenum* bufs) {
+ MojoGLES2MakeCurrent(context_);
+ glDrawBuffersEXT(count, bufs);
+}
+void MojoGLES2Impl::DiscardBackbufferCHROMIUM() {
+ MojoGLES2MakeCurrent(context_);
+ glDiscardBackbufferCHROMIUM();
+}
+void MojoGLES2Impl::ScheduleOverlayPlaneCHROMIUM(GLint plane_z_order,
+ GLenum plane_transform,
+ GLuint overlay_texture_id,
+ GLint bounds_x,
+ GLint bounds_y,
+ GLint bounds_width,
+ GLint bounds_height,
+ GLfloat uv_x,
+ GLfloat uv_y,
+ GLfloat uv_width,
+ GLfloat uv_height) {
+ MojoGLES2MakeCurrent(context_);
+ glScheduleOverlayPlaneCHROMIUM(
+ plane_z_order, plane_transform, overlay_texture_id, bounds_x, bounds_y,
+ bounds_width, bounds_height, uv_x, uv_y, uv_width, uv_height);
+}
+void MojoGLES2Impl::ScheduleCALayerCHROMIUM(GLuint contents_texture_id,
+ const GLfloat* contents_rect,
+ GLfloat opacity,
+ GLuint background_color,
+ GLuint edge_aa_mask,
+ const GLfloat* bounds_rect,
+ GLboolean is_clipped,
+ const GLfloat* clip_rect,
+ GLint sorting_context_id,
+ const GLfloat* transform,
+ GLuint filter) {
+ MojoGLES2MakeCurrent(context_);
+ glScheduleCALayerCHROMIUM(contents_texture_id, contents_rect, opacity,
+ background_color, edge_aa_mask, bounds_rect,
+ is_clipped, clip_rect, sorting_context_id,
+ transform, filter);
+}
+void MojoGLES2Impl::CommitOverlayPlanesCHROMIUM() {
+ MojoGLES2MakeCurrent(context_);
+ glCommitOverlayPlanesCHROMIUM();
+}
+void MojoGLES2Impl::SwapInterval(GLint interval) {
+ MojoGLES2MakeCurrent(context_);
+ glSwapInterval(interval);
+}
+void MojoGLES2Impl::FlushDriverCachesCHROMIUM() {
+ MojoGLES2MakeCurrent(context_);
+ glFlushDriverCachesCHROMIUM();
+}
+GLuint MojoGLES2Impl::GetLastFlushIdCHROMIUM() {
+ MojoGLES2MakeCurrent(context_);
+ return glGetLastFlushIdCHROMIUM();
+}
+void MojoGLES2Impl::MatrixLoadfCHROMIUM(GLenum matrixMode, const GLfloat* m) {
+ MojoGLES2MakeCurrent(context_);
+ glMatrixLoadfCHROMIUM(matrixMode, m);
+}
+void MojoGLES2Impl::MatrixLoadIdentityCHROMIUM(GLenum matrixMode) {
+ MojoGLES2MakeCurrent(context_);
+ glMatrixLoadIdentityCHROMIUM(matrixMode);
+}
+GLuint MojoGLES2Impl::GenPathsCHROMIUM(GLsizei range) {
+ MojoGLES2MakeCurrent(context_);
+ return glGenPathsCHROMIUM(range);
+}
+void MojoGLES2Impl::DeletePathsCHROMIUM(GLuint path, GLsizei range) {
+ MojoGLES2MakeCurrent(context_);
+ glDeletePathsCHROMIUM(path, range);
+}
+GLboolean MojoGLES2Impl::IsPathCHROMIUM(GLuint path) {
+ MojoGLES2MakeCurrent(context_);
+ return glIsPathCHROMIUM(path);
+}
+void MojoGLES2Impl::PathCommandsCHROMIUM(GLuint path,
+ GLsizei numCommands,
+ const GLubyte* commands,
+ GLsizei numCoords,
+ GLenum coordType,
+ const GLvoid* coords) {
+ MojoGLES2MakeCurrent(context_);
+ glPathCommandsCHROMIUM(path, numCommands, commands, numCoords, coordType,
+ coords);
+}
+void MojoGLES2Impl::PathParameterfCHROMIUM(GLuint path,
+ GLenum pname,
+ GLfloat value) {
+ MojoGLES2MakeCurrent(context_);
+ glPathParameterfCHROMIUM(path, pname, value);
+}
+void MojoGLES2Impl::PathParameteriCHROMIUM(GLuint path,
+ GLenum pname,
+ GLint value) {
+ MojoGLES2MakeCurrent(context_);
+ glPathParameteriCHROMIUM(path, pname, value);
+}
+void MojoGLES2Impl::PathStencilFuncCHROMIUM(GLenum func,
+ GLint ref,
+ GLuint mask) {
+ MojoGLES2MakeCurrent(context_);
+ glPathStencilFuncCHROMIUM(func, ref, mask);
+}
+void MojoGLES2Impl::StencilFillPathCHROMIUM(GLuint path,
+ GLenum fillMode,
+ GLuint mask) {
+ MojoGLES2MakeCurrent(context_);
+ glStencilFillPathCHROMIUM(path, fillMode, mask);
+}
+void MojoGLES2Impl::StencilStrokePathCHROMIUM(GLuint path,
+ GLint reference,
+ GLuint mask) {
+ MojoGLES2MakeCurrent(context_);
+ glStencilStrokePathCHROMIUM(path, reference, mask);
+}
+void MojoGLES2Impl::CoverFillPathCHROMIUM(GLuint path, GLenum coverMode) {
+ MojoGLES2MakeCurrent(context_);
+ glCoverFillPathCHROMIUM(path, coverMode);
+}
+void MojoGLES2Impl::CoverStrokePathCHROMIUM(GLuint path, GLenum coverMode) {
+ MojoGLES2MakeCurrent(context_);
+ glCoverStrokePathCHROMIUM(path, coverMode);
+}
+void MojoGLES2Impl::StencilThenCoverFillPathCHROMIUM(GLuint path,
+ GLenum fillMode,
+ GLuint mask,
+ GLenum coverMode) {
+ MojoGLES2MakeCurrent(context_);
+ glStencilThenCoverFillPathCHROMIUM(path, fillMode, mask, coverMode);
+}
+void MojoGLES2Impl::StencilThenCoverStrokePathCHROMIUM(GLuint path,
+ GLint reference,
+ GLuint mask,
+ GLenum coverMode) {
+ MojoGLES2MakeCurrent(context_);
+ glStencilThenCoverStrokePathCHROMIUM(path, reference, mask, coverMode);
+}
+void MojoGLES2Impl::StencilFillPathInstancedCHROMIUM(
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const GLvoid* paths,
+ GLuint pathBase,
+ GLenum fillMode,
+ GLuint mask,
+ GLenum transformType,
+ const GLfloat* transformValues) {
+ MojoGLES2MakeCurrent(context_);
+ glStencilFillPathInstancedCHROMIUM(numPaths, pathNameType, paths, pathBase,
+ fillMode, mask, transformType,
+ transformValues);
+}
+void MojoGLES2Impl::StencilStrokePathInstancedCHROMIUM(
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const GLvoid* paths,
+ GLuint pathBase,
+ GLint reference,
+ GLuint mask,
+ GLenum transformType,
+ const GLfloat* transformValues) {
+ MojoGLES2MakeCurrent(context_);
+ glStencilStrokePathInstancedCHROMIUM(numPaths, pathNameType, paths, pathBase,
+ reference, mask, transformType,
+ transformValues);
+}
+void MojoGLES2Impl::CoverFillPathInstancedCHROMIUM(
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const GLvoid* paths,
+ GLuint pathBase,
+ GLenum coverMode,
+ GLenum transformType,
+ const GLfloat* transformValues) {
+ MojoGLES2MakeCurrent(context_);
+ glCoverFillPathInstancedCHROMIUM(numPaths, pathNameType, paths, pathBase,
+ coverMode, transformType, transformValues);
+}
+void MojoGLES2Impl::CoverStrokePathInstancedCHROMIUM(
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const GLvoid* paths,
+ GLuint pathBase,
+ GLenum coverMode,
+ GLenum transformType,
+ const GLfloat* transformValues) {
+ MojoGLES2MakeCurrent(context_);
+ glCoverStrokePathInstancedCHROMIUM(numPaths, pathNameType, paths, pathBase,
+ coverMode, transformType, transformValues);
+}
+void MojoGLES2Impl::StencilThenCoverFillPathInstancedCHROMIUM(
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const GLvoid* paths,
+ GLuint pathBase,
+ GLenum fillMode,
+ GLuint mask,
+ GLenum coverMode,
+ GLenum transformType,
+ const GLfloat* transformValues) {
+ MojoGLES2MakeCurrent(context_);
+ glStencilThenCoverFillPathInstancedCHROMIUM(
+ numPaths, pathNameType, paths, pathBase, fillMode, mask, coverMode,
+ transformType, transformValues);
+}
+void MojoGLES2Impl::StencilThenCoverStrokePathInstancedCHROMIUM(
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const GLvoid* paths,
+ GLuint pathBase,
+ GLint reference,
+ GLuint mask,
+ GLenum coverMode,
+ GLenum transformType,
+ const GLfloat* transformValues) {
+ MojoGLES2MakeCurrent(context_);
+ glStencilThenCoverStrokePathInstancedCHROMIUM(
+ numPaths, pathNameType, paths, pathBase, reference, mask, coverMode,
+ transformType, transformValues);
+}
+void MojoGLES2Impl::BindFragmentInputLocationCHROMIUM(GLuint program,
+ GLint location,
+ const char* name) {
+ MojoGLES2MakeCurrent(context_);
+ glBindFragmentInputLocationCHROMIUM(program, location, name);
+}
+void MojoGLES2Impl::ProgramPathFragmentInputGenCHROMIUM(GLuint program,
+ GLint location,
+ GLenum genMode,
+ GLint components,
+ const GLfloat* coeffs) {
+ MojoGLES2MakeCurrent(context_);
+ glProgramPathFragmentInputGenCHROMIUM(program, location, genMode, components,
+ coeffs);
+}
+void MojoGLES2Impl::CoverageModulationCHROMIUM(GLenum components) {
+ MojoGLES2MakeCurrent(context_);
+ glCoverageModulationCHROMIUM(components);
+}
+GLenum MojoGLES2Impl::GetGraphicsResetStatusKHR() {
+ MojoGLES2MakeCurrent(context_);
+ return glGetGraphicsResetStatusKHR();
+}
+void MojoGLES2Impl::BlendBarrierKHR() {
+ MojoGLES2MakeCurrent(context_);
+ glBlendBarrierKHR();
+}
+void MojoGLES2Impl::ApplyScreenSpaceAntialiasingCHROMIUM() {
+ MojoGLES2MakeCurrent(context_);
+ glApplyScreenSpaceAntialiasingCHROMIUM();
+}
+void MojoGLES2Impl::BindFragDataLocationIndexedEXT(GLuint program,
+ GLuint colorNumber,
+ GLuint index,
+ const char* name) {
+ MojoGLES2MakeCurrent(context_);
+ glBindFragDataLocationIndexedEXT(program, colorNumber, index, name);
+}
+void MojoGLES2Impl::BindFragDataLocationEXT(GLuint program,
+ GLuint colorNumber,
+ const char* name) {
+ MojoGLES2MakeCurrent(context_);
+ glBindFragDataLocationEXT(program, colorNumber, name);
+}
+GLint MojoGLES2Impl::GetFragDataIndexEXT(GLuint program, const char* name) {
+ MojoGLES2MakeCurrent(context_);
+ return glGetFragDataIndexEXT(program, name);
+}
+void MojoGLES2Impl::UniformMatrix4fvStreamTextureMatrixCHROMIUM(
+ GLint location,
+ GLboolean transpose,
+ const GLfloat* default_value) {
+ MojoGLES2MakeCurrent(context_);
+ glUniformMatrix4fvStreamTextureMatrixCHROMIUM(location, transpose,
+ default_value);
+}
+
+} // namespace mojo
diff --git a/mojo/gpu/mojo_gles2_impl_autogen.h b/mojo/gpu/mojo_gles2_impl_autogen.h
new file mode 100644
index 0000000..fd0e5bd
--- /dev/null
+++ b/mojo/gpu/mojo_gles2_impl_autogen.h
@@ -0,0 +1,890 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is auto-generated from
+// gpu/command_buffer/build_gles2_cmd_buffer.py
+// It's formatted by clang-format using chromium coding style:
+// clang-format -i -style=chromium filename
+// DO NOT EDIT!
+
+// This file is included by gles2_interface.h to declare the
+// GL api functions.
+#ifndef MOJO_GPU_MOJO_GLES2_IMPL_AUTOGEN_H_
+#define MOJO_GPU_MOJO_GLES2_IMPL_AUTOGEN_H_
+
+#include <memory>
+
+#include "gpu/command_buffer/client/gles2_interface.h"
+#include "mojo/public/c/gles2/gles2.h"
+
+namespace mojo {
+
+class MojoGLES2Impl : public gpu::gles2::GLES2Interface {
+ public:
+ explicit MojoGLES2Impl(MojoGLES2Context context) { context_ = context; }
+ ~MojoGLES2Impl() override {}
+ void ActiveTexture(GLenum texture) override;
+ void AttachShader(GLuint program, GLuint shader) override;
+ void BindAttribLocation(GLuint program,
+ GLuint index,
+ const char* name) override;
+ void BindBuffer(GLenum target, GLuint buffer) override;
+ void BindBufferBase(GLenum target, GLuint index, GLuint buffer) override;
+ void BindBufferRange(GLenum target,
+ GLuint index,
+ GLuint buffer,
+ GLintptr offset,
+ GLsizeiptr size) override;
+ void BindFramebuffer(GLenum target, GLuint framebuffer) override;
+ void BindRenderbuffer(GLenum target, GLuint renderbuffer) override;
+ void BindSampler(GLuint unit, GLuint sampler) override;
+ void BindTexture(GLenum target, GLuint texture) override;
+ void BindTransformFeedback(GLenum target, GLuint transformfeedback) override;
+ void BlendColor(GLclampf red,
+ GLclampf green,
+ GLclampf blue,
+ GLclampf alpha) override;
+ void BlendEquation(GLenum mode) override;
+ void BlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) override;
+ void BlendFunc(GLenum sfactor, GLenum dfactor) override;
+ void BlendFuncSeparate(GLenum srcRGB,
+ GLenum dstRGB,
+ GLenum srcAlpha,
+ GLenum dstAlpha) override;
+ void BufferData(GLenum target,
+ GLsizeiptr size,
+ const void* data,
+ GLenum usage) override;
+ void BufferSubData(GLenum target,
+ GLintptr offset,
+ GLsizeiptr size,
+ const void* data) override;
+ GLenum CheckFramebufferStatus(GLenum target) override;
+ void Clear(GLbitfield mask) override;
+ void ClearBufferfi(GLenum buffer,
+ GLint drawbuffers,
+ GLfloat depth,
+ GLint stencil) override;
+ void ClearBufferfv(GLenum buffer,
+ GLint drawbuffers,
+ const GLfloat* value) override;
+ void ClearBufferiv(GLenum buffer,
+ GLint drawbuffers,
+ const GLint* value) override;
+ void ClearBufferuiv(GLenum buffer,
+ GLint drawbuffers,
+ const GLuint* value) override;
+ void ClearColor(GLclampf red,
+ GLclampf green,
+ GLclampf blue,
+ GLclampf alpha) override;
+ void ClearDepthf(GLclampf depth) override;
+ void ClearStencil(GLint s) override;
+ GLenum ClientWaitSync(GLsync sync,
+ GLbitfield flags,
+ GLuint64 timeout) override;
+ void ColorMask(GLboolean red,
+ GLboolean green,
+ GLboolean blue,
+ GLboolean alpha) override;
+ void CompileShader(GLuint shader) override;
+ void CompressedTexImage2D(GLenum target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLsizei imageSize,
+ const void* data) override;
+ void CompressedTexSubImage2D(GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLsizei imageSize,
+ const void* data) override;
+ void CompressedTexImage3D(GLenum target,
+ GLint level,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLsizei imageSize,
+ const void* data) override;
+ void CompressedTexSubImage3D(GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLenum format,
+ GLsizei imageSize,
+ const void* data) override;
+ void CopyBufferSubData(GLenum readtarget,
+ GLenum writetarget,
+ GLintptr readoffset,
+ GLintptr writeoffset,
+ GLsizeiptr size) override;
+ void CopyTexImage2D(GLenum target,
+ GLint level,
+ GLenum internalformat,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLint border) override;
+ void CopyTexSubImage2D(GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height) override;
+ void CopyTexSubImage3D(GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height) override;
+ GLuint CreateProgram() override;
+ GLuint CreateShader(GLenum type) override;
+ void CullFace(GLenum mode) override;
+ void DeleteBuffers(GLsizei n, const GLuint* buffers) override;
+ void DeleteFramebuffers(GLsizei n, const GLuint* framebuffers) override;
+ void DeleteProgram(GLuint program) override;
+ void DeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) override;
+ void DeleteSamplers(GLsizei n, const GLuint* samplers) override;
+ void DeleteSync(GLsync sync) override;
+ void DeleteShader(GLuint shader) override;
+ void DeleteTextures(GLsizei n, const GLuint* textures) override;
+ void DeleteTransformFeedbacks(GLsizei n, const GLuint* ids) override;
+ void DepthFunc(GLenum func) override;
+ void DepthMask(GLboolean flag) override;
+ void DepthRangef(GLclampf zNear, GLclampf zFar) override;
+ void DetachShader(GLuint program, GLuint shader) override;
+ void Disable(GLenum cap) override;
+ void DisableVertexAttribArray(GLuint index) override;
+ void DrawArrays(GLenum mode, GLint first, GLsizei count) override;
+ void DrawElements(GLenum mode,
+ GLsizei count,
+ GLenum type,
+ const void* indices) override;
+ void DrawRangeElements(GLenum mode,
+ GLuint start,
+ GLuint end,
+ GLsizei count,
+ GLenum type,
+ const void* indices) override;
+ void Enable(GLenum cap) override;
+ void EnableVertexAttribArray(GLuint index) override;
+ GLsync FenceSync(GLenum condition, GLbitfield flags) override;
+ void Finish() override;
+ void Flush() override;
+ void FramebufferRenderbuffer(GLenum target,
+ GLenum attachment,
+ GLenum renderbuffertarget,
+ GLuint renderbuffer) override;
+ void FramebufferTexture2D(GLenum target,
+ GLenum attachment,
+ GLenum textarget,
+ GLuint texture,
+ GLint level) override;
+ void FramebufferTextureLayer(GLenum target,
+ GLenum attachment,
+ GLuint texture,
+ GLint level,
+ GLint layer) override;
+ void FrontFace(GLenum mode) override;
+ void GenBuffers(GLsizei n, GLuint* buffers) override;
+ void GenerateMipmap(GLenum target) override;
+ void GenFramebuffers(GLsizei n, GLuint* framebuffers) override;
+ void GenRenderbuffers(GLsizei n, GLuint* renderbuffers) override;
+ void GenSamplers(GLsizei n, GLuint* samplers) override;
+ void GenTextures(GLsizei n, GLuint* textures) override;
+ void GenTransformFeedbacks(GLsizei n, GLuint* ids) override;
+ void GetActiveAttrib(GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei* length,
+ GLint* size,
+ GLenum* type,
+ char* name) override;
+ void GetActiveUniform(GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei* length,
+ GLint* size,
+ GLenum* type,
+ char* name) override;
+ void GetActiveUniformBlockiv(GLuint program,
+ GLuint index,
+ GLenum pname,
+ GLint* params) override;
+ void GetActiveUniformBlockName(GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei* length,
+ char* name) override;
+ void GetActiveUniformsiv(GLuint program,
+ GLsizei count,
+ const GLuint* indices,
+ GLenum pname,
+ GLint* params) override;
+ void GetAttachedShaders(GLuint program,
+ GLsizei maxcount,
+ GLsizei* count,
+ GLuint* shaders) override;
+ GLint GetAttribLocation(GLuint program, const char* name) override;
+ void GetBooleanv(GLenum pname, GLboolean* params) override;
+ void GetBufferParameteri64v(GLenum target,
+ GLenum pname,
+ GLint64* params) override;
+ void GetBufferParameteriv(GLenum target,
+ GLenum pname,
+ GLint* params) override;
+ GLenum GetError() override;
+ void GetFloatv(GLenum pname, GLfloat* params) override;
+ GLint GetFragDataLocation(GLuint program, const char* name) override;
+ void GetFramebufferAttachmentParameteriv(GLenum target,
+ GLenum attachment,
+ GLenum pname,
+ GLint* params) override;
+ void GetInteger64v(GLenum pname, GLint64* params) override;
+ void GetIntegeri_v(GLenum pname, GLuint index, GLint* data) override;
+ void GetInteger64i_v(GLenum pname, GLuint index, GLint64* data) override;
+ void GetIntegerv(GLenum pname, GLint* params) override;
+ void GetInternalformativ(GLenum target,
+ GLenum format,
+ GLenum pname,
+ GLsizei bufSize,
+ GLint* params) override;
+ void GetProgramiv(GLuint program, GLenum pname, GLint* params) override;
+ void GetProgramInfoLog(GLuint program,
+ GLsizei bufsize,
+ GLsizei* length,
+ char* infolog) override;
+ void GetRenderbufferParameteriv(GLenum target,
+ GLenum pname,
+ GLint* params) override;
+ void GetSamplerParameterfv(GLuint sampler,
+ GLenum pname,
+ GLfloat* params) override;
+ void GetSamplerParameteriv(GLuint sampler,
+ GLenum pname,
+ GLint* params) override;
+ void GetShaderiv(GLuint shader, GLenum pname, GLint* params) override;
+ void GetShaderInfoLog(GLuint shader,
+ GLsizei bufsize,
+ GLsizei* length,
+ char* infolog) override;
+ void GetShaderPrecisionFormat(GLenum shadertype,
+ GLenum precisiontype,
+ GLint* range,
+ GLint* precision) override;
+ void GetShaderSource(GLuint shader,
+ GLsizei bufsize,
+ GLsizei* length,
+ char* source) override;
+ const GLubyte* GetString(GLenum name) override;
+ const GLubyte* GetStringi(GLenum name, GLuint index) override;
+ void GetSynciv(GLsync sync,
+ GLenum pname,
+ GLsizei bufsize,
+ GLsizei* length,
+ GLint* values) override;
+ void GetTexParameterfv(GLenum target, GLenum pname, GLfloat* params) override;
+ void GetTexParameteriv(GLenum target, GLenum pname, GLint* params) override;
+ void GetTransformFeedbackVarying(GLuint program,
+ GLuint index,
+ GLsizei bufsize,
+ GLsizei* length,
+ GLsizei* size,
+ GLenum* type,
+ char* name) override;
+ GLuint GetUniformBlockIndex(GLuint program, const char* name) override;
+ void GetUniformfv(GLuint program, GLint location, GLfloat* params) override;
+ void GetUniformiv(GLuint program, GLint location, GLint* params) override;
+ void GetUniformuiv(GLuint program, GLint location, GLuint* params) override;
+ void GetUniformIndices(GLuint program,
+ GLsizei count,
+ const char* const* names,
+ GLuint* indices) override;
+ GLint GetUniformLocation(GLuint program, const char* name) override;
+ void GetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params) override;
+ void GetVertexAttribiv(GLuint index, GLenum pname, GLint* params) override;
+ void GetVertexAttribIiv(GLuint index, GLenum pname, GLint* params) override;
+ void GetVertexAttribIuiv(GLuint index, GLenum pname, GLuint* params) override;
+ void GetVertexAttribPointerv(GLuint index,
+ GLenum pname,
+ void** pointer) override;
+ void Hint(GLenum target, GLenum mode) override;
+ void InvalidateFramebuffer(GLenum target,
+ GLsizei count,
+ const GLenum* attachments) override;
+ void InvalidateSubFramebuffer(GLenum target,
+ GLsizei count,
+ const GLenum* attachments,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height) override;
+ GLboolean IsBuffer(GLuint buffer) override;
+ GLboolean IsEnabled(GLenum cap) override;
+ GLboolean IsFramebuffer(GLuint framebuffer) override;
+ GLboolean IsProgram(GLuint program) override;
+ GLboolean IsRenderbuffer(GLuint renderbuffer) override;
+ GLboolean IsSampler(GLuint sampler) override;
+ GLboolean IsShader(GLuint shader) override;
+ GLboolean IsSync(GLsync sync) override;
+ GLboolean IsTexture(GLuint texture) override;
+ GLboolean IsTransformFeedback(GLuint transformfeedback) override;
+ void LineWidth(GLfloat width) override;
+ void LinkProgram(GLuint program) override;
+ void PauseTransformFeedback() override;
+ void PixelStorei(GLenum pname, GLint param) override;
+ void PolygonOffset(GLfloat factor, GLfloat units) override;
+ void ReadBuffer(GLenum src) override;
+ void ReadPixels(GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ void* pixels) override;
+ void ReleaseShaderCompiler() override;
+ void RenderbufferStorage(GLenum target,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height) override;
+ void ResumeTransformFeedback() override;
+ void SampleCoverage(GLclampf value, GLboolean invert) override;
+ void SamplerParameterf(GLuint sampler, GLenum pname, GLfloat param) override;
+ void SamplerParameterfv(GLuint sampler,
+ GLenum pname,
+ const GLfloat* params) override;
+ void SamplerParameteri(GLuint sampler, GLenum pname, GLint param) override;
+ void SamplerParameteriv(GLuint sampler,
+ GLenum pname,
+ const GLint* params) override;
+ void Scissor(GLint x, GLint y, GLsizei width, GLsizei height) override;
+ void ShaderBinary(GLsizei n,
+ const GLuint* shaders,
+ GLenum binaryformat,
+ const void* binary,
+ GLsizei length) override;
+ void ShaderSource(GLuint shader,
+ GLsizei count,
+ const GLchar* const* str,
+ const GLint* length) override;
+ void ShallowFinishCHROMIUM() override;
+ void ShallowFlushCHROMIUM() override;
+ void OrderingBarrierCHROMIUM() override;
+ void StencilFunc(GLenum func, GLint ref, GLuint mask) override;
+ void StencilFuncSeparate(GLenum face,
+ GLenum func,
+ GLint ref,
+ GLuint mask) override;
+ void StencilMask(GLuint mask) override;
+ void StencilMaskSeparate(GLenum face, GLuint mask) override;
+ void StencilOp(GLenum fail, GLenum zfail, GLenum zpass) override;
+ void StencilOpSeparate(GLenum face,
+ GLenum fail,
+ GLenum zfail,
+ GLenum zpass) override;
+ void TexImage2D(GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const void* pixels) override;
+ void TexImage3D(GLenum target,
+ GLint level,
+ GLint internalformat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLint border,
+ GLenum format,
+ GLenum type,
+ const void* pixels) override;
+ void TexParameterf(GLenum target, GLenum pname, GLfloat param) override;
+ void TexParameterfv(GLenum target,
+ GLenum pname,
+ const GLfloat* params) override;
+ void TexParameteri(GLenum target, GLenum pname, GLint param) override;
+ void TexParameteriv(GLenum target,
+ GLenum pname,
+ const GLint* params) override;
+ void TexStorage3D(GLenum target,
+ GLsizei levels,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth) override;
+ void TexSubImage2D(GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ const void* pixels) override;
+ void TexSubImage3D(GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLint zoffset,
+ GLsizei width,
+ GLsizei height,
+ GLsizei depth,
+ GLenum format,
+ GLenum type,
+ const void* pixels) override;
+ void TransformFeedbackVaryings(GLuint program,
+ GLsizei count,
+ const char* const* varyings,
+ GLenum buffermode) override;
+ void Uniform1f(GLint location, GLfloat x) override;
+ void Uniform1fv(GLint location, GLsizei count, const GLfloat* v) override;
+ void Uniform1i(GLint location, GLint x) override;
+ void Uniform1iv(GLint location, GLsizei count, const GLint* v) override;
+ void Uniform1ui(GLint location, GLuint x) override;
+ void Uniform1uiv(GLint location, GLsizei count, const GLuint* v) override;
+ void Uniform2f(GLint location, GLfloat x, GLfloat y) override;
+ void Uniform2fv(GLint location, GLsizei count, const GLfloat* v) override;
+ void Uniform2i(GLint location, GLint x, GLint y) override;
+ void Uniform2iv(GLint location, GLsizei count, const GLint* v) override;
+ void Uniform2ui(GLint location, GLuint x, GLuint y) override;
+ void Uniform2uiv(GLint location, GLsizei count, const GLuint* v) override;
+ void Uniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z) override;
+ void Uniform3fv(GLint location, GLsizei count, const GLfloat* v) override;
+ void Uniform3i(GLint location, GLint x, GLint y, GLint z) override;
+ void Uniform3iv(GLint location, GLsizei count, const GLint* v) override;
+ void Uniform3ui(GLint location, GLuint x, GLuint y, GLuint z) override;
+ void Uniform3uiv(GLint location, GLsizei count, const GLuint* v) override;
+ void Uniform4f(GLint location,
+ GLfloat x,
+ GLfloat y,
+ GLfloat z,
+ GLfloat w) override;
+ void Uniform4fv(GLint location, GLsizei count, const GLfloat* v) override;
+ void Uniform4i(GLint location, GLint x, GLint y, GLint z, GLint w) override;
+ void Uniform4iv(GLint location, GLsizei count, const GLint* v) override;
+ void Uniform4ui(GLint location,
+ GLuint x,
+ GLuint y,
+ GLuint z,
+ GLuint w) override;
+ void Uniform4uiv(GLint location, GLsizei count, const GLuint* v) override;
+ void UniformBlockBinding(GLuint program,
+ GLuint index,
+ GLuint binding) override;
+ void UniformMatrix2fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) override;
+ void UniformMatrix2x3fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) override;
+ void UniformMatrix2x4fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) override;
+ void UniformMatrix3fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) override;
+ void UniformMatrix3x2fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) override;
+ void UniformMatrix3x4fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) override;
+ void UniformMatrix4fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) override;
+ void UniformMatrix4x2fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) override;
+ void UniformMatrix4x3fv(GLint location,
+ GLsizei count,
+ GLboolean transpose,
+ const GLfloat* value) override;
+ void UseProgram(GLuint program) override;
+ void ValidateProgram(GLuint program) override;
+ void VertexAttrib1f(GLuint indx, GLfloat x) override;
+ void VertexAttrib1fv(GLuint indx, const GLfloat* values) override;
+ void VertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) override;
+ void VertexAttrib2fv(GLuint indx, const GLfloat* values) override;
+ void VertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z) override;
+ void VertexAttrib3fv(GLuint indx, const GLfloat* values) override;
+ void VertexAttrib4f(GLuint indx,
+ GLfloat x,
+ GLfloat y,
+ GLfloat z,
+ GLfloat w) override;
+ void VertexAttrib4fv(GLuint indx, const GLfloat* values) override;
+ void VertexAttribI4i(GLuint indx,
+ GLint x,
+ GLint y,
+ GLint z,
+ GLint w) override;
+ void VertexAttribI4iv(GLuint indx, const GLint* values) override;
+ void VertexAttribI4ui(GLuint indx,
+ GLuint x,
+ GLuint y,
+ GLuint z,
+ GLuint w) override;
+ void VertexAttribI4uiv(GLuint indx, const GLuint* values) override;
+ void VertexAttribIPointer(GLuint indx,
+ GLint size,
+ GLenum type,
+ GLsizei stride,
+ const void* ptr) override;
+ void VertexAttribPointer(GLuint indx,
+ GLint size,
+ GLenum type,
+ GLboolean normalized,
+ GLsizei stride,
+ const void* ptr) override;
+ void Viewport(GLint x, GLint y, GLsizei width, GLsizei height) override;
+ void WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) override;
+ void BlitFramebufferCHROMIUM(GLint srcX0,
+ GLint srcY0,
+ GLint srcX1,
+ GLint srcY1,
+ GLint dstX0,
+ GLint dstY0,
+ GLint dstX1,
+ GLint dstY1,
+ GLbitfield mask,
+ GLenum filter) override;
+ void RenderbufferStorageMultisampleCHROMIUM(GLenum target,
+ GLsizei samples,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height) override;
+ void RenderbufferStorageMultisampleEXT(GLenum target,
+ GLsizei samples,
+ GLenum internalformat,
+ GLsizei width,
+ GLsizei height) override;
+ void FramebufferTexture2DMultisampleEXT(GLenum target,
+ GLenum attachment,
+ GLenum textarget,
+ GLuint texture,
+ GLint level,
+ GLsizei samples) override;
+ void TexStorage2DEXT(GLenum target,
+ GLsizei levels,
+ GLenum internalFormat,
+ GLsizei width,
+ GLsizei height) override;
+ void GenQueriesEXT(GLsizei n, GLuint* queries) override;
+ void DeleteQueriesEXT(GLsizei n, const GLuint* queries) override;
+ void QueryCounterEXT(GLuint id, GLenum target) override;
+ GLboolean IsQueryEXT(GLuint id) override;
+ void BeginQueryEXT(GLenum target, GLuint id) override;
+ void BeginTransformFeedback(GLenum primitivemode) override;
+ void EndQueryEXT(GLenum target) override;
+ void EndTransformFeedback() override;
+ void GetQueryivEXT(GLenum target, GLenum pname, GLint* params) override;
+ void GetQueryObjectivEXT(GLuint id, GLenum pname, GLint* params) override;
+ void GetQueryObjectuivEXT(GLuint id, GLenum pname, GLuint* params) override;
+ void GetQueryObjecti64vEXT(GLuint id, GLenum pname, GLint64* params) override;
+ void GetQueryObjectui64vEXT(GLuint id,
+ GLenum pname,
+ GLuint64* params) override;
+ void SetDisjointValueSyncCHROMIUM() override;
+ void InsertEventMarkerEXT(GLsizei length, const GLchar* marker) override;
+ void PushGroupMarkerEXT(GLsizei length, const GLchar* marker) override;
+ void PopGroupMarkerEXT() override;
+ void GenVertexArraysOES(GLsizei n, GLuint* arrays) override;
+ void DeleteVertexArraysOES(GLsizei n, const GLuint* arrays) override;
+ GLboolean IsVertexArrayOES(GLuint array) override;
+ void BindVertexArrayOES(GLuint array) override;
+ void SwapBuffers() override;
+ GLuint GetMaxValueInBufferCHROMIUM(GLuint buffer_id,
+ GLsizei count,
+ GLenum type,
+ GLuint offset) override;
+ GLboolean EnableFeatureCHROMIUM(const char* feature) override;
+ void* MapBufferCHROMIUM(GLuint target, GLenum access) override;
+ GLboolean UnmapBufferCHROMIUM(GLuint target) override;
+ void* MapBufferSubDataCHROMIUM(GLuint target,
+ GLintptr offset,
+ GLsizeiptr size,
+ GLenum access) override;
+ void UnmapBufferSubDataCHROMIUM(const void* mem) override;
+ void* MapBufferRange(GLenum target,
+ GLintptr offset,
+ GLsizeiptr size,
+ GLbitfield access) override;
+ GLboolean UnmapBuffer(GLenum target) override;
+ void* MapTexSubImage2DCHROMIUM(GLenum target,
+ GLint level,
+ GLint xoffset,
+ GLint yoffset,
+ GLsizei width,
+ GLsizei height,
+ GLenum format,
+ GLenum type,
+ GLenum access) override;
+ void UnmapTexSubImage2DCHROMIUM(const void* mem) override;
+ void ResizeCHROMIUM(GLuint width,
+ GLuint height,
+ GLfloat scale_factor,
+ GLboolean alpha) override;
+ const GLchar* GetRequestableExtensionsCHROMIUM() override;
+ void RequestExtensionCHROMIUM(const char* extension) override;
+ void GetProgramInfoCHROMIUM(GLuint program,
+ GLsizei bufsize,
+ GLsizei* size,
+ void* info) override;
+ void GetUniformBlocksCHROMIUM(GLuint program,
+ GLsizei bufsize,
+ GLsizei* size,
+ void* info) override;
+ void GetTransformFeedbackVaryingsCHROMIUM(GLuint program,
+ GLsizei bufsize,
+ GLsizei* size,
+ void* info) override;
+ void GetUniformsES3CHROMIUM(GLuint program,
+ GLsizei bufsize,
+ GLsizei* size,
+ void* info) override;
+ GLuint CreateImageCHROMIUM(ClientBuffer buffer,
+ GLsizei width,
+ GLsizei height,
+ GLenum internalformat) override;
+ void DestroyImageCHROMIUM(GLuint image_id) override;
+ GLuint CreateGpuMemoryBufferImageCHROMIUM(GLsizei width,
+ GLsizei height,
+ GLenum internalformat,
+ GLenum usage) override;
+ void GetTranslatedShaderSourceANGLE(GLuint shader,
+ GLsizei bufsize,
+ GLsizei* length,
+ char* source) override;
+ void PostSubBufferCHROMIUM(GLint x,
+ GLint y,
+ GLint width,
+ GLint height) override;
+ void CopyTextureCHROMIUM(GLenum source_id,
+ GLenum dest_id,
+ GLint internalformat,
+ GLenum dest_type,
+ GLboolean unpack_flip_y,
+ GLboolean unpack_premultiply_alpha,
+ GLboolean unpack_unmultiply_alpha) override;
+ void CopySubTextureCHROMIUM(GLenum source_id,
+ GLenum dest_id,
+ GLint xoffset,
+ GLint yoffset,
+ GLint x,
+ GLint y,
+ GLsizei width,
+ GLsizei height,
+ GLboolean unpack_flip_y,
+ GLboolean unpack_premultiply_alpha,
+ GLboolean unpack_unmultiply_alpha) override;
+ void CompressedCopyTextureCHROMIUM(GLenum source_id, GLenum dest_id) override;
+ void DrawArraysInstancedANGLE(GLenum mode,
+ GLint first,
+ GLsizei count,
+ GLsizei primcount) override;
+ void DrawElementsInstancedANGLE(GLenum mode,
+ GLsizei count,
+ GLenum type,
+ const void* indices,
+ GLsizei primcount) override;
+ void VertexAttribDivisorANGLE(GLuint index, GLuint divisor) override;
+ void GenMailboxCHROMIUM(GLbyte* mailbox) override;
+ void ProduceTextureCHROMIUM(GLenum target, const GLbyte* mailbox) override;
+ void ProduceTextureDirectCHROMIUM(GLuint texture,
+ GLenum target,
+ const GLbyte* mailbox) override;
+ void ConsumeTextureCHROMIUM(GLenum target, const GLbyte* mailbox) override;
+ GLuint CreateAndConsumeTextureCHROMIUM(GLenum target,
+ const GLbyte* mailbox) override;
+ void BindUniformLocationCHROMIUM(GLuint program,
+ GLint location,
+ const char* name) override;
+ void BindTexImage2DCHROMIUM(GLenum target, GLint imageId) override;
+ void ReleaseTexImage2DCHROMIUM(GLenum target, GLint imageId) override;
+ void TraceBeginCHROMIUM(const char* category_name,
+ const char* trace_name) override;
+ void TraceEndCHROMIUM() override;
+ void DiscardFramebufferEXT(GLenum target,
+ GLsizei count,
+ const GLenum* attachments) override;
+ void LoseContextCHROMIUM(GLenum current, GLenum other) override;
+ GLuint64 InsertFenceSyncCHROMIUM() override;
+ void GenSyncTokenCHROMIUM(GLuint64 fence_sync, GLbyte* sync_token) override;
+ void GenUnverifiedSyncTokenCHROMIUM(GLuint64 fence_sync,
+ GLbyte* sync_token) override;
+ void VerifySyncTokensCHROMIUM(GLbyte** sync_tokens, GLsizei count) override;
+ void WaitSyncTokenCHROMIUM(const GLbyte* sync_token) override;
+ void DrawBuffersEXT(GLsizei count, const GLenum* bufs) override;
+ void DiscardBackbufferCHROMIUM() override;
+ void ScheduleOverlayPlaneCHROMIUM(GLint plane_z_order,
+ GLenum plane_transform,
+ GLuint overlay_texture_id,
+ GLint bounds_x,
+ GLint bounds_y,
+ GLint bounds_width,
+ GLint bounds_height,
+ GLfloat uv_x,
+ GLfloat uv_y,
+ GLfloat uv_width,
+ GLfloat uv_height) override;
+ void ScheduleCALayerCHROMIUM(GLuint contents_texture_id,
+ const GLfloat* contents_rect,
+ GLfloat opacity,
+ GLuint background_color,
+ GLuint edge_aa_mask,
+ const GLfloat* bounds_rect,
+ GLboolean is_clipped,
+ const GLfloat* clip_rect,
+ GLint sorting_context_id,
+ const GLfloat* transform,
+ GLuint filter) override;
+ void CommitOverlayPlanesCHROMIUM() override;
+ void SwapInterval(GLint interval) override;
+ void FlushDriverCachesCHROMIUM() override;
+ GLuint GetLastFlushIdCHROMIUM() override;
+ void MatrixLoadfCHROMIUM(GLenum matrixMode, const GLfloat* m) override;
+ void MatrixLoadIdentityCHROMIUM(GLenum matrixMode) override;
+ GLuint GenPathsCHROMIUM(GLsizei range) override;
+ void DeletePathsCHROMIUM(GLuint path, GLsizei range) override;
+ GLboolean IsPathCHROMIUM(GLuint path) override;
+ void PathCommandsCHROMIUM(GLuint path,
+ GLsizei numCommands,
+ const GLubyte* commands,
+ GLsizei numCoords,
+ GLenum coordType,
+ const GLvoid* coords) override;
+ void PathParameterfCHROMIUM(GLuint path,
+ GLenum pname,
+ GLfloat value) override;
+ void PathParameteriCHROMIUM(GLuint path, GLenum pname, GLint value) override;
+ void PathStencilFuncCHROMIUM(GLenum func, GLint ref, GLuint mask) override;
+ void StencilFillPathCHROMIUM(GLuint path,
+ GLenum fillMode,
+ GLuint mask) override;
+ void StencilStrokePathCHROMIUM(GLuint path,
+ GLint reference,
+ GLuint mask) override;
+ void CoverFillPathCHROMIUM(GLuint path, GLenum coverMode) override;
+ void CoverStrokePathCHROMIUM(GLuint path, GLenum coverMode) override;
+ void StencilThenCoverFillPathCHROMIUM(GLuint path,
+ GLenum fillMode,
+ GLuint mask,
+ GLenum coverMode) override;
+ void StencilThenCoverStrokePathCHROMIUM(GLuint path,
+ GLint reference,
+ GLuint mask,
+ GLenum coverMode) override;
+ void StencilFillPathInstancedCHROMIUM(
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const GLvoid* paths,
+ GLuint pathBase,
+ GLenum fillMode,
+ GLuint mask,
+ GLenum transformType,
+ const GLfloat* transformValues) override;
+ void StencilStrokePathInstancedCHROMIUM(
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const GLvoid* paths,
+ GLuint pathBase,
+ GLint reference,
+ GLuint mask,
+ GLenum transformType,
+ const GLfloat* transformValues) override;
+ void CoverFillPathInstancedCHROMIUM(GLsizei numPaths,
+ GLenum pathNameType,
+ const GLvoid* paths,
+ GLuint pathBase,
+ GLenum coverMode,
+ GLenum transformType,
+ const GLfloat* transformValues) override;
+ void CoverStrokePathInstancedCHROMIUM(
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const GLvoid* paths,
+ GLuint pathBase,
+ GLenum coverMode,
+ GLenum transformType,
+ const GLfloat* transformValues) override;
+ void StencilThenCoverFillPathInstancedCHROMIUM(
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const GLvoid* paths,
+ GLuint pathBase,
+ GLenum fillMode,
+ GLuint mask,
+ GLenum coverMode,
+ GLenum transformType,
+ const GLfloat* transformValues) override;
+ void StencilThenCoverStrokePathInstancedCHROMIUM(
+ GLsizei numPaths,
+ GLenum pathNameType,
+ const GLvoid* paths,
+ GLuint pathBase,
+ GLint reference,
+ GLuint mask,
+ GLenum coverMode,
+ GLenum transformType,
+ const GLfloat* transformValues) override;
+ void BindFragmentInputLocationCHROMIUM(GLuint program,
+ GLint location,
+ const char* name) override;
+ void ProgramPathFragmentInputGenCHROMIUM(GLuint program,
+ GLint location,
+ GLenum genMode,
+ GLint components,
+ const GLfloat* coeffs) override;
+ void CoverageModulationCHROMIUM(GLenum components) override;
+ GLenum GetGraphicsResetStatusKHR() override;
+ void BlendBarrierKHR() override;
+ void ApplyScreenSpaceAntialiasingCHROMIUM() override;
+ void BindFragDataLocationIndexedEXT(GLuint program,
+ GLuint colorNumber,
+ GLuint index,
+ const char* name) override;
+ void BindFragDataLocationEXT(GLuint program,
+ GLuint colorNumber,
+ const char* name) override;
+ GLint GetFragDataIndexEXT(GLuint program, const char* name) override;
+ void UniformMatrix4fvStreamTextureMatrixCHROMIUM(
+ GLint location,
+ GLboolean transpose,
+ const GLfloat* default_value) override;
+
+ private:
+ MojoGLES2Context context_;
+};
+
+} // namespace mojo
+#endif // MOJO_GPU_MOJO_GLES2_IMPL_AUTOGEN_H_
diff --git a/mojo/message_pump/BUILD.gn b/mojo/message_pump/BUILD.gn
new file mode 100644
index 0000000..e1ae4af
--- /dev/null
+++ b/mojo/message_pump/BUILD.gn
@@ -0,0 +1,24 @@
+# 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.
+
+import("//testing/test.gni")
+
+component("message_pump") {
+ sources = [
+ "handle_watcher.cc",
+ "handle_watcher.h",
+ "message_pump_mojo.cc",
+ "message_pump_mojo.h",
+ "message_pump_mojo_handler.h",
+ "time_helper.cc",
+ "time_helper.h",
+ ]
+
+ defines = [ "MOJO_MESSAGE_PUMP_IMPLEMENTATION" ]
+
+ public_deps = [
+ "//base",
+ "//mojo/public/cpp/system",
+ ]
+}
diff --git a/mojo/message_pump/handle_watcher.cc b/mojo/message_pump/handle_watcher.cc
new file mode 100644
index 0000000..7f6f561
--- /dev/null
+++ b/mojo/message_pump/handle_watcher.cc
@@ -0,0 +1,480 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/message_pump/handle_watcher.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+
+#include "base/atomic_sequence_num.h"
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "mojo/message_pump/message_pump_mojo.h"
+#include "mojo/message_pump/message_pump_mojo_handler.h"
+#include "mojo/message_pump/time_helper.h"
+#include "mojo/public/c/system/message_pipe.h"
+
+namespace mojo {
+namespace common {
+
+typedef int WatcherID;
+
+namespace {
+
+const char kWatcherThreadName[] = "handle-watcher-thread";
+
+base::TimeTicks MojoDeadlineToTimeTicks(MojoDeadline deadline) {
+ return deadline == MOJO_DEADLINE_INDEFINITE ? base::TimeTicks() :
+ internal::NowTicks() + base::TimeDelta::FromMicroseconds(deadline);
+}
+
+// Tracks the data for a single call to Start().
+struct WatchData {
+ WatchData()
+ : id(0), handle_signals(MOJO_HANDLE_SIGNAL_NONE), task_runner(NULL) {}
+
+ WatcherID id;
+ Handle handle;
+ MojoHandleSignals handle_signals;
+ base::TimeTicks deadline;
+ base::Callback<void(MojoResult)> callback;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner;
+};
+
+// WatcherBackend --------------------------------------------------------------
+
+// WatcherBackend is responsible for managing the requests and interacting with
+// MessagePumpMojo. All access (outside of creation/destruction) is done on the
+// thread WatcherThreadManager creates.
+class WatcherBackend : public MessagePumpMojoHandler {
+ public:
+ WatcherBackend();
+ ~WatcherBackend() override;
+
+ void StartWatching(const WatchData& data);
+ void StopWatching(WatcherID watcher_id);
+
+ private:
+ typedef std::map<Handle, WatchData> HandleToWatchDataMap;
+
+ // Invoked when a handle needs to be removed and notified.
+ void RemoveAndNotify(const Handle& handle, MojoResult result);
+
+ // Searches through |handle_to_data_| for |watcher_id|. Returns true if found
+ // and sets |handle| to the Handle. Returns false if not a known id.
+ bool GetMojoHandleByWatcherID(WatcherID watcher_id, Handle* handle) const;
+
+ // MessagePumpMojoHandler overrides:
+ void OnHandleReady(const Handle& handle) override;
+ void OnHandleError(const Handle& handle, MojoResult result) override;
+
+ // Maps from assigned id to WatchData.
+ HandleToWatchDataMap handle_to_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(WatcherBackend);
+};
+
+WatcherBackend::WatcherBackend() {
+}
+
+WatcherBackend::~WatcherBackend() {
+}
+
+void WatcherBackend::StartWatching(const WatchData& data) {
+ RemoveAndNotify(data.handle, MOJO_RESULT_CANCELLED);
+
+ DCHECK_EQ(0u, handle_to_data_.count(data.handle));
+
+ handle_to_data_[data.handle] = data;
+ MessagePumpMojo::current()->AddHandler(this, data.handle,
+ data.handle_signals,
+ data.deadline);
+}
+
+void WatcherBackend::StopWatching(WatcherID watcher_id) {
+ // Because of the thread hop it is entirely possible to get here and not
+ // have a valid handle registered for |watcher_id|.
+ Handle handle;
+ if (!GetMojoHandleByWatcherID(watcher_id, &handle))
+ return;
+
+ handle_to_data_.erase(handle);
+ MessagePumpMojo::current()->RemoveHandler(handle);
+}
+
+void WatcherBackend::RemoveAndNotify(const Handle& handle,
+ MojoResult result) {
+ if (handle_to_data_.count(handle) == 0)
+ return;
+
+ const WatchData data(handle_to_data_[handle]);
+ handle_to_data_.erase(handle);
+ MessagePumpMojo::current()->RemoveHandler(handle);
+
+ data.task_runner->PostTask(FROM_HERE, base::Bind(data.callback, result));
+}
+
+bool WatcherBackend::GetMojoHandleByWatcherID(WatcherID watcher_id,
+ Handle* handle) const {
+ for (HandleToWatchDataMap::const_iterator i = handle_to_data_.begin();
+ i != handle_to_data_.end(); ++i) {
+ if (i->second.id == watcher_id) {
+ *handle = i->second.handle;
+ return true;
+ }
+ }
+ return false;
+}
+
+void WatcherBackend::OnHandleReady(const Handle& handle) {
+ RemoveAndNotify(handle, MOJO_RESULT_OK);
+}
+
+void WatcherBackend::OnHandleError(const Handle& handle, MojoResult result) {
+ RemoveAndNotify(handle, result);
+}
+
+// WatcherThreadManager --------------------------------------------------------
+
+// WatcherThreadManager manages the background thread that listens for handles
+// to be ready. All requests are handled by WatcherBackend.
+class WatcherThreadManager {
+ public:
+ ~WatcherThreadManager();
+
+ // Returns the shared instance.
+ static WatcherThreadManager* GetInstance();
+
+ // Starts watching the requested handle. Returns a unique ID that is used to
+ // stop watching the handle. When the handle is ready |callback| is notified
+ // on the thread StartWatching() was invoked on.
+ // This may be invoked on any thread.
+ WatcherID StartWatching(const Handle& handle,
+ MojoHandleSignals handle_signals,
+ base::TimeTicks deadline,
+ const base::Callback<void(MojoResult)>& callback);
+
+ // Stops watching a handle.
+ // This may be invoked on any thread.
+ void StopWatching(WatcherID watcher_id);
+
+ private:
+ enum RequestType {
+ REQUEST_START,
+ REQUEST_STOP,
+ };
+
+ // See description of |requests_| for details.
+ struct RequestData {
+ RequestData() : type(REQUEST_START), stop_id(0) {}
+
+ RequestType type;
+ WatchData start_data;
+ WatcherID stop_id;
+ };
+
+ typedef std::vector<RequestData> Requests;
+
+ friend struct base::DefaultSingletonTraits<WatcherThreadManager>;
+
+ WatcherThreadManager();
+
+ // Schedules a request on the background thread. See |requests_| for details.
+ void AddRequest(const RequestData& data);
+
+ // Processes requests added to |requests_|. This is invoked on the backend
+ // thread.
+ void ProcessRequestsOnBackendThread();
+
+ base::Thread thread_;
+
+ base::AtomicSequenceNumber watcher_id_generator_;
+
+ WatcherBackend backend_;
+
+ // Protects |requests_|.
+ base::Lock lock_;
+
+ // Start/Stop result in adding a RequestData to |requests_| (protected by
+ // |lock_|). When the background thread wakes up it processes the requests.
+ Requests requests_;
+
+ DISALLOW_COPY_AND_ASSIGN(WatcherThreadManager);
+};
+
+WatcherThreadManager::~WatcherThreadManager() {
+ thread_.Stop();
+}
+
+WatcherThreadManager* WatcherThreadManager::GetInstance() {
+ return base::Singleton<WatcherThreadManager>::get();
+}
+
+WatcherID WatcherThreadManager::StartWatching(
+ const Handle& handle,
+ MojoHandleSignals handle_signals,
+ base::TimeTicks deadline,
+ const base::Callback<void(MojoResult)>& callback) {
+ RequestData request_data;
+ request_data.type = REQUEST_START;
+ request_data.start_data.id = watcher_id_generator_.GetNext();
+ request_data.start_data.handle = handle;
+ request_data.start_data.callback = callback;
+ request_data.start_data.handle_signals = handle_signals;
+ request_data.start_data.deadline = deadline;
+ request_data.start_data.task_runner = base::ThreadTaskRunnerHandle::Get();
+ AddRequest(request_data);
+ return request_data.start_data.id;
+}
+
+void WatcherThreadManager::StopWatching(WatcherID watcher_id) {
+ // Handle the case of StartWatching() followed by StopWatching() before
+ // |thread_| woke up.
+ {
+ base::AutoLock auto_lock(lock_);
+ for (Requests::iterator i = requests_.begin(); i != requests_.end(); ++i) {
+ if (i->type == REQUEST_START && i->start_data.id == watcher_id) {
+ // Watcher ids are not reused, so if we find it we can stop.
+ requests_.erase(i);
+ return;
+ }
+ }
+ }
+
+ RequestData request_data;
+ request_data.type = REQUEST_STOP;
+ request_data.stop_id = watcher_id;
+ AddRequest(request_data);
+}
+
+void WatcherThreadManager::AddRequest(const RequestData& data) {
+ {
+ base::AutoLock auto_lock(lock_);
+ const bool was_empty = requests_.empty();
+ requests_.push_back(data);
+ if (!was_empty)
+ return;
+ }
+
+ // We outlive |thread_|, so it's safe to use Unretained() here.
+ thread_.task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&WatcherThreadManager::ProcessRequestsOnBackendThread,
+ base::Unretained(this)));
+}
+
+void WatcherThreadManager::ProcessRequestsOnBackendThread() {
+ DCHECK(thread_.task_runner()->BelongsToCurrentThread());
+
+ Requests requests;
+ {
+ base::AutoLock auto_lock(lock_);
+ requests_.swap(requests);
+ }
+ for (size_t i = 0; i < requests.size(); ++i) {
+ if (requests[i].type == REQUEST_START) {
+ backend_.StartWatching(requests[i].start_data);
+ } else {
+ backend_.StopWatching(requests[i].stop_id);
+ }
+ }
+}
+
+WatcherThreadManager::WatcherThreadManager()
+ : thread_(kWatcherThreadName) {
+ base::Thread::Options thread_options;
+ thread_options.message_pump_factory = base::Bind(&MessagePumpMojo::Create);
+ thread_.StartWithOptions(thread_options);
+}
+
+} // namespace
+
+// HandleWatcher::StateBase and subclasses -------------------------------------
+
+// The base class of HandleWatcher's state. Owns the user's callback and
+// monitors the current thread's MessageLoop to know when to force the callback
+// to run (with an error) even though the pipe hasn't been signaled yet.
+class HandleWatcher::StateBase : public base::MessageLoop::DestructionObserver {
+ public:
+ StateBase(HandleWatcher* watcher,
+ const base::Callback<void(MojoResult)>& callback)
+ : watcher_(watcher),
+ callback_(callback),
+ got_ready_(false) {
+ base::MessageLoop::current()->AddDestructionObserver(this);
+ }
+
+ ~StateBase() override {
+ base::MessageLoop::current()->RemoveDestructionObserver(this);
+ }
+
+ protected:
+ void NotifyHandleReady(MojoResult result) {
+ got_ready_ = true;
+ NotifyAndDestroy(result);
+ }
+
+ bool got_ready() const { return got_ready_; }
+
+ private:
+ void WillDestroyCurrentMessageLoop() override {
+ // The current thread is exiting. Simulate a watch error.
+ NotifyAndDestroy(MOJO_RESULT_ABORTED);
+ }
+
+ void NotifyAndDestroy(MojoResult result) {
+ base::Callback<void(MojoResult)> callback = callback_;
+ watcher_->Stop(); // Destroys |this|.
+
+ callback.Run(result);
+ }
+
+ HandleWatcher* watcher_;
+ base::Callback<void(MojoResult)> callback_;
+
+ // Have we been notified that the handle is ready?
+ bool got_ready_;
+
+ DISALLOW_COPY_AND_ASSIGN(StateBase);
+};
+
+// If the thread on which HandleWatcher is used runs MessagePumpMojo,
+// SameThreadWatchingState is used to directly watch the handle on the same
+// thread.
+class HandleWatcher::SameThreadWatchingState : public StateBase,
+ public MessagePumpMojoHandler {
+ public:
+ SameThreadWatchingState(HandleWatcher* watcher,
+ const Handle& handle,
+ MojoHandleSignals handle_signals,
+ MojoDeadline deadline,
+ const base::Callback<void(MojoResult)>& callback)
+ : StateBase(watcher, callback),
+ handle_(handle) {
+ DCHECK(MessagePumpMojo::IsCurrent());
+
+ MessagePumpMojo::current()->AddHandler(
+ this, handle, handle_signals, MojoDeadlineToTimeTicks(deadline));
+ }
+
+ ~SameThreadWatchingState() override {
+ if (!got_ready())
+ MessagePumpMojo::current()->RemoveHandler(handle_);
+ }
+
+ private:
+ // MessagePumpMojoHandler overrides:
+ void OnHandleReady(const Handle& handle) override {
+ StopWatchingAndNotifyReady(handle, MOJO_RESULT_OK);
+ }
+
+ void OnHandleError(const Handle& handle, MojoResult result) override {
+ StopWatchingAndNotifyReady(handle, result);
+ }
+
+ void StopWatchingAndNotifyReady(const Handle& handle, MojoResult result) {
+ DCHECK_EQ(handle.value(), handle_.value());
+ MessagePumpMojo::current()->RemoveHandler(handle_);
+ NotifyHandleReady(result);
+ }
+
+ Handle handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(SameThreadWatchingState);
+};
+
+// If the thread on which HandleWatcher is used runs a message pump different
+// from MessagePumpMojo, SecondaryThreadWatchingState is used to watch the
+// handle on the handle watcher thread.
+class HandleWatcher::SecondaryThreadWatchingState : public StateBase {
+ public:
+ SecondaryThreadWatchingState(HandleWatcher* watcher,
+ const Handle& handle,
+ MojoHandleSignals handle_signals,
+ MojoDeadline deadline,
+ const base::Callback<void(MojoResult)>& callback)
+ : StateBase(watcher, callback),
+ weak_factory_(this) {
+ watcher_id_ = WatcherThreadManager::GetInstance()->StartWatching(
+ handle,
+ handle_signals,
+ MojoDeadlineToTimeTicks(deadline),
+ base::Bind(&SecondaryThreadWatchingState::NotifyHandleReady,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ ~SecondaryThreadWatchingState() override {
+ // If we've been notified the handle is ready (|got_ready()| is true) then
+ // the watch has been implicitly removed by
+ // WatcherThreadManager/MessagePumpMojo and we don't have to call
+ // StopWatching(). To do so would needlessly entail posting a task and
+ // blocking until the background thread services it.
+ if (!got_ready())
+ WatcherThreadManager::GetInstance()->StopWatching(watcher_id_);
+ }
+
+ private:
+ WatcherID watcher_id_;
+
+ // Used to weakly bind |this| to the WatcherThreadManager.
+ base::WeakPtrFactory<SecondaryThreadWatchingState> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SecondaryThreadWatchingState);
+};
+
+// HandleWatcher ---------------------------------------------------------------
+
+HandleWatcher::HandleWatcher() {
+}
+
+HandleWatcher::~HandleWatcher() {
+}
+
+void HandleWatcher::Start(const Handle& handle,
+ MojoHandleSignals handle_signals,
+ MojoDeadline deadline,
+ const base::Callback<void(MojoResult)>& callback) {
+ DCHECK(handle.is_valid());
+ DCHECK_NE(MOJO_HANDLE_SIGNAL_NONE, handle_signals);
+
+ // Need to clear the state before creating a new one.
+ state_.reset();
+ if (MessagePumpMojo::IsCurrent()) {
+ state_.reset(new SameThreadWatchingState(
+ this, handle, handle_signals, deadline, callback));
+ } else {
+#if !defined(OFFICIAL_BUILD)
+ // Just for making debugging non-transferable message pipes easier. Since
+ // they can't be sent after they're read/written/listened to,
+ // MessagePipeDispatcher saves the callstack of when it's "bound" to a
+ // pipe id. Triggering a read here, instead of later in the PostTask, means
+ // we have a callstack that is useful to check if the pipe is erronously
+ // attempted to be sent.
+ uint32_t temp = 0;
+ MojoReadMessage(handle.value(), nullptr, &temp, nullptr, nullptr,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+#endif
+ state_.reset(new SecondaryThreadWatchingState(
+ this, handle, handle_signals, deadline, callback));
+ }
+}
+
+void HandleWatcher::Stop() {
+ state_.reset();
+}
+
+} // namespace common
+} // namespace mojo
diff --git a/mojo/message_pump/handle_watcher.h b/mojo/message_pump/handle_watcher.h
new file mode 100644
index 0000000..10056b1
--- /dev/null
+++ b/mojo/message_pump/handle_watcher.h
@@ -0,0 +1,65 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_MESSAGE_PUMP_HANDLE_WATCHER_H_
+#define MOJO_MESSAGE_PUMP_HANDLE_WATCHER_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "mojo/message_pump/mojo_message_pump_export.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace base {
+class Thread;
+}
+
+namespace mojo {
+namespace common {
+namespace test {
+class HandleWatcherTest;
+}
+
+// HandleWatcher is used to asynchronously wait on a handle and notify a Closure
+// when the handle is ready, or the deadline has expired.
+class MOJO_MESSAGE_PUMP_EXPORT HandleWatcher {
+ public:
+ HandleWatcher();
+
+ ~HandleWatcher();
+
+ // Starts listening for |handle|. This implicitly invokes Stop(). In other
+ // words, Start() performs one asynchronous watch at a time. It is ok to call
+ // Start() multiple times, but it cancels any existing watches. |callback| is
+ // notified when the handle is ready, invalid or deadline has passed and is
+ // notified on the thread Start() was invoked on. If the current thread exits
+ // before the handle is ready, then |callback| is invoked with a result of
+ // MOJO_RESULT_ABORTED.
+ void Start(const Handle& handle,
+ MojoHandleSignals handle_signals,
+ MojoDeadline deadline,
+ const base::Callback<void(MojoResult)>& callback);
+
+ // Stops listening. Does nothing if not in the process of listening.
+ void Stop();
+
+ bool is_watching() const { return !!state_; }
+
+ private:
+ class StateBase;
+ class SameThreadWatchingState;
+ class SecondaryThreadWatchingState;
+
+ // If non-NULL Start() has been invoked.
+ std::unique_ptr<StateBase> state_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleWatcher);
+};
+
+} // namespace common
+} // namespace mojo
+
+#endif // MOJO_MESSAGE_PUMP_HANDLE_WATCHER_H_
diff --git a/mojo/message_pump/handle_watcher_perftest.cc b/mojo/message_pump/handle_watcher_perftest.cc
new file mode 100644
index 0000000..96c8495
--- /dev/null
+++ b/mojo/message_pump/handle_watcher_perftest.cc
@@ -0,0 +1,207 @@
+// 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 <stdint.h>
+
+#include <string>
+#include <utility>
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/scoped_vector.h"
+#include "base/run_loop.h"
+#include "base/time/time.h"
+#include "mojo/message_pump/handle_watcher.h"
+#include "mojo/message_pump/message_pump_mojo.h"
+#include "mojo/public/cpp/test_support/test_support.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace common {
+namespace test {
+
+enum MessageLoopConfig {
+ MESSAGE_LOOP_CONFIG_DEFAULT = 0,
+ MESSAGE_LOOP_CONFIG_MOJO = 1
+};
+
+std::unique_ptr<base::MessageLoop> CreateMessageLoop(MessageLoopConfig config) {
+ std::unique_ptr<base::MessageLoop> loop;
+ if (config == MESSAGE_LOOP_CONFIG_DEFAULT)
+ loop.reset(new base::MessageLoop());
+ else
+ loop.reset(new base::MessageLoop(MessagePumpMojo::Create()));
+ return loop;
+}
+
+void OnWatcherSignaled(const base::Closure& callback, MojoResult /* result */) {
+ callback.Run();
+}
+
+class ScopedPerfTimer {
+ public:
+ ScopedPerfTimer(const std::string& test_name,
+ const std::string& sub_test_name,
+ uint64_t iterations)
+ : test_name_(test_name),
+ sub_test_name_(sub_test_name),
+ iterations_(iterations),
+ start_time_(base::TimeTicks::Now()) {}
+ ~ScopedPerfTimer() {
+ base::TimeTicks end_time = base::TimeTicks::Now();
+ mojo::test::LogPerfResult(
+ test_name_.c_str(), sub_test_name_.c_str(),
+ iterations_ / (end_time - start_time_).InSecondsF(),
+ "iterations/second");
+ }
+
+ private:
+ const std::string test_name_;
+ const std::string sub_test_name_;
+ const uint64_t iterations_;
+ base::TimeTicks start_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedPerfTimer);
+};
+
+class HandleWatcherPerftest : public testing::TestWithParam<MessageLoopConfig> {
+ public:
+ HandleWatcherPerftest() : message_loop_(CreateMessageLoop(GetParam())) {}
+
+ protected:
+ std::string GetMessageLoopName() const {
+ return (GetParam() == MESSAGE_LOOP_CONFIG_DEFAULT) ? "DefaultMessageLoop"
+ : "MojoMessageLoop";
+ }
+
+ private:
+ std::unique_ptr<base::MessageLoop> message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleWatcherPerftest);
+};
+
+INSTANTIATE_TEST_CASE_P(MultipleMessageLoopConfigs,
+ HandleWatcherPerftest,
+ testing::Values(MESSAGE_LOOP_CONFIG_DEFAULT,
+ MESSAGE_LOOP_CONFIG_MOJO));
+
+void NeverReached(MojoResult result) {
+ FAIL() << "Callback should never be invoked " << result;
+}
+
+TEST_P(HandleWatcherPerftest, StartStop) {
+ const uint64_t kIterations = 100000;
+ MessagePipe pipe;
+ HandleWatcher watcher;
+
+ ScopedPerfTimer timer("StartStop", GetMessageLoopName(), kIterations);
+ for (uint64_t i = 0; i < kIterations; i++) {
+ watcher.Start(pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE, base::Bind(&NeverReached));
+ watcher.Stop();
+ }
+}
+
+TEST_P(HandleWatcherPerftest, StartAllThenStop_1000Handles) {
+ const uint64_t kIterations = 100;
+ const uint64_t kHandles = 1000;
+
+ struct TestData {
+ MessagePipe pipe;
+ HandleWatcher watcher;
+ };
+ ScopedVector<TestData> data_vector;
+ // Create separately from the start/stop loops to avoid affecting the
+ // benchmark.
+ for (uint64_t i = 0; i < kHandles; i++) {
+ std::unique_ptr<TestData> test_data(new TestData);
+ ASSERT_TRUE(test_data->pipe.handle0.is_valid());
+ data_vector.push_back(std::move(test_data));
+ }
+
+ ScopedPerfTimer timer("StartAllThenStop_1000Handles", GetMessageLoopName(),
+ kIterations * kHandles);
+ for (uint64_t iter = 0; iter < kIterations; iter++) {
+ for (uint64_t i = 0; i < kHandles; i++) {
+ TestData* test_data = data_vector[i];
+ test_data->watcher.Start(
+ test_data->pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE, base::Bind(&NeverReached));
+ }
+ for (uint64_t i = 0; i < kHandles; i++) {
+ TestData* test_data = data_vector[i];
+ test_data->watcher.Stop();
+ }
+ }
+}
+
+TEST_P(HandleWatcherPerftest, StartAndSignal) {
+ const uint64_t kIterations = 10000;
+ const std::string kMessage = "hello";
+ MessagePipe pipe;
+ HandleWatcher watcher;
+ std::string received_message;
+
+ ScopedPerfTimer timer("StartAndSignal", GetMessageLoopName(), kIterations);
+ for (uint64_t i = 0; i < kIterations; i++) {
+ base::RunLoop run_loop;
+ watcher.Start(pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ base::Bind(&OnWatcherSignaled, run_loop.QuitClosure()));
+ ASSERT_TRUE(mojo::test::WriteTextMessage(pipe.handle1.get(), kMessage));
+ run_loop.Run();
+ watcher.Stop();
+
+ ASSERT_TRUE(
+ mojo::test::ReadTextMessage(pipe.handle0.get(), &received_message));
+ EXPECT_EQ(kMessage, received_message);
+ received_message.clear();
+ }
+}
+
+TEST_P(HandleWatcherPerftest, StartAndSignal_1000Waiting) {
+ const uint64_t kIterations = 10000;
+ const uint64_t kWaitingHandles = 1000;
+ const std::string kMessage = "hello";
+ MessagePipe pipe;
+ HandleWatcher watcher;
+ std::string received_message;
+
+ struct TestData {
+ MessagePipe pipe;
+ HandleWatcher watcher;
+ };
+ ScopedVector<TestData> data_vector;
+ for (uint64_t i = 0; i < kWaitingHandles; i++) {
+ std::unique_ptr<TestData> test_data(new TestData);
+ ASSERT_TRUE(test_data->pipe.handle0.is_valid());
+ test_data->watcher.Start(
+ test_data->pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE, base::Bind(&NeverReached));
+ data_vector.push_back(std::move(test_data));
+ }
+
+ ScopedPerfTimer timer("StartAndSignal_1000Waiting", GetMessageLoopName(),
+ kIterations);
+ for (uint64_t i = 0; i < kIterations; i++) {
+ base::RunLoop run_loop;
+ watcher.Start(pipe.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ base::Bind(&OnWatcherSignaled, run_loop.QuitClosure()));
+ ASSERT_TRUE(mojo::test::WriteTextMessage(pipe.handle1.get(), kMessage));
+ run_loop.Run();
+ watcher.Stop();
+
+ ASSERT_TRUE(
+ mojo::test::ReadTextMessage(pipe.handle0.get(), &received_message));
+ EXPECT_EQ(kMessage, received_message);
+ received_message.clear();
+ }
+}
+
+} // namespace test
+} // namespace common
+} // namespace mojo
diff --git a/mojo/message_pump/handle_watcher_unittest.cc b/mojo/message_pump/handle_watcher_unittest.cc
new file mode 100644
index 0000000..fd1f49b
--- /dev/null
+++ b/mojo/message_pump/handle_watcher_unittest.cc
@@ -0,0 +1,493 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/message_pump/handle_watcher.h"
+
+#include <memory>
+#include <string>
+
+#include "base/at_exit.h"
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/scoped_vector.h"
+#include "base/run_loop.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/threading/thread.h"
+#include "mojo/message_pump/message_pump_mojo.h"
+#include "mojo/message_pump/time_helper.h"
+#include "mojo/public/cpp/system/core.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace common {
+namespace test {
+
+enum MessageLoopConfig {
+ MESSAGE_LOOP_CONFIG_DEFAULT = 0,
+ MESSAGE_LOOP_CONFIG_MOJO = 1
+};
+
+void ObserveCallback(bool* was_signaled,
+ MojoResult* result_observed,
+ MojoResult result) {
+ *was_signaled = true;
+ *result_observed = result;
+}
+
+void RunUntilIdle() {
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+}
+
+void DeleteWatcherAndForwardResult(
+ HandleWatcher* watcher,
+ base::Callback<void(MojoResult)> next_callback,
+ MojoResult result) {
+ delete watcher;
+ next_callback.Run(result);
+}
+
+std::unique_ptr<base::MessageLoop> CreateMessageLoop(MessageLoopConfig config) {
+ std::unique_ptr<base::MessageLoop> loop;
+ if (config == MESSAGE_LOOP_CONFIG_DEFAULT)
+ loop.reset(new base::MessageLoop());
+ else
+ loop.reset(new base::MessageLoop(MessagePumpMojo::Create()));
+ return loop;
+}
+
+// Helper class to manage the callback and running the message loop waiting for
+// message to be received. Typical usage is something like:
+// Schedule callback returned from GetCallback().
+// RunUntilGotCallback();
+// EXPECT_TRUE(got_callback());
+// clear_callback();
+class CallbackHelper {
+ public:
+ CallbackHelper()
+ : got_callback_(false),
+ run_loop_(NULL),
+ weak_factory_(this) {}
+ ~CallbackHelper() {}
+
+ // See description above |got_callback_|.
+ bool got_callback() const { return got_callback_; }
+ void clear_callback() { got_callback_ = false; }
+
+ // Runs the current MessageLoop until the callback returned from GetCallback()
+ // is notified.
+ void RunUntilGotCallback() {
+ ASSERT_TRUE(run_loop_ == NULL);
+ base::RunLoop run_loop;
+ base::AutoReset<base::RunLoop*> reseter(&run_loop_, &run_loop);
+ run_loop.Run();
+ }
+
+ base::Callback<void(MojoResult)> GetCallback() {
+ return base::Bind(&CallbackHelper::OnCallback, weak_factory_.GetWeakPtr());
+ }
+
+ void Start(HandleWatcher* watcher, const MessagePipeHandle& handle) {
+ StartWithCallback(watcher, handle, GetCallback());
+ }
+
+ void StartWithCallback(HandleWatcher* watcher,
+ const MessagePipeHandle& handle,
+ const base::Callback<void(MojoResult)>& callback) {
+ watcher->Start(handle, MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE, callback);
+ }
+
+ private:
+ void OnCallback(MojoResult result) {
+ got_callback_ = true;
+ if (run_loop_)
+ run_loop_->Quit();
+ }
+
+ // Set to true when the callback is called.
+ bool got_callback_;
+
+ // If non-NULL we're in RunUntilGotCallback().
+ base::RunLoop* run_loop_;
+
+ base::WeakPtrFactory<CallbackHelper> weak_factory_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CallbackHelper);
+};
+
+class HandleWatcherTest : public testing::TestWithParam<MessageLoopConfig> {
+ public:
+ HandleWatcherTest()
+ : at_exit_(new base::ShadowingAtExitManager),
+ message_loop_(CreateMessageLoop(GetParam())) {}
+ virtual ~HandleWatcherTest() {
+ // By explicitly destroying |at_exit_| before resetting the tick clock, it
+ // ensures that the handle watcher thread (if there is one) is shut down,
+ // preventing a race with users of the tick clock in MessagePumpMojo.
+ at_exit_.reset();
+ test::SetTickClockForTest(NULL);
+ }
+
+ protected:
+ void TearDownMessageLoop() {
+ message_loop_.reset();
+ }
+
+ // This should be called at the beginning of any test that needs it, so that
+ // it is installed before the handle watcher thread starts.
+ void InstallTickClock() {
+ test::SetTickClockForTest(&tick_clock_);
+ }
+
+ base::SimpleTestTickClock tick_clock_;
+
+ private:
+ std::unique_ptr<base::ShadowingAtExitManager> at_exit_;
+ std::unique_ptr<base::MessageLoop> message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleWatcherTest);
+};
+
+INSTANTIATE_TEST_CASE_P(
+ MultipleMessageLoopConfigs, HandleWatcherTest,
+ testing::Values(MESSAGE_LOOP_CONFIG_DEFAULT, MESSAGE_LOOP_CONFIG_MOJO));
+
+// Trivial test case with a single handle to watch.
+TEST_P(HandleWatcherTest, SingleHandler) {
+ MessagePipe test_pipe;
+ ASSERT_TRUE(test_pipe.handle0.is_valid());
+ CallbackHelper callback_helper;
+ HandleWatcher watcher;
+ callback_helper.Start(&watcher, test_pipe.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper.got_callback());
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe.handle1.get(),
+ std::string()));
+ callback_helper.RunUntilGotCallback();
+ EXPECT_TRUE(callback_helper.got_callback());
+}
+
+// Creates three handles and notfies them in reverse order ensuring each one is
+// notified appropriately.
+TEST_P(HandleWatcherTest, ThreeHandles) {
+ MessagePipe test_pipe1;
+ MessagePipe test_pipe2;
+ MessagePipe test_pipe3;
+ CallbackHelper callback_helper1;
+ CallbackHelper callback_helper2;
+ CallbackHelper callback_helper3;
+ ASSERT_TRUE(test_pipe1.handle0.is_valid());
+ ASSERT_TRUE(test_pipe2.handle0.is_valid());
+ ASSERT_TRUE(test_pipe3.handle0.is_valid());
+
+ HandleWatcher watcher1;
+ callback_helper1.Start(&watcher1, test_pipe1.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+ EXPECT_FALSE(callback_helper3.got_callback());
+
+ HandleWatcher watcher2;
+ callback_helper2.Start(&watcher2, test_pipe2.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+ EXPECT_FALSE(callback_helper3.got_callback());
+
+ HandleWatcher watcher3;
+ callback_helper3.Start(&watcher3, test_pipe3.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+ EXPECT_FALSE(callback_helper3.got_callback());
+
+ // Write to 3 and make sure it's notified.
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe3.handle1.get(),
+ std::string()));
+ callback_helper3.RunUntilGotCallback();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+ EXPECT_TRUE(callback_helper3.got_callback());
+ callback_helper3.clear_callback();
+
+ // Write to 1 and 3. Only 1 should be notified since 3 was is no longer
+ // running.
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe1.handle1.get(),
+ std::string()));
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe3.handle1.get(),
+ std::string()));
+ callback_helper1.RunUntilGotCallback();
+ EXPECT_TRUE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+ EXPECT_FALSE(callback_helper3.got_callback());
+ callback_helper1.clear_callback();
+
+ // Write to 1 and 2. Only 2 should be notified (since 1 was already notified).
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe1.handle1.get(),
+ std::string()));
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe2.handle1.get(),
+ std::string()));
+ callback_helper2.RunUntilGotCallback();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_TRUE(callback_helper2.got_callback());
+ EXPECT_FALSE(callback_helper3.got_callback());
+}
+
+// Verifies Start() invoked a second time works.
+TEST_P(HandleWatcherTest, Restart) {
+ MessagePipe test_pipe1;
+ MessagePipe test_pipe2;
+ CallbackHelper callback_helper1;
+ CallbackHelper callback_helper2;
+ ASSERT_TRUE(test_pipe1.handle0.is_valid());
+ ASSERT_TRUE(test_pipe2.handle0.is_valid());
+
+ HandleWatcher watcher1;
+ callback_helper1.Start(&watcher1, test_pipe1.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+
+ HandleWatcher watcher2;
+ callback_helper2.Start(&watcher2, test_pipe2.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+
+ // Write to 1 and make sure it's notified.
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe1.handle1.get(),
+ std::string()));
+ callback_helper1.RunUntilGotCallback();
+ EXPECT_TRUE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+ callback_helper1.clear_callback();
+ EXPECT_TRUE(mojo::test::DiscardMessage(test_pipe1.handle0.get()));
+
+ // Write to 2 and make sure it's notified.
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe2.handle1.get(),
+ std::string()));
+ callback_helper2.RunUntilGotCallback();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_TRUE(callback_helper2.got_callback());
+ callback_helper2.clear_callback();
+
+ // Listen on 1 again.
+ callback_helper1.Start(&watcher1, test_pipe1.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+
+ // Write to 1 and make sure it's notified.
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe1.handle1.get(),
+ std::string()));
+ callback_helper1.RunUntilGotCallback();
+ EXPECT_TRUE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+}
+
+// Verifies Start() invoked a second time on the same handle works.
+TEST_P(HandleWatcherTest, RestartOnSameHandle) {
+ MessagePipe test_pipe;
+ CallbackHelper callback_helper;
+ ASSERT_TRUE(test_pipe.handle0.is_valid());
+
+ HandleWatcher watcher;
+ callback_helper.Start(&watcher, test_pipe.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper.got_callback());
+
+ callback_helper.Start(&watcher, test_pipe.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper.got_callback());
+}
+
+// Verifies deadline is honored.
+TEST_P(HandleWatcherTest, Deadline) {
+ InstallTickClock();
+
+ MessagePipe test_pipe1;
+ MessagePipe test_pipe2;
+ MessagePipe test_pipe3;
+ CallbackHelper callback_helper1;
+ CallbackHelper callback_helper2;
+ CallbackHelper callback_helper3;
+ ASSERT_TRUE(test_pipe1.handle0.is_valid());
+ ASSERT_TRUE(test_pipe2.handle0.is_valid());
+ ASSERT_TRUE(test_pipe3.handle0.is_valid());
+
+ // Add a watcher with an infinite timeout.
+ HandleWatcher watcher1;
+ callback_helper1.Start(&watcher1, test_pipe1.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+ EXPECT_FALSE(callback_helper3.got_callback());
+
+ // Add another watcher wth a timeout of 500 microseconds.
+ HandleWatcher watcher2;
+ watcher2.Start(test_pipe2.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE, 500,
+ callback_helper2.GetCallback());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_FALSE(callback_helper2.got_callback());
+ EXPECT_FALSE(callback_helper3.got_callback());
+
+ // Advance the clock passed the deadline. We also have to start another
+ // watcher to wake up the background thread.
+ tick_clock_.Advance(base::TimeDelta::FromMicroseconds(501));
+
+ HandleWatcher watcher3;
+ callback_helper3.Start(&watcher3, test_pipe3.handle0.get());
+
+ callback_helper2.RunUntilGotCallback();
+ EXPECT_FALSE(callback_helper1.got_callback());
+ EXPECT_TRUE(callback_helper2.got_callback());
+ EXPECT_FALSE(callback_helper3.got_callback());
+}
+
+TEST_P(HandleWatcherTest, DeleteInCallback) {
+ MessagePipe test_pipe;
+ CallbackHelper callback_helper;
+
+ HandleWatcher* watcher = new HandleWatcher();
+ callback_helper.StartWithCallback(watcher, test_pipe.handle1.get(),
+ base::Bind(&DeleteWatcherAndForwardResult,
+ watcher,
+ callback_helper.GetCallback()));
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe.handle0.get(),
+ std::string()));
+ callback_helper.RunUntilGotCallback();
+ EXPECT_TRUE(callback_helper.got_callback());
+}
+
+TEST_P(HandleWatcherTest, AbortedOnMessageLoopDestruction) {
+ bool was_signaled = false;
+ MojoResult result = MOJO_RESULT_OK;
+
+ MessagePipe pipe;
+ HandleWatcher watcher;
+ watcher.Start(pipe.handle0.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ base::Bind(&ObserveCallback, &was_signaled, &result));
+
+ // Now, let the MessageLoop get torn down. We expect our callback to run.
+ TearDownMessageLoop();
+
+ EXPECT_TRUE(was_signaled);
+ EXPECT_EQ(MOJO_RESULT_ABORTED, result);
+}
+
+void NeverReached(MojoResult result) {
+ FAIL() << "Callback should never be invoked " << result;
+}
+
+// Called on the main thread when a thread is done. Decrements |active_count|
+// and if |active_count| is zero quits |run_loop|.
+void StressThreadDone(base::RunLoop* run_loop, int* active_count) {
+ (*active_count)--;
+ EXPECT_GE(*active_count, 0);
+ if (*active_count == 0)
+ run_loop->Quit();
+}
+
+// See description of StressTest. This is called on the background thread.
+// |count| is the number of HandleWatchers to create. |active_count| is the
+// number of outstanding threads, |task_runner| the task runner for the main
+// thread and |run_loop| the run loop that should be quit when there are no more
+// threads running. When done StressThreadDone() is invoked on the main thread.
+// |active_count| and |run_loop| should only be used on the main thread.
+void RunStressTest(int count,
+ scoped_refptr<base::TaskRunner> task_runner,
+ base::RunLoop* run_loop,
+ int* active_count) {
+ struct TestData {
+ MessagePipe pipe;
+ HandleWatcher watcher;
+ };
+ ScopedVector<TestData> data_vector;
+ for (int i = 0; i < count; ++i) {
+ if (i % 20 == 0) {
+ // Every so often we wait. This results in some level of thread balancing
+ // as well as making sure HandleWatcher has time to actually start some
+ // watches.
+ MessagePipe test_pipe;
+ ASSERT_TRUE(test_pipe.handle0.is_valid());
+ CallbackHelper callback_helper;
+ HandleWatcher watcher;
+ callback_helper.Start(&watcher, test_pipe.handle0.get());
+ RunUntilIdle();
+ EXPECT_FALSE(callback_helper.got_callback());
+ EXPECT_TRUE(mojo::test::WriteTextMessage(test_pipe.handle1.get(),
+ std::string()));
+ base::MessageLoop::ScopedNestableTaskAllower scoper(
+ base::MessageLoop::current());
+ callback_helper.RunUntilGotCallback();
+ EXPECT_TRUE(callback_helper.got_callback());
+ } else {
+ std::unique_ptr<TestData> test_data(new TestData);
+ ASSERT_TRUE(test_data->pipe.handle0.is_valid());
+ test_data->watcher.Start(test_data->pipe.handle0.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE,
+ base::Bind(&NeverReached));
+ data_vector.push_back(test_data.release());
+ }
+ if (i % 15 == 0)
+ data_vector.clear();
+ }
+ task_runner->PostTask(FROM_HERE,
+ base::Bind(&StressThreadDone, run_loop,
+ active_count));
+}
+
+// This test is meant to stress HandleWatcher. It uses from various threads
+// repeatedly starting and stopping watches. It spins up kThreadCount
+// threads. Each thread creates kWatchCount watches. Every so often each thread
+// writes to a pipe and waits for the response.
+TEST(HandleWatcherCleanEnvironmentTest, StressTest) {
+#if defined(NDEBUG)
+ const int kThreadCount = 15;
+ const int kWatchCount = 400;
+#else
+ const int kThreadCount = 10;
+ const int kWatchCount = 250;
+#endif
+
+ base::ShadowingAtExitManager at_exit;
+ base::MessageLoop message_loop;
+ base::RunLoop run_loop;
+ ScopedVector<base::Thread> threads;
+ int threads_active_counter = kThreadCount;
+ // Starts the threads first and then post the task in hopes of having more
+ // threads running at once.
+ for (int i = 0; i < kThreadCount; ++i) {
+ std::unique_ptr<base::Thread> thread(new base::Thread("test thread"));
+ if (i % 2) {
+ base::Thread::Options thread_options;
+ thread_options.message_pump_factory =
+ base::Bind(&MessagePumpMojo::Create);
+ thread->StartWithOptions(thread_options);
+ } else {
+ thread->Start();
+ }
+ threads.push_back(thread.release());
+ }
+ for (int i = 0; i < kThreadCount; ++i) {
+ threads[i]->task_runner()->PostTask(
+ FROM_HERE, base::Bind(&RunStressTest, kWatchCount,
+ message_loop.task_runner(),
+ &run_loop, &threads_active_counter));
+ }
+ run_loop.Run();
+ ASSERT_EQ(0, threads_active_counter);
+}
+
+} // namespace test
+} // namespace common
+} // namespace mojo
diff --git a/mojo/message_pump/message_pump_mojo.cc b/mojo/message_pump/message_pump_mojo.cc
new file mode 100644
index 0000000..b907ba3
--- /dev/null
+++ b/mojo/message_pump/message_pump_mojo.cc
@@ -0,0 +1,448 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/message_pump/message_pump_mojo.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <map>
+#include <vector>
+
+#include "base/containers/small_map.h"
+#include "base/debug/alias.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/thread_local.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time/time.h"
+#include "mojo/message_pump/message_pump_mojo_handler.h"
+#include "mojo/message_pump/time_helper.h"
+#include "mojo/public/c/system/wait_set.h"
+
+namespace mojo {
+namespace common {
+namespace {
+
+base::LazyInstance<base::ThreadLocalPointer<MessagePumpMojo> >::Leaky
+ g_tls_current_pump = LAZY_INSTANCE_INITIALIZER;
+
+MojoDeadline TimeTicksToMojoDeadline(base::TimeTicks time_ticks,
+ base::TimeTicks now) {
+ // The is_null() check matches that of HandleWatcher as well as how
+ // |delayed_work_time| is used.
+ if (time_ticks.is_null())
+ return MOJO_DEADLINE_INDEFINITE;
+ const int64_t delta = (time_ticks - now).InMicroseconds();
+ return delta < 0 ? static_cast<MojoDeadline>(0) :
+ static_cast<MojoDeadline>(delta);
+}
+
+} // namespace
+
+struct MessagePumpMojo::RunState {
+ RunState() : should_quit(false) {}
+
+ base::TimeTicks delayed_work_time;
+
+ bool should_quit;
+};
+
+MessagePumpMojo::MessagePumpMojo()
+ : run_state_(NULL),
+ next_handler_id_(0),
+ event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ base::WaitableEvent::InitialState::NOT_SIGNALED) {
+ DCHECK(!current())
+ << "There is already a MessagePumpMojo instance on this thread.";
+ g_tls_current_pump.Pointer()->Set(this);
+
+ MojoResult result = CreateMessagePipe(nullptr, &read_handle_, &write_handle_);
+ CHECK_EQ(result, MOJO_RESULT_OK);
+ CHECK(read_handle_.is_valid());
+ CHECK(write_handle_.is_valid());
+
+ MojoHandle handle;
+ result = MojoCreateWaitSet(&handle);
+ CHECK_EQ(result, MOJO_RESULT_OK);
+ wait_set_handle_.reset(Handle(handle));
+ CHECK(wait_set_handle_.is_valid());
+
+ result =
+ MojoAddHandle(wait_set_handle_.get().value(), read_handle_.get().value(),
+ MOJO_HANDLE_SIGNAL_READABLE);
+ CHECK_EQ(result, MOJO_RESULT_OK);
+}
+
+MessagePumpMojo::~MessagePumpMojo() {
+ DCHECK_EQ(this, current());
+ g_tls_current_pump.Pointer()->Set(NULL);
+}
+
+// static
+std::unique_ptr<base::MessagePump> MessagePumpMojo::Create() {
+ return std::unique_ptr<MessagePump>(new MessagePumpMojo());
+}
+
+// static
+MessagePumpMojo* MessagePumpMojo::current() {
+ return g_tls_current_pump.Pointer()->Get();
+}
+
+void MessagePumpMojo::AddHandler(MessagePumpMojoHandler* handler,
+ const Handle& handle,
+ MojoHandleSignals wait_signals,
+ base::TimeTicks deadline) {
+ CHECK(handler);
+ DCHECK(handle.is_valid());
+ // Assume it's an error if someone tries to reregister an existing handle.
+ CHECK_EQ(0u, handlers_.count(handle));
+ Handler handler_data;
+ handler_data.handler = handler;
+ handler_data.wait_signals = wait_signals;
+ handler_data.deadline = deadline;
+ handler_data.id = next_handler_id_++;
+ handlers_[handle] = handler_data;
+ if (!deadline.is_null()) {
+ bool inserted = deadline_handles_.insert(handle).second;
+ DCHECK(inserted);
+ }
+
+ MojoResult result = MojoAddHandle(wait_set_handle_.get().value(),
+ handle.value(), wait_signals);
+ // Because stopping a HandleWatcher is now asynchronous, it's possible for the
+ // handle to no longer be open at this point.
+ CHECK(result == MOJO_RESULT_OK || result == MOJO_RESULT_INVALID_ARGUMENT);
+}
+
+void MessagePumpMojo::RemoveHandler(const Handle& handle) {
+ MojoResult result =
+ MojoRemoveHandle(wait_set_handle_.get().value(), handle.value());
+ // At this point, it's possible that the handle has been closed, which would
+ // cause MojoRemoveHandle() to return MOJO_RESULT_INVALID_ARGUMENT. It's also
+ // possible for the handle to have already been removed, so all of the
+ // possible error codes are valid here.
+ CHECK(result == MOJO_RESULT_OK || result == MOJO_RESULT_NOT_FOUND ||
+ result == MOJO_RESULT_INVALID_ARGUMENT);
+
+ handlers_.erase(handle);
+ deadline_handles_.erase(handle);
+}
+
+void MessagePumpMojo::AddObserver(Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void MessagePumpMojo::RemoveObserver(Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void MessagePumpMojo::Run(Delegate* delegate) {
+ RunState run_state;
+ RunState* old_state = NULL;
+ {
+ base::AutoLock auto_lock(run_state_lock_);
+ old_state = run_state_;
+ run_state_ = &run_state;
+ }
+ DoRunLoop(&run_state, delegate);
+ {
+ base::AutoLock auto_lock(run_state_lock_);
+ run_state_ = old_state;
+ }
+}
+
+void MessagePumpMojo::Quit() {
+ base::AutoLock auto_lock(run_state_lock_);
+ if (run_state_)
+ run_state_->should_quit = true;
+}
+
+void MessagePumpMojo::ScheduleWork() {
+ SignalControlPipe();
+}
+
+void MessagePumpMojo::ScheduleDelayedWork(
+ const base::TimeTicks& delayed_work_time) {
+ base::AutoLock auto_lock(run_state_lock_);
+ if (!run_state_)
+ return;
+ run_state_->delayed_work_time = delayed_work_time;
+}
+
+void MessagePumpMojo::DoRunLoop(RunState* run_state, Delegate* delegate) {
+ bool more_work_is_plausible = true;
+ for (;;) {
+ const bool block = !more_work_is_plausible;
+ if (read_handle_.is_valid()) {
+ more_work_is_plausible = DoInternalWork(*run_state, block);
+ } else {
+ more_work_is_plausible = DoNonMojoWork(*run_state, block);
+ }
+
+ if (run_state->should_quit)
+ break;
+
+ more_work_is_plausible |= delegate->DoWork();
+ if (run_state->should_quit)
+ break;
+
+ more_work_is_plausible |= delegate->DoDelayedWork(
+ &run_state->delayed_work_time);
+ if (run_state->should_quit)
+ break;
+
+ if (more_work_is_plausible)
+ continue;
+
+ more_work_is_plausible = delegate->DoIdleWork();
+ if (run_state->should_quit)
+ break;
+ }
+}
+
+bool MessagePumpMojo::DoInternalWork(const RunState& run_state, bool block) {
+ bool did_work = block;
+ if (block) {
+ // If the wait isn't blocking (deadline == 0), there's no point in waiting.
+ // Wait sets do not require a wait operation to be performed in order to
+ // retreive any ready handles. Performing a wait with deadline == 0 is
+ // unnecessary work.
+ did_work = WaitForReadyHandles(run_state);
+ }
+
+ did_work |= ProcessReadyHandles();
+ did_work |= RemoveExpiredHandles();
+
+ return did_work;
+}
+
+bool MessagePumpMojo::DoNonMojoWork(const RunState& run_state, bool block) {
+ bool did_work = block;
+ if (block) {
+ const MojoDeadline deadline = GetDeadlineForWait(run_state);
+ // Stolen from base/message_loop/message_pump_default.cc
+ base::ThreadRestrictions::ScopedAllowWait allow_wait;
+ if (deadline == MOJO_DEADLINE_INDEFINITE) {
+ event_.Wait();
+ } else {
+ if (deadline > 0) {
+ event_.TimedWait(base::TimeDelta::FromMicroseconds(deadline));
+ } else {
+ did_work = false;
+ }
+ }
+ // Since event_ is auto-reset, we don't need to do anything special here
+ // other than service each delegate method.
+ }
+
+ did_work |= RemoveExpiredHandles();
+
+ return did_work;
+}
+
+bool MessagePumpMojo::WaitForReadyHandles(const RunState& run_state) const {
+ const MojoDeadline deadline = GetDeadlineForWait(run_state);
+ const MojoResult wait_result = Wait(
+ wait_set_handle_.get(), MOJO_HANDLE_SIGNAL_READABLE, deadline, nullptr);
+ if (wait_result == MOJO_RESULT_OK) {
+ // Handles may be ready. Or not since wake-ups can be spurious in certain
+ // circumstances.
+ return true;
+ } else if (wait_result == MOJO_RESULT_DEADLINE_EXCEEDED) {
+ return false;
+ }
+
+ base::debug::Alias(&wait_result);
+ // Unexpected result is likely fatal, crash so we can determine cause.
+ CHECK(false);
+ return false;
+}
+
+bool MessagePumpMojo::ProcessReadyHandles() {
+ // Maximum number of handles to retrieve and process. Experimentally, the 95th
+ // percentile is 1 handle, and the long-term average is 1.1. However, this has
+ // been seen to reach >10 under heavy load. 8 is a hand-wavy compromise.
+ const uint32_t kMaxServiced = 8;
+ uint32_t num_ready_handles = kMaxServiced;
+ MojoHandle handles[kMaxServiced];
+ MojoResult handle_results[kMaxServiced];
+
+ const MojoResult get_result =
+ MojoGetReadyHandles(wait_set_handle_.get().value(), &num_ready_handles,
+ handles, handle_results, nullptr);
+ CHECK(get_result == MOJO_RESULT_OK || get_result == MOJO_RESULT_SHOULD_WAIT);
+ if (get_result != MOJO_RESULT_OK)
+ return false;
+
+ DCHECK(num_ready_handles);
+ DCHECK_LE(num_ready_handles, kMaxServiced);
+ // Do this in two steps, because notifying a handler may remove/add other
+ // handles that may have also been woken up.
+ // First, enumerate the IDs of the ready handles. Then, iterate over the
+ // handles and only take action if the ID hasn't changed.
+ // Since the size of this map is bounded by |kMaxServiced|, use a SmallMap to
+ // avoid the per-element allocation.
+ base::SmallMap<std::map<Handle, int>, kMaxServiced> ready_handles;
+ for (uint32_t i = 0; i < num_ready_handles; i++) {
+ const Handle handle = Handle(handles[i]);
+ // Skip the control handle. It's special.
+ if (handle.value() == read_handle_.get().value())
+ continue;
+ DCHECK(handle.is_valid());
+ const auto it = handlers_.find(handle);
+ // Skip handles that have been removed. This is possible because
+ // RemoveHandler() can be called with a handle that has been closed. Because
+ // the handle is closed, the MojoRemoveHandle() call in RemoveHandler()
+ // would have failed, but the handle is still in the wait set. Once the
+ // handle is retrieved using MojoGetReadyHandles(), it is implicitly removed
+ // from the set. The result is either the pending result that existed when
+ // the handle was closed, or |MOJO_RESULT_CANCELLED| to indicate that the
+ // handle was closed.
+ if (it == handlers_.end())
+ continue;
+ ready_handles[handle] = it->second.id;
+ }
+
+ for (uint32_t i = 0; i < num_ready_handles; i++) {
+ const Handle handle = Handle(handles[i]);
+
+ // If the handle has been removed, or it's ID has changed, skip over it.
+ // If the handle's ID has changed, and it still satisfies its signals,
+ // then it'll be caught in the next message pump iteration.
+ const auto it = handlers_.find(handle);
+ if ((handle.value() != read_handle_.get().value()) &&
+ (it == handlers_.end() || it->second.id != ready_handles[handle])) {
+ continue;
+ }
+
+ switch (handle_results[i]) {
+ case MOJO_RESULT_CANCELLED:
+ case MOJO_RESULT_FAILED_PRECONDITION:
+ DVLOG(1) << "Error: " << handle_results[i]
+ << " handle: " << handle.value();
+ if (handle.value() == read_handle_.get().value()) {
+ // The Mojo EDK is shutting down. We can't just quit the message pump
+ // because that may cause the thread to quit, which causes the
+ // thread's MessageLoop to be destroyed, which races with any use of
+ // |Thread::task_runner()|. So instead, we enter a "dumb" mode which
+ // bypasses Mojo and just acts like a trivial message pump. That way,
+ // we can wait for the usual thread exiting mechanism to happen.
+ // The dumb mode is indicated by releasing the control pipe's read
+ // handle.
+ read_handle_.reset();
+ } else {
+ SignalHandleError(handle, handle_results[i]);
+ }
+ break;
+ case MOJO_RESULT_OK:
+ if (handle.value() == read_handle_.get().value()) {
+ DVLOG(1) << "Signaled control pipe";
+ // Control pipe was written to.
+ ReadMessageRaw(read_handle_.get(), nullptr, nullptr, nullptr, nullptr,
+ MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
+ } else {
+ DVLOG(1) << "Handle ready: " << handle.value();
+ SignalHandleReady(handle);
+ }
+ break;
+ default:
+ base::debug::Alias(&i);
+ base::debug::Alias(&handle_results[i]);
+ // Unexpected result is likely fatal, crash so we can determine cause.
+ CHECK(false);
+ }
+ }
+ return true;
+}
+
+bool MessagePumpMojo::RemoveExpiredHandles() {
+ bool removed = false;
+ // Notify and remove any handlers whose time has expired. First, iterate over
+ // the set of handles that have a deadline, and add the expired handles to a
+ // map of <Handle, id>. Then, iterate over those expired handles and remove
+ // them. The two-step process is because a handler can add/remove new
+ // handlers.
+ std::map<Handle, int> expired_handles;
+ const base::TimeTicks now(internal::NowTicks());
+ for (const Handle handle : deadline_handles_) {
+ const auto it = handlers_.find(handle);
+ // Expect any handle in |deadline_handles_| to also be in |handlers_| since
+ // the two are modified in lock-step.
+ DCHECK(it != handlers_.end());
+ if (!it->second.deadline.is_null() && it->second.deadline < now)
+ expired_handles[handle] = it->second.id;
+ }
+ for (const auto& pair : expired_handles) {
+ auto it = handlers_.find(pair.first);
+ // Don't need to check deadline again since it can't change if id hasn't
+ // changed.
+ if (it != handlers_.end() && it->second.id == pair.second) {
+ SignalHandleError(pair.first, MOJO_RESULT_DEADLINE_EXCEEDED);
+ removed = true;
+ }
+ }
+ return removed;
+}
+
+void MessagePumpMojo::SignalControlPipe() {
+ const MojoResult result =
+ WriteMessageRaw(write_handle_.get(), NULL, 0, NULL, 0,
+ MOJO_WRITE_MESSAGE_FLAG_NONE);
+ if (result == MOJO_RESULT_FAILED_PRECONDITION) {
+ // Mojo EDK is shutting down.
+ event_.Signal();
+ return;
+ }
+
+ // If we can't write we likely won't wake up the thread and there is a strong
+ // chance we'll deadlock.
+ CHECK_EQ(MOJO_RESULT_OK, result);
+}
+
+MojoDeadline MessagePumpMojo::GetDeadlineForWait(
+ const RunState& run_state) const {
+ const base::TimeTicks now(internal::NowTicks());
+ MojoDeadline deadline = TimeTicksToMojoDeadline(run_state.delayed_work_time,
+ now);
+ for (const Handle handle : deadline_handles_) {
+ auto it = handlers_.find(handle);
+ DCHECK(it != handlers_.end());
+ deadline = std::min(
+ TimeTicksToMojoDeadline(it->second.deadline, now), deadline);
+ }
+ return deadline;
+}
+
+void MessagePumpMojo::SignalHandleReady(Handle handle) {
+ auto it = handlers_.find(handle);
+ DCHECK(it != handlers_.end());
+ MessagePumpMojoHandler* handler = it->second.handler;
+
+ WillSignalHandler();
+ handler->OnHandleReady(handle);
+ DidSignalHandler();
+}
+
+void MessagePumpMojo::SignalHandleError(Handle handle, MojoResult result) {
+ auto it = handlers_.find(handle);
+ DCHECK(it != handlers_.end());
+ MessagePumpMojoHandler* handler = it->second.handler;
+
+ RemoveHandler(handle);
+ WillSignalHandler();
+ handler->OnHandleError(handle, result);
+ DidSignalHandler();
+}
+
+void MessagePumpMojo::WillSignalHandler() {
+ FOR_EACH_OBSERVER(Observer, observers_, WillSignalHandler());
+}
+
+void MessagePumpMojo::DidSignalHandler() {
+ FOR_EACH_OBSERVER(Observer, observers_, DidSignalHandler());
+}
+
+} // namespace common
+} // namespace mojo
diff --git a/mojo/message_pump/message_pump_mojo.h b/mojo/message_pump/message_pump_mojo.h
new file mode 100644
index 0000000..ef2f55a
--- /dev/null
+++ b/mojo/message_pump/message_pump_mojo.h
@@ -0,0 +1,178 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_MESSAGE_PUMP_MESSAGE_PUMP_MOJO_H_
+#define MOJO_MESSAGE_PUMP_MESSAGE_PUMP_MOJO_H_
+
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <set>
+#include <unordered_map>
+
+#include "base/macros.h"
+#include "base/message_loop/message_pump.h"
+#include "base/observer_list.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/time/time.h"
+#include "mojo/message_pump/mojo_message_pump_export.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace common {
+
+class MessagePumpMojoHandler;
+
+// Mojo implementation of MessagePump.
+class MOJO_MESSAGE_PUMP_EXPORT MessagePumpMojo : public base::MessagePump {
+ public:
+ class MOJO_MESSAGE_PUMP_EXPORT Observer {
+ public:
+ Observer() {}
+
+ virtual void WillSignalHandler() = 0;
+ virtual void DidSignalHandler() = 0;
+
+ protected:
+ virtual ~Observer() {}
+ };
+
+ MessagePumpMojo();
+ ~MessagePumpMojo() override;
+
+ // Static factory function (for using with |base::Thread::Options|, wrapped
+ // using |base::Bind()|).
+ static std::unique_ptr<base::MessagePump> Create();
+
+ // Returns the MessagePumpMojo instance of the current thread, if it exists.
+ static MessagePumpMojo* current();
+
+ static bool IsCurrent() { return !!current(); }
+
+ // Registers a MessagePumpMojoHandler for the specified handle. Only one
+ // handler can be registered for a specified handle.
+ // NOTE: a value of 0 for |deadline| indicates an indefinite timeout.
+ void AddHandler(MessagePumpMojoHandler* handler,
+ const Handle& handle,
+ MojoHandleSignals wait_signals,
+ base::TimeTicks deadline);
+
+ void RemoveHandler(const Handle& handle);
+
+ void AddObserver(Observer*);
+ void RemoveObserver(Observer*);
+
+ // MessagePump:
+ void Run(Delegate* delegate) override;
+ void Quit() override;
+ void ScheduleWork() override;
+ void ScheduleDelayedWork(const base::TimeTicks& delayed_work_time) override;
+
+ private:
+ struct RunState;
+
+ // Contains the data needed to track a request to AddHandler().
+ struct Handler {
+ Handler() : handler(NULL), wait_signals(MOJO_HANDLE_SIGNAL_NONE), id(0) {}
+
+ MessagePumpMojoHandler* handler;
+ MojoHandleSignals wait_signals;
+ base::TimeTicks deadline;
+ // See description of |MessagePumpMojo::next_handler_id_| for details.
+ int id;
+ };
+
+ struct HandleHasher {
+ size_t operator()(const Handle& handle) const {
+ return std::hash<uint32_t>()(static_cast<uint32_t>(handle.value()));
+ }
+ };
+
+ using HandleToHandler = std::unordered_map<Handle, Handler, HandleHasher>;
+
+ // Implementation of Run().
+ void DoRunLoop(RunState* run_state, Delegate* delegate);
+
+ // Services the set of handles ready. If |block| is true this waits for a
+ // handle to become ready, otherwise this does not block. Returns |true| if a
+ // handle has become ready, |false| otherwise.
+ bool DoInternalWork(const RunState& run_state, bool block);
+
+ bool DoNonMojoWork(const RunState& run_state, bool block);
+
+ // Waits for handles in the wait set to become ready. Returns |true| if ready
+ // handles may be available, or |false| if the wait's deadline was exceeded.
+ // Note, ready handles may be unavailable, even though |true| was returned.
+ bool WaitForReadyHandles(const RunState& run_state) const;
+
+ // Retrieves any 'ready' handles from the wait set, and runs the handler's
+ // OnHandleReady() or OnHandleError() functions as necessary. Returns |true|
+ // if any handles were ready and processed.
+ bool ProcessReadyHandles();
+
+ // Removes any handles that have expired their deadline. Runs the handler's
+ // OnHandleError() function with |MOJO_RESULT_DEADLINE_EXCEEDED| as the
+ // result. Returns |true| if any handles were removed.
+ bool RemoveExpiredHandles();
+
+ void SignalControlPipe();
+
+ // Returns the deadline for the call to MojoWait().
+ MojoDeadline GetDeadlineForWait(const RunState& run_state) const;
+
+ // Run |OnHandleReady()| for the handler registered with |handle|. |handle|
+ // must be registered.
+ void SignalHandleReady(Handle handle);
+
+ // Run |OnHandleError()| for the handler registered with |handle| and the
+ // error code |result|. |handle| must be registered, and will be removed
+ // before calling |OnHandleError()|.
+ void SignalHandleError(Handle handle, MojoResult result);
+
+ void WillSignalHandler();
+ void DidSignalHandler();
+
+ // If non-NULL we're running (inside Run()). Member is reference to value on
+ // stack.
+ RunState* run_state_;
+
+ // Lock for accessing |run_state_|. In general the only method that we have to
+ // worry about is ScheduleWork(). All other methods are invoked on the same
+ // thread.
+ base::Lock run_state_lock_;
+
+ HandleToHandler handlers_;
+ // Set of handles that have a deadline set. Avoids iterating over all elements
+ // in |handles_| in the common case (no deadline set).
+ // TODO(amistry): Make this better and avoid special-casing deadlines.
+ std::set<Handle> deadline_handles_;
+
+ // An ever increasing value assigned to each Handler::id. Used to detect
+ // uniqueness while notifying. That is, while notifying expired timers we copy
+ // |handlers_| and only notify handlers whose id match. If the id does not
+ // match it means the handler was removed then added so that we shouldn't
+ // notify it.
+ int next_handler_id_;
+
+ base::ObserverList<Observer> observers_;
+
+ // Mojo handle for the wait set.
+ ScopedHandle wait_set_handle_;
+ // Used to wake up run loop from |SignalControlPipe()|.
+ ScopedMessagePipeHandle read_handle_;
+ ScopedMessagePipeHandle write_handle_;
+
+ // Used to sleep until there is more work to do, when the Mojo EDK is shutting
+ // down.
+ base::WaitableEvent event_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpMojo);
+};
+
+} // namespace common
+} // namespace mojo
+
+#endif // MOJO_MESSAGE_PUMP_MESSAGE_PUMP_MOJO_H_
diff --git a/mojo/message_pump/message_pump_mojo_handler.h b/mojo/message_pump/message_pump_mojo_handler.h
new file mode 100644
index 0000000..8beb9ba
--- /dev/null
+++ b/mojo/message_pump/message_pump_mojo_handler.h
@@ -0,0 +1,29 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_MESSAGE_PUMP_MESSAGE_PUMP_MOJO_HANDLER_H_
+#define MOJO_MESSAGE_PUMP_MESSAGE_PUMP_MOJO_HANDLER_H_
+
+#include "mojo/message_pump/mojo_message_pump_export.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace common {
+
+// Used by MessagePumpMojo to notify when a handle is either ready or has become
+// invalid. In case of error, the handler will be removed.
+class MOJO_MESSAGE_PUMP_EXPORT MessagePumpMojoHandler {
+ public:
+ virtual void OnHandleReady(const Handle& handle) = 0;
+
+ virtual void OnHandleError(const Handle& handle, MojoResult result) = 0;
+
+ protected:
+ virtual ~MessagePumpMojoHandler() {}
+};
+
+} // namespace common
+} // namespace mojo
+
+#endif // MOJO_MESSAGE_PUMP_MESSAGE_PUMP_MOJO_HANDLER_H_
diff --git a/mojo/message_pump/message_pump_mojo_unittest.cc b/mojo/message_pump/message_pump_mojo_unittest.cc
new file mode 100644
index 0000000..1abb27c
--- /dev/null
+++ b/mojo/message_pump/message_pump_mojo_unittest.cc
@@ -0,0 +1,193 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/message_pump/message_pump_mojo.h"
+
+#include "base/macros.h"
+#include "base/message_loop/message_loop_test.h"
+#include "base/run_loop.h"
+#include "mojo/message_pump/message_pump_mojo_handler.h"
+#include "mojo/public/cpp/system/core.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace common {
+namespace test {
+
+std::unique_ptr<base::MessagePump> CreateMojoMessagePump() {
+ return std::unique_ptr<base::MessagePump>(new MessagePumpMojo());
+}
+
+RUN_MESSAGE_LOOP_TESTS(Mojo, &CreateMojoMessagePump);
+
+class CountingMojoHandler : public MessagePumpMojoHandler {
+ public:
+ CountingMojoHandler() : success_count_(0), error_count_(0) {}
+
+ void OnHandleReady(const Handle& handle) override {
+ ReadMessageRaw(static_cast<const MessagePipeHandle&>(handle),
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ MOJO_READ_MESSAGE_FLAG_NONE);
+ ++success_count_;
+ if (success_count_ == success_callback_count_ &&
+ !success_callback_.is_null()) {
+ success_callback_.Run();
+ success_callback_.Reset();
+ }
+ }
+
+ void set_success_callback(const base::Closure& callback,
+ int success_count) {
+ success_callback_ = callback;
+ success_callback_count_ = success_count;
+ }
+
+ void OnHandleError(const Handle& handle, MojoResult result) override {
+ ++error_count_;
+ if (!error_callback_.is_null()) {
+ error_callback_.Run();
+ error_callback_.Reset();
+ }
+ }
+
+ void set_error_callback(const base::Closure& callback) {
+ error_callback_ = callback;
+ }
+
+ int success_count() { return success_count_; }
+ int error_count() { return error_count_; }
+
+ private:
+ int success_count_;
+ int error_count_;
+
+ base::Closure error_callback_;
+ int success_callback_count_;
+
+ base::Closure success_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(CountingMojoHandler);
+};
+
+class CountingObserver : public MessagePumpMojo::Observer {
+ public:
+ void WillSignalHandler() override { will_signal_handler_count++; }
+ void DidSignalHandler() override { did_signal_handler_count++; }
+
+ int will_signal_handler_count = 0;
+ int did_signal_handler_count = 0;
+};
+
+TEST(MessagePumpMojo, RunUntilIdle) {
+ base::MessageLoop message_loop(MessagePumpMojo::Create());
+ CountingMojoHandler handler;
+ base::RunLoop run_loop;
+ handler.set_success_callback(run_loop.QuitClosure(), 2);
+ MessagePipe handles;
+ MessagePumpMojo::current()->AddHandler(&handler,
+ handles.handle0.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ base::TimeTicks());
+ WriteMessageRaw(
+ handles.handle1.get(), NULL, 0, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
+ WriteMessageRaw(
+ handles.handle1.get(), NULL, 0, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
+ MojoHandleSignalsState hss;
+ ASSERT_EQ(MOJO_RESULT_OK,
+ MojoWait(handles.handle0.get().value(), MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE, &hss));
+ run_loop.Run();
+ EXPECT_EQ(2, handler.success_count());
+}
+
+TEST(MessagePumpMojo, Observer) {
+ base::MessageLoop message_loop(MessagePumpMojo::Create());
+
+ CountingObserver observer;
+ MessagePumpMojo::current()->AddObserver(&observer);
+
+ CountingMojoHandler handler;
+ base::RunLoop run_loop;
+ handler.set_success_callback(run_loop.QuitClosure(), 1);
+ MessagePipe handles;
+ MessagePumpMojo::current()->AddHandler(&handler,
+ handles.handle0.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ base::TimeTicks());
+ WriteMessageRaw(
+ handles.handle1.get(), NULL, 0, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
+
+ MojoHandleSignalsState hss;
+ ASSERT_EQ(MOJO_RESULT_OK,
+ MojoWait(handles.handle0.get().value(), MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE, &hss));
+ run_loop.Run();
+ EXPECT_EQ(1, handler.success_count());
+ EXPECT_EQ(1, observer.will_signal_handler_count);
+ EXPECT_EQ(1, observer.did_signal_handler_count);
+ MessagePumpMojo::current()->RemoveObserver(&observer);
+
+ base::RunLoop run_loop2;
+ handler.set_success_callback(run_loop2.QuitClosure(), 2);
+ WriteMessageRaw(
+ handles.handle1.get(), NULL, 0, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
+ ASSERT_EQ(MOJO_RESULT_OK,
+ MojoWait(handles.handle0.get().value(), MOJO_HANDLE_SIGNAL_READABLE,
+ MOJO_DEADLINE_INDEFINITE, &hss));
+ run_loop2.Run();
+ EXPECT_EQ(2, handler.success_count());
+ EXPECT_EQ(1, observer.will_signal_handler_count);
+ EXPECT_EQ(1, observer.did_signal_handler_count);
+}
+
+TEST(MessagePumpMojo, UnregisterAfterDeadline) {
+ base::MessageLoop message_loop(MessagePumpMojo::Create());
+ CountingMojoHandler handler;
+ base::RunLoop run_loop;
+ handler.set_error_callback(run_loop.QuitClosure());
+ MessagePipe handles;
+ MessagePumpMojo::current()->AddHandler(
+ &handler,
+ handles.handle0.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1));
+ run_loop.Run();
+ EXPECT_EQ(1, handler.error_count());
+}
+
+TEST(MessagePumpMojo, AddClosedHandle) {
+ base::MessageLoop message_loop(MessagePumpMojo::Create());
+ CountingMojoHandler handler;
+ MessagePipe handles;
+ Handle closed_handle = handles.handle0.get();
+ handles.handle0.reset();
+ MessagePumpMojo::current()->AddHandler(
+ &handler, closed_handle, MOJO_HANDLE_SIGNAL_READABLE, base::TimeTicks());
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ MessagePumpMojo::current()->RemoveHandler(closed_handle);
+ EXPECT_EQ(0, handler.error_count());
+ EXPECT_EQ(0, handler.success_count());
+}
+
+TEST(MessagePumpMojo, CloseAfterAdding) {
+ base::MessageLoop message_loop(MessagePumpMojo::Create());
+ CountingMojoHandler handler;
+ MessagePipe handles;
+ MessagePumpMojo::current()->AddHandler(&handler, handles.handle0.get(),
+ MOJO_HANDLE_SIGNAL_READABLE,
+ base::TimeTicks());
+ handles.handle0.reset();
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ EXPECT_EQ(1, handler.error_count());
+ EXPECT_EQ(0, handler.success_count());
+}
+
+} // namespace test
+} // namespace common
+} // namespace mojo
diff --git a/mojo/message_pump/mojo_message_pump_export.h b/mojo/message_pump/mojo_message_pump_export.h
new file mode 100644
index 0000000..f8c1864
--- /dev/null
+++ b/mojo/message_pump/mojo_message_pump_export.h
@@ -0,0 +1,32 @@
+// 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_MESSAGE_PUMP_MOJO_MESSAGE_PUMP_EXPORT_H_
+#define MOJO_MESSAGE_PUMP_MOJO_MESSAGE_PUMP_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(MOJO_MESSAGE_PUMP_IMPLEMENTATION)
+#define MOJO_MESSAGE_PUMP_EXPORT __declspec(dllexport)
+#else
+#define MOJO_MESSAGE_PUMP_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_MESSAGE_PUMP_IMPLEMENTATION)
+#define MOJO_MESSAGE_PUMP_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_MESSAGE_PUMP_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#else // !defined(COMPONENT_BUILD)
+#define MOJO_MESSAGE_PUMP_EXPORT
+#endif
+
+#endif // MOJO_MESSAGE_PUMP_MOJO_MESSAGE_PUMP_EXPORT_H_
diff --git a/mojo/message_pump/time_helper.cc b/mojo/message_pump/time_helper.cc
new file mode 100644
index 0000000..ffd667e
--- /dev/null
+++ b/mojo/message_pump/time_helper.cc
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/message_pump/time_helper.h"
+
+#include "base/time/tick_clock.h"
+
+namespace mojo {
+namespace common {
+
+namespace {
+
+base::TickClock* tick_clock = NULL;
+
+} // namespace
+
+namespace test {
+
+void SetTickClockForTest(base::TickClock* clock) {
+ tick_clock = clock;
+}
+} // namespace test
+
+namespace internal {
+
+base::TimeTicks NowTicks() {
+ return tick_clock ? tick_clock->NowTicks() : base::TimeTicks::Now();
+}
+
+} // namespace internal
+} // namespace common
+} // namespace mojo
diff --git a/mojo/message_pump/time_helper.h b/mojo/message_pump/time_helper.h
new file mode 100644
index 0000000..6079000
--- /dev/null
+++ b/mojo/message_pump/time_helper.h
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_MESSAGE_PUMP_TIME_HELPER_H_
+#define MOJO_MESSAGE_PUMP_TIME_HELPER_H_
+
+#include "base/time/time.h"
+#include "mojo/message_pump/mojo_message_pump_export.h"
+
+namespace base {
+class TickClock;
+}
+
+namespace mojo {
+namespace common {
+namespace test {
+
+// Sets the TickClock used for getting TimeTicks::Now(). This is currently used
+// by both HandleWatcher and MessagePumpMojo.
+MOJO_MESSAGE_PUMP_EXPORT void SetTickClockForTest(base::TickClock* clock);
+
+} // namespace test
+
+namespace internal {
+
+// Returns now. Used internally; generally not useful.
+MOJO_MESSAGE_PUMP_EXPORT base::TimeTicks NowTicks();
+
+} // namespace internal
+} // namespace common
+} // namespace mojo
+
+#endif // MOJO_MESSAGE_PUMP_TIME_HELPER_H_
diff --git a/mojo/mojo.gyp b/mojo/mojo.gyp
new file mode 100644
index 0000000..1ef0c4d
--- /dev/null
+++ b/mojo/mojo.gyp
@@ -0,0 +1,18 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ # GN version: //mojo
+ 'target_name': 'mojo',
+ 'type': 'none',
+ 'dependencies': [
+ 'mojo_base.gyp:mojo_base',
+ 'mojo_edk_tests.gyp:mojo_edk_tests',
+ 'mojo_public.gyp:mojo_public',
+ ],
+ },
+ ]
+}
diff --git a/mojo/mojo_base.gyp b/mojo/mojo_base.gyp
new file mode 100644
index 0000000..a067353
--- /dev/null
+++ b/mojo/mojo_base.gyp
@@ -0,0 +1,192 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Essential components (and their tests) that are needed to build
+# Chrome should be here. Other components that are useful only in
+# Mojo land like mojo_shell should be in mojo.gyp.
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'mojo_base',
+ 'type': 'none',
+ 'dependencies': [
+ # NOTE: If adding a new dependency here, please consider whether it
+ # should also be added to the list of Mojo-related dependencies of
+ # build/all.gyp:All on iOS, as All cannot depend on the mojo_base
+ # target on iOS due to the presence of the js targets, which cause v8
+ # to be built.
+ 'mojo_common_lib',
+ 'mojo_common_unittests',
+ ],
+ 'conditions': [
+ ['OS == "android"', {
+ 'dependencies': [
+ 'mojo_public.gyp:mojo_bindings_java',
+ 'mojo_public.gyp:mojo_public_java',
+ ],
+ }],
+ ]
+ },
+ {
+ 'target_name': 'mojo_none',
+ 'type': 'none',
+ },
+ {
+ # GN version: //mojo/common
+ 'target_name': 'mojo_common_lib',
+ 'type': '<(component)',
+ 'defines': [
+ 'MOJO_COMMON_IMPLEMENTATION',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../mojo/mojo_public.gyp:mojo_public_system',
+ ],
+ 'sources': [
+ 'common/common_type_converters.cc',
+ 'common/common_type_converters.h',
+ 'common/data_pipe_file_utils.cc',
+ 'common/data_pipe_utils.cc',
+ 'common/data_pipe_utils.h',
+ ],
+ },
+ {
+ # GN version: //mojo/common:common_custom_types
+ 'target_name': 'mojo_common_custom_types_mojom',
+ 'type': 'none',
+ 'variables': {
+ 'mojom_files': [
+ 'common/common_custom_types.mojom',
+ ],
+ 'mojom_typemaps': [
+ 'common/common_custom_types.typemap',
+ ],
+ },
+ 'dependencies': [
+ '../ipc/ipc.gyp:ipc',
+ ],
+ 'includes': [ 'mojom_bindings_generator_explicit.gypi' ],
+ },
+ {
+ # GN version: //mojo/common:test_common_custom_types
+ 'target_name': 'mojo_test_common_custom_types',
+ 'type': 'static_library',
+ 'variables': {
+ 'mojom_typemaps': [
+ 'common/common_custom_types.typemap',
+ ],
+ },
+ 'sources': [
+ 'common/test_common_custom_types.mojom',
+ ],
+ 'dependencies': [
+ 'mojo_common_custom_types_mojom',
+ ],
+ 'includes': [ 'mojom_bindings_generator.gypi' ],
+ },
+ {
+ # GN version: //mojo/common:mojo_common_unittests
+ 'target_name': 'mojo_common_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:test_support_base',
+ '../base/base.gyp:base_message_loop_tests',
+ '../testing/gtest.gyp:gtest',
+ '../url/url.gyp:url_lib',
+ 'mojo_common_custom_types_mojom',
+ 'mojo_common_lib',
+ 'mojo_test_common_custom_types',
+ 'mojo_edk.gyp:mojo_system_impl',
+ 'mojo_edk.gyp:mojo_common_test_support',
+ 'mojo_edk.gyp:mojo_run_all_unittests',
+ 'mojo_public.gyp:mojo_cpp_bindings',
+ 'mojo_public.gyp:mojo_public_test_utils',
+ ],
+ 'sources': [
+ 'common/common_custom_types_unittest.cc',
+ 'common/common_type_converters_unittest.cc',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS=="android"', {
+ 'targets': [
+ {
+ 'target_name': 'mojo_jni_headers',
+ 'type': 'none',
+ 'dependencies': [
+ 'mojo_java_set_jni_headers',
+ ],
+ 'sources': [
+ 'android/javatests/src/org/chromium/mojo/MojoTestCase.java',
+ 'android/javatests/src/org/chromium/mojo/bindings/ValidationTestUtil.java',
+ 'android/system/src/org/chromium/mojo/system/impl/CoreImpl.java',
+ ],
+ 'variables': {
+ 'jni_gen_package': 'mojo',
+ },
+ 'includes': [ '../build/jni_generator.gypi' ],
+ },
+ {
+ 'target_name': 'libmojo_system_java',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'mojo_common_lib',
+ 'mojo_edk.gyp:mojo_system_impl',
+ 'mojo_jni_headers',
+ 'mojo_public.gyp:mojo_message_pump_lib',
+ ],
+ 'sources': [
+ 'android/system/core_impl.cc',
+ 'android/system/core_impl.h',
+ ],
+ },
+ {
+ 'target_name': 'mojo_java_set_jni_headers',
+ 'type': 'none',
+ 'variables': {
+ 'jni_gen_package': 'mojo',
+ 'input_java_class': 'java/util/HashSet.class',
+ },
+ 'includes': [ '../build/jar_file_jni_generator.gypi' ],
+ },
+ {
+ 'target_name': 'mojo_system_java',
+ 'type': 'none',
+ 'dependencies': [
+ '../base/base.gyp:base_java',
+ 'libmojo_system_java',
+ 'mojo_public.gyp:mojo_public_java',
+ ],
+ 'variables': {
+ 'java_in_dir': '<(DEPTH)/mojo/android/system',
+ },
+ 'includes': [ '../build/java.gypi' ],
+ },
+ ],
+ }],
+ ['test_isolation_mode != "noop"', {
+ 'targets': [
+ {
+ 'target_name': 'mojo_common_unittests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'mojo_common_unittests',
+ ],
+ 'includes': [
+ '../build/isolate.gypi',
+ ],
+ 'sources': [
+ 'mojo_common_unittests.isolate',
+ ],
+ },
+ ],
+ }],
+ ]
+}
diff --git a/mojo/mojo_common_unittests.isolate b/mojo/mojo_common_unittests.isolate
new file mode 100644
index 0000000..a72311c
--- /dev/null
+++ b/mojo/mojo_common_unittests.isolate
@@ -0,0 +1,23 @@
+# 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.
+{
+ 'includes': [
+ '../base/base.isolate',
+ ],
+ 'conditions': [
+ ['OS=="win" or OS=="mac" or OS=="linux"', {
+ 'variables': {
+ 'command': [
+ '../testing/test_env.py',
+ '<(PRODUCT_DIR)/mojo_common_unittests<(EXECUTABLE_SUFFIX)',
+ '--brave-new-test-launcher',
+ '--test-launcher-bot-mode',
+ ],
+ 'files': [
+ '../testing/test_env.py',
+ ],
+ },
+ }],
+ ],
+}
diff --git a/mojo/mojo_edk.gyp b/mojo/mojo_edk.gyp
new file mode 100644
index 0000000..5541839
--- /dev/null
+++ b/mojo/mojo_edk.gyp
@@ -0,0 +1,233 @@
+# 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'includes': [
+ 'mojo_edk.gypi',
+ ],
+ 'target_defaults' : {
+ 'include_dirs': [
+ '..',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ },
+ 'targets': [
+ {
+ # GN version: //mojo/edk/system/ports
+ 'target_name': 'mojo_system_ports',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../crypto/crypto.gyp:crypto',
+ ],
+ 'sources': [
+ '<@(mojo_edk_ports_sources)',
+ ],
+ },
+ {
+ # GN version: //mojo/edk/system
+ 'target_name': 'mojo_system_impl',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../crypto/crypto.gyp:crypto',
+ 'mojo_public.gyp:mojo_public_system',
+ 'mojo_system_ports',
+ ],
+ 'defines': [
+ 'MOJO_SYSTEM_IMPL_IMPLEMENTATION',
+ ],
+ 'sources': [
+ '<@(mojo_edk_system_impl_sources)',
+ '<@(mojo_edk_system_impl_non_nacl_sources)',
+ ],
+ 'conditions': [
+ ['OS=="android"', {
+ 'dependencies': [
+ '../third_party/ashmem/ashmem.gyp:ashmem',
+ ],
+ }],
+ ['OS=="android" or chromeos==1', {
+ 'defines': [
+ 'MOJO_EDK_LEGACY_PROTOCOL',
+ ],
+ }],
+ ['OS=="win"', {
+ # Structure was padded due to __declspec(align()), which is
+ # uninteresting.
+ 'msvs_disabled_warnings': [ 4324 ],
+ }],
+ ['OS=="mac" and OS!="ios"', {
+ 'sources': [
+ 'edk/system/mach_port_relay.cc',
+ 'edk/system/mach_port_relay.h',
+ ],
+ }],
+ ],
+ },
+ {
+ # GN version: //mojo/edk/js
+ 'target_name': 'mojo_js_lib',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../gin/gin.gyp:gin',
+ '../v8/src/v8.gyp:v8',
+ ],
+ 'export_dependent_settings': [
+ '../base/base.gyp:base',
+ '../gin/gin.gyp:gin',
+ ],
+ 'sources': [
+ # Sources list duplicated in GN build.
+ 'edk/js/core.cc',
+ 'edk/js/core.h',
+ 'edk/js/drain_data.cc',
+ 'edk/js/drain_data.h',
+ 'edk/js/handle.cc',
+ 'edk/js/handle.h',
+ 'edk/js/handle_close_observer.h',
+ 'edk/js/mojo_runner_delegate.cc',
+ 'edk/js/mojo_runner_delegate.h',
+ 'edk/js/support.cc',
+ 'edk/js/support.h',
+ 'edk/js/threading.cc',
+ 'edk/js/threading.h',
+ 'edk/js/waiting_callback.cc',
+ 'edk/js/waiting_callback.h',
+ ],
+ },
+ {
+ # GN version: //mojo/edk/test:test_support_impl
+ 'target_name': 'mojo_test_support_impl',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'sources': [
+ 'edk/test/test_support_impl.cc',
+ 'edk/test/test_support_impl.h',
+ ],
+ },
+ {
+ # GN version: //mojo/edk/test:test_support
+ 'target_name': 'mojo_common_test_support',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:test_support_base',
+ '../testing/gtest.gyp:gtest',
+ 'mojo_system_impl',
+ ],
+ 'sources': [
+ 'edk/test/mojo_test_base.cc',
+ 'edk/test/mojo_test_base.h',
+ 'edk/test/multiprocess_test_helper.cc',
+ 'edk/test/multiprocess_test_helper.h',
+ 'edk/test/scoped_ipc_support.cc',
+ 'edk/test/scoped_ipc_support.h',
+ 'edk/test/test_utils.h',
+ 'edk/test/test_utils_posix.cc',
+ 'edk/test/test_utils_win.cc',
+ ],
+ 'conditions': [
+ ['OS=="ios"', {
+ 'sources!': [
+ 'edk/test/multiprocess_test_helper.cc',
+ ],
+ }],
+ ],
+ },
+ {
+ # GN version: //mojo/edk/test:run_all_unittests
+ 'target_name': 'mojo_run_all_unittests',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:test_support_base',
+ '../testing/gtest.gyp:gtest',
+ 'mojo_common_test_support',
+ 'mojo_public.gyp:mojo_public_test_support',
+ 'mojo_system_impl',
+ 'mojo_test_support_impl',
+ ],
+ 'sources': [
+ 'edk/test/run_all_unittests.cc',
+ ],
+ },
+ {
+ # GN version: //mojo/edk/test:run_all_perftests
+ 'target_name': 'mojo_run_all_perftests',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:test_support_base',
+ '../testing/gtest.gyp:gtest',
+ 'mojo_common_test_support',
+ 'mojo_public.gyp:mojo_public_test_support',
+ 'mojo_system_impl',
+ 'mojo_test_support_impl',
+ ],
+ 'sources': [
+ 'edk/test/run_all_perftests.cc',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS == "win" and target_arch=="ia32"', {
+ 'targets': [
+ {
+ # GN version: //mojo/edk/system/ports
+ 'target_name': 'mojo_system_ports_win64',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base_win64',
+ '../crypto/crypto.gyp:crypto_nacl_win64',
+ ],
+ 'sources': [
+ '<@(mojo_edk_ports_sources)',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ {
+ # GN version: //mojo/edk/system
+ 'target_name': 'mojo_system_impl_win64',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../base/base.gyp:base_win64',
+ '../crypto/crypto.gyp:crypto_nacl_win64',
+ 'mojo_public.gyp:mojo_public_system_win64',
+ 'mojo_system_ports_win64',
+ ],
+ 'defines': [
+ 'MOJO_SYSTEM_IMPL_IMPLEMENTATION',
+ ],
+ 'sources': [
+ '<@(mojo_edk_system_impl_sources)',
+ '<@(mojo_edk_system_impl_non_nacl_sources)',
+ ],
+ # Structure was padded due to __declspec(align()), which is
+ # uninteresting.
+ 'msvs_disabled_warnings': [ 4324 ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ ],
+ }],
+ ]
+}
diff --git a/mojo/mojo_edk_tests.gyp b/mojo/mojo_edk_tests.gyp
new file mode 100644
index 0000000..e192462
--- /dev/null
+++ b/mojo/mojo_edk_tests.gyp
@@ -0,0 +1,397 @@
+# 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'mojo_edk_tests',
+ 'type': 'none',
+ 'dependencies': [
+ # NOTE: If adding a new dependency here, please consider whether it
+ # should also be added to the list of Mojo-related dependencies of
+ # build/all.gyp:All on iOS, as All cannot depend on the mojo_base
+ # target on iOS due to the presence of the js targets, which cause v8
+ # to be built.
+ 'mojo_message_pipe_perftests',
+ 'mojo_public_bindings_perftests',
+ 'mojo_public_bindings_unittests',
+ 'mojo_public_system_perftests',
+ 'mojo_public_system_unittests',
+ 'mojo_system_unittests',
+ 'mojo_js_unittests',
+ 'mojo_js_integration_tests',
+ ],
+ },
+ {
+ # GN version: //mojo/edk/test:mojo_public_bindings_unittests
+ 'target_name': 'mojo_public_bindings_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../testing/gtest.gyp:gtest',
+ 'mojo_edk.gyp:mojo_run_all_unittests',
+ 'mojo_public.gyp:mojo_cpp_bindings',
+ 'mojo_public.gyp:mojo_public_bindings_test_utils',
+ 'mojo_public.gyp:mojo_public_test_utils',
+ 'mojo_public_tests.gyp:mojo_public_test_associated_interfaces',
+ 'mojo_public_tests.gyp:mojo_public_test_interfaces',
+ 'mojo_public_tests.gyp:mojo_public_test_interfaces_blink',
+ 'mojo_public_tests.gyp:mojo_public_test_interfaces_struct_traits',
+ ],
+ 'variables': {
+ 'clang_warning_flags_unset': [ '-Wglobal-constructors' ],
+ },
+ 'sources': [
+ 'public/cpp/bindings/tests/array_common_test.h',
+ 'public/cpp/bindings/tests/array_unittest.cc',
+ 'public/cpp/bindings/tests/associated_interface_unittest.cc',
+ 'public/cpp/bindings/tests/bind_task_runner_unittest.cc',
+ 'public/cpp/bindings/tests/binding_callback_unittest.cc',
+ 'public/cpp/bindings/tests/binding_unittest.cc',
+ 'public/cpp/bindings/tests/buffer_unittest.cc',
+ 'public/cpp/bindings/tests/connector_unittest.cc',
+ 'public/cpp/bindings/tests/constant_unittest.cc',
+ 'public/cpp/bindings/tests/container_test_util.cc',
+ 'public/cpp/bindings/tests/container_test_util.h',
+ 'public/cpp/bindings/tests/equals_unittest.cc',
+ 'public/cpp/bindings/tests/handle_passing_unittest.cc',
+ 'public/cpp/bindings/tests/interface_ptr_unittest.cc',
+ 'public/cpp/bindings/tests/map_common_test.h',
+ 'public/cpp/bindings/tests/map_unittest.cc',
+ 'public/cpp/bindings/tests/message_queue.cc',
+ 'public/cpp/bindings/tests/message_queue.h',
+ 'public/cpp/bindings/tests/multiplex_router_unittest.cc',
+ 'public/cpp/bindings/tests/pickle_unittest.cc',
+ 'public/cpp/bindings/tests/pickled_types_blink.cc',
+ 'public/cpp/bindings/tests/pickled_types_blink.h',
+ 'public/cpp/bindings/tests/pickled_types_chromium.cc',
+ 'public/cpp/bindings/tests/pickled_types_chromium.h',
+ 'public/cpp/bindings/tests/rect_blink.h',
+ 'public/cpp/bindings/tests/rect_blink_traits.h',
+ 'public/cpp/bindings/tests/rect_chromium.h',
+ 'public/cpp/bindings/tests/rect_chromium_traits.h',
+ 'public/cpp/bindings/tests/request_response_unittest.cc',
+ 'public/cpp/bindings/tests/router_test_util.cc',
+ 'public/cpp/bindings/tests/router_test_util.h',
+ 'public/cpp/bindings/tests/router_unittest.cc',
+ 'public/cpp/bindings/tests/sample_service_unittest.cc',
+ 'public/cpp/bindings/tests/serialization_warning_unittest.cc',
+ 'public/cpp/bindings/tests/stl_converters_unittest.cc',
+ 'public/cpp/bindings/tests/string_unittest.cc',
+ 'public/cpp/bindings/tests/struct_traits_unittest.cc',
+ 'public/cpp/bindings/tests/struct_unittest.cc',
+ 'public/cpp/bindings/tests/struct_with_traits_impl.cc',
+ 'public/cpp/bindings/tests/struct_with_traits_impl.h',
+ 'public/cpp/bindings/tests/struct_with_traits_impl_traits.cc',
+ 'public/cpp/bindings/tests/struct_with_traits_impl_traits.h',
+ 'public/cpp/bindings/tests/sync_method_unittest.cc',
+ 'public/cpp/bindings/tests/type_conversion_unittest.cc',
+ 'public/cpp/bindings/tests/union_unittest.cc',
+ 'public/cpp/bindings/tests/validation_context_unittest.cc',
+ 'public/cpp/bindings/tests/validation_unittest.cc',
+ 'public/cpp/bindings/tests/variant_test_util.h',
+ ],
+ 'conditions': [
+ # TODO(yzshen): Blink-flavor bindings tests should be moved into
+ # mojo_public_bindings_for_blink_tests (which should eventually be moved
+ # into blink).
+ ['OS=="ios"', {
+ 'dependencies!': [
+ 'mojo_public.gyp:mojo_public_test_interfaces_blink',
+ ],
+ 'sources!': [
+ 'public/cpp/bindings/tests/pickle_unittest.cc',
+ 'public/cpp/bindings/tests/pickled_types_blink.cc',
+ 'public/cpp/bindings/tests/pickled_types_blink.h',
+ 'public/cpp/bindings/tests/pickled_types_chromium.cc',
+ 'public/cpp/bindings/tests/pickled_types_chromium.h',
+ 'public/cpp/bindings/tests/rect_blink.h',
+ 'public/cpp/bindings/tests/rect_blink_traits.h',
+ 'public/cpp/bindings/tests/struct_traits_unittest.cc',
+ ],
+ }],
+ ],
+ },
+ {
+ # GN version: //mojo/public/cpp/bindings/tests:for_blink_tests
+ 'target_name': 'mojo_public_bindings_for_blink_tests',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../testing/gtest.gyp:gtest',
+ 'mojo_public.gyp:mojo_cpp_bindings',
+ 'mojo_public_tests.gyp:mojo_public_test_interfaces',
+ 'mojo_public_tests.gyp:mojo_public_test_wtf_types',
+ 'mojo_public_tests.gyp:mojo_public_test_wtf_types_blink',
+ ],
+ 'variables': {
+ 'clang_warning_flags_unset': [ '-Wglobal-constructors' ],
+ },
+ 'sources': [
+ 'public/cpp/bindings/tests/array_common_test.h',
+ 'public/cpp/bindings/tests/container_test_util.cc',
+ 'public/cpp/bindings/tests/container_test_util.h',
+ 'public/cpp/bindings/tests/map_common_test.h',
+ 'public/cpp/bindings/tests/variant_test_util.h',
+ 'public/cpp/bindings/tests/wtf_array_unittest.cc',
+ 'public/cpp/bindings/tests/wtf_map_unittest.cc',
+ 'public/cpp/bindings/tests/wtf_types_unittest.cc',
+ ],
+ },
+ {
+ # GN version: //mojo/edk/test:mojo_public_bindings_perftests
+ 'target_name': 'mojo_public_bindings_perftests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:test_support_base',
+ '../testing/gtest.gyp:gtest',
+ 'mojo_base.gyp:mojo_common_lib',
+ 'mojo_edk.gyp:mojo_run_all_perftests',
+ 'mojo_public.gyp:mojo_cpp_bindings',
+ 'mojo_public.gyp:mojo_public_bindings_test_utils',
+ 'mojo_public.gyp:mojo_public_test_utils',
+ 'mojo_public_tests.gyp:mojo_public_test_interfaces',
+ ],
+ 'sources': [
+ 'public/cpp/bindings/tests/bindings_perftest.cc',
+ 'public/cpp/bindings/tests/e2e_perftest.cc',
+ ],
+ },
+ {
+ # GN version: //mojo/public/cpp/system/tests:mojo_public_system_unittests
+ # and //mojo/public/c/system/tests
+ 'target_name': 'mojo_public_system_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../testing/gtest.gyp:gtest',
+ 'mojo_edk.gyp:mojo_run_all_unittests',
+ 'mojo_public.gyp:mojo_cpp_system',
+ 'mojo_public.gyp:mojo_public_test_utils',
+ ],
+ 'sources': [
+ '<(DEPTH)/mojo/public/c/system/tests/core_unittest.cc',
+ '<(DEPTH)/mojo/public/c/system/tests/core_unittest_pure_c.c',
+ '<(DEPTH)/mojo/public/c/system/tests/macros_unittest.cc',
+ '<(DEPTH)/mojo/public/cpp/system/tests/core_unittest.cc',
+ '<(DEPTH)/mojo/public/cpp/system/tests/watcher_unittest.cc',
+ ],
+ },
+ {
+ # GN version: //mojo/edk/test:mojo_public_system_perftests
+ 'target_name': 'mojo_public_system_perftests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../testing/gtest.gyp:gtest',
+ 'mojo_edk.gyp:mojo_run_all_perftests',
+ 'mojo_public.gyp:mojo_public_system',
+ 'mojo_public.gyp:mojo_public_test_utils',
+ ],
+ 'sources': [
+ 'public/c/system/tests/core_perftest.cc',
+ ],
+ },
+ {
+ # GN version: //mojo/edk/system:mojo_system_unittests
+ 'target_name': 'mojo_system_unittests',
+ 'type': '<(gtest_target_type)',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../testing/gtest.gyp:gtest',
+ 'mojo_edk.gyp:mojo_common_test_support',
+ 'mojo_edk.gyp:mojo_run_all_unittests',
+ 'mojo_edk.gyp:mojo_system_impl',
+ 'mojo_edk.gyp:mojo_system_ports',
+ 'mojo_public.gyp:mojo_public_system',
+ ],
+ 'sources': [
+ 'edk/embedder/embedder_unittest.cc',
+ 'edk/embedder/platform_channel_pair_posix_unittest.cc',
+ 'edk/embedder/platform_shared_buffer_unittest.cc',
+ 'edk/system/awakable_list_unittest.cc',
+ 'edk/system/core_test_base.cc',
+ 'edk/system/core_test_base.h',
+ 'edk/system/core_unittest.cc',
+ 'edk/system/message_pipe_unittest.cc',
+ 'edk/system/multiprocess_message_pipe_unittest.cc',
+ 'edk/system/options_validation_unittest.cc',
+ 'edk/system/platform_handle_dispatcher_unittest.cc',
+ 'edk/system/platform_wrapper_unittest.cc',
+ 'edk/system/ports/ports_unittest.cc',
+ 'edk/system/shared_buffer_dispatcher_unittest.cc',
+ 'edk/system/shared_buffer_unittest.cc',
+ 'edk/system/test_utils.cc',
+ 'edk/system/test_utils.h',
+ 'edk/system/wait_set_dispatcher_unittest.cc',
+ 'edk/system/waiter_test_utils.cc',
+ 'edk/system/waiter_test_utils.h',
+ 'edk/system/waiter_unittest.cc',
+ 'edk/system/watch_unittest.cc',
+ ],
+ 'conditions': [
+ ['OS=="ios"', {
+ 'sources!': [
+ 'edk/system/multiprocess_message_pipe_unittest.cc',
+ ],
+ }],
+ ['OS == "android"', {
+ 'dependencies': [
+ '../testing/android/native_test.gyp:native_test_native_code',
+ ],
+ }],
+ ],
+ },
+ {
+ # GN version: //mojo/edk/system:mojo_message_pipe_perftests
+ 'target_name': 'mojo_message_pipe_perftests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:test_support_base',
+ '../testing/gtest.gyp:gtest',
+ 'mojo_edk.gyp:mojo_common_test_support',
+ 'mojo_edk.gyp:mojo_run_all_perftests',
+ 'mojo_edk.gyp:mojo_system_impl',
+ 'mojo_public.gyp:mojo_public_system',
+ ],
+ 'sources': [
+ 'edk/system/message_pipe_perftest.cc',
+ 'edk/system/test_utils.cc',
+ 'edk/system/test_utils.h',
+ ],
+ },
+ # TODO(yzshen): fix the following two targets.
+ {
+ # GN version: //mojo/edk/js/test:js_unittests
+ 'target_name': 'mojo_js_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../gin/gin.gyp:gin_test',
+ 'mojo_edk.gyp:mojo_common_test_support',
+ 'mojo_edk.gyp:mojo_run_all_unittests',
+ 'mojo_edk.gyp:mojo_js_lib',
+ 'mojo_public_tests.gyp:mojo_public_test_interfaces',
+ ],
+ 'sources': [
+ 'edk/js/handle_unittest.cc',
+ 'edk/js/test/run_js_tests.cc',
+ ],
+ },
+ {
+ # GN version: //mojo/edk/js/test:js_integration_tests
+ 'target_name': 'mojo_js_integration_tests',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../gin/gin.gyp:gin_test',
+ 'mojo_base.gyp:mojo_common_lib',
+ 'mojo_edk.gyp:mojo_js_lib',
+ 'mojo_edk.gyp:mojo_run_all_unittests',
+ 'mojo_js_to_cpp_bindings',
+ 'mojo_public_tests.gyp:mojo_public_test_interfaces',
+ ],
+ 'sources': [
+ 'edk/js/test/run_js_integration_tests.cc',
+ 'edk/js/tests/js_to_cpp_tests.cc',
+ ],
+ },
+ {
+ 'target_name': 'mojo_js_to_cpp_bindings',
+ 'type': 'none',
+ 'variables': {
+ 'mojom_files': [
+ 'edk/js/tests/js_to_cpp.mojom',
+ ],
+ },
+ 'includes': [ 'mojom_bindings_generator_explicit.gypi' ],
+ },
+ ],
+ 'conditions': [
+ ['test_isolation_mode != "noop"', {
+ 'targets': [
+ {
+ 'target_name': 'mojo_public_bindings_unittests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'mojo_public_bindings_unittests',
+ ],
+ 'includes': [
+ '../build/isolate.gypi',
+ ],
+ 'sources': [
+ 'mojo_public_bindings_unittests.isolate',
+ ],
+ },
+ {
+ 'target_name': 'mojo_public_system_unittests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'mojo_public_system_unittests',
+ ],
+ 'includes': [
+ '../build/isolate.gypi',
+ ],
+ 'sources': [
+ 'mojo_public_system_unittests.isolate',
+ ],
+ },
+ {
+ 'target_name': 'mojo_js_unittests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'mojo_js_unittests',
+ ],
+ 'includes': [
+ '../build/isolate.gypi',
+ ],
+ 'sources': [
+ 'mojo_js_unittests.isolate',
+ ],
+ },
+ {
+ 'target_name': 'mojo_js_integration_tests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'mojo_js_integration_tests',
+ ],
+ 'includes': [
+ '../build/isolate.gypi',
+ ],
+ 'sources': [
+ 'mojo_js_integration_tests.isolate',
+ ],
+ },
+ {
+ 'target_name': 'mojo_system_unittests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'mojo_system_unittests',
+ ],
+ 'includes': [
+ '../build/isolate.gypi',
+ ],
+ 'sources': [
+ 'mojo_system_unittests.isolate',
+ ],
+ },
+ ],
+ }],
+ ['OS == "android"', {
+ 'targets': [
+ {
+ 'target_name': 'mojo_system_unittests_apk',
+ 'type': 'none',
+ 'dependencies': [
+ 'mojo_system_unittests',
+ ],
+ 'variables': {
+ 'test_suite_name': 'mojo_system_unittests',
+ },
+ 'includes': [ '../build/apk_test.gypi' ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/mojo/mojo_js_unittests.isolate b/mojo/mojo_js_unittests.isolate
new file mode 100644
index 0000000..81bae0b
--- /dev/null
+++ b/mojo/mojo_js_unittests.isolate
@@ -0,0 +1,46 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+ 'includes': [
+ '../base/base.isolate',
+ '../gin/v8.isolate',
+ '../third_party/icu/icu.isolate',
+ ],
+ 'conditions': [
+ ['OS=="win" or OS=="mac" or OS=="linux"', {
+ 'variables': {
+ 'command': [
+ '../testing/test_env.py',
+ '<(PRODUCT_DIR)/mojo_js_unittests<(EXECUTABLE_SUFFIX)',
+ '--brave-new-test-launcher',
+ '--test-launcher-bot-mode',
+ ],
+ 'files': [
+ '../gin/test/expect.js',
+ '../testing/test_env.py',
+ 'public/interfaces/bindings/tests/data/validation/',
+ 'public/js/',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/math_calculator.mojom.js',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/no_module.mojom.js',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/ping_service.mojom.js',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/rect.mojom.js',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/regression_tests.mojom.js',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/sample_factory.mojom.js',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/sample_import.mojom.js',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/sample_import2.mojom.js',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.js',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/sample_service.mojom.js',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/scoping.mojom.js',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/serialization_test_structs.mojom.js',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/test_constants.mojom.js',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/test_native_types.mojom.js',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/test_structs.mojom.js',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/test_sync_methods.mojom.js',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/test_unions.mojom.js',
+ '<(PRODUCT_DIR)/gen/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom.js',
+ ],
+ },
+ }],
+ ],
+}
diff --git a/mojo/mojo_public.gyp b/mojo/mojo_public.gyp
new file mode 100644
index 0000000..005e012
--- /dev/null
+++ b/mojo/mojo_public.gyp
@@ -0,0 +1,310 @@
+# Copyright 2014 The Chroium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'includes': [
+ 'mojo_public.gypi',
+ ],
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'target_defaults' : {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'mojo_public',
+ 'type': 'none',
+ 'dependencies': [
+ 'mojo_js_bindings',
+ 'mojo_public_system',
+ ],
+ },
+ {
+ # GN version: //mojo/public/c/system
+ 'target_name': 'mojo_public_system',
+ 'type': '<(component)',
+ 'sources': [
+ '<@(mojo_public_system_sources)',
+ ],
+ 'defines': [
+ 'MOJO_SYSTEM_IMPLEMENTATION',
+ ],
+ },
+ {
+ # GN version: //mojo/public/cpp/system
+ 'target_name': 'mojo_cpp_system',
+ 'type': 'static_library',
+ 'sources': [
+ '<@(mojo_cpp_system_sources)',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'mojo_public_system',
+ ],
+ },
+ {
+ # GN version: //mojo/public/cpp/bindings
+ 'target_name': 'mojo_cpp_bindings',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..'
+ ],
+ 'sources': [
+ '<@(mojo_cpp_bindings_sources)',
+
+ # This comes from the mojo_interface_bindings_cpp_sources dependency.
+ '>@(mojom_generated_sources)',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'mojo_cpp_system',
+ 'mojo_interface_bindings_cpp_sources',
+ ],
+ },
+ {
+ # GN version: //mojo/message_pump
+ 'target_name': 'mojo_message_pump_lib',
+ 'type': '<(component)',
+ 'defines': [
+ 'MOJO_MESSAGE_PUMP_IMPLEMENTATION',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'mojo_cpp_system',
+ ],
+ 'sources': [
+ 'message_pump/handle_watcher.cc',
+ 'message_pump/handle_watcher.h',
+ 'message_pump/message_pump_mojo.cc',
+ 'message_pump/message_pump_mojo.h',
+ 'message_pump/message_pump_mojo_handler.h',
+ 'message_pump/time_helper.cc',
+ 'message_pump/time_helper.h',
+ ],
+ },
+ {
+ # GN version: //mojo/public/js
+ 'target_name': 'mojo_js_bindings',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..'
+ ],
+ 'sources': [
+ 'public/js/constants.cc',
+ 'public/js/constants.h',
+ ],
+ },
+ {
+ 'target_name': 'mojo_interface_bindings_mojom',
+ 'type': 'none',
+ 'variables': {
+ 'require_interface_bindings': 0,
+ 'mojom_files': [
+ 'public/interfaces/bindings/interface_control_messages.mojom',
+ 'public/interfaces/bindings/pipe_control_messages.mojom',
+ ],
+ },
+ 'includes': [ 'mojom_bindings_generator_explicit.gypi' ],
+ },
+ {
+ 'target_name': 'mojo_interface_bindings_cpp_sources',
+ 'type': 'none',
+ 'dependencies': [
+ 'mojo_interface_bindings_mojom',
+ ],
+ },
+ {
+ # This target can be used to introduce a dependency on interface bindings
+ # generation without introducing any side-effects in the dependent
+ # target's configuration.
+ 'target_name': 'mojo_interface_bindings_generation',
+ 'type': 'none',
+ 'dependencies': [
+ 'mojo_interface_bindings_cpp_sources',
+ ],
+ },
+ {
+ # GN version: //mojo/public/c/test_support
+ 'target_name': 'mojo_public_test_support',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ 'sources': [
+ 'public/c/test_support/test_support.h',
+ # TODO(vtl): Convert this to thunks http://crbug.com/386799
+ 'public/tests/test_support_private.cc',
+ 'public/tests/test_support_private.h',
+ ],
+ },
+ {
+ # GN version: //mojo/public/cpp/test_support:test_utils
+ 'target_name': 'mojo_public_test_utils',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../testing/gtest.gyp:gtest',
+ 'mojo_public_test_support',
+ ],
+ 'sources': [
+ 'public/cpp/test_support/lib/test_support.cc',
+ 'public/cpp/test_support/lib/test_utils.cc',
+ 'public/cpp/test_support/test_utils.h',
+ ],
+ },
+ {
+ # GN version: //mojo/public/cpp/bindings/tests:mojo_public_bindings_test_utils
+ 'target_name': 'mojo_public_bindings_test_utils',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'sources': [
+ 'public/cpp/bindings/tests/validation_test_input_parser.cc',
+ 'public/cpp/bindings/tests/validation_test_input_parser.h',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS == "android"', {
+ 'targets': [
+ {
+ # GN version: //mojo/public/java:system
+ 'target_name': 'mojo_public_java',
+ 'type': 'none',
+ 'variables': {
+ 'chromium_code': 0,
+ 'java_in_dir': 'public/java/system',
+ },
+ 'includes': [ '../build/java.gypi' ],
+ },
+ {
+ 'target_name': 'mojo_interface_bindings_java_sources',
+ 'type': 'none',
+ 'dependencies': [
+ 'mojo_interface_bindings_mojom',
+ ],
+ },
+ {
+ # GN version: //mojo/public/java:bindings
+ 'target_name': 'mojo_bindings_java',
+ 'type': 'none',
+ 'variables': {
+ 'chromium_code': 0,
+ 'java_in_dir': 'public/java/bindings',
+ },
+ 'dependencies': [
+ 'mojo_interface_bindings_java_sources',
+ 'mojo_public_java',
+ '<(DEPTH)/base/base.gyp:base_java',
+ ],
+ 'includes': [ '../build/java.gypi' ],
+ },
+ ],
+ }],
+ ['OS != "ios"', {
+ 'targets': [
+ {
+ # TODO(yzshen): crbug.com/617718 Consider moving this into blink.
+ # GN version: //mojo/public/cpp/bindings:wtf_support
+ 'target_name': 'mojo_cpp_bindings_wtf_support',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..'
+ ],
+ 'sources': [
+ 'public/cpp/bindings/array_traits_wtf.h',
+ 'public/cpp/bindings/array_traits_wtf_vector.h',
+ 'public/cpp/bindings/lib/string_traits_wtf.cc',
+ 'public/cpp/bindings/lib/wtf_clone_equals_util.h',
+ 'public/cpp/bindings/lib/wtf_serialization.h',
+ 'public/cpp/bindings/map_traits_wtf.h',
+ 'public/cpp/bindings/map_traits_wtf_hash_map.h',
+ 'public/cpp/bindings/string_traits_wtf.h',
+ 'public/cpp/bindings/wtf_array.h',
+ 'public/cpp/bindings/wtf_map.h',
+ ],
+ 'dependencies': [
+ 'mojo_cpp_bindings',
+ '../third_party/WebKit/Source/config.gyp:config',
+ '../third_party/WebKit/Source/wtf/wtf.gyp:wtf',
+ ],
+ 'export_dependent_settings': [
+ 'mojo_cpp_bindings',
+ '../third_party/WebKit/Source/config.gyp:config',
+ ],
+ },
+ ],
+ }],
+ ['OS == "win" and target_arch=="ia32"', {
+ 'targets': [
+ {
+ # GN version: //mojo/public/c/system
+ 'target_name': 'mojo_public_system_win64',
+ 'type': '<(component)',
+ 'sources': [
+ '<@(mojo_public_system_sources)',
+ ],
+ 'defines': [
+ 'MOJO_SYSTEM_IMPLEMENTATION',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ {
+ # GN version: //mojo/public/cpp/system
+ 'target_name': 'mojo_cpp_system_win64',
+ 'type': 'static_library',
+ 'sources': [
+ '<@(mojo_cpp_system_sources)',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base_win64',
+ 'mojo_public_system_win64',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ {
+ # GN version: //mojo/public/cpp/bindings
+ 'target_name': 'mojo_cpp_bindings_win64',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..'
+ ],
+ 'sources': [
+ '<@(mojo_cpp_bindings_sources)',
+
+ # This comes from the mojo_interface_bindings_cpp_sources dependency.
+ '>@(mojom_generated_sources)',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base_win64',
+ 'mojo_cpp_system_win64',
+ 'mojo_interface_bindings_cpp_sources',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ },
+ ],
+ }],
+ ],
+}
diff --git a/mojo/mojo_public_bindings_unittests.isolate b/mojo/mojo_public_bindings_unittests.isolate
new file mode 100644
index 0000000..846bf78
--- /dev/null
+++ b/mojo/mojo_public_bindings_unittests.isolate
@@ -0,0 +1,24 @@
+# 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.
+{
+ 'includes': [
+ '../base/base.isolate',
+ ],
+ 'conditions': [
+ ['OS=="win" or OS=="mac" or OS=="linux"', {
+ 'variables': {
+ 'command': [
+ '../testing/test_env.py',
+ '<(PRODUCT_DIR)/mojo_public_bindings_unittests<(EXECUTABLE_SUFFIX)',
+ '--brave-new-test-launcher',
+ '--test-launcher-bot-mode',
+ ],
+ 'files': [
+ '../testing/test_env.py',
+ 'public/interfaces/bindings/tests/data/validation/',
+ ],
+ },
+ }],
+ ],
+}
diff --git a/mojo/mojo_public_system_unittests.isolate b/mojo/mojo_public_system_unittests.isolate
new file mode 100644
index 0000000..f761bf7
--- /dev/null
+++ b/mojo/mojo_public_system_unittests.isolate
@@ -0,0 +1,23 @@
+# 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.
+{
+ 'includes': [
+ '../base/base.isolate',
+ ],
+ 'conditions': [
+ ['OS=="win" or OS=="mac" or OS=="linux"', {
+ 'variables': {
+ 'command': [
+ '../testing/test_env.py',
+ '<(PRODUCT_DIR)/mojo_public_system_unittests<(EXECUTABLE_SUFFIX)',
+ '--brave-new-test-launcher',
+ '--test-launcher-bot-mode',
+ ],
+ 'files': [
+ '../testing/test_env.py',
+ ],
+ },
+ }],
+ ],
+}
diff --git a/mojo/mojom_bindings_generator.gypi b/mojo/mojom_bindings_generator.gypi
new file mode 100644
index 0000000..c8c5f15
--- /dev/null
+++ b/mojo/mojom_bindings_generator.gypi
@@ -0,0 +1,178 @@
+# 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.
+
+{
+ 'includes': [
+ 'mojom_bindings_generator_variables.gypi',
+ ],
+ 'variables': {
+ 'variables': {
+ 'variables': {
+ 'for_blink%': 'false',
+ 'use_new_wrapper_types%': 'false',
+ },
+ 'for_blink%': '<(for_blink)',
+ 'use_new_wrapper_types%': '<(use_new_wrapper_types)',
+ 'conditions': [
+ ['for_blink=="true"', {
+ 'mojom_output_languages%': 'c++',
+ 'mojom_variant%': 'blink',
+ 'mojom_generator_wtf_arg%': [
+ '--for_blink',
+ ],
+ 'wtf_dependencies%': [
+ '<(DEPTH)/mojo/mojo_public.gyp:mojo_cpp_bindings_wtf_support',
+ '<(DEPTH)/third_party/WebKit/Source/wtf/wtf.gyp:wtf',
+ ],
+ }, {
+ 'mojom_output_languages%': 'c++,javascript,java',
+ 'mojom_variant%': 'none',
+ 'mojom_generator_wtf_arg%': [],
+ 'wtf_dependencies%': [],
+ }],
+ ['use_new_wrapper_types=="true"', {
+ 'mojom_generator_new_wrappers_arg%': [
+ '--use_new_wrapper_types',
+ ],
+ }, {
+ 'mojom_generator_new_wrappers_arg%': [],
+ }],
+ ],
+ },
+ 'for_blink%': '<(for_blink)',
+ 'use_new_wrapper_types%': '<(use_new_wrapper_types)',
+ 'mojom_variant%': '<(mojom_variant)',
+ 'mojom_generator_wtf_arg%': '<(mojom_generator_wtf_arg)',
+ 'mojom_generator_new_wrappers_arg%': '<(mojom_generator_new_wrappers_arg)',
+ 'wtf_dependencies%': '<(wtf_dependencies)',
+ 'mojom_output_languages%': '<(mojom_output_languages)',
+ 'mojom_typemaps%': [],
+ 'mojom_base_output_dir':
+ '<!(python <(DEPTH)/build/inverse_depth.py <(DEPTH))',
+ 'mojom_generated_outputs': [
+ '<!@(python <(DEPTH)/mojo/public/tools/bindings/mojom_list_outputs.py --basedir <(mojom_base_output_dir) --variant <(mojom_variant) <@(_sources))',
+ ],
+ 'generated_typemap_file': '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(_target_name)_type_mappings',
+ },
+ 'actions': [
+ {
+ 'variables': {
+ 'java_out_dir': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/src',
+ 'stamp_filename': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/<(_target_name).stamp',
+ },
+ 'action_name': '<(_target_name)_mojom_bindings_stamp',
+ # The java output directory is deleted to ensure that the java library
+ # doesn't try to compile stale files.
+ 'action': [
+ 'python', '<(DEPTH)/build/rmdir_and_stamp.py',
+ '<(java_out_dir)',
+ '<(stamp_filename)',
+ ],
+ 'inputs': [ '<@(_sources)' ],
+ 'outputs': [ '<(stamp_filename)' ],
+ },
+ {
+ 'variables': {
+ 'output': '<(generated_typemap_file)',
+ },
+ 'action_name': '<(_target_name)_type_mappings',
+ 'action': [
+ 'python', '<(DEPTH)/mojo/public/tools/bindings/generate_type_mappings.py',
+ '--output',
+ '<(output)',
+ '<!@(python <(DEPTH)/mojo/public/tools/bindings/format_typemap_generator_args.py <@(mojom_typemaps))',
+ ],
+ 'inputs':[
+ '<(DEPTH)/mojo/public/tools/bindings/generate_type_mappings.py',
+ ],
+ 'outputs': [ '<(output)' ],
+ },
+ ],
+ 'rules': [
+ {
+ 'rule_name': '<(_target_name)_mojom_bindings_generator',
+ 'extension': 'mojom',
+ 'variables': {
+ 'java_out_dir': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/src',
+ 'mojom_import_args%': [
+ '-I<(DEPTH)',
+ '-I<(DEPTH)/mojo/services',
+ ],
+ 'stamp_filename': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/<(_target_name).stamp',
+ },
+ 'inputs': [
+ '<@(mojom_bindings_generator_sources)',
+ '<(stamp_filename)',
+ '<(generated_typemap_file)',
+ '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/cpp_templates.zip',
+ '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/java_templates.zip',
+ '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/js_templates.zip',
+ ],
+ 'conditions': [
+ ['mojom_variant=="none"', {
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom.cc',
+ '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom.h',
+ '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom.js',
+ '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom-internal.h',
+ ]
+ }, {
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom-<(mojom_variant).cc',
+ '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom-<(mojom_variant).h',
+ '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom-<(mojom_variant)-internal.h',
+ ],
+ }]
+ ],
+ 'action': [
+ 'python', '<@(mojom_bindings_generator)',
+ '--use_bundled_pylibs', 'generate',
+ './<(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom',
+ '-d', '<(DEPTH)',
+ '<@(mojom_import_args)',
+ '-o', '<(SHARED_INTERMEDIATE_DIR)',
+ '--java_output_directory=<(java_out_dir)',
+ '--variant', '<(mojom_variant)',
+ '-g', '<(mojom_output_languages)',
+ '--typemap',
+ '<(generated_typemap_file)',
+ '<@(mojom_generator_wtf_arg)',
+ '<@(mojom_generator_new_wrappers_arg)',
+ '--bytecode_path',
+ '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings',
+ ],
+ 'message': 'Generating Mojo bindings from <(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom',
+ 'process_outputs_as_sources': 1,
+ }
+ ],
+ 'dependencies': [
+ '<(DEPTH)/base/base.gyp:base',
+ '<(DEPTH)/mojo/mojo_public.gyp:mojo_interface_bindings_generation',
+ '<(DEPTH)/mojo/public/tools/bindings/bindings.gyp:precompile_mojom_bindings_generator_templates',
+ '<@(wtf_dependencies)',
+ ],
+ 'export_dependent_settings': [
+ '<@(wtf_dependencies)',
+ ],
+ 'include_dirs': [
+ '<(DEPTH)',
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '<(DEPTH)',
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ 'variables': {
+ 'generated_src_dirs': [
+ '<(PRODUCT_DIR)/java_mojo/<(_target_name)/src',
+ ],
+ 'additional_input_paths': [
+ '<@(mojom_bindings_generator_sources)',
+ '<@(_sources)',
+ ],
+ },
+ },
+ 'hard_dependency': 1,
+}
diff --git a/mojo/mojom_bindings_generator_explicit.gypi b/mojo/mojom_bindings_generator_explicit.gypi
new file mode 100644
index 0000000..51676e9
--- /dev/null
+++ b/mojo/mojom_bindings_generator_explicit.gypi
@@ -0,0 +1,195 @@
+# 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.
+
+{
+ 'includes': [
+ 'mojom_bindings_generator_variables.gypi',
+ ],
+ 'variables': {
+ 'variables': {
+ 'variables': {
+ 'for_blink%': 'false',
+ 'use_new_wrapper_types%': 'false',
+ },
+ 'for_blink%': 'false',
+ 'use_new_wrapper_types%': 'false',
+ 'conditions': [
+ ['for_blink=="true"', {
+ 'mojom_output_languages%': 'c++',
+ 'mojom_variant%': 'blink',
+ 'mojom_generator_wtf_arg%': [
+ '--for_blink',
+ ],
+ 'wtf_dependencies%': [
+ '<(DEPTH)/mojo/mojo_public.gyp:mojo_cpp_bindings_wtf_support',
+ '<(DEPTH)/third_party/WebKit/Source/wtf/wtf.gyp:wtf',
+ ],
+ }, {
+ 'mojom_output_languages%': 'c++,javascript,java',
+ 'mojom_variant%': 'none',
+ 'mojom_generator_wtf_arg%': [],
+ 'wtf_dependencies%': [],
+ }],
+ ['use_new_wrapper_types=="true"', {
+ 'mojom_generator_new_wrappers_arg%': [
+ '--use_new_wrapper_types',
+ ],
+ }, {
+ 'mojom_generator_new_wrappers_arg%': [],
+ }],
+ ],
+ },
+ 'for_blink%': '<(for_blink)',
+ 'use_new_wrapper_types%': '<(use_new_wrapper_types)',
+ 'mojom_variant%': '<(mojom_variant)',
+ 'mojom_generator_wtf_arg%': '<(mojom_generator_wtf_arg)',
+ 'mojom_generator_new_wrappers_arg%': '<(mojom_generator_new_wrappers_arg)',
+ 'wtf_dependencies%': '<(wtf_dependencies)',
+ 'mojom_output_languages%': '<(mojom_output_languages)',
+ 'mojom_typemaps%': [],
+ 'mojom_base_output_dir':
+ '<!(python <(DEPTH)/build/inverse_depth.py <(DEPTH))',
+ 'mojom_generated_outputs': [
+ '<!@(python <(DEPTH)/mojo/public/tools/bindings/mojom_list_outputs.py --basedir <(mojom_base_output_dir) --variant <(mojom_variant) <@(mojom_files))',
+ ],
+ 'generated_typemap_file': '<(SHARED_INTERMEDIATE_DIR)/<(mojom_base_output_dir)/<(_target_name)_type_mappings',
+ 'mojom_include_path%': '<(DEPTH)',
+ 'require_interface_bindings%': 1,
+ },
+ # Given mojom files as inputs, generate sources. These sources will be
+ # exported to another target (via dependent_settings) to be compiled. This
+ # keeps code generation separate from compilation, allowing the same sources
+ # to be compiled with multiple toolchains - target, NaCl, etc.
+ 'actions': [
+ {
+ 'variables': {
+ 'java_out_dir': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/src',
+ 'stamp_filename': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/<(_target_name).stamp',
+ },
+ 'action_name': '<(_target_name)_mojom_bindings_stamp',
+ # The java output directory is deleted to ensure that the java library
+ # doesn't try to compile stale files.
+ 'action': [
+ 'python', '<(DEPTH)/build/rmdir_and_stamp.py',
+ '<(java_out_dir)',
+ '<(stamp_filename)',
+ ],
+ 'inputs': [
+ '<@(mojom_files)',
+ ],
+ 'outputs': [ '<(stamp_filename)' ],
+ },
+ {
+ 'variables': {
+ 'output': '<(generated_typemap_file)',
+ },
+ 'action_name': '<(_target_name)_type_mappings',
+ 'action': [
+ 'python', '<(DEPTH)/mojo/public/tools/bindings/generate_type_mappings.py',
+ '--output',
+ '<(output)',
+ '<!@(python <(DEPTH)/mojo/public/tools/bindings/format_typemap_generator_args.py <@(mojom_typemaps))',
+ ],
+ 'inputs':[
+ '<(DEPTH)/mojo/public/tools/bindings/generate_type_mappings.py',
+ ],
+ 'outputs': [ '<(output)' ],
+ },
+ {
+ 'action_name': '<(_target_name)_mojom_bindings_generator',
+ 'variables': {
+ 'java_out_dir': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/src',
+ 'stamp_filename': '<(PRODUCT_DIR)/java_mojo/<(_target_name)/<(_target_name).stamp',
+ 'mojom_import_args%': [
+ '-I<(DEPTH)',
+ '-I<(DEPTH)/mojo/services',
+ '-I<(mojom_include_path)',
+ ],
+ },
+ 'inputs': [
+ '<@(mojom_bindings_generator_sources)',
+ '<@(mojom_files)',
+ '<(stamp_filename)',
+ '<(generated_typemap_file)',
+ '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/cpp_templates.zip',
+ '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/java_templates.zip',
+ '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/js_templates.zip',
+ ],
+ 'outputs': [
+ '<@(mojom_generated_outputs)',
+ ],
+ 'action': [
+ 'python', '<@(mojom_bindings_generator)',
+ '--use_bundled_pylibs', 'generate',
+ '<@(mojom_files)',
+ '-d', '<(DEPTH)',
+ '<@(mojom_import_args)',
+ '-o', '<(SHARED_INTERMEDIATE_DIR)',
+ '--java_output_directory=<(java_out_dir)',
+ '--variant', '<(mojom_variant)',
+ '-g', '<(mojom_output_languages)',
+ '--typemap',
+ '<(generated_typemap_file)',
+ '<@(mojom_generator_wtf_arg)',
+ '<@(mojom_generator_new_wrappers_arg)',
+ '--bytecode_path',
+ '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings',
+ ],
+ 'message': 'Generating Mojo bindings from <@(mojom_files)',
+ }
+ ],
+ 'conditions': [
+ ['require_interface_bindings==1', {
+ 'dependencies': [
+ '<(DEPTH)/base/base.gyp:base',
+ '<(DEPTH)/mojo/mojo_public.gyp:mojo_interface_bindings_generation',
+ ],
+ }],
+ ],
+ 'dependencies': [
+ '<(DEPTH)/mojo/public/tools/bindings/bindings.gyp:precompile_mojom_bindings_generator_templates',
+ '<@(wtf_dependencies)',
+ ],
+ 'export_dependent_settings': [
+ '<@(wtf_dependencies)',
+ ],
+ # Prevent the generated sources from being injected into the "all" target by
+ # preventing the code generator from being directly depended on by the "all"
+ # target.
+ 'suppress_wildcard': '1',
+ 'hard_dependency': '1',
+ 'direct_dependent_settings': {
+ # A target directly depending on this action will compile the generated
+ # sources.
+ 'sources': [
+ '<@(mojom_generated_outputs)',
+ ],
+ # Include paths needed to compile the generated sources into a library.
+ 'include_dirs': [
+ '<(DEPTH)',
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ # Make sure the generated header files are available for any static library
+ # that depends on a static library that depends on this generator.
+ 'hard_dependency': 1,
+ 'direct_dependent_settings': {
+ # Include paths needed to find the generated header files and their
+ # transitive dependancies when using the library.
+ 'include_dirs': [
+ '<(DEPTH)',
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ 'variables': {
+ 'generated_src_dirs': [
+ '<(PRODUCT_DIR)/java_mojo/<(_target_name)/src',
+ ],
+ 'additional_input_paths': [
+ '<@(mojom_bindings_generator_sources)',
+ '<@(mojom_files)',
+ ],
+ 'mojom_generated_sources': [ '<@(mojom_generated_outputs)' ],
+ },
+ }
+ },
+}
diff --git a/mojo/mojom_bindings_generator_variables.gypi b/mojo/mojom_bindings_generator_variables.gypi
new file mode 100644
index 0000000..9e3c400
--- /dev/null
+++ b/mojo/mojom_bindings_generator_variables.gypi
@@ -0,0 +1,29 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'mojom_bindings_generator':
+ '<(DEPTH)/mojo/public/tools/bindings/mojom_bindings_generator.py',
+ 'mojom_bindings_generator_sources': [
+ '<(mojom_bindings_generator)',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/mojom_cpp_generator.py',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/mojom_java_generator.py',
+ '<(DEPTH)/mojo/public/tools/bindings/generators/mojom_js_generator.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/__init__.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/error.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/__init__.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/data.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/generator.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/module.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/pack.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/__init__.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/ast.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/lexer.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/parser.py',
+ '<(DEPTH)/mojo/public/tools/bindings/pylib/mojom/parse/translate.py',
+ ]
+ }
+}
diff --git a/mojo/public/BUILD.gn b/mojo/public/BUILD.gn
index 3baf667..9442479 100644
--- a/mojo/public/BUILD.gn
+++ b/mojo/public/BUILD.gn
@@ -13,8 +13,8 @@
if (is_android) {
deps += [
- "java:bindings_java",
- "java:system_java",
+ "java:bindings",
+ "java:system",
]
}
}
@@ -23,6 +23,7 @@
deps = [
"c/system",
"cpp/bindings",
+ "interfaces/bindings",
"js",
]
}
diff --git a/mojo/public/README.md b/mojo/public/README.md
index dd91742..a31a8a8 100644
--- a/mojo/public/README.md
+++ b/mojo/public/README.md
@@ -31,7 +31,7 @@
--------
The platform/ subdirectory contains any build-time requirements (e.g., static
-libraries) that may be needed to produce a Service library for certain
+libraries) that may be needed to produce a Mojo application for certain
platforms, such as a native shared library or as a NaCl binary.
Tools
diff --git a/mojo/public/c/system/BUILD.gn b/mojo/public/c/system/BUILD.gn
index c3b3d5f..3ec12f6 100644
--- a/mojo/public/c/system/BUILD.gn
+++ b/mojo/public/c/system/BUILD.gn
@@ -25,7 +25,7 @@
# This should ONLY be depended upon directly by shared_library targets which
# need to export the MojoSetSystemThunks symbol, like targets generated by the
-# mojo_native_application template in //services/service_manager/public/cpp/service.gni.
+# mojo_native_application template in //mojo/public/mojo_application.gni.
source_set("set_thunks_for_app") {
sources = [
"set_thunks_for_app.cc",
diff --git a/mojo/public/c/system/core.h b/mojo/public/c/system/core.h
index 77d452b..3775e90 100644
--- a/mojo/public/c/system/core.h
+++ b/mojo/public/c/system/core.h
@@ -13,6 +13,7 @@
#include "mojo/public/c/system/data_pipe.h"
#include "mojo/public/c/system/functions.h"
#include "mojo/public/c/system/macros.h"
+#include "mojo/public/c/system/main.h"
#include "mojo/public/c/system/message_pipe.h"
#include "mojo/public/c/system/platform_handle.h"
#include "mojo/public/c/system/system_export.h"
diff --git a/mojo/public/c/system/main.h b/mojo/public/c/system/main.h
new file mode 100644
index 0000000..65d0837
--- /dev/null
+++ b/mojo/public/c/system/main.h
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_C_SYSTEM_MAIN_H_
+#define MOJO_PUBLIC_C_SYSTEM_MAIN_H_
+
+#include "mojo/public/c/system/types.h"
+
+// Implement MojoMain directly as the entry point for an application.
+//
+// MojoResult MojoMain(MojoHandle application_request) {
+// ...
+// }
+//
+// TODO(davemoore): Establish this as part of our SDK for third party mojo
+// application writers.
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if defined(WIN32)
+__declspec(dllexport) MojoResult
+ __cdecl MojoMain(MojoHandle application_request);
+#else // !defined(WIN32)
+__attribute__((visibility("default"))) MojoResult
+ MojoMain(MojoHandle service_provider_handle);
+#endif // defined(WIN32)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // MOJO_PUBLIC_C_SYSTEM_MAIN_H_
diff --git a/mojo/public/c/system/tests/core_unittest.cc b/mojo/public/c/system/tests/core_unittest.cc
index 8a54380..ef44a87 100644
--- a/mojo/public/c/system/tests/core_unittest.cc
+++ b/mojo/public/c/system/tests/core_unittest.cc
@@ -192,8 +192,7 @@
MojoWait(hc, MOJO_HANDLE_SIGNAL_READABLE, 0, &state));
EXPECT_EQ(MOJO_HANDLE_SIGNAL_NONE, state.satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
state.satisfiable_signals);
// The producer |hp| should be writable.
@@ -230,10 +229,8 @@
&result_index, states));
EXPECT_EQ(0u, result_index);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
- states[0].satisfied_signals);
- EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED |
- MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, states[0].satisfied_signals);
+ EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
states[0].satisfiable_signals);
// Do a two-phase write to |hp|.
diff --git a/mojo/public/c/system/tests/core_unittest_pure_c.c b/mojo/public/c/system/tests/core_unittest_pure_c.c
index fa3caa5..a01e14b 100644
--- a/mojo/public/c/system/tests/core_unittest_pure_c.c
+++ b/mojo/public/c/system/tests/core_unittest_pure_c.c
@@ -21,7 +21,7 @@
#define FAILURE(message) \
__FILE__ "(" STRINGIFY2(__LINE__) "): Failure: " message
-// Makeshift gtest.
+// Poor man's gtest.
#define EXPECT_EQ(a, b) \
do { \
if ((a) != (b)) \
diff --git a/mojo/public/c/system/types.h b/mojo/public/c/system/types.h
index 7e02eeb..3482d4e 100644
--- a/mojo/public/c/system/types.h
+++ b/mojo/public/c/system/types.h
@@ -149,11 +149,6 @@
// |MOJO_HANDLE_SIGNAL_READABLE| - Can read (e.g., a message) from the handle.
// |MOJO_HANDLE_SIGNAL_WRITABLE| - Can write (e.g., a message) to the handle.
// |MOJO_HANDLE_SIGNAL_PEER_CLOSED| - The peer handle is closed.
-// |MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE| - Can read data from a data pipe
-// consumer handle (implying MOJO_HANDLE_SIGNAL_READABLE is also set),
-// AND there is some nonzero quantity of new data available on the pipe
-// since the last |MojoReadData()| or |MojoBeginReadData()| call on the
-// handle.
typedef uint32_t MojoHandleSignals;
@@ -162,13 +157,11 @@
const MojoHandleSignals MOJO_HANDLE_SIGNAL_READABLE = 1 << 0;
const MojoHandleSignals MOJO_HANDLE_SIGNAL_WRITABLE = 1 << 1;
const MojoHandleSignals MOJO_HANDLE_SIGNAL_PEER_CLOSED = 1 << 2;
-const MojoHandleSignals MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE = 1 << 3;
#else
#define MOJO_HANDLE_SIGNAL_NONE ((MojoHandleSignals)0)
#define MOJO_HANDLE_SIGNAL_READABLE ((MojoHandleSignals)1 << 0)
#define MOJO_HANDLE_SIGNAL_WRITABLE ((MojoHandleSignals)1 << 1)
#define MOJO_HANDLE_SIGNAL_PEER_CLOSED ((MojoHandleSignals)1 << 2)
-#define MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE ((MojoHandleSignals)1 << 3);
#endif
// |MojoHandleSignalsState|: Returned by wait functions to indicate the
diff --git a/mojo/public/c/test_support/BUILD.gn b/mojo/public/c/test_support/BUILD.gn
index e2abd58..75d9c13 100644
--- a/mojo/public/c/test_support/BUILD.gn
+++ b/mojo/public/c/test_support/BUILD.gn
@@ -2,6 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+# GYP version: mojo/mojo_public.gyp:mojo_test_support
static_library("test_support") {
output_name = "mojo_public_test_support"
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index 5c41384..3ec9824 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -2,31 +2,14 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-interfaces_bindings_gen_dir = "$root_gen_dir/mojo/public/interfaces/bindings"
-
-component("bindings") {
+static_library("bindings") {
sources = [
- # Normally, targets should depend on the source_sets generated by mojom
- # targets. However, the generated source_sets use portions of the bindings
- # library. In order to avoid linker warnings about locally-defined imports
- # in Windows components build, this target depends on the generated C++
- # files directly so that the EXPORT macro defintions match.
- "$interfaces_bindings_gen_dir/interface_control_messages.mojom-shared-internal.h",
- "$interfaces_bindings_gen_dir/interface_control_messages.mojom-shared.cc",
- "$interfaces_bindings_gen_dir/interface_control_messages.mojom-shared.h",
- "$interfaces_bindings_gen_dir/interface_control_messages.mojom.cc",
- "$interfaces_bindings_gen_dir/interface_control_messages.mojom.h",
- "$interfaces_bindings_gen_dir/pipe_control_messages.mojom-shared-internal.h",
- "$interfaces_bindings_gen_dir/pipe_control_messages.mojom-shared.cc",
- "$interfaces_bindings_gen_dir/pipe_control_messages.mojom-shared.h",
- "$interfaces_bindings_gen_dir/pipe_control_messages.mojom.cc",
- "$interfaces_bindings_gen_dir/pipe_control_messages.mojom.h",
- "array_data_view.h",
+ "array.h",
"array_traits.h",
"array_traits_carray.h",
+ "array_traits_standard.h",
"array_traits_stl.h",
"associated_binding.h",
- "associated_binding_set.h",
"associated_group.h",
"associated_group_controller.h",
"associated_interface_ptr.h",
@@ -34,13 +17,8 @@
"associated_interface_request.h",
"binding.h",
"binding_set.h",
- "bindings_export.h",
- "clone_traits.h",
- "connection_error_callback.h",
"connector.h",
- "disconnect_reason.h",
- "filter_chain.h",
- "interface_data_view.h",
+ "enum_traits.h",
"interface_endpoint_client.h",
"interface_endpoint_controller.h",
"interface_id.h",
@@ -51,35 +29,34 @@
"lib/array_internal.cc",
"lib/array_internal.h",
"lib/array_serialization.h",
- "lib/associated_binding.cc",
"lib/associated_group.cc",
"lib/associated_group_controller.cc",
"lib/associated_interface_ptr_state.h",
- "lib/binding_state.cc",
"lib/binding_state.h",
+ "lib/bindings_internal.cc",
"lib/bindings_internal.h",
"lib/buffer.h",
+ "lib/clone_equals_util.h",
"lib/connector.cc",
"lib/control_message_handler.cc",
"lib/control_message_handler.h",
"lib/control_message_proxy.cc",
"lib/control_message_proxy.h",
- "lib/equals_traits.h",
"lib/filter_chain.cc",
+ "lib/filter_chain.h",
"lib/fixed_buffer.cc",
"lib/fixed_buffer.h",
"lib/handle_interface_serialization.h",
- "lib/hash_util.h",
"lib/interface_endpoint_client.cc",
"lib/interface_ptr_state.h",
"lib/map_data_internal.h",
"lib/map_serialization.h",
- "lib/may_auto_lock.h",
"lib/message.cc",
"lib/message_buffer.cc",
"lib/message_buffer.h",
"lib/message_builder.cc",
"lib/message_builder.h",
+ "lib/message_filter.cc",
"lib/message_header_validator.cc",
"lib/message_internal.h",
"lib/multiplex_router.cc",
@@ -91,8 +68,11 @@
"lib/native_struct_data.h",
"lib/native_struct_serialization.cc",
"lib/native_struct_serialization.h",
+ "lib/no_interface.cc",
"lib/pipe_control_message_handler.cc",
"lib/pipe_control_message_proxy.cc",
+ "lib/router.cc",
+ "lib/router.h",
"lib/scoped_interface_endpoint_handle.cc",
"lib/serialization.h",
"lib/serialization_context.cc",
@@ -114,35 +94,32 @@
"lib/validation_util.cc",
"lib/validation_util.h",
"map.h",
- "map_data_view.h",
"map_traits.h",
+ "map_traits_standard.h",
"map_traits_stl.h",
"message.h",
+ "message_filter.h",
"message_header_validator.h",
"native_enum.h",
"native_struct.h",
- "native_struct_data_view.h",
+ "no_interface.h",
"pipe_control_message_handler.h",
"pipe_control_message_handler_delegate.h",
"pipe_control_message_proxy.h",
- "raw_ptr_impl_ref_traits.h",
"scoped_interface_endpoint_handle.h",
- "string_data_view.h",
+ "stl_converters.h",
+ "string.h",
"string_traits.h",
+ "string_traits_standard.h",
"string_traits_stl.h",
"string_traits_string16.h",
"string_traits_string_piece.h",
- "strong_associated_binding.h",
"strong_binding.h",
- "strong_binding_set.h",
"struct_ptr.h",
"sync_call_restrictions.h",
"sync_handle_registry.h",
"sync_handle_watcher.h",
- "thread_safe_interface_ptr.h",
"type_converter.h",
- "union_traits.h",
- "unique_ptr_impl_ref_traits.h",
]
public_deps = [
@@ -154,16 +131,12 @@
deps = [
"//base",
- "//mojo/public/interfaces/bindings:bindings__generator",
- "//mojo/public/interfaces/bindings:bindings_shared__generator",
+ "//mojo/public/interfaces/bindings:bindings_cpp_sources",
]
-
- defines = [ "MOJO_CPP_BINDINGS_IMPLEMENTATION" ]
}
source_set("struct_traits") {
sources = [
- "enum_traits.h",
"struct_traits.h",
]
}
@@ -172,13 +145,16 @@
# TODO(yzshen): crbug.com/617718 Consider moving this into blink.
source_set("wtf_support") {
sources = [
+ "array_traits_wtf.h",
"array_traits_wtf_vector.h",
"lib/string_traits_wtf.cc",
"lib/wtf_clone_equals_util.h",
- "lib/wtf_hash_util.h",
"lib/wtf_serialization.h",
+ "map_traits_wtf.h",
"map_traits_wtf_hash_map.h",
"string_traits_wtf.h",
+ "wtf_array.h",
+ "wtf_map.h",
]
public_deps = [
diff --git a/mojo/public/cpp/bindings/array.h b/mojo/public/cpp/bindings/array.h
new file mode 100644
index 0000000..a253da1
--- /dev/null
+++ b/mojo/public/cpp/bindings/array.h
@@ -0,0 +1,279 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_
+
+#include <stddef.h>
+#include <string.h>
+#include <algorithm>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/clone_equals_util.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+namespace mojo {
+
+// Represents a moveable array with contents of type |T|. The array can be null,
+// meaning that no value has been assigned to it. Null is distinct from empty.
+template <typename T>
+class Array {
+ public:
+ using ConstRefType = typename std::vector<T>::const_reference;
+ using RefType = typename std::vector<T>::reference;
+
+ using Element = T;
+
+ using iterator = typename std::vector<T>::iterator;
+ using const_iterator = typename std::vector<T>::const_iterator;
+
+ // Constructs an empty array.
+ Array() : is_null_(false) {}
+ // Constructs a null array.
+ Array(std::nullptr_t null_pointer) : is_null_(true) {}
+
+ // Constructs a new non-null array of the specified size. The elements will
+ // be value-initialized (meaning that they will be initialized by their
+ // default constructor, if any, or else zero-initialized).
+ explicit Array(size_t size) : vec_(size), is_null_(false) {}
+ ~Array() {}
+
+ // Copies the contents of |other| into this array.
+ Array(const std::vector<T>& other) : vec_(other), is_null_(false) {}
+
+ // Moves the contents of |other| into this array.
+ Array(std::vector<T>&& other) : vec_(std::move(other)), is_null_(false) {}
+ Array(Array&& other) : is_null_(true) { Take(&other); }
+
+ Array& operator=(std::vector<T>&& other) {
+ vec_ = std::move(other);
+ is_null_ = false;
+ return *this;
+ }
+ Array& operator=(Array&& other) {
+ Take(&other);
+ return *this;
+ }
+
+ Array& operator=(std::nullptr_t null_pointer) {
+ is_null_ = true;
+ vec_.clear();
+ return *this;
+ }
+
+ // Creates a non-null array of the specified size. The elements will be
+ // value-initialized (meaning that they will be initialized by their default
+ // constructor, if any, or else zero-initialized).
+ static Array New(size_t size) { return Array(size); }
+
+ // Creates a new array with a copy of the contents of |other|.
+ template <typename U>
+ static Array From(const U& other) {
+ return TypeConverter<Array, U>::Convert(other);
+ }
+
+ // Copies the contents of this array to a new object of type |U|.
+ template <typename U>
+ U To() const {
+ return TypeConverter<U, Array>::Convert(*this);
+ }
+
+ // Indicates whether the array is null (which is distinct from empty).
+ bool is_null() const { return is_null_; }
+
+ // Indicates whether the array is empty (which is distinct from null).
+ bool empty() const { return vec_.empty() && !is_null_; }
+
+ // Returns a reference to the first element of the array. Calling this on a
+ // null or empty array causes undefined behavior.
+ ConstRefType front() const { return vec_.front(); }
+ RefType front() { return vec_.front(); }
+
+ iterator begin() { return vec_.begin(); }
+ const_iterator begin() const { return vec_.begin(); }
+ iterator end() { return vec_.end(); }
+ const_iterator end() const { return vec_.end(); }
+
+ // Returns the size of the array, which will be zero if the array is null.
+ size_t size() const { return vec_.size(); }
+
+ // Returns a reference to the element at zero-based |offset|. Calling this on
+ // an array with size less than |offset|+1 causes undefined behavior.
+ ConstRefType at(size_t offset) const { return vec_.at(offset); }
+ ConstRefType operator[](size_t offset) const { return at(offset); }
+ RefType at(size_t offset) { return vec_.at(offset); }
+ RefType operator[](size_t offset) { return at(offset); }
+
+ // Pushes |value| onto the back of the array. If this array was null, it will
+ // become non-null with a size of 1.
+ void push_back(const T& value) {
+ is_null_ = false;
+ vec_.push_back(value);
+ }
+ void push_back(T&& value) {
+ is_null_ = false;
+ vec_.push_back(std::move(value));
+ }
+
+ // Resizes the array to |size| and makes it non-null. Otherwise, works just
+ // like the resize method of |std::vector|.
+ void resize(size_t size) {
+ is_null_ = false;
+ vec_.resize(size);
+ }
+
+ // Sets the array to empty (even if previously it was null.)
+ void SetToEmpty() { resize(0); }
+
+ // Returns a const reference to the |std::vector| managed by this class. If
+ // the array is null, this will be an empty vector.
+ const std::vector<T>& storage() const { return vec_; }
+
+ // Passes the underlying storage and resets this array to null.
+ std::vector<T> PassStorage() {
+ is_null_ = true;
+ return std::move(vec_);
+ }
+
+ operator const std::vector<T>&() const { return vec_; }
+
+ void Swap(Array* other) {
+ std::swap(is_null_, other->is_null_);
+ vec_.swap(other->vec_);
+ }
+
+ // Swaps the contents of this array with the specified vector, making this
+ // array non-null. Since the vector cannot represent null, it will just be
+ // made empty if this array is null.
+ void Swap(std::vector<T>* other) {
+ is_null_ = false;
+ vec_.swap(*other);
+ }
+
+ // Returns a copy of the array where each value of the new array has been
+ // "cloned" from the corresponding value of this array. If the element type
+ // defines a Clone() method, it will be used; otherwise copy
+ // constructor/assignment will be used.
+ //
+ // Please note that calling this method will fail compilation if the element
+ // type cannot be cloned (which usually means that it is a Mojo handle type or
+ // a type containing Mojo handles).
+ Array Clone() const {
+ Array result;
+ result.is_null_ = is_null_;
+ result.vec_ = internal::Clone(vec_);
+ return result;
+ }
+
+ // Indicates whether the contents of this array are equal to |other|. A null
+ // array is only equal to another null array. If the element type defines an
+ // Equals() method, it will be used; otherwise == operator will be used.
+ bool Equals(const Array& other) const {
+ if (is_null() != other.is_null())
+ return false;
+ return internal::Equals(vec_, other.vec_);
+ }
+
+ private:
+ typedef std::vector<T> Array::*Testable;
+
+ public:
+ operator Testable() const { return is_null_ ? 0 : &Array::vec_; }
+
+ private:
+ // Forbid the == and != operators explicitly, otherwise Array will be
+ // converted to Testable to do == or != comparison.
+ template <typename U>
+ bool operator==(const Array<U>& other) const = delete;
+ template <typename U>
+ bool operator!=(const Array<U>& other) const = delete;
+
+ void Take(Array* other) {
+ operator=(nullptr);
+ Swap(other);
+ }
+
+ std::vector<T> vec_;
+ bool is_null_;
+
+ DISALLOW_COPY_AND_ASSIGN(Array);
+};
+
+// A |TypeConverter| that will create an |Array<T>| containing a copy of the
+// contents of an |std::vector<E>|, using |TypeConverter<T, E>| to copy each
+// element. The returned array will always be non-null.
+template <typename T, typename E>
+struct TypeConverter<Array<T>, std::vector<E>> {
+ static Array<T> Convert(const std::vector<E>& input) {
+ Array<T> result(input.size());
+ for (size_t i = 0; i < input.size(); ++i)
+ result[i] = TypeConverter<T, E>::Convert(input[i]);
+ return std::move(result);
+ }
+};
+
+// A |TypeConverter| that will create an |std::vector<E>| containing a copy of
+// the contents of an |Array<T>|, using |TypeConverter<E, T>| to copy each
+// element. If the input array is null, the output vector will be empty.
+template <typename E, typename T>
+struct TypeConverter<std::vector<E>, Array<T>> {
+ static std::vector<E> Convert(const Array<T>& input) {
+ std::vector<E> result;
+ if (!input.is_null()) {
+ result.resize(input.size());
+ for (size_t i = 0; i < input.size(); ++i)
+ result[i] = TypeConverter<E, T>::Convert(input[i]);
+ }
+ return result;
+ }
+};
+
+// A |TypeConverter| that will create an |Array<T>| containing a copy of the
+// contents of an |std::set<E>|, using |TypeConverter<T, E>| to copy each
+// element. The returned array will always be non-null.
+template <typename T, typename E>
+struct TypeConverter<Array<T>, std::set<E>> {
+ static Array<T> Convert(const std::set<E>& input) {
+ Array<T> result;
+ for (auto i : input)
+ result.push_back(TypeConverter<T, E>::Convert(i));
+ return std::move(result);
+ }
+};
+
+// A |TypeConverter| that will create an |std::set<E>| containing a copy of
+// the contents of an |Array<T>|, using |TypeConverter<E, T>| to copy each
+// element. If the input array is null, the output set will be empty.
+template <typename E, typename T>
+struct TypeConverter<std::set<E>, Array<T>> {
+ static std::set<E> Convert(const Array<T>& input) {
+ std::set<E> result;
+ if (!input.is_null()) {
+ for (size_t i = 0; i < input.size(); ++i)
+ result.insert(TypeConverter<E, T>::Convert(input[i]));
+ }
+ return result;
+ }
+};
+
+// Less than operator to allow Arrays as keys in std maps and sets.
+template <typename T>
+inline bool operator<(const Array<T>& a, const Array<T>& b) {
+ if (a.is_null())
+ return !b.is_null();
+ if (b.is_null())
+ return false;
+ return a.storage() < b.storage();
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_
diff --git a/mojo/public/cpp/bindings/array_data_view.h b/mojo/public/cpp/bindings/array_data_view.h
deleted file mode 100644
index d02a884..0000000
--- a/mojo/public/cpp/bindings/array_data_view.h
+++ /dev/null
@@ -1,244 +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.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_ARRAY_DATA_VIEW_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_DATA_VIEW_H_
-
-#include <type_traits>
-
-#include "mojo/public/cpp/bindings/lib/array_internal.h"
-#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
-#include "mojo/public/cpp/bindings/lib/serialization_context.h"
-#include "mojo/public/cpp/bindings/lib/serialization_forward.h"
-
-namespace mojo {
-namespace internal {
-
-template <typename T, typename EnableType = void>
-class ArrayDataViewImpl;
-
-template <typename T>
-class ArrayDataViewImpl<
- T,
- typename std::enable_if<
- BelongsTo<T, MojomTypeCategory::POD>::value>::type> {
- public:
- using Data_ = typename MojomTypeTraits<ArrayDataView<T>>::Data;
-
- ArrayDataViewImpl(Data_* data, SerializationContext* context)
- : data_(data), context_(context) {}
-
- T operator[](size_t index) const { return data_->at(index); }
-
- const T* data() const { return data_->storage(); }
-
- protected:
- Data_* data_;
- SerializationContext* context_;
-};
-
-template <typename T>
-class ArrayDataViewImpl<
- T,
- typename std::enable_if<
- BelongsTo<T, MojomTypeCategory::BOOLEAN>::value>::type> {
- public:
- using Data_ = typename MojomTypeTraits<ArrayDataView<T>>::Data;
-
- ArrayDataViewImpl(Data_* data, SerializationContext* context)
- : data_(data), context_(context) {}
-
- bool operator[](size_t index) const { return data_->at(index); }
-
- protected:
- Data_* data_;
- SerializationContext* context_;
-};
-
-template <typename T>
-class ArrayDataViewImpl<
- T,
- typename std::enable_if<
- BelongsTo<T, MojomTypeCategory::ENUM>::value>::type> {
- public:
- static_assert(sizeof(T) == sizeof(int32_t), "Unexpected enum size");
-
- using Data_ = typename MojomTypeTraits<ArrayDataView<T>>::Data;
-
- ArrayDataViewImpl(Data_* data, SerializationContext* context)
- : data_(data), context_(context) {}
-
- T operator[](size_t index) const { return static_cast<T>(data_->at(index)); }
-
- const T* data() const { return reinterpret_cast<const T*>(data_->storage()); }
-
- template <typename U>
- bool Read(size_t index, U* output) {
- return Deserialize<T>(data_->at(index), output);
- }
-
- protected:
- Data_* data_;
- SerializationContext* context_;
-};
-
-template <typename T>
-class ArrayDataViewImpl<
- T,
- typename std::enable_if<
- BelongsTo<T,
- MojomTypeCategory::ASSOCIATED_INTERFACE |
- MojomTypeCategory::ASSOCIATED_INTERFACE_REQUEST |
- MojomTypeCategory::INTERFACE |
- MojomTypeCategory::INTERFACE_REQUEST>::value>::type> {
- public:
- using Data_ = typename MojomTypeTraits<ArrayDataView<T>>::Data;
-
- ArrayDataViewImpl(Data_* data, SerializationContext* context)
- : data_(data), context_(context) {}
-
- template <typename U>
- U Take(size_t index) {
- U result;
- bool ret = Deserialize<T>(&data_->at(index), &result, context_);
- DCHECK(ret);
- return result;
- }
-
- protected:
- Data_* data_;
- SerializationContext* context_;
-};
-
-template <typename T>
-class ArrayDataViewImpl<
- T,
- typename std::enable_if<
- BelongsTo<T, MojomTypeCategory::HANDLE>::value>::type> {
- public:
- using Data_ = typename MojomTypeTraits<ArrayDataView<T>>::Data;
-
- ArrayDataViewImpl(Data_* data, SerializationContext* context)
- : data_(data), context_(context) {}
-
- T Take(size_t index) {
- T result;
- bool ret = Deserialize<T>(&data_->at(index), &result, context_);
- DCHECK(ret);
- return result;
- }
-
- protected:
- Data_* data_;
- SerializationContext* context_;
-};
-
-template <typename T>
-class ArrayDataViewImpl<T,
- typename std::enable_if<BelongsTo<
- T,
- MojomTypeCategory::ARRAY | MojomTypeCategory::MAP |
- MojomTypeCategory::STRING |
- MojomTypeCategory::STRUCT>::value>::type> {
- public:
- using Data_ = typename MojomTypeTraits<ArrayDataView<T>>::Data;
-
- ArrayDataViewImpl(Data_* data, SerializationContext* context)
- : data_(data), context_(context) {}
-
- void GetDataView(size_t index, T* output) {
- *output = T(data_->at(index).Get(), context_);
- }
-
- template <typename U>
- bool Read(size_t index, U* output) {
- return Deserialize<T>(data_->at(index).Get(), output, context_);
- }
-
- protected:
- Data_* data_;
- SerializationContext* context_;
-};
-
-template <typename T>
-class ArrayDataViewImpl<
- T,
- typename std::enable_if<
- BelongsTo<T, MojomTypeCategory::UNION>::value>::type> {
- public:
- using Data_ = typename MojomTypeTraits<ArrayDataView<T>>::Data;
-
- ArrayDataViewImpl(Data_* data, SerializationContext* context)
- : data_(data), context_(context) {}
-
- void GetDataView(size_t index, T* output) {
- *output = T(&data_->at(index), context_);
- }
-
- template <typename U>
- bool Read(size_t index, U* output) {
- return Deserialize<T>(&data_->at(index), output, context_);
- }
-
- protected:
- Data_* data_;
- SerializationContext* context_;
-};
-
-} // namespace internal
-
-template <typename K, typename V>
-class MapDataView;
-
-template <typename T>
-class ArrayDataView : public internal::ArrayDataViewImpl<T> {
- public:
- using Element = T;
- using Data_ = typename internal::ArrayDataViewImpl<T>::Data_;
-
- ArrayDataView() : internal::ArrayDataViewImpl<T>(nullptr, nullptr) {}
-
- ArrayDataView(Data_* data, internal::SerializationContext* context)
- : internal::ArrayDataViewImpl<T>(data, context) {}
-
- bool is_null() const { return !this->data_; }
-
- size_t size() const { return this->data_->size(); }
-
- // Methods to access elements are different for different element types. They
- // are inherited from internal::ArrayDataViewImpl:
-
- // POD types except boolean and enums:
- // T operator[](size_t index) const;
- // const T* data() const;
-
- // Boolean:
- // bool operator[](size_t index) const;
-
- // Enums:
- // T operator[](size_t index) const;
- // const T* data() const;
- // template <typename U>
- // bool Read(size_t index, U* output);
-
- // Handles:
- // T Take(size_t index);
-
- // Interfaces:
- // template <typename U>
- // U Take(size_t index);
-
- // Object types:
- // void GetDataView(size_t index, T* output);
- // template <typename U>
- // bool Read(size_t index, U* output);
-
- private:
- template <typename K, typename V>
- friend class MapDataView;
-};
-
-} // namespace mojo
-
-#endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_DATA_VIEW_H_
diff --git a/mojo/public/cpp/bindings/array_traits.h b/mojo/public/cpp/bindings/array_traits.h
index 594b2e0..366573d 100644
--- a/mojo/public/cpp/bindings/array_traits.h
+++ b/mojo/public/cpp/bindings/array_traits.h
@@ -47,9 +47,7 @@
// static void AdvanceIterator(Iterator& iterator);
//
// // Returns a reference to the value at the current position of
-// // |iterator|. Optionally, the ConstIterator version of GetValue can
-// // return by value instead of by reference if it makes sense for the
-// // type.
+// // |iterator|.
// static const T& GetValue(ConstIterator& iterator);
// static T& GetValue(Iterator& iterator);
//
diff --git a/mojo/public/cpp/bindings/array_traits_carray.h b/mojo/public/cpp/bindings/array_traits_carray.h
index 3ff694b..ffcf9d5 100644
--- a/mojo/public/cpp/bindings/array_traits_carray.h
+++ b/mojo/public/cpp/bindings/array_traits_carray.h
@@ -20,14 +20,6 @@
};
template <typename T>
-struct ConstCArray {
- ConstCArray() : size(0), data(nullptr) {}
- ConstCArray(size_t size, const T* data) : size(size), data(data) {}
- size_t size;
- const T* data;
-};
-
-template <typename T>
struct ArrayTraits<CArray<T>> {
using Element = T;
@@ -56,21 +48,6 @@
}
};
-template <typename T>
-struct ArrayTraits<ConstCArray<T>> {
- using Element = T;
-
- static bool IsNull(const ConstCArray<T>& input) { return !input.data; }
-
- static size_t GetSize(const ConstCArray<T>& input) { return input.size; }
-
- static const T* GetData(const ConstCArray<T>& input) { return input.data; }
-
- static const T& GetAt(const ConstCArray<T>& input, size_t index) {
- return input.data[index];
- }
-};
-
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_CARRAY_H_
diff --git a/mojo/public/cpp/bindings/array_traits_standard.h b/mojo/public/cpp/bindings/array_traits_standard.h
new file mode 100644
index 0000000..862de6b
--- /dev/null
+++ b/mojo/public/cpp/bindings/array_traits_standard.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_ARRAY_TRAITS_STANDARD_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STANDARD_H_
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/array_traits.h"
+
+namespace mojo {
+
+template <typename T>
+struct ArrayTraits<Array<T>> {
+ using Element = T;
+
+ static bool IsNull(const Array<T>& input) { return input.is_null(); }
+ static void SetToNull(Array<T>* output) { *output = nullptr; }
+
+ static size_t GetSize(const Array<T>& input) { return input.size(); }
+
+ static T* GetData(Array<T>& input) { return &input.front(); }
+
+ static const T* GetData(const Array<T>& input) { return &input.front(); }
+
+ static typename Array<T>::RefType GetAt(Array<T>& input, size_t index) {
+ return input[index];
+ }
+
+ static typename Array<T>::ConstRefType GetAt(const Array<T>& input,
+ size_t index) {
+ return input[index];
+ }
+
+ static bool Resize(Array<T>& input, size_t size) {
+ input.resize(size);
+ return true;
+ }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STANDARD_H_
diff --git a/mojo/public/cpp/bindings/array_traits_stl.h b/mojo/public/cpp/bindings/array_traits_stl.h
index dec47bf..9054a92 100644
--- a/mojo/public/cpp/bindings/array_traits_stl.h
+++ b/mojo/public/cpp/bindings/array_traits_stl.h
@@ -5,8 +5,6 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STL_H_
#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STL_H_
-#include <map>
-#include <set>
#include <vector>
#include "mojo/public/cpp/bindings/array_traits.h"
@@ -44,17 +42,13 @@
return input[index];
}
- static inline bool Resize(std::vector<T>& input, size_t size) {
- // Instead of calling std::vector<T>::resize() directly, this is a hack to
- // make compilers happy. Some compilers (e.g., Mac, Android, Linux MSan)
- // currently don't allow resizing types like
- // std::vector<std::vector<MoveOnlyType>>.
- // Because the deserialization code doesn't care about the original contents
- // of |input|, we discard them directly.
- //
- // The "inline" keyword of this method matters. Without it, we have observed
- // significant perf regression with some tests on Mac. crbug.com/631415
+ static bool Resize(std::vector<T>& input, size_t size) {
if (input.size() != size) {
+ // This is a hack to make compilers for Mac and Android happy. They
+ // currently don't allow resizing types like
+ // std::vector<std::vector<MoveOnlyType>>.
+ // Because the deserialization code doesn't care about the original
+ // contents of |input|, we discard them directly.
std::vector<T> temp(size);
input.swap(temp);
}
@@ -63,65 +57,6 @@
}
};
-// This ArrayTraits specialization is used only for serialization.
-template <typename T>
-struct ArrayTraits<std::set<T>> {
- using Element = T;
- using ConstIterator = typename std::set<T>::const_iterator;
-
- static bool IsNull(const std::set<T>& input) {
- // std::set<> is always converted to non-null mojom array.
- return false;
- }
-
- static size_t GetSize(const std::set<T>& input) { return input.size(); }
-
- static ConstIterator GetBegin(const std::set<T>& input) {
- return input.begin();
- }
- static void AdvanceIterator(ConstIterator& iterator) {
- ++iterator;
- }
- static const T& GetValue(ConstIterator& iterator) {
- return *iterator;
- }
-};
-
-template <typename K, typename V>
-struct MapValuesArrayView {
- explicit MapValuesArrayView(const std::map<K, V>& map) : map(map) {}
- const std::map<K, V>& map;
-};
-
-// Convenience function to create a MapValuesArrayView<> that infers the
-// template arguments from its argument type.
-template <typename K, typename V>
-MapValuesArrayView<K, V> MapValuesToArray(const std::map<K, V>& map) {
- return MapValuesArrayView<K, V>(map);
-}
-
-// This ArrayTraits specialization is used only for serialization and converts
-// a map<K, V> into an array<V>, discarding the keys.
-template <typename K, typename V>
-struct ArrayTraits<MapValuesArrayView<K, V>> {
- using Element = V;
- using ConstIterator = typename std::map<K, V>::const_iterator;
-
- static bool IsNull(const MapValuesArrayView<K, V>& input) {
- // std::map<> is always converted to non-null mojom array.
- return false;
- }
-
- static size_t GetSize(const MapValuesArrayView<K, V>& input) {
- return input.map.size();
- }
- static ConstIterator GetBegin(const MapValuesArrayView<K, V>& input) {
- return input.map.begin();
- }
- static void AdvanceIterator(ConstIterator& iterator) { ++iterator; }
- static const V& GetValue(ConstIterator& iterator) { return iterator->second; }
-};
-
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STL_H_
diff --git a/mojo/public/cpp/bindings/array_traits_wtf.h b/mojo/public/cpp/bindings/array_traits_wtf.h
new file mode 100644
index 0000000..7e773fc
--- /dev/null
+++ b/mojo/public/cpp/bindings/array_traits_wtf.h
@@ -0,0 +1,40 @@
+// 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_ARRAY_TRAITS_WTF_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_WTF_H_
+
+#include "mojo/public/cpp/bindings/array_traits.h"
+#include "mojo/public/cpp/bindings/wtf_array.h"
+
+namespace mojo {
+
+template <typename U>
+struct ArrayTraits<WTFArray<U>> {
+ using Element = U;
+
+ static bool IsNull(const WTFArray<U>& input) { return input.is_null(); }
+ static void SetToNull(WTFArray<U>* output) { *output = nullptr; }
+
+ static size_t GetSize(const WTFArray<U>& input) { return input.size(); }
+
+ static U* GetData(WTFArray<U>& input) { return &input.front(); }
+
+ static const U* GetData(const WTFArray<U>& input) { return &input.front(); }
+
+ static U& GetAt(WTFArray<U>& input, size_t index) { return input[index]; }
+
+ static const U& GetAt(const WTFArray<U>& input, size_t index) {
+ return input[index];
+ }
+
+ static bool Resize(WTFArray<U>& input, size_t size) {
+ input.resize(size);
+ return true;
+ }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_WTF_H_
diff --git a/mojo/public/cpp/bindings/associated_binding.h b/mojo/public/cpp/bindings/associated_binding.h
index 5941166..1da5009 100644
--- a/mojo/public/cpp/bindings/associated_binding.h
+++ b/mojo/public/cpp/bindings/associated_binding.h
@@ -6,78 +6,23 @@
#define MOJO_PUBLIC_CPP_BINDINGS_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/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/associated_group.h"
+#include "mojo/public/cpp/bindings/associated_group_controller.h"
#include "mojo/public/cpp/bindings/associated_interface_request.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
-#include "mojo/public/cpp/bindings/connection_error_callback.h"
#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
-#include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h"
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
namespace mojo {
-class MessageReceiver;
-
-// Base class used to factor out code in AssociatedBinding<T> expansions, in
-// particular for Bind().
-class MOJO_CPP_BINDINGS_EXPORT AssociatedBindingBase {
- public:
- AssociatedBindingBase();
- ~AssociatedBindingBase();
-
- // Adds a message filter to be notified of each incoming message before
- // dispatch. If a filter returns |false| from Accept(), the message is not
- // dispatched and the pipe is closed. Filters cannot be removed.
- void AddFilter(std::unique_ptr<MessageReceiver> filter);
-
- // Closes the associated interface. Puts this object into a state where it can
- // be rebound.
- void Close();
-
- // Similar to the method above, but also specifies a disconnect reason.
- void CloseWithReason(uint32_t custom_reason, const std::string& description);
-
- // Sets an error handler that will be called if a connection error occurs.
- //
- // This method may only be called after this AssociatedBinding has been bound
- // to a message pipe. The error handler will be reset when this
- // AssociatedBinding is unbound or closed.
- void set_connection_error_handler(const base::Closure& error_handler);
-
- void set_connection_error_with_reason_handler(
- const ConnectionErrorWithReasonCallback& error_handler);
-
- // Indicates whether the associated binding has been completed.
- bool is_bound() const { return !!endpoint_client_; }
-
- // 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();
-
- protected:
- void BindImpl(ScopedInterfaceEndpointHandle handle,
- MessageReceiverWithResponderStatus* receiver,
- std::unique_ptr<MessageReceiver> payload_validator,
- bool expect_sync_requests,
- scoped_refptr<base::SingleThreadTaskRunner> runner,
- uint32_t interface_version);
-
- std::unique_ptr<InterfaceEndpointClient> endpoint_client_;
-};
-
// Represents the implementation side of an associated interface. It is similar
// to Binding, except that it doesn't own a message pipe handle.
//
@@ -88,48 +33,53 @@
// single thread for the purposes of task scheduling. Please note that incoming
// synchrounous method calls may not be run from this task runner, when they
// reenter outgoing synchrounous calls on the same thread.
-template <typename Interface,
- typename ImplRefTraits = RawPtrImplRefTraits<Interface>>
-class AssociatedBinding : public AssociatedBindingBase {
+template <typename Interface>
+class AssociatedBinding {
public:
- using ImplPointerType = typename ImplRefTraits::PointerType;
-
// Constructs an incomplete associated binding that will use the
// implementation |impl|. It may be completed with a subsequent call to the
// |Bind| method. Does not take ownership of |impl|, which must outlive this
// object.
- explicit AssociatedBinding(ImplPointerType impl) { stub_.set_sink(impl); }
+ explicit AssociatedBinding(Interface* impl) : impl_(impl) {
+ stub_.set_sink(impl_);
+ }
// Constructs a completed associated binding of |impl|. The output |ptr_info|
- // should be sent by another interface. |impl| must outlive this object.
- AssociatedBinding(ImplPointerType impl,
+ // should be passed through the message pipe endpoint referred to by
+ // |associated_group| to setup the corresponding asssociated interface
+ // pointer. |impl| must outlive this object.
+ AssociatedBinding(Interface* impl,
AssociatedInterfacePtrInfo<Interface>* ptr_info,
+ AssociatedGroup* associated_group,
scoped_refptr<base::SingleThreadTaskRunner> runner =
base::ThreadTaskRunnerHandle::Get())
- : AssociatedBinding(std::move(impl)) {
- Bind(ptr_info, std::move(runner));
+ : AssociatedBinding(impl) {
+ Bind(ptr_info, associated_group, std::move(runner));
}
// Constructs a completed associated binding of |impl|. |impl| must outlive
// the binding.
- AssociatedBinding(ImplPointerType impl,
+ AssociatedBinding(Interface* impl,
AssociatedInterfaceRequest<Interface> request,
scoped_refptr<base::SingleThreadTaskRunner> runner =
base::ThreadTaskRunnerHandle::Get())
- : AssociatedBinding(std::move(impl)) {
+ : AssociatedBinding(impl) {
Bind(std::move(request), std::move(runner));
}
~AssociatedBinding() {}
// Creates an associated inteface and sets up this object as the
- // implementation side. The output |ptr_info| should be sent by another
- // interface.
+ // implementation side. The output |ptr_info| should be passed through the
+ // message pipe endpoint referred to by |associated_group| to setup the
+ // corresponding asssociated interface pointer.
void Bind(AssociatedInterfacePtrInfo<Interface>* ptr_info,
+ AssociatedGroup* associated_group,
scoped_refptr<base::SingleThreadTaskRunner> runner =
base::ThreadTaskRunnerHandle::Get()) {
- auto request = MakeRequest(ptr_info);
- ptr_info->set_version(Interface::Version_);
+ AssociatedInterfaceRequest<Interface> request;
+ associated_group->CreateAssociatedInterface(AssociatedGroup::WILL_PASS_PTR,
+ ptr_info, &request);
Bind(std::move(request), std::move(runner));
}
@@ -137,10 +87,35 @@
void Bind(AssociatedInterfaceRequest<Interface> request,
scoped_refptr<base::SingleThreadTaskRunner> runner =
base::ThreadTaskRunnerHandle::Get()) {
- BindImpl(request.PassHandle(), &stub_,
- base::WrapUnique(new typename Interface::RequestValidator_()),
- Interface::HasSyncMethods_, std::move(runner),
- Interface::Version_);
+ ScopedInterfaceEndpointHandle handle = request.PassHandle();
+
+ DCHECK(handle.is_local())
+ << "The AssociatedInterfaceRequest is supposed to be used at the "
+ << "other side of the message pipe.";
+
+ if (!handle.is_valid() || !handle.is_local()) {
+ endpoint_client_.reset();
+ return;
+ }
+
+ endpoint_client_.reset(new InterfaceEndpointClient(
+ std::move(handle), &stub_,
+ base::WrapUnique(new typename Interface::RequestValidator_()),
+ Interface::HasSyncMethods_, std::move(runner)));
+ endpoint_client_->set_connection_error_handler(
+ base::Bind(&AssociatedBinding::RunConnectionErrorHandler,
+ base::Unretained(this)));
+
+ stub_.serialization_context()->group_controller =
+ endpoint_client_->group_controller();
+ }
+
+ // Closes the associated interface. Puts this object into a state where it can
+ // be rebound.
+ void Close() {
+ DCHECK(endpoint_client_);
+ endpoint_client_.reset();
+ connection_error_handler_.Reset();
}
// Unbinds and returns the associated interface request so it can be
@@ -153,15 +128,44 @@
request.Bind(endpoint_client_->PassHandle());
endpoint_client_.reset();
+ connection_error_handler_.Reset();
return request;
}
+ // Sets an error handler that will be called if a connection error occurs.
+ //
+ // This method may only be called after this AssociatedBinding has been bound
+ // to a message pipe. The error handler will be reset when this
+ // AssociatedBinding is unbound or closed.
+ void set_connection_error_handler(const base::Closure& error_handler) {
+ DCHECK(is_bound());
+ connection_error_handler_ = error_handler;
+ }
+
// Returns the interface implementation that was previously specified.
- Interface* impl() { return ImplRefTraits::GetRawPointer(&stub_.sink()); }
+ Interface* impl() { return impl_; }
+
+ // Indicates whether the associated binding has been completed.
+ bool is_bound() const { return !!endpoint_client_; }
+
+ // Returns the associated group that this object belongs to. Returns null if
+ // the object is not bound.
+ AssociatedGroup* associated_group() {
+ return endpoint_client_ ? endpoint_client_->associated_group() : nullptr;
+ }
private:
- typename Interface::template Stub_<ImplRefTraits> stub_;
+ void RunConnectionErrorHandler() {
+ if (!connection_error_handler_.is_null())
+ connection_error_handler_.Run();
+ }
+
+ std::unique_ptr<InterfaceEndpointClient> endpoint_client_;
+
+ typename Interface::Stub_ stub_;
+ Interface* impl_;
+ base::Closure connection_error_handler_;
DISALLOW_COPY_AND_ASSIGN(AssociatedBinding);
};
diff --git a/mojo/public/cpp/bindings/associated_group.h b/mojo/public/cpp/bindings/associated_group.h
index 14e78ec..836c0d6 100644
--- a/mojo/public/cpp/bindings/associated_group.h
+++ b/mojo/public/cpp/bindings/associated_group.h
@@ -5,9 +5,11 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_GROUP_H_
#define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_GROUP_H_
-#include "base/callback.h"
+#include <utility>
+
#include "base/memory/ref_counted.h"
-#include "mojo/public/cpp/bindings/bindings_export.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/scoped_interface_endpoint_handle.h"
namespace mojo {
@@ -15,34 +17,71 @@
class AssociatedGroupController;
// AssociatedGroup refers to all the interface endpoints running at one end of a
-// message pipe.
+// message pipe. It is used to create associated interfaces for that message
+// pipe.
// It is thread safe and cheap to make copies.
-class MOJO_CPP_BINDINGS_EXPORT AssociatedGroup {
+class AssociatedGroup {
public:
+ // Configuration used by CreateAssociatedInterface(). Please see the comments
+ // of that method for more details.
+ enum AssociatedInterfaceConfig { WILL_PASS_PTR, WILL_PASS_REQUEST };
+
AssociatedGroup();
-
- explicit AssociatedGroup(scoped_refptr<AssociatedGroupController> controller);
-
- explicit AssociatedGroup(const ScopedInterfaceEndpointHandle& handle);
-
AssociatedGroup(const AssociatedGroup& other);
~AssociatedGroup();
AssociatedGroup& operator=(const AssociatedGroup& other);
- // The return value of this getter if this object is initialized with a
- // ScopedInterfaceEndpointHandle:
- // - If the handle is invalid, the return value will always be null.
- // - If the handle is valid and non-pending, the return value will be
- // non-null and remain unchanged even if the handle is later reset.
- // - If the handle is pending asssociation, the return value will initially
- // be null, change to non-null when/if the handle is associated, and
- // remain unchanged ever since.
- AssociatedGroupController* GetController();
+ // |config| indicates whether |ptr_info| or |request| will be sent to the
+ // remote side of the message pipe.
+ //
+ // NOTE: If |config| is |WILL_PASS_REQUEST|, you will want to bind |ptr_info|
+ // to a local AssociatedInterfacePtr to make calls. However, there is one
+ // restriction: the pointer should NOT be used to make calls before |request|
+ // is sent. Violating that will cause the message pipe to be closed. On the
+ // other hand, as soon as |request| is sent, the pointer is usable. There is
+ // no need to wait until |request| is bound to an implementation at the remote
+ // side.
+ template <typename T>
+ void CreateAssociatedInterface(
+ AssociatedInterfaceConfig config,
+ AssociatedInterfacePtrInfo<T>* ptr_info,
+ AssociatedInterfaceRequest<T>* request) {
+ ScopedInterfaceEndpointHandle local;
+ ScopedInterfaceEndpointHandle remote;
+ CreateEndpointHandlePair(&local, &remote);
+
+ if (!local.is_valid() || !remote.is_valid()) {
+ *ptr_info = AssociatedInterfacePtrInfo<T>();
+ *request = AssociatedInterfaceRequest<T>();
+ return;
+ }
+
+ if (config == WILL_PASS_PTR) {
+ ptr_info->set_handle(std::move(remote));
+
+ // The implementation is local, therefore set the version according to
+ // the interface definition that this code is built against.
+ ptr_info->set_version(T::Version_);
+ request->Bind(std::move(local));
+ } else {
+ ptr_info->set_handle(std::move(local));
+
+ // The implementation is remote, we don't know about its actual version
+ // yet.
+ ptr_info->set_version(0u);
+ request->Bind(std::move(remote));
+ }
+ }
private:
- base::Callback<AssociatedGroupController*()> controller_getter_;
+ friend class AssociatedGroupController;
+
+ void CreateEndpointHandlePair(
+ ScopedInterfaceEndpointHandle* local_endpoint,
+ ScopedInterfaceEndpointHandle* remote_endpoint);
+
scoped_refptr<AssociatedGroupController> controller_;
};
diff --git a/mojo/public/cpp/bindings/associated_group_controller.h b/mojo/public/cpp/bindings/associated_group_controller.h
index d33c277..0ab8253 100644
--- a/mojo/public/cpp/bindings/associated_group_controller.h
+++ b/mojo/public/cpp/bindings/associated_group_controller.h
@@ -9,47 +9,41 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "base/optional.h"
+#include "base/memory/ref_counted_delete_on_message_loop.h"
#include "base/single_thread_task_runner.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
-#include "mojo/public/cpp/bindings/disconnect_reason.h"
#include "mojo/public/cpp/bindings/interface_id.h"
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
namespace mojo {
+class AssociatedGroup;
class InterfaceEndpointClient;
class InterfaceEndpointController;
-// An internal interface used to manage endpoints within an associated group,
-// which corresponds to one end of a message pipe.
-class MOJO_CPP_BINDINGS_EXPORT AssociatedGroupController
- : public base::RefCountedThreadSafe<AssociatedGroupController> {
+// An internal interface used to manage endpoints within an associated group.
+class AssociatedGroupController :
+ public base::RefCountedDeleteOnMessageLoop<AssociatedGroupController> {
public:
- // Associates an interface with this AssociatedGroupController's message pipe.
- // It takes ownership of |handle_to_send| and returns an interface ID that
- // could be sent by any endpoints within the same associated group.
- // If |handle_to_send| is not in pending association state, it returns
- // kInvalidInterfaceId. Otherwise, the peer handle of |handle_to_send| joins
- // the associated group and is no longer pending.
- virtual InterfaceId AssociateInterface(
- ScopedInterfaceEndpointHandle handle_to_send) = 0;
+ explicit AssociatedGroupController(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+ // Creates a pair of interface endpoint handles. The method generates a new
+ // interface ID and assigns it to the two handles. |local_endpoint| is used
+ // locally; while |remote_endpoint| is sent over the message pipe.
+ virtual void CreateEndpointHandlePair(
+ ScopedInterfaceEndpointHandle* local_endpoint,
+ ScopedInterfaceEndpointHandle* remote_endpoint) = 0;
// Creates an interface endpoint handle from a given interface ID. The handle
- // joins this associated group.
+ // is used locally.
// Typically, this method is used to (1) create an endpoint handle for the
// master interface; or (2) create an endpoint handle on receiving an
// interface ID from the message pipe.
- //
- // On failure, the method returns an invalid handle. Usually that is because
- // the ID has already been used to create a handle.
virtual ScopedInterfaceEndpointHandle CreateLocalEndpointHandle(
InterfaceId id) = 0;
// Closes an interface endpoint handle.
- virtual void CloseEndpointHandle(
- InterfaceId id,
- const base::Optional<DisconnectReason>& reason) = 0;
+ virtual void CloseEndpointHandle(InterfaceId id, bool is_local) = 0;
// Attaches a client to the specified endpoint to send and receive messages.
// The returned object is still owned by the controller. It must only be used
@@ -69,23 +63,21 @@
// and notifies all interfaces running on this pipe.
virtual void RaiseError() = 0;
+ std::unique_ptr<AssociatedGroup> CreateAssociatedGroup();
+
protected:
- friend class base::RefCountedThreadSafe<AssociatedGroupController>;
+ friend class base::RefCountedDeleteOnMessageLoop<AssociatedGroupController>;
+ friend class base::DeleteHelper<AssociatedGroupController>;
- // Creates a new ScopedInterfaceEndpointHandle within this associated group.
+ // Creates a new ScopedInterfaceEndpointHandle associated with this
+ // controller.
ScopedInterfaceEndpointHandle CreateScopedInterfaceEndpointHandle(
- InterfaceId id);
-
- // Notifies that the interface represented by |handle_to_send| and its peer
- // has been associated with this AssociatedGroupController's message pipe, and
- // |handle_to_send|'s peer has joined this associated group. (Note: it is the
- // peer who has joined the associated group; |handle_to_send| will be sent to
- // the remote side.)
- // Returns false if |handle_to_send|'s peer has closed.
- bool NotifyAssociation(ScopedInterfaceEndpointHandle* handle_to_send,
- InterfaceId id);
+ InterfaceId id,
+ bool is_local);
virtual ~AssociatedGroupController();
+
+ DISALLOW_COPY_AND_ASSIGN(AssociatedGroupController);
};
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/associated_interface_ptr.h b/mojo/public/cpp/bindings/associated_interface_ptr.h
index 8e66f4e..10494ce 100644
--- a/mojo/public/cpp/bindings/associated_interface_ptr.h
+++ b/mojo/public/cpp/bindings/associated_interface_ptr.h
@@ -6,8 +6,6 @@
#define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_
#include <stdint.h>
-
-#include <string>
#include <utility>
#include "base/callback.h"
@@ -16,12 +14,10 @@
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/cpp/bindings/associated_group.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/connection_error_callback.h"
#include "mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h"
-#include "mojo/public/cpp/bindings/lib/multiplex_router.h"
-#include "mojo/public/cpp/system/message_pipe.h"
namespace mojo {
@@ -30,9 +26,6 @@
template <typename Interface>
class AssociatedInterfacePtr {
public:
- using InterfaceType = Interface;
- using PtrInfoType = AssociatedInterfacePtrInfo<Interface>;
-
// Constructs an unbound AssociatedInterfacePtr.
AssociatedInterfacePtr() {}
AssociatedInterfacePtr(decltype(nullptr)) {}
@@ -65,16 +58,20 @@
// multiple task runners to a single thread for the purposes of task
// scheduling.
//
- // NOTE: The corresponding AssociatedInterfaceRequest must be sent over
- // another interface before using this object to make calls. Please see the
- // comments of MakeRequest(AssociatedInterfacePtr<Interface>*) for more
- // details.
+ // NOTE: Please see the comments of
+ // AssociatedGroup.CreateAssociatedInterface() about when you can use this
+ // object to make calls.
void Bind(AssociatedInterfacePtrInfo<Interface> info,
scoped_refptr<base::SingleThreadTaskRunner> runner =
base::ThreadTaskRunnerHandle::Get()) {
reset();
- if (info.is_valid())
+ bool is_local = info.handle().is_local();
+
+ DCHECK(is_local) << "The AssociatedInterfacePtrInfo is supposed to be used "
+ "at the other side of the message pipe.";
+
+ if (info.is_valid() && is_local)
internal_state_.Bind(std::move(info), std::move(runner));
}
@@ -89,6 +86,9 @@
// Returns the version number of the interface that the remote side supports.
uint32_t version() const { return internal_state_.version(); }
+ // Returns the internal interface ID of this associated interface.
+ uint32_t interface_id() const { return internal_state_.interface_id(); }
+
// Queries the max version that the remote side supports. On completion, the
// result will be returned as the input of |callback|. The version number of
// this object will also be updated.
@@ -107,12 +107,6 @@
internal_state_.RequireVersion(version);
}
- // 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() { internal_state_.FlushForTesting(); }
-
// Closes the associated interface (if any) and returns the pointer to the
// unbound state.
void reset() {
@@ -120,13 +114,6 @@
internal_state_.Swap(&doomed);
}
- // Similar to the method above, but also specifies a disconnect reason.
- void ResetWithReason(uint32_t custom_reason, const std::string& description) {
- if (internal_state_.is_bound())
- internal_state_.CloseWithReason(custom_reason, description);
- reset();
- }
-
// Indicates whether an error has been encountered. If true, method calls made
// on this interface will be dropped (and may already have been dropped).
bool encountered_error() const { return internal_state_.encountered_error(); }
@@ -139,11 +126,6 @@
internal_state_.set_connection_error_handler(error_handler);
}
- void set_connection_error_with_reason_handler(
- const ConnectionErrorWithReasonCallback& error_handler) {
- internal_state_.set_connection_error_with_reason_handler(error_handler);
- }
-
// Unbinds and returns the associated interface pointer information which
// could be used to setup an AssociatedInterfacePtr again. This method may be
// used to move the proxy to a different thread.
@@ -159,6 +141,12 @@
return state.PassInterface();
}
+ // Returns the associated group that this object belongs to. Returns null if
+ // the object is not bound.
+ AssociatedGroup* associated_group() {
+ return internal_state_.associated_group();
+ }
+
// DO NOT USE. Exposed only for internal use and for testing.
internal::AssociatedInterfacePtrState<Interface>* internal_state() {
return &internal_state_;
@@ -191,95 +179,30 @@
DISALLOW_COPY_AND_ASSIGN(AssociatedInterfacePtr);
};
-// Creates an associated interface. The returned request is supposed to be sent
-// over another interface (either associated or non-associated).
+// Creates an associated interface. The output |ptr| should be used locally
+// while the returned request should be passed through the message pipe endpoint
+// referred to by |associated_group| to setup the corresponding asssociated
+// interface implementation at the remote side.
//
-// NOTE: |ptr| must NOT be used to make calls before the request is sent.
-// Violating that will lead to crash. On the other hand, as soon as the request
-// is sent, |ptr| is usable. There is no need to wait until the request is bound
-// to an implementation at the remote side.
+// NOTE: |ptr| should NOT be used to make calls before the request is sent.
+// Violating that will cause the message pipe to be closed. On the other hand,
+// as soon as the request is sent, |ptr| is usable. There is no need to wait
+// until the request is bound to an implementation at the remote side.
template <typename Interface>
-AssociatedInterfaceRequest<Interface> MakeRequest(
+AssociatedInterfaceRequest<Interface> GetProxy(
AssociatedInterfacePtr<Interface>* ptr,
+ AssociatedGroup* group,
scoped_refptr<base::SingleThreadTaskRunner> runner =
base::ThreadTaskRunnerHandle::Get()) {
+ AssociatedInterfaceRequest<Interface> request;
AssociatedInterfacePtrInfo<Interface> ptr_info;
- auto request = MakeRequest(&ptr_info);
+ group->CreateAssociatedInterface(AssociatedGroup::WILL_PASS_REQUEST,
+ &ptr_info, &request);
+
ptr->Bind(std::move(ptr_info), std::move(runner));
return request;
}
-// Creates an associated interface. One of the two endpoints is supposed to be
-// sent over another interface (either associated or non-associated); while the
-// other is used locally.
-//
-// NOTE: If |ptr_info| is used locally and bound to an AssociatedInterfacePtr,
-// the interface pointer must NOT be used to make calls before the request is
-// sent. Please see NOTE of the previous function for more details.
-template <typename Interface>
-AssociatedInterfaceRequest<Interface> MakeRequest(
- AssociatedInterfacePtrInfo<Interface>* ptr_info) {
- ScopedInterfaceEndpointHandle handle0;
- ScopedInterfaceEndpointHandle handle1;
- ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(&handle0,
- &handle1);
-
- ptr_info->set_handle(std::move(handle0));
- ptr_info->set_version(0);
-
- AssociatedInterfaceRequest<Interface> request;
- request.Bind(std::move(handle1));
- return request;
-}
-
-// Like |GetProxy|, but the interface is never associated with any other
-// interface. The returned request can be bound directly to the corresponding
-// associated interface implementation, without first passing it through a
-// message pipe endpoint.
-//
-// This function has two main uses:
-//
-// * In testing, where the returned request is bound to e.g. a mock and there
-// are no other interfaces involved.
-//
-// * When discarding messages sent on an interface, which can be done by
-// discarding the returned request.
-template <typename Interface>
-AssociatedInterfaceRequest<Interface> GetIsolatedProxy(
- AssociatedInterfacePtr<Interface>* ptr) {
- MessagePipe pipe;
- scoped_refptr<internal::MultiplexRouter> router0 =
- new internal::MultiplexRouter(std::move(pipe.handle0),
- internal::MultiplexRouter::MULTI_INTERFACE,
- false, base::ThreadTaskRunnerHandle::Get());
- scoped_refptr<internal::MultiplexRouter> router1 =
- new internal::MultiplexRouter(std::move(pipe.handle1),
- internal::MultiplexRouter::MULTI_INTERFACE,
- true, base::ThreadTaskRunnerHandle::Get());
-
- ScopedInterfaceEndpointHandle endpoint0, endpoint1;
- ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(&endpoint0,
- &endpoint1);
- InterfaceId id = router1->AssociateInterface(std::move(endpoint0));
- endpoint0 = router0->CreateLocalEndpointHandle(id);
-
- ptr->Bind(AssociatedInterfacePtrInfo<Interface>(std::move(endpoint0),
- Interface::Version_));
-
- AssociatedInterfaceRequest<Interface> request;
- request.Bind(std::move(endpoint1));
- return request;
-}
-
-// Creates an associated interface proxy in its own AssociatedGroup.
-// TODO(yzshen): Rename GetIsolatedProxy() to MakeIsolatedRequest(), and change
-// all callsites of this function to directly use that.
-template <typename Interface>
-AssociatedInterfaceRequest<Interface> MakeRequestForTesting(
- AssociatedInterfacePtr<Interface>* ptr) {
- return GetIsolatedProxy(ptr);
-}
-
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_
diff --git a/mojo/public/cpp/bindings/associated_interface_ptr_info.h b/mojo/public/cpp/bindings/associated_interface_ptr_info.h
index 3c6ca54..bfb3297 100644
--- a/mojo/public/cpp/bindings/associated_interface_ptr_info.h
+++ b/mojo/public/cpp/bindings/associated_interface_ptr_info.h
@@ -20,7 +20,6 @@
class AssociatedInterfacePtrInfo {
public:
AssociatedInterfacePtrInfo() : version_(0u) {}
- AssociatedInterfacePtrInfo(std::nullptr_t) : version_(0u) {}
AssociatedInterfacePtrInfo(AssociatedInterfacePtrInfo&& other)
: handle_(std::move(other.handle_)), version_(other.version_) {
diff --git a/mojo/public/cpp/bindings/associated_interface_request.h b/mojo/public/cpp/bindings/associated_interface_request.h
index c37636c..30fcd16 100644
--- a/mojo/public/cpp/bindings/associated_interface_request.h
+++ b/mojo/public/cpp/bindings/associated_interface_request.h
@@ -5,7 +5,6 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_REQUEST_H_
#define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_REQUEST_H_
-#include <string>
#include <utility>
#include "base/macros.h"
@@ -65,10 +64,6 @@
return !is_pending() && !other.is_pending();
}
- void ResetWithReason(uint32_t custom_reason, const std::string& description) {
- handle_.ResetWithReason(custom_reason, description);
- }
-
private:
ScopedInterfaceEndpointHandle handle_;
diff --git a/mojo/public/cpp/bindings/binding.h b/mojo/public/cpp/bindings/binding.h
index 1da331b..8c9ee2f 100644
--- a/mojo/public/cpp/bindings/binding.h
+++ b/mojo/public/cpp/bindings/binding.h
@@ -5,7 +5,6 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_BINDING_H_
#define MOJO_PUBLIC_CPP_BINDINGS_BINDING_H_
-#include <string>
#include <utility>
#include "base/callback_forward.h"
@@ -13,17 +12,15 @@
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "mojo/public/cpp/bindings/connection_error_callback.h"
#include "mojo/public/cpp/bindings/interface_ptr.h"
#include "mojo/public/cpp/bindings/interface_ptr_info.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "mojo/public/cpp/bindings/lib/binding_state.h"
-#include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h"
#include "mojo/public/cpp/system/core.h"
namespace mojo {
-class MessageReceiver;
+class AssociatedGroup;
// Represents the binding of an interface implementation to a message pipe.
// When the |Binding| object is destroyed, the binding between the message pipe
@@ -66,24 +63,21 @@
// single thread for the purposes of task scheduling. Please note that incoming
// synchrounous method calls may not be run from this task runner, when they
// reenter outgoing synchrounous calls on the same thread.
-template <typename Interface,
- typename ImplRefTraits = RawPtrImplRefTraits<Interface>>
+template <typename Interface>
class Binding {
public:
- using ImplPointerType = typename ImplRefTraits::PointerType;
-
// Constructs an incomplete binding that will use the implementation |impl|.
// The binding may be completed with a subsequent call to the |Bind| method.
// Does not take ownership of |impl|, which must outlive the binding.
- explicit Binding(ImplPointerType impl) : internal_state_(std::move(impl)) {}
+ explicit Binding(Interface* impl) : internal_state_(impl) {}
// Constructs a completed binding of message pipe |handle| to implementation
// |impl|. Does not take ownership of |impl|, which must outlive the binding.
- Binding(ImplPointerType impl,
+ Binding(Interface* impl,
ScopedMessagePipeHandle handle,
scoped_refptr<base::SingleThreadTaskRunner> runner =
base::ThreadTaskRunnerHandle::Get())
- : Binding(std::move(impl)) {
+ : Binding(impl) {
Bind(std::move(handle), std::move(runner));
}
@@ -92,22 +86,22 @@
// pass |ptr| on to the client of the service. Does not take ownership of any
// of the parameters. |impl| must outlive the binding. |ptr| only needs to
// last until the constructor returns.
- Binding(ImplPointerType impl,
+ Binding(Interface* impl,
InterfacePtr<Interface>* ptr,
scoped_refptr<base::SingleThreadTaskRunner> runner =
base::ThreadTaskRunnerHandle::Get())
- : Binding(std::move(impl)) {
+ : Binding(impl) {
Bind(ptr, std::move(runner));
}
// Constructs a completed binding of |impl| to the message pipe endpoint in
// |request|, taking ownership of the endpoint. Does not take ownership of
// |impl|, which must outlive the binding.
- Binding(ImplPointerType impl,
+ Binding(Interface* impl,
InterfaceRequest<Interface> request,
scoped_refptr<base::SingleThreadTaskRunner> runner =
base::ThreadTaskRunnerHandle::Get())
- : Binding(std::move(impl)) {
+ : Binding(impl) {
Bind(request.PassMessagePipe(), std::move(runner));
}
@@ -158,14 +152,6 @@
Bind(request.PassMessagePipe(), std::move(runner));
}
- // Adds a message filter to be notified of each incoming message before
- // dispatch. If a filter returns |false| from Accept(), the message is not
- // dispatched and the pipe is closed. Filters cannot be removed.
- void AddFilter(std::unique_ptr<MessageReceiver> filter) {
- DCHECK(is_bound());
- internal_state_.AddFilter(std::move(filter));
- }
-
// Whether there are any associated interfaces running on the pipe currently.
bool HasAssociatedInterfaces() const {
return internal_state_.HasAssociatedInterfaces();
@@ -203,11 +189,6 @@
// state where it can be rebound to a new pipe.
void Close() { internal_state_.Close(); }
- // Similar to the method above, but also specifies a disconnect reason.
- void CloseWithReason(uint32_t custom_reason, const std::string& description) {
- internal_state_.CloseWithReason(custom_reason, description);
- }
-
// Unbinds the underlying pipe from this binding and returns it so it can be
// used in another context, such as on another thread or with a different
// implementation. Put this object into a state where it can be rebound to a
@@ -236,12 +217,6 @@
internal_state_.set_connection_error_handler(error_handler);
}
- void set_connection_error_with_reason_handler(
- const ConnectionErrorWithReasonCallback& error_handler) {
- DCHECK(is_bound());
- internal_state_.set_connection_error_with_reason_handler(error_handler);
- }
-
// Returns the interface implementation that was previously specified. Caller
// does not take ownership.
Interface* impl() { return internal_state_.impl(); }
@@ -256,17 +231,20 @@
// transferred to the caller.
MessagePipeHandle handle() const { return internal_state_.handle(); }
- // Sends a no-op 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() { internal_state_.FlushForTesting(); }
+ // Returns the associated group that this object belongs to. Returns null if:
+ // - this object is not bound; or
+ // - the interface doesn't have methods to pass associated interface
+ // pointers or requests.
+ AssociatedGroup* associated_group() {
+ return internal_state_.associated_group();
+ }
// Exposed for testing, should not generally be used.
void EnableTestingMode() { internal_state_.EnableTestingMode(); }
private:
- internal::BindingState<Interface, ImplRefTraits> internal_state_;
+ internal::BindingState<Interface, Interface::PassesAssociatedKinds_>
+ internal_state_;
DISALLOW_COPY_AND_ASSIGN(Binding);
};
diff --git a/mojo/public/cpp/bindings/binding_set.h b/mojo/public/cpp/bindings/binding_set.h
index 919f9c0..b1baca6 100644
--- a/mojo/public/cpp/bindings/binding_set.h
+++ b/mojo/public/cpp/bindings/binding_set.h
@@ -5,263 +5,111 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_
#define MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_
-#include <string>
+#include <algorithm>
#include <utility>
+#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/macros.h"
-#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
#include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/public/cpp/bindings/connection_error_callback.h"
-#include "mojo/public/cpp/bindings/interface_ptr.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
-#include "mojo/public/cpp/bindings/message.h"
namespace mojo {
-template <typename BindingType>
-struct BindingSetTraits;
-
-template <typename Interface, typename ImplRefTraits>
-struct BindingSetTraits<Binding<Interface, ImplRefTraits>> {
- using ProxyType = InterfacePtr<Interface>;
- using RequestType = InterfaceRequest<Interface>;
- using BindingType = Binding<Interface, ImplRefTraits>;
- using ImplPointerType = typename BindingType::ImplPointerType;
-
- static RequestType MakeRequest(ProxyType* proxy) {
- return mojo::MakeRequest(proxy);
- }
-};
-
-using BindingId = size_t;
-
-template <typename ContextType>
-struct BindingSetContextTraits {
- using Type = ContextType;
-
- static constexpr bool SupportsContext() { return true; }
-};
-
-template <>
-struct BindingSetContextTraits<void> {
- // NOTE: This choice of Type only matters insofar as it affects the size of
- // the |context_| field of a BindingSetBase::Entry with void context. The
- // context value is never used in this case.
- using Type = bool;
-
- static constexpr bool SupportsContext() { return false; }
-};
-
-// Generic definition used for BindingSet and AssociatedBindingSet to own a
-// collection of bindings which point to the same implementation.
-//
-// If |ContextType| is non-void, then every added binding must include a context
-// value of that type, and |dispatch_context()| will return that value during
-// the extent of any message dispatch targeting that specific binding.
-template <typename Interface, typename BindingType, typename ContextType>
-class BindingSetBase {
+// Use this class to manage a set of bindings, which are automatically destroyed
+// and removed from the set when the pipe they are bound to is disconnected.
+template <typename Interface>
+class BindingSet {
public:
- using ContextTraits = BindingSetContextTraits<ContextType>;
- using Context = typename ContextTraits::Type;
- using PreDispatchCallback = base::Callback<void(const Context&)>;
- using Traits = BindingSetTraits<BindingType>;
- using ProxyType = typename Traits::ProxyType;
- using RequestType = typename Traits::RequestType;
- using ImplPointerType = typename Traits::ImplPointerType;
-
- BindingSetBase() {}
+ BindingSet() {}
+ ~BindingSet() { CloseAllBindings(); }
void set_connection_error_handler(const base::Closure& error_handler) {
error_handler_ = error_handler;
- error_with_reason_handler_.Reset();
}
- void set_connection_error_with_reason_handler(
- const ConnectionErrorWithReasonCallback& error_handler) {
- error_with_reason_handler_ = error_handler;
- error_handler_.Reset();
+ void AddBinding(Interface* impl, InterfaceRequest<Interface> request) {
+ auto binding = new Element(impl, std::move(request));
+ binding->set_connection_error_handler(
+ base::Bind(&BindingSet::OnConnectionError, base::Unretained(this)));
+ bindings_.push_back(binding->GetWeakPtr());
}
- // Sets a callback to be invoked immediately before dispatching any message or
- // error received by any of the bindings in the set. This may only be used
- // with a non-void |ContextType|.
- void set_pre_dispatch_handler(const PreDispatchCallback& handler) {
- static_assert(ContextTraits::SupportsContext(),
- "Pre-dispatch handler usage requires non-void context type.");
- pre_dispatch_handler_ = handler;
+ // Returns an InterfacePtr bound to one end of a pipe whose other end is
+ // bound to |this|.
+ InterfacePtr<Interface> CreateInterfacePtrAndBind(Interface* impl) {
+ InterfacePtr<Interface> interface_ptr;
+ AddBinding(impl, GetProxy(&interface_ptr));
+ return interface_ptr;
}
- // Adds a new binding to the set which binds |request| to |impl| with no
- // additional context.
- BindingId AddBinding(ImplPointerType impl, RequestType request) {
- static_assert(!ContextTraits::SupportsContext(),
- "Context value required for non-void context type.");
- return AddBindingImpl(std::move(impl), std::move(request), false);
+ void CloseAllBindings() {
+ for (const auto& it : bindings_) {
+ if (it) {
+ it->Close();
+ delete it.get();
+ }
+ }
+ bindings_.clear();
}
- // Adds a new binding associated with |context|.
- BindingId AddBinding(ImplPointerType impl,
- RequestType request,
- Context context) {
- static_assert(ContextTraits::SupportsContext(),
- "Context value unsupported for void context type.");
- return AddBindingImpl(std::move(impl), std::move(request),
- std::move(context));
- }
-
- // Removes a binding from the set. Note that this is safe to call even if the
- // binding corresponding to |id| has already been removed.
- //
- // Returns |true| if the binding was removed and |false| if it didn't exist.
- bool RemoveBinding(BindingId id) {
- auto it = bindings_.find(id);
- if (it == bindings_.end())
- return false;
- bindings_.erase(it);
- return true;
- }
-
- // Returns a proxy bound to one end of a pipe whose other end is bound to
- // |this|. If |id_storage| is not null, |*id_storage| will be set to the ID
- // of the added binding.
- ProxyType CreateInterfacePtrAndBind(ImplPointerType impl,
- BindingId* id_storage = nullptr) {
- ProxyType proxy;
- BindingId id = AddBinding(std::move(impl), Traits::MakeRequest(&proxy));
- if (id_storage)
- *id_storage = id;
- return proxy;
- }
-
- void CloseAllBindings() { bindings_.clear(); }
-
bool empty() const { return bindings_.empty(); }
- // Implementations may call this when processing a dispatched message or
- // error. During the extent of message or error dispatch, this will return the
- // context associated with the specific binding which received the message or
- // error. Use AddBinding() to associated a context with a specific binding.
- const Context& dispatch_context() const {
- static_assert(ContextTraits::SupportsContext(),
- "dispatch_context() requires non-void context type.");
- DCHECK(dispatch_context_);
- return *dispatch_context_;
- }
-
- void FlushForTesting() {
- for (auto& binding : bindings_)
- binding.second->FlushForTesting();
- }
-
private:
- friend class Entry;
-
- class Entry {
+ class Element {
public:
- Entry(ImplPointerType impl,
- RequestType request,
- BindingSetBase* binding_set,
- BindingId binding_id,
- Context context)
- : binding_(std::move(impl), std::move(request)),
- binding_set_(binding_set),
- binding_id_(binding_id),
- context_(std::move(context)) {
- if (ContextTraits::SupportsContext())
- binding_.AddFilter(base::MakeUnique<DispatchFilter>(this));
- binding_.set_connection_error_with_reason_handler(
- base::Bind(&Entry::OnConnectionError, base::Unretained(this)));
+ Element(Interface* impl, InterfaceRequest<Interface> request)
+ : binding_(impl, std::move(request)), weak_ptr_factory_(this) {
+ binding_.set_connection_error_handler(
+ base::Bind(&Element::OnConnectionError, base::Unretained(this)));
}
- void FlushForTesting() { binding_.FlushForTesting(); }
+ ~Element() {}
+
+ void set_connection_error_handler(const base::Closure& error_handler) {
+ error_handler_ = error_handler;
+ }
+
+ base::WeakPtr<Element> GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+ }
+
+ void Close() { binding_.Close(); }
+
+ void OnConnectionError() {
+ base::Closure error_handler = error_handler_;
+ delete this;
+ if (!error_handler.is_null())
+ error_handler.Run();
+ }
private:
- class DispatchFilter : public MessageReceiver {
- public:
- explicit DispatchFilter(Entry* entry) : entry_(entry) {}
- ~DispatchFilter() override {}
+ Binding<Interface> binding_;
+ base::Closure error_handler_;
+ base::WeakPtrFactory<Element> weak_ptr_factory_;
- private:
- // MessageReceiver:
- bool Accept(Message* message) override {
- entry_->WillDispatch();
- return true;
- }
-
- Entry* entry_;
-
- DISALLOW_COPY_AND_ASSIGN(DispatchFilter);
- };
-
- void WillDispatch() {
- DCHECK(ContextTraits::SupportsContext());
- binding_set_->SetDispatchContext(&context_);
- }
-
- void OnConnectionError(uint32_t custom_reason,
- const std::string& description) {
- if (ContextTraits::SupportsContext())
- WillDispatch();
- binding_set_->OnConnectionError(binding_id_, custom_reason, description);
- }
-
- BindingType binding_;
- BindingSetBase* const binding_set_;
- const BindingId binding_id_;
- Context const context_;
-
- DISALLOW_COPY_AND_ASSIGN(Entry);
+ DISALLOW_COPY_AND_ASSIGN(Element);
};
- void SetDispatchContext(const Context* context) {
- DCHECK(ContextTraits::SupportsContext());
- dispatch_context_ = context;
- if (!pre_dispatch_handler_.is_null())
- pre_dispatch_handler_.Run(*context);
- }
-
- BindingId AddBindingImpl(ImplPointerType impl,
- RequestType request,
- Context context) {
- BindingId id = next_binding_id_++;
- DCHECK_GE(next_binding_id_, 0u);
- auto entry = base::MakeUnique<Entry>(std::move(impl), std::move(request),
- this, id, std::move(context));
- bindings_.insert(std::make_pair(id, std::move(entry)));
- return id;
- }
-
- void OnConnectionError(BindingId id,
- uint32_t custom_reason,
- const std::string& description) {
- auto it = bindings_.find(id);
- DCHECK(it != bindings_.end());
-
- // We keep the Entry alive throughout error dispatch.
- std::unique_ptr<Entry> entry = std::move(it->second);
- bindings_.erase(it);
+ void OnConnectionError() {
+ // Clear any deleted bindings.
+ bindings_.erase(std::remove_if(bindings_.begin(), bindings_.end(),
+ [](const base::WeakPtr<Element>& p) {
+ return p.get() == nullptr;
+ }),
+ bindings_.end());
if (!error_handler_.is_null())
error_handler_.Run();
- else if (!error_with_reason_handler_.is_null())
- error_with_reason_handler_.Run(custom_reason, description);
}
base::Closure error_handler_;
- ConnectionErrorWithReasonCallback error_with_reason_handler_;
- PreDispatchCallback pre_dispatch_handler_;
- BindingId next_binding_id_ = 0;
- std::map<BindingId, std::unique_ptr<Entry>> bindings_;
- const Context* dispatch_context_ = nullptr;
+ std::vector<base::WeakPtr<Element>> bindings_;
- DISALLOW_COPY_AND_ASSIGN(BindingSetBase);
+ DISALLOW_COPY_AND_ASSIGN(BindingSet);
};
-template <typename Interface, typename ContextType = void>
-using BindingSet = BindingSetBase<Interface, Binding<Interface>, ContextType>;
-
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_
diff --git a/mojo/public/cpp/bindings/bindings_export.h b/mojo/public/cpp/bindings/bindings_export.h
deleted file mode 100644
index 9fd7a27..0000000
--- a/mojo/public/cpp/bindings/bindings_export.h
+++ /dev/null
@@ -1,34 +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.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_BINDINGS_EXPORT_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_BINDINGS_EXPORT_H_
-
-#if defined(COMPONENT_BUILD)
-
-#if defined(WIN32)
-
-#if defined(MOJO_CPP_BINDINGS_IMPLEMENTATION)
-#define MOJO_CPP_BINDINGS_EXPORT __declspec(dllexport)
-#else
-#define MOJO_CPP_BINDINGS_EXPORT __declspec(dllimport)
-#endif
-
-#else // !defined(WIN32)
-
-#if defined(MOJO_CPP_BINDINGS_IMPLEMENTATION)
-#define MOJO_CPP_BINDINGS_EXPORT __attribute((visibility("default")))
-#else
-#define MOJO_CPP_BINDINGS_EXPORT
-#endif
-
-#endif // defined(WIN32)
-
-#else // !defined(COMPONENT_BUILD)
-
-#define MOJO_CPP_BINDINGS_EXPORT
-
-#endif // defined(COMPONENT_BUILD)
-
-#endif // MOJO_PUBLIC_CPP_BINDINGS_BINDINGS_EXPORT_H_
diff --git a/mojo/public/cpp/bindings/clone_traits.h b/mojo/public/cpp/bindings/clone_traits.h
deleted file mode 100644
index 203ab34..0000000
--- a/mojo/public/cpp/bindings/clone_traits.h
+++ /dev/null
@@ -1,86 +0,0 @@
-// 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_CLONE_TRAITS_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_CLONE_TRAITS_H_
-
-#include <type_traits>
-#include <unordered_map>
-#include <vector>
-
-#include "base/optional.h"
-#include "mojo/public/cpp/bindings/lib/template_util.h"
-
-namespace mojo {
-
-template <typename T>
-struct HasCloneMethod {
- template <typename U>
- static char Test(decltype(&U::Clone));
- template <typename U>
- static int Test(...);
- static const bool value = sizeof(Test<T>(0)) == sizeof(char);
-
- private:
- internal::EnsureTypeIsComplete<T> check_t_;
-};
-
-template <typename T, bool has_clone_method = HasCloneMethod<T>::value>
-struct CloneTraits;
-
-template <typename T>
-T Clone(const T& input);
-
-template <typename T>
-struct CloneTraits<T, true> {
- static T Clone(const T& input) { return input.Clone(); }
-};
-
-template <typename T>
-struct CloneTraits<T, false> {
- static T Clone(const T& input) { return input; }
-};
-
-template <typename T>
-struct CloneTraits<base::Optional<T>, false> {
- static base::Optional<T> Clone(const base::Optional<T>& input) {
- if (!input)
- return base::nullopt;
-
- return base::Optional<T>(mojo::Clone(*input));
- }
-};
-
-template <typename T>
-struct CloneTraits<std::vector<T>, false> {
- static std::vector<T> Clone(const std::vector<T>& input) {
- std::vector<T> result;
- result.reserve(input.size());
- for (const auto& element : input)
- result.push_back(mojo::Clone(element));
-
- return result;
- }
-};
-
-template <typename K, typename V>
-struct CloneTraits<std::unordered_map<K, V>, false> {
- static std::unordered_map<K, V> Clone(const std::unordered_map<K, V>& input) {
- std::unordered_map<K, V> result;
- for (const auto& element : input) {
- result.insert(std::make_pair(mojo::Clone(element.first),
- mojo::Clone(element.second)));
- }
- return result;
- }
-};
-
-template <typename T>
-T Clone(const T& input) {
- return CloneTraits<T>::Clone(input);
-};
-
-} // namespace mojo
-
-#endif // MOJO_PUBLIC_CPP_BINDINGS_CLONE_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/connection_error_callback.h b/mojo/public/cpp/bindings/connection_error_callback.h
deleted file mode 100644
index 306e99e..0000000
--- a/mojo/public/cpp/bindings/connection_error_callback.h
+++ /dev/null
@@ -1,21 +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.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_CONNECTION_ERROR_CALLBACK_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_CONNECTION_ERROR_CALLBACK_H_
-
-#include "base/callback.h"
-
-namespace mojo {
-
-// This callback type accepts user-defined disconnect reason and description. If
-// the other side specifies a reason on closing the connection, it will be
-// passed to the error handler.
-using ConnectionErrorWithReasonCallback =
- base::Callback<void(uint32_t /* custom_reason */,
- const std::string& /* description */)>;
-
-} // namespace mojo
-
-#endif // MOJO_PUBLIC_CPP_BINDINGS_CONNECTION_ERROR_CALLBACK_H_
diff --git a/mojo/public/cpp/bindings/connector.h b/mojo/public/cpp/bindings/connector.h
index 01e9236..d14ad17 100644
--- a/mojo/public/cpp/bindings/connector.h
+++ b/mojo/public/cpp/bindings/connector.h
@@ -8,13 +8,10 @@
#include <memory>
#include "base/callback.h"
-#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_checker.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/cpp/bindings/sync_handle_watcher.h"
#include "mojo/public/cpp/system/core.h"
@@ -36,8 +33,7 @@
// - Sending messages can be configured to be thread safe (please see comments
// of the constructor). Other than that, the object should only be accessed
// on the creating thread.
-class MOJO_CPP_BINDINGS_EXPORT Connector
- : NON_EXPORTED_BASE(public MessageReceiver) {
+class Connector : public MessageReceiver {
public:
enum ConnectorConfig {
// Connector::Accept() is only called from a single thread.
@@ -142,7 +138,6 @@
// Whether currently the control flow is inside the sync handle watcher
// callback.
- // It always returns false after CloseMessagePipe()/PassMessagePipe().
bool during_sync_handle_watcher_callback() const {
return sync_handle_watcher_callback_count_ > 0;
}
@@ -151,10 +146,6 @@
return task_runner_.get();
}
- // Sets the tag used by the heap profiler.
- // |tag| must be a const string literal.
- void SetWatcherHeapProfilerTag(const char* tag);
-
private:
// Callback of mojo::Watcher.
void OnWatcherHandleReady(MojoResult result);
@@ -164,8 +155,7 @@
void WaitToReadMore();
- // Returns false if it is impossible to receive more messages in the future.
- // |this| may have been destroyed in that case.
+ // Returns false if |this| was destroyed during message dispatch.
WARN_UNUSED_RESULT bool ReadSingleMessage(MojoResult* read_result);
// |this| can be destroyed during message dispatch.
@@ -185,40 +175,31 @@
base::Closure connection_error_handler_;
ScopedMessagePipeHandle message_pipe_;
- MessageReceiver* incoming_receiver_ = nullptr;
+ MessageReceiver* incoming_receiver_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
- std::unique_ptr<Watcher> handle_watcher_;
+ Watcher handle_watcher_;
- bool error_ = false;
- bool drop_writes_ = false;
- bool enforce_errors_from_incoming_receiver_ = true;
+ bool error_;
+ bool drop_writes_;
+ bool enforce_errors_from_incoming_receiver_;
- bool paused_ = false;
+ bool paused_;
// If sending messages is allowed from multiple threads, |lock_| is used to
// protect modifications to |message_pipe_| and |drop_writes_|.
- base::Optional<base::Lock> lock_;
+ std::unique_ptr<base::Lock> lock_;
std::unique_ptr<SyncHandleWatcher> sync_watcher_;
- bool allow_woken_up_by_others_ = false;
+ bool allow_woken_up_by_others_;
// If non-zero, currently the control flow is inside the sync handle watcher
// callback.
- size_t sync_handle_watcher_callback_count_ = 0;
+ size_t sync_handle_watcher_callback_count_;
base::ThreadChecker thread_checker_;
- base::Lock connected_lock_;
- bool connected_ = true;
-
- // The tag used to track heap allocations that originated from a Watcher
- // notification.
- const char* heap_profiler_tag_ = nullptr;
-
// Create a single weak ptr and use it everywhere, to avoid the malloc/free
// cost of creating a new weak ptr whenever it is needed.
- // NOTE: This weak pointer is invalidated when the message pipe is closed or
- // transferred (i.e., when |connected_| is set to false).
base::WeakPtr<Connector> weak_self_;
base::WeakPtrFactory<Connector> weak_factory_;
diff --git a/mojo/public/cpp/bindings/disconnect_reason.h b/mojo/public/cpp/bindings/disconnect_reason.h
deleted file mode 100644
index c04e8ad..0000000
--- a/mojo/public/cpp/bindings/disconnect_reason.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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_DISCONNECT_REASON_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_DISCONNECT_REASON_H_
-
-#include <stdint.h>
-
-#include <string>
-
-namespace mojo {
-
-struct DisconnectReason {
- public:
- DisconnectReason(uint32_t in_custom_reason, const std::string& in_description)
- : custom_reason(in_custom_reason), description(in_description) {}
-
- uint32_t custom_reason;
- std::string description;
-};
-
-} // namespace mojo
-
-#endif // MOJO_PUBLIC_CPP_BINDINGS_DISCONNECT_REASON_H_
diff --git a/mojo/public/cpp/bindings/filter_chain.h b/mojo/public/cpp/bindings/filter_chain.h
deleted file mode 100644
index 1262f39..0000000
--- a/mojo/public/cpp/bindings/filter_chain.h
+++ /dev/null
@@ -1,61 +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.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_FILTER_CHAIN_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_FILTER_CHAIN_H_
-
-#include <utility>
-#include <vector>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
-#include "mojo/public/cpp/bindings/message.h"
-
-namespace mojo {
-
-class MOJO_CPP_BINDINGS_EXPORT FilterChain
- : NON_EXPORTED_BASE(public MessageReceiver) {
- public:
- // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while
- // this object is alive.
- explicit FilterChain(MessageReceiver* sink = nullptr);
-
- FilterChain(FilterChain&& other);
- FilterChain& operator=(FilterChain&& other);
- ~FilterChain() override;
-
- template <typename FilterType, typename... Args>
- inline void Append(Args&&... args);
-
- void Append(std::unique_ptr<MessageReceiver> filter);
-
- // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while
- // this object is alive.
- void SetSink(MessageReceiver* sink);
-
- // MessageReceiver:
- bool Accept(Message* message) override;
-
- private:
- std::vector<std::unique_ptr<MessageReceiver>> filters_;
-
- MessageReceiver* sink_;
-
- DISALLOW_COPY_AND_ASSIGN(FilterChain);
-};
-
-template <typename FilterType, typename... Args>
-inline void FilterChain::Append(Args&&... args) {
- Append(base::MakeUnique<FilterType>(std::forward<Args>(args)...));
-}
-
-template <>
-inline void FilterChain::Append<PassThroughFilter>() {
-}
-
-} // namespace mojo
-
-#endif // MOJO_PUBLIC_CPP_BINDINGS_FILTER_CHAIN_H_
diff --git a/mojo/public/cpp/bindings/interface_data_view.h b/mojo/public/cpp/bindings/interface_data_view.h
deleted file mode 100644
index ef12254..0000000
--- a/mojo/public/cpp/bindings/interface_data_view.h
+++ /dev/null
@@ -1,25 +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.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_DATA_VIEW_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_DATA_VIEW_H_
-
-namespace mojo {
-
-// They are used for type identification purpose only.
-template <typename Interface>
-class AssociatedInterfacePtrInfoDataView {};
-
-template <typename Interface>
-class AssociatedInterfaceRequestDataView {};
-
-template <typename Interface>
-class InterfacePtrDataView {};
-
-template <typename Interface>
-class InterfaceRequestDataView {};
-
-} // namespace mojo
-
-#endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_DATA_VIEW_H_
diff --git a/mojo/public/cpp/bindings/interface_endpoint_client.h b/mojo/public/cpp/bindings/interface_endpoint_client.h
index 0aea756..9dc40a2 100644
--- a/mojo/public/cpp/bindings/interface_endpoint_client.h
+++ b/mojo/public/cpp/bindings/interface_endpoint_client.h
@@ -11,42 +11,34 @@
#include <memory>
#include "base/callback.h"
-#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_checker.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
-#include "mojo/public/cpp/bindings/connection_error_callback.h"
-#include "mojo/public/cpp/bindings/disconnect_reason.h"
-#include "mojo/public/cpp/bindings/filter_chain.h"
-#include "mojo/public/cpp/bindings/lib/control_message_handler.h"
-#include "mojo/public/cpp/bindings/lib/control_message_proxy.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"
namespace mojo {
class AssociatedGroup;
+class AssociatedGroupController;
class InterfaceEndpointController;
// InterfaceEndpointClient handles message sending and receiving of an interface
// endpoint, either the implementation side or the client side.
// It should only be accessed and destructed on the creating thread.
-class MOJO_CPP_BINDINGS_EXPORT InterfaceEndpointClient
- : NON_EXPORTED_BASE(public MessageReceiverWithResponder) {
+class InterfaceEndpointClient : public MessageReceiverWithResponder {
public:
// |receiver| is okay to be null. If it is not null, it must outlive this
// object.
InterfaceEndpointClient(ScopedInterfaceEndpointHandle handle,
MessageReceiverWithResponderStatus* receiver,
- std::unique_ptr<MessageReceiver> payload_validator,
+ std::unique_ptr<MessageFilter> payload_validator,
bool expect_sync_requests,
- scoped_refptr<base::SingleThreadTaskRunner> runner,
- uint32_t interface_version);
+ scoped_refptr<base::SingleThreadTaskRunner> runner);
~InterfaceEndpointClient() override;
// Sets the error handler to receive notifications when an error is
@@ -54,14 +46,6 @@
void set_connection_error_handler(const base::Closure& error_handler) {
DCHECK(thread_checker_.CalledOnValidThread());
error_handler_ = error_handler;
- error_with_reason_handler_.Reset();
- }
-
- void set_connection_error_with_reason_handler(
- const ConnectionErrorWithReasonCallback& error_handler) {
- DCHECK(thread_checker_.CalledOnValidThread());
- error_with_reason_handler_ = error_handler;
- error_handler_.Reset();
}
// Returns true if an error was encountered.
@@ -76,11 +60,11 @@
return !async_responders_.empty() || !sync_responses_.empty();
}
+ AssociatedGroupController* group_controller() const {
+ return handle_.group_controller();
+ }
AssociatedGroup* associated_group();
-
- // Adds a MessageReceiver which can filter a message after validation but
- // before dispatch.
- void AddFilter(std::unique_ptr<MessageReceiver> filter);
+ uint32_t interface_id() const;
// After this call the object is in an invalid state and shouldn't be reused.
ScopedInterfaceEndpointHandle PassHandle();
@@ -89,11 +73,7 @@
// and notifies all interfaces running on this pipe.
void RaiseError();
- void CloseWithReason(uint32_t custom_reason, const std::string& description);
-
// MessageReceiverWithResponder implementation:
- // They must only be called when the handle is not in pending association
- // state.
bool Accept(Message* message) override;
bool AcceptWithResponder(Message* message,
MessageReceiver* responder) override;
@@ -103,14 +83,7 @@
// NOTE: |message| must have passed message header validation.
bool HandleIncomingMessage(Message* message);
- void NotifyError(const base::Optional<DisconnectReason>& reason);
-
- // The following methods send interface control messages.
- // They must only be called when the handle is not in pending association
- // state.
- void QueryVersion(const base::Callback<void(uint32_t)>& callback);
- void RequireVersion(uint32_t version);
- void FlushForTesting();
+ void NotifyError();
private:
// Maps from the id of a response to the MessageReceiver that handles the
@@ -123,7 +96,7 @@
explicit SyncResponseInfo(bool* in_response_received);
~SyncResponseInfo();
- Message response;
+ std::unique_ptr<Message> response;
// Points to a stack-allocated variable.
bool* response_received;
@@ -150,37 +123,26 @@
DISALLOW_COPY_AND_ASSIGN(HandleIncomingMessageThunk);
};
- void InitControllerIfNecessary();
-
- void OnAssociationEvent(
- ScopedInterfaceEndpointHandle::AssociationEvent event);
-
bool HandleValidatedMessage(Message* message);
- const bool expect_sync_requests_ = false;
-
ScopedInterfaceEndpointHandle handle_;
std::unique_ptr<AssociatedGroup> associated_group_;
- InterfaceEndpointController* controller_ = nullptr;
+ InterfaceEndpointController* controller_;
- MessageReceiverWithResponderStatus* const incoming_receiver_ = nullptr;
+ MessageReceiverWithResponderStatus* const incoming_receiver_;
+ std::unique_ptr<MessageFilter> payload_validator_;
HandleIncomingMessageThunk thunk_;
- FilterChain filters_;
AsyncResponderMap async_responders_;
SyncResponseMap sync_responses_;
- uint64_t next_request_id_ = 1;
+ uint64_t next_request_id_;
base::Closure error_handler_;
- ConnectionErrorWithReasonCallback error_with_reason_handler_;
- bool encountered_error_ = false;
+ bool encountered_error_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
- internal::ControlMessageProxy control_message_proxy_;
- internal::ControlMessageHandler control_message_handler_;
-
base::ThreadChecker thread_checker_;
base::WeakPtrFactory<InterfaceEndpointClient> weak_ptr_factory_;
diff --git a/mojo/public/cpp/bindings/interface_ptr.h b/mojo/public/cpp/bindings/interface_ptr.h
index e88be74..edcb9bf 100644
--- a/mojo/public/cpp/bindings/interface_ptr.h
+++ b/mojo/public/cpp/bindings/interface_ptr.h
@@ -6,8 +6,6 @@
#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_
#include <stdint.h>
-
-#include <string>
#include <utility>
#include "base/callback_forward.h"
@@ -16,12 +14,13 @@
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "mojo/public/cpp/bindings/connection_error_callback.h"
#include "mojo/public/cpp/bindings/interface_ptr_info.h"
#include "mojo/public/cpp/bindings/lib/interface_ptr_state.h"
namespace mojo {
+class AssociatedGroup;
+
// A pointer to a local proxy of a remote Interface implementation. Uses a
// message pipe to communicate with the remote implementation, and automatically
// closes the pipe and deletes the proxy on destruction. The pointer must be
@@ -38,9 +37,6 @@
template <typename Interface>
class InterfacePtr {
public:
- using InterfaceType = Interface;
- using PtrInfoType = InterfacePtrInfo<Interface>;
-
// Constructs an unbound InterfacePtr.
InterfacePtr() {}
InterfacePtr(decltype(nullptr)) {}
@@ -118,12 +114,6 @@
internal_state_.RequireVersion(version);
}
- // Sends a no-op 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() { internal_state_.FlushForTesting(); }
-
// Closes the bound message pipe (if any) and returns the pointer to the
// unbound state.
void reset() {
@@ -131,13 +121,6 @@
internal_state_.Swap(&doomed);
}
- // Similar to the method above, but also specifies a disconnect reason.
- void ResetWithReason(uint32_t custom_reason, const std::string& description) {
- if (internal_state_.is_bound())
- internal_state_.CloseWithReason(custom_reason, description);
- reset();
- }
-
// Whether there are any associated interfaces running on the pipe currently.
bool HasAssociatedInterfaces() const {
return internal_state_.HasAssociatedInterfaces();
@@ -157,11 +140,6 @@
internal_state_.set_connection_error_handler(error_handler);
}
- void set_connection_error_with_reason_handler(
- const ConnectionErrorWithReasonCallback& error_handler) {
- internal_state_.set_connection_error_with_reason_handler(error_handler);
- }
-
// Unbinds the InterfacePtr and returns the information which could be used
// to setup an InterfacePtr again. This method may be used to move the proxy
// to a different thread (see class comments for details).
@@ -184,6 +162,14 @@
return state.PassInterface();
}
+ // Returns the associated group that this object belongs to. Returns null if:
+ // - this object is not bound; or
+ // - the interface doesn't have methods to pass associated interface
+ // pointers or requests.
+ AssociatedGroup* associated_group() {
+ return internal_state_.associated_group();
+ }
+
bool Equals(const InterfacePtr& other) const {
if (this == &other)
return true;
@@ -194,7 +180,8 @@
}
// DO NOT USE. Exposed only for internal use and for testing.
- internal::InterfacePtrState<Interface>* internal_state() {
+ internal::InterfacePtrState<Interface, Interface::PassesAssociatedKinds_>*
+ internal_state() {
return &internal_state_;
}
@@ -202,7 +189,9 @@
// implicitly convertible to a real bool (which is dangerous).
private:
// TODO(dcheng): Use an explicit conversion operator.
- typedef internal::InterfacePtrState<Interface> InterfacePtr::*Testable;
+ typedef internal::InterfacePtrState<Interface,
+ Interface::PassesAssociatedKinds_>
+ InterfacePtr::*Testable;
public:
operator Testable() const {
@@ -218,7 +207,8 @@
template <typename T>
bool operator!=(const InterfacePtr<T>& other) const = delete;
- typedef internal::InterfacePtrState<Interface> State;
+ typedef internal::InterfacePtrState<Interface,
+ Interface::PassesAssociatedKinds_> State;
mutable State internal_state_;
DISALLOW_COPY_AND_ASSIGN(InterfacePtr);
diff --git a/mojo/public/cpp/bindings/interface_ptr_set.h b/mojo/public/cpp/bindings/interface_ptr_set.h
index 09a2682..d4b2046 100644
--- a/mojo/public/cpp/bindings/interface_ptr_set.h
+++ b/mojo/public/cpp/bindings/interface_ptr_set.h
@@ -16,10 +16,6 @@
namespace mojo {
namespace internal {
-// TODO(blundell): This class should be rewritten to be structured
-// similarly to BindingSet if possible, with PtrSet owning its
-// Elements and those Elements calling back into PtrSet on connection
-// error.
template <typename Interface, template <typename> class Ptr>
class PtrSet {
public:
@@ -59,13 +55,7 @@
~Element() {}
- void Close() {
- ptr_.reset();
-
- // Resetting the interface ptr means that it won't call this object back
- // on connection error anymore, so this object must delete itself now.
- DeleteElement(this);
- }
+ void Close() { ptr_.reset(); }
Interface* get() { return ptr_.get(); }
diff --git a/mojo/public/cpp/bindings/interface_request.h b/mojo/public/cpp/bindings/interface_request.h
index 29d8836..fc23aec 100644
--- a/mojo/public/cpp/bindings/interface_request.h
+++ b/mojo/public/cpp/bindings/interface_request.h
@@ -5,17 +5,12 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_
-#include <string>
#include <utility>
#include "base/macros.h"
-#include "base/optional.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
-#include "mojo/public/cpp/bindings/disconnect_reason.h"
#include "mojo/public/cpp/bindings/interface_ptr.h"
-#include "mojo/public/cpp/bindings/pipe_control_message_proxy.h"
-#include "mojo/public/cpp/system/message_pipe.h"
namespace mojo {
@@ -33,19 +28,6 @@
InterfaceRequest() {}
InterfaceRequest(decltype(nullptr)) {}
- // Creates a new message pipe over which Interface is to be served, binding
- // the specified InterfacePtr to one end of the message pipe and this
- // InterfaceRequest to the other. For example usage, see comments on
- // MakeRequest(InterfacePtr*) below.
- explicit InterfaceRequest(InterfacePtr<Interface>* ptr,
- scoped_refptr<base::SingleThreadTaskRunner> runner =
- base::ThreadTaskRunnerHandle::Get()) {
- MessagePipe pipe;
- ptr->Bind(InterfacePtrInfo<Interface>(std::move(pipe.handle0), 0u),
- std::move(runner));
- Bind(std::move(pipe.handle1));
- }
-
// Takes the message pipe from another InterfaceRequest.
InterfaceRequest(InterfaceRequest&& other) {
handle_ = std::move(other.handle_);
@@ -82,20 +64,6 @@
return !is_pending() && !other.is_pending();
}
- void ResetWithReason(uint32_t custom_reason, const std::string& description) {
- if (!handle_.is_valid())
- return;
-
- Message message =
- PipeControlMessageProxy::ConstructPeerEndpointClosedMessage(
- kMasterInterfaceId, DisconnectReason(custom_reason, description));
- MojoResult result = WriteMessageNew(
- handle_.get(), message.TakeMojoMessage(), MOJO_WRITE_MESSAGE_FLAG_NONE);
- DCHECK_EQ(MOJO_RESULT_OK, result);
-
- handle_.reset();
- }
-
private:
ScopedMessagePipeHandle handle_;
@@ -135,9 +103,9 @@
//
// DatabasePtr database = ...; // Connect to database.
// TablePtr table;
-// database->OpenTable(MakeRequest(&table));
+// database->OpenTable(GetProxy(&table));
//
-// Upon return from MakeRequest, |table| is ready to have methods called on it.
+// Upon return from GetProxy, |table| is ready to have methods called on it.
//
// Example #2: Registering a local implementation with a remote service.
// =====================================================================
@@ -151,16 +119,19 @@
//
// CollectorPtr collector = ...; // Connect to Collector.
// SourcePtr source;
-// InterfaceRequest<Source> source_request(&source);
+// InterfaceRequest<Source> source_request = GetProxy(&source);
// collector->RegisterSource(std::move(source));
// CreateSource(std::move(source_request)); // Create implementation locally.
//
template <typename Interface>
-InterfaceRequest<Interface> MakeRequest(
+InterfaceRequest<Interface> GetProxy(
InterfacePtr<Interface>* ptr,
scoped_refptr<base::SingleThreadTaskRunner> runner =
base::ThreadTaskRunnerHandle::Get()) {
- return InterfaceRequest<Interface>(ptr, runner);
+ MessagePipe pipe;
+ ptr->Bind(InterfacePtrInfo<Interface>(std::move(pipe.handle0), 0u),
+ std::move(runner));
+ return MakeRequest<Interface>(std::move(pipe.handle1));
}
// Fuses an InterfaceRequest<T> endpoint with an InterfacePtrInfo<T> endpoint.
diff --git a/mojo/public/cpp/bindings/lib/array_internal.h b/mojo/public/cpp/bindings/lib/array_internal.h
index eecfcfb..ba6d16e 100644
--- a/mojo/public/cpp/bindings/lib/array_internal.h
+++ b/mojo/public/cpp/bindings/lib/array_internal.h
@@ -13,7 +13,6 @@
#include "base/logging.h"
#include "mojo/public/c/system/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
#include "mojo/public/cpp/bindings/lib/buffer.h"
#include "mojo/public/cpp/bindings/lib/serialization_util.h"
@@ -29,13 +28,13 @@
template <typename K, typename V>
class Map_Data;
-MOJO_CPP_BINDINGS_EXPORT std::string
-MakeMessageWithArrayIndex(const char* message, size_t size, size_t index);
+std::string MakeMessageWithArrayIndex(const char* message,
+ size_t size,
+ size_t index);
-MOJO_CPP_BINDINGS_EXPORT std::string MakeMessageWithExpectedArraySize(
- const char* message,
- size_t size,
- size_t expected_size);
+std::string MakeMessageWithExpectedArraySize(const char* message,
+ size_t size,
+ size_t expected_size);
template <typename T>
struct ArrayDataTraits {
@@ -68,7 +67,7 @@
struct ArrayDataTraits<bool> {
// Helper class to emulate a reference to a bool, used for direct element
// access.
- class MOJO_CPP_BINDINGS_EXPORT BitRef {
+ class BitRef {
public:
~BitRef();
BitRef& operator=(bool value);
@@ -110,7 +109,7 @@
//
// TODO(yzshen): Validation code should be organzied in a way similar to
// Serializer<>, or merged into it. It should be templatized with the mojo
-// data view type instead of the data type, that way we can use MojomTypeTraits
+// wrapper type instead of the data type, that way we can use MojomTypeTraits
// to determine the categories.
template <typename T, bool is_union, bool is_handle_or_interface>
@@ -263,7 +262,7 @@
T,
IsUnionDataType<T>::value,
std::is_same<T, AssociatedInterface_Data>::value ||
- std::is_same<T, AssociatedEndpointHandle_Data>::value ||
+ std::is_same<T, AssociatedInterfaceRequest_Data>::value ||
std::is_same<T, Interface_Data>::value ||
std::is_same<T, Handle_Data>::value>;
using Element = T;
diff --git a/mojo/public/cpp/bindings/lib/array_serialization.h b/mojo/public/cpp/bindings/lib/array_serialization.h
index d2f8ecf..5db27a5 100644
--- a/mojo/public/cpp/bindings/lib/array_serialization.h
+++ b/mojo/public/cpp/bindings/lib/array_serialization.h
@@ -14,11 +14,12 @@
#include <vector>
#include "base/logging.h"
-#include "mojo/public/cpp/bindings/array_data_view.h"
+#include "mojo/public/cpp/bindings/array.h"
#include "mojo/public/cpp/bindings/lib/array_internal.h"
#include "mojo/public/cpp/bindings/lib/serialization_forward.h"
#include "mojo/public/cpp/bindings/lib/template_util.h"
#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/map.h"
namespace mojo {
namespace internal {
@@ -45,7 +46,7 @@
using GetNextResult =
decltype(Traits::GetValue(std::declval<IteratorType&>()));
GetNextResult GetNext() {
- GetNextResult value = Traits::GetValue(iter_);
+ auto& value = Traits::GetValue(iter_);
Traits::AdvanceIterator(iter_);
return value;
}
@@ -286,19 +287,13 @@
using Element = typename MojomType::Element;
using Traits = ArrayTraits<UserType>;
+ static_assert(std::is_same<Element, typename Traits::Element>::value,
+ "Incorrect array serializer");
+
static size_t GetSerializedSize(UserTypeIterator* input,
SerializationContext* context) {
- size_t element_count = input->GetSize();
- if (BelongsTo<Element,
- MojomTypeCategory::ASSOCIATED_INTERFACE |
- MojomTypeCategory::ASSOCIATED_INTERFACE_REQUEST>::value) {
- for (size_t i = 0; i < element_count; ++i) {
- typename UserTypeIterator::GetNextResult next = input->GetNext();
- size_t size = PrepareToSerialize<Element>(next, context);
- DCHECK_EQ(size, 0u);
- }
- }
- return sizeof(Data) + Align(element_count * sizeof(typename Data::Element));
+ return sizeof(Data) +
+ Align(input->GetSize() * sizeof(typename Data::Element));
}
static void SerializeElements(UserTypeIterator* input,
@@ -311,8 +306,7 @@
size_t size = input->GetSize();
for (size_t i = 0; i < size; ++i) {
- typename UserTypeIterator::GetNextResult next = input->GetNext();
- Serialize<Element>(next, &output->at(i), context);
+ Serialize<Element>(input->GetNext(), &output->at(i), context);
static const ValidationError kError =
BelongsTo<Element,
@@ -367,10 +361,8 @@
SerializationContext* context) {
size_t element_count = input->GetSize();
size_t size = sizeof(Data) + element_count * sizeof(typename Data::Element);
- for (size_t i = 0; i < element_count; ++i) {
- typename UserTypeIterator::GetNextResult next = input->GetNext();
- size += PrepareToSerialize<Element>(next, context);
- }
+ for (size_t i = 0; i < element_count; ++i)
+ size += PrepareToSerialize<Element>(input->GetNext(), context);
return size;
}
@@ -382,8 +374,7 @@
size_t size = input->GetSize();
for (size_t i = 0; i < size; ++i) {
DataElementPtr data_ptr;
- typename UserTypeIterator::GetNextResult next = input->GetNext();
- SerializeCaller<Element>::Run(next, buf, &data_ptr,
+ SerializeCaller<Element>::Run(input->GetNext(), buf, &data_ptr,
validate_params->element_validate_params,
context);
output->at(i).Set(data_ptr);
@@ -453,6 +444,10 @@
using Element = typename MojomType::Element;
using Traits = ArrayTraits<UserType>;
+ static_assert(std::is_same<typename MojomType::Element,
+ typename Traits::Element>::value,
+ "Incorrect array serializer");
+
static size_t GetSerializedSize(UserTypeIterator* input,
SerializationContext* context) {
size_t element_count = input->GetSize();
@@ -460,8 +455,7 @@
for (size_t i = 0; i < element_count; ++i) {
// Call with |inlined| set to false, so that it will account for both the
// data in the union and the space in the array used to hold the union.
- typename UserTypeIterator::GetNextResult next = input->GetNext();
- size += PrepareToSerialize<Element>(next, false, context);
+ size += PrepareToSerialize<Element>(input->GetNext(), false, context);
}
return size;
}
@@ -474,8 +468,7 @@
size_t size = input->GetSize();
for (size_t i = 0; i < size; ++i) {
typename Data::Element* result = output->storage() + i;
- typename UserTypeIterator::GetNextResult next = input->GetNext();
- Serialize<Element>(next, buf, &result, true, context);
+ Serialize<Element>(input->GetNext(), buf, &result, true, context);
MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
!validate_params->element_is_nullable && output->at(i).is_null(),
VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
@@ -499,13 +492,13 @@
};
template <typename Element, typename MaybeConstUserType>
-struct Serializer<ArrayDataView<Element>, MaybeConstUserType> {
+struct Serializer<Array<Element>, MaybeConstUserType> {
using UserType = typename std::remove_const<MaybeConstUserType>::type;
using Traits = ArrayTraits<UserType>;
- using Impl = ArraySerializer<ArrayDataView<Element>,
+ using Impl = ArraySerializer<Array<Element>,
MaybeConstUserType,
ArrayIterator<Traits, MaybeConstUserType>>;
- using Data = typename MojomTypeTraits<ArrayDataView<Element>>::Data;
+ using Data = typename MojomTypeTraits<Array<Element>>::Data;
static size_t PrepareToSerialize(MaybeConstUserType& input,
SerializationContext* context) {
diff --git a/mojo/public/cpp/bindings/lib/associated_group.cc b/mojo/public/cpp/bindings/lib/associated_group.cc
index 3e95eeb..a9c53b5 100644
--- a/mojo/public/cpp/bindings/lib/associated_group.cc
+++ b/mojo/public/cpp/bindings/lib/associated_group.cc
@@ -8,27 +8,28 @@
namespace mojo {
-AssociatedGroup::AssociatedGroup() = default;
+AssociatedGroup::AssociatedGroup() {}
-AssociatedGroup::AssociatedGroup(
- scoped_refptr<AssociatedGroupController> controller)
- : controller_(std::move(controller)) {}
+AssociatedGroup::AssociatedGroup(const AssociatedGroup& other)
+ : controller_(other.controller_) {}
-AssociatedGroup::AssociatedGroup(const ScopedInterfaceEndpointHandle& handle)
- : controller_getter_(handle.CreateGroupControllerGetter()) {}
+AssociatedGroup::~AssociatedGroup() {}
-AssociatedGroup::AssociatedGroup(const AssociatedGroup& other) = default;
+AssociatedGroup& AssociatedGroup::operator=(const AssociatedGroup& other) {
+ if (this == &other)
+ return *this;
-AssociatedGroup::~AssociatedGroup() = default;
+ controller_ = other.controller_;
+ return *this;
+}
-AssociatedGroup& AssociatedGroup::operator=(const AssociatedGroup& other) =
- default;
+void AssociatedGroup::CreateEndpointHandlePair(
+ ScopedInterfaceEndpointHandle* local_endpoint,
+ ScopedInterfaceEndpointHandle* remote_endpoint) {
+ if (!controller_)
+ return;
-AssociatedGroupController* AssociatedGroup::GetController() {
- if (controller_)
- return controller_.get();
-
- return controller_getter_.Run();
+ controller_->CreateEndpointHandlePair(local_endpoint, remote_endpoint);
}
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/associated_group_controller.cc b/mojo/public/cpp/bindings/lib/associated_group_controller.cc
index f4a9aa2..42db9b3 100644
--- a/mojo/public/cpp/bindings/lib/associated_group_controller.cc
+++ b/mojo/public/cpp/bindings/lib/associated_group_controller.cc
@@ -8,17 +8,25 @@
namespace mojo {
+AssociatedGroupController::AssociatedGroupController(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+ : base::RefCountedDeleteOnMessageLoop<AssociatedGroupController>(
+ task_runner) {}
+
AssociatedGroupController::~AssociatedGroupController() {}
-ScopedInterfaceEndpointHandle
-AssociatedGroupController::CreateScopedInterfaceEndpointHandle(InterfaceId id) {
- return ScopedInterfaceEndpointHandle(id, this);
+std::unique_ptr<AssociatedGroup>
+AssociatedGroupController::CreateAssociatedGroup() {
+ std::unique_ptr<AssociatedGroup> group(new AssociatedGroup);
+ group->controller_ = this;
+ return group;
}
-bool AssociatedGroupController::NotifyAssociation(
- ScopedInterfaceEndpointHandle* handle_to_send,
- InterfaceId id) {
- return handle_to_send->NotifyAssociation(id, this);
+ScopedInterfaceEndpointHandle
+AssociatedGroupController::CreateScopedInterfaceEndpointHandle(
+ InterfaceId id,
+ bool is_local) {
+ return ScopedInterfaceEndpointHandle(id, is_local, this);
}
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h b/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h
index 72f7960..c7f74fb 100644
--- a/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h
+++ b/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h
@@ -9,7 +9,6 @@
#include <algorithm> // For |std::swap()|.
#include <memory>
-#include <string>
#include <utility>
#include "base/bind.h"
@@ -19,10 +18,11 @@
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "mojo/public/cpp/bindings/associated_group.h"
+#include "mojo/public/cpp/bindings/associated_group_controller.h"
#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
-#include "mojo/public/cpp/bindings/connection_error_callback.h"
#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
#include "mojo/public/cpp/bindings/interface_id.h"
+#include "mojo/public/cpp/bindings/lib/control_message_proxy.h"
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
#include "mojo/public/cpp/system/message_pipe.h"
@@ -46,12 +46,18 @@
uint32_t version() const { return version_; }
+ uint32_t interface_id() const {
+ DCHECK(is_bound());
+ return endpoint_client_->interface_id();
+ }
+
void QueryVersion(const base::Callback<void(uint32_t)>& callback) {
- // It is safe to capture |this| because the callback won't be run after this
- // object goes away.
- endpoint_client_->QueryVersion(
- base::Bind(&AssociatedInterfacePtrState::OnQueryVersion,
- base::Unretained(this), callback));
+ // Do a static cast in case the interface contains methods with the same
+ // name. It is safe to capture |this| because the callback won't be run
+ // after this object goes away.
+ static_cast<ControlMessageProxy*>(proxy_.get())
+ ->QueryVersion(base::Bind(&AssociatedInterfacePtrState::OnQueryVersion,
+ base::Unretained(this), callback));
}
void RequireVersion(uint32_t version) {
@@ -59,13 +65,9 @@
return;
version_ = version;
- endpoint_client_->RequireVersion(version);
- }
-
- void FlushForTesting() { endpoint_client_->FlushForTesting(); }
-
- void CloseWithReason(uint32_t custom_reason, const std::string& description) {
- endpoint_client_->CloseWithReason(custom_reason, description);
+ // Do a static cast in case the interface contains methods with the same
+ // name.
+ static_cast<ControlMessageProxy*>(proxy_.get())->RequireVersion(version);
}
void Swap(AssociatedInterfacePtrState* other) {
@@ -83,13 +85,13 @@
DCHECK(info.is_valid());
version_ = info.version();
- // The version is only queried from the client so the value passed here
- // will not be used.
endpoint_client_.reset(new InterfaceEndpointClient(
info.PassHandle(), nullptr,
base::WrapUnique(new typename Interface::ResponseValidator_()), false,
- std::move(runner), 0u));
+ std::move(runner)));
proxy_.reset(new Proxy(endpoint_client_.get()));
+ proxy_->serialization_context()->group_controller =
+ endpoint_client_->group_controller();
}
// After this method is called, the object is in an invalid state and
@@ -112,12 +114,6 @@
endpoint_client_->set_connection_error_handler(error_handler);
}
- void set_connection_error_with_reason_handler(
- const ConnectionErrorWithReasonCallback& error_handler) {
- DCHECK(endpoint_client_);
- endpoint_client_->set_connection_error_with_reason_handler(error_handler);
- }
-
// Returns true if bound and awaiting a response to a message.
bool has_pending_callbacks() const {
return endpoint_client_ && endpoint_client_->has_pending_responders();
@@ -127,13 +123,6 @@
return endpoint_client_ ? endpoint_client_->associated_group() : nullptr;
}
- void ForwardMessage(Message message) { endpoint_client_->Accept(&message); }
-
- void ForwardMessageWithResponder(Message message,
- std::unique_ptr<MessageReceiver> responder) {
- endpoint_client_->AcceptWithResponder(&message, responder.release());
- }
-
private:
using Proxy = typename Interface::Proxy_;
diff --git a/mojo/public/cpp/bindings/lib/binding_state.h b/mojo/public/cpp/bindings/lib/binding_state.h
index 0b0dbee..c8d3e83 100644
--- a/mojo/public/cpp/bindings/lib/binding_state.h
+++ b/mojo/public/cpp/bindings/lib/binding_state.h
@@ -6,7 +6,6 @@
#define MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDING_STATE_H_
#include <memory>
-#include <string>
#include <utility>
#include "base/bind.h"
@@ -16,15 +15,15 @@
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
-#include "mojo/public/cpp/bindings/connection_error_callback.h"
-#include "mojo/public/cpp/bindings/filter_chain.h"
+#include "mojo/public/cpp/bindings/associated_group.h"
#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
#include "mojo/public/cpp/bindings/interface_id.h"
#include "mojo/public/cpp/bindings/interface_ptr.h"
#include "mojo/public/cpp/bindings/interface_ptr_info.h"
#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
#include "mojo/public/cpp/bindings/lib/multiplex_router.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
#include "mojo/public/cpp/bindings/message_header_validator.h"
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
#include "mojo/public/cpp/system/core.h"
@@ -32,34 +31,76 @@
namespace mojo {
namespace internal {
-class MOJO_CPP_BINDINGS_EXPORT BindingStateBase {
+template <typename Interface, bool use_multiplex_router>
+class BindingState;
+
+// Uses a single-threaded, dedicated router. If |Interface| doesn't have any
+// methods to pass associated interface pointers or requests, there won't be
+// multiple interfaces running on the underlying message pipe. In that case, we
+// can use this specialization to reduce cost.
+template <typename Interface>
+class BindingState<Interface, false> {
public:
- BindingStateBase();
- ~BindingStateBase();
+ explicit BindingState(Interface* impl) : impl_(impl) {
+ stub_.set_sink(impl_);
+ }
- void AddFilter(std::unique_ptr<MessageReceiver> filter);
+ ~BindingState() { Close(); }
- bool HasAssociatedInterfaces() const;
+ void Bind(ScopedMessagePipeHandle handle,
+ scoped_refptr<base::SingleThreadTaskRunner> runner) {
+ DCHECK(!router_);
+ internal::FilterChain filters;
+ filters.Append<MessageHeaderValidator>(Interface::Name_);
+ filters.Append<typename Interface::RequestValidator_>();
- void PauseIncomingMethodCallProcessing();
- void ResumeIncomingMethodCallProcessing();
+ router_ =
+ new internal::Router(std::move(handle), std::move(filters),
+ Interface::HasSyncMethods_, std::move(runner));
+ router_->set_incoming_receiver(&stub_);
+ router_->set_connection_error_handler(
+ base::Bind(&BindingState::RunConnectionErrorHandler,
+ base::Unretained(this)));
+ }
+
+ bool HasAssociatedInterfaces() const { return false; }
+
+ void PauseIncomingMethodCallProcessing() {
+ DCHECK(router_);
+ router_->PauseIncomingMethodCallProcessing();
+ }
+ void ResumeIncomingMethodCallProcessing() {
+ DCHECK(router_);
+ router_->ResumeIncomingMethodCallProcessing();
+ }
bool WaitForIncomingMethodCall(
- MojoDeadline deadline = MOJO_DEADLINE_INDEFINITE);
+ MojoDeadline deadline = MOJO_DEADLINE_INDEFINITE) {
+ DCHECK(router_);
+ return router_->WaitForIncomingMessage(deadline);
+ }
- void Close();
- void CloseWithReason(uint32_t custom_reason, const std::string& description);
+ void Close() {
+ if (!router_)
+ return;
+
+ router_->CloseMessagePipe();
+ DestroyRouter();
+ }
+
+ InterfaceRequest<Interface> Unbind() {
+ InterfaceRequest<Interface> request =
+ MakeRequest<Interface>(router_->PassMessagePipe());
+ DestroyRouter();
+ return std::move(request);
+ }
void set_connection_error_handler(const base::Closure& error_handler) {
DCHECK(is_bound());
- endpoint_client_->set_connection_error_handler(error_handler);
+ connection_error_handler_ = error_handler;
}
- void set_connection_error_with_reason_handler(
- const ConnectionErrorWithReasonCallback& error_handler) {
- DCHECK(is_bound());
- endpoint_client_->set_connection_error_with_reason_handler(error_handler);
- }
+ Interface* impl() { return impl_; }
bool is_bound() const { return !!router_; }
@@ -68,42 +109,90 @@
return router_->handle();
}
- void FlushForTesting();
+ AssociatedGroup* associated_group() { return nullptr; }
- void EnableTestingMode();
+ void EnableTestingMode() {
+ DCHECK(is_bound());
+ router_->EnableTestingMode();
+ }
- protected:
- void BindInternal(ScopedMessagePipeHandle handle,
- scoped_refptr<base::SingleThreadTaskRunner> runner,
- const char* interface_name,
- std::unique_ptr<MessageReceiver> request_validator,
- bool passes_associated_kinds,
- bool has_sync_methods,
- MessageReceiverWithResponderStatus* stub,
- uint32_t interface_version);
+ private:
+ void DestroyRouter() {
+ router_->set_connection_error_handler(base::Closure());
+ delete router_;
+ router_ = nullptr;
+ connection_error_handler_.Reset();
+ }
- scoped_refptr<internal::MultiplexRouter> router_;
- std::unique_ptr<InterfaceEndpointClient> endpoint_client_;
+ void RunConnectionErrorHandler() {
+ if (!connection_error_handler_.is_null())
+ connection_error_handler_.Run();
+ }
+
+ internal::Router* router_ = nullptr;
+ typename Interface::Stub_ stub_;
+ Interface* impl_;
+ base::Closure connection_error_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(BindingState);
};
-template <typename Interface, typename ImplRefTraits>
-class BindingState : public BindingStateBase {
+// Uses a multiplexing router. If |Interface| has methods to pass associated
+// interface pointers or requests, this specialization should be used.
+template <typename Interface>
+class BindingState<Interface, true> {
public:
- using ImplPointerType = typename ImplRefTraits::PointerType;
-
- explicit BindingState(ImplPointerType impl) {
- stub_.set_sink(std::move(impl));
+ explicit BindingState(Interface* impl) : impl_(impl) {
+ stub_.set_sink(impl_);
}
~BindingState() { Close(); }
void Bind(ScopedMessagePipeHandle handle,
scoped_refptr<base::SingleThreadTaskRunner> runner) {
- BindingStateBase::BindInternal(
- std::move(handle), runner, Interface::Name_,
- base::MakeUnique<typename Interface::RequestValidator_>(),
- Interface::PassesAssociatedKinds_, Interface::HasSyncMethods_, &stub_,
- Interface::Version_);
+ DCHECK(!router_);
+
+ router_ = new internal::MultiplexRouter(false, std::move(handle), runner);
+ router_->SetMasterInterfaceName(Interface::Name_);
+ stub_.serialization_context()->group_controller = router_;
+
+ endpoint_client_.reset(new InterfaceEndpointClient(
+ router_->CreateLocalEndpointHandle(kMasterInterfaceId),
+ &stub_, base::WrapUnique(new typename Interface::RequestValidator_()),
+ Interface::HasSyncMethods_, std::move(runner)));
+
+ endpoint_client_->set_connection_error_handler(
+ base::Bind(&BindingState::RunConnectionErrorHandler,
+ base::Unretained(this)));
+ }
+
+ bool HasAssociatedInterfaces() const {
+ return router_ ? router_->HasAssociatedEndpoints() : false;
+ }
+
+ void PauseIncomingMethodCallProcessing() {
+ DCHECK(router_);
+ router_->PauseIncomingMethodCallProcessing();
+ }
+ void ResumeIncomingMethodCallProcessing() {
+ DCHECK(router_);
+ router_->ResumeIncomingMethodCallProcessing();
+ }
+
+ bool WaitForIncomingMethodCall(
+ MojoDeadline deadline = MOJO_DEADLINE_INDEFINITE) {
+ DCHECK(router_);
+ return router_->WaitForIncomingMessage(deadline);
+ }
+
+ void Close() {
+ if (!router_)
+ return;
+
+ endpoint_client_.reset();
+ router_->CloseMessagePipe();
+ router_ = nullptr;
+ connection_error_handler_.Reset();
}
InterfaceRequest<Interface> Unbind() {
@@ -111,13 +200,45 @@
InterfaceRequest<Interface> request =
MakeRequest<Interface>(router_->PassMessagePipe());
router_ = nullptr;
+ connection_error_handler_.Reset();
return request;
}
- Interface* impl() { return ImplRefTraits::GetRawPointer(&stub_.sink()); }
+ void set_connection_error_handler(const base::Closure& error_handler) {
+ DCHECK(is_bound());
+ connection_error_handler_ = error_handler;
+ }
+
+ Interface* impl() { return impl_; }
+
+ bool is_bound() const { return !!router_; }
+
+ MessagePipeHandle handle() const {
+ DCHECK(is_bound());
+ return router_->handle();
+ }
+
+ AssociatedGroup* associated_group() {
+ return endpoint_client_ ? endpoint_client_->associated_group() : nullptr;
+ }
+
+ void EnableTestingMode() {
+ DCHECK(is_bound());
+ router_->EnableTestingMode();
+ }
private:
- typename Interface::template Stub_<ImplRefTraits> stub_;
+ void RunConnectionErrorHandler() {
+ if (!connection_error_handler_.is_null())
+ connection_error_handler_.Run();
+ }
+
+ scoped_refptr<internal::MultiplexRouter> router_;
+ std::unique_ptr<InterfaceEndpointClient> endpoint_client_;
+
+ typename Interface::Stub_ stub_;
+ Interface* impl_;
+ base::Closure connection_error_handler_;
DISALLOW_COPY_AND_ASSIGN(BindingState);
};
diff --git a/mojo/public/cpp/bindings/lib/bindings_internal.cc b/mojo/public/cpp/bindings/lib/bindings_internal.cc
new file mode 100644
index 0000000..a3bdb1f
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/bindings_internal.cc
@@ -0,0 +1,47 @@
+// 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/bindings_internal.h"
+
+namespace mojo {
+namespace internal {
+
+namespace {
+
+const size_t kAlignment = 8;
+
+template <typename T>
+T AlignImpl(T t) {
+ return t + (kAlignment - (t % kAlignment)) % kAlignment;
+}
+
+} // namespace
+
+size_t Align(size_t size) {
+ return AlignImpl(size);
+}
+
+char* AlignPointer(char* ptr) {
+ return reinterpret_cast<char*>(AlignImpl(reinterpret_cast<uintptr_t>(ptr)));
+}
+
+bool IsAligned(const void* ptr) {
+ return !(reinterpret_cast<uintptr_t>(ptr) % kAlignment);
+}
+
+void EncodePointer(const void* ptr, uint64_t* offset) {
+ if (!ptr) {
+ *offset = 0;
+ return;
+ }
+
+ const char* p_obj = reinterpret_cast<const char*>(ptr);
+ const char* p_slot = reinterpret_cast<const char*>(offset);
+ DCHECK(p_obj > p_slot);
+
+ *offset = static_cast<uint64_t>(p_obj - p_slot);
+}
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/bindings_internal.h b/mojo/public/cpp/bindings/lib/bindings_internal.h
index 631daec..b37d872 100644
--- a/mojo/public/cpp/bindings/lib/bindings_internal.h
+++ b/mojo/public/cpp/bindings/lib/bindings_internal.h
@@ -17,26 +17,30 @@
namespace mojo {
template <typename T>
-class ArrayDataView;
+class Array;
template <typename T>
-class AssociatedInterfacePtrInfoDataView;
+class AssociatedInterfacePtrInfo;
template <typename T>
-class AssociatedInterfaceRequestDataView;
+class AssociatedInterfaceRequest;
template <typename T>
-class InterfacePtrDataView;
+class InterfacePtr;
template <typename T>
-class InterfaceRequestDataView;
+class InterfaceRequest;
template <typename K, typename V>
-class MapDataView;
+class Map;
-class NativeStructDataView;
+class String;
-class StringDataView;
+template <typename T>
+class StructPtr;
+
+template <typename T>
+class InlinedStructPtr;
namespace internal {
@@ -54,17 +58,12 @@
template <typename K, typename V>
class Map_Data;
-class NativeStruct_Data;
-
using String_Data = Array_Data<char>;
-inline size_t Align(size_t size) {
- return (size + 7) & ~0x7;
-}
+size_t Align(size_t size);
+char* AlignPointer(char* ptr);
-inline bool IsAligned(const void* ptr) {
- return !(reinterpret_cast<uintptr_t>(ptr) & 0x7);
-}
+bool IsAligned(const void* ptr);
// Pointers are encoded as relative offsets. The offsets are relative to the
// address of where the offset value is stored, such that the pointer may be
@@ -74,19 +73,7 @@
//
// A null pointer is encoded as an offset value of 0.
//
-inline void EncodePointer(const void* ptr, uint64_t* offset) {
- if (!ptr) {
- *offset = 0;
- return;
- }
-
- const char* p_obj = reinterpret_cast<const char*>(ptr);
- const char* p_slot = reinterpret_cast<const char*>(offset);
- DCHECK(p_obj > p_slot);
-
- *offset = static_cast<uint64_t>(p_obj - p_slot);
-}
-
+void EncodePointer(const void* ptr, uint64_t* offset);
// Note: This function doesn't validate the encoded pointer value.
inline const void* DecodePointer(const uint64_t* offset) {
if (!*offset)
@@ -124,8 +111,6 @@
};
static_assert(sizeof(Pointer<char>) == 8, "Bad_sizeof(Pointer)");
-using GenericPointer = Pointer<void>;
-
struct Handle_Data {
Handle_Data() = default;
explicit Handle_Data(uint32_t value) : value(value) {}
@@ -142,24 +127,19 @@
};
static_assert(sizeof(Interface_Data) == 8, "Bad_sizeof(Interface_Data)");
-struct AssociatedEndpointHandle_Data {
- AssociatedEndpointHandle_Data() = default;
- explicit AssociatedEndpointHandle_Data(uint32_t value) : value(value) {}
-
- bool is_valid() const { return value != kEncodedInvalidHandleValue; }
-
- uint32_t value;
-};
-static_assert(sizeof(AssociatedEndpointHandle_Data) == 4,
- "Bad_sizeof(AssociatedEndpointHandle_Data)");
-
struct AssociatedInterface_Data {
- AssociatedEndpointHandle_Data handle;
+ InterfaceId interface_id;
uint32_t version;
};
static_assert(sizeof(AssociatedInterface_Data) == 8,
"Bad_sizeof(AssociatedInterface_Data)");
+struct AssociatedInterfaceRequest_Data {
+ InterfaceId interface_id;
+};
+static_assert(sizeof(AssociatedInterfaceRequest_Data) == 4,
+ "Bad_sizeof(AssociatedInterfaceRequest_Data)");
+
#pragma pack(pop)
template <typename T>
@@ -223,7 +203,7 @@
};
template <typename T>
-struct MojomTypeTraits<ArrayDataView<T>, false> {
+struct MojomTypeTraits<Array<T>, false> {
using Data = Array_Data<typename MojomTypeTraits<T>::DataAsArrayElement>;
using DataAsArrayElement = Pointer<Data>;
@@ -231,7 +211,7 @@
};
template <typename T>
-struct MojomTypeTraits<AssociatedInterfacePtrInfoDataView<T>, false> {
+struct MojomTypeTraits<AssociatedInterfacePtrInfo<T>, false> {
using Data = AssociatedInterface_Data;
using DataAsArrayElement = Data;
@@ -240,8 +220,8 @@
};
template <typename T>
-struct MojomTypeTraits<AssociatedInterfaceRequestDataView<T>, false> {
- using Data = AssociatedEndpointHandle_Data;
+struct MojomTypeTraits<AssociatedInterfaceRequest<T>, false> {
+ using Data = AssociatedInterfaceRequest_Data;
using DataAsArrayElement = Data;
static const MojomTypeCategory category =
@@ -273,7 +253,7 @@
};
template <typename T>
-struct MojomTypeTraits<InterfacePtrDataView<T>, false> {
+struct MojomTypeTraits<InterfacePtr<T>, false> {
using Data = Interface_Data;
using DataAsArrayElement = Data;
@@ -281,7 +261,7 @@
};
template <typename T>
-struct MojomTypeTraits<InterfaceRequestDataView<T>, false> {
+struct MojomTypeTraits<InterfaceRequest<T>, false> {
using Data = Handle_Data;
using DataAsArrayElement = Data;
@@ -290,7 +270,7 @@
};
template <typename K, typename V>
-struct MojomTypeTraits<MapDataView<K, V>, false> {
+struct MojomTypeTraits<Map<K, V>, false> {
using Data = Map_Data<typename MojomTypeTraits<K>::DataAsArrayElement,
typename MojomTypeTraits<V>::DataAsArrayElement>;
using DataAsArrayElement = Pointer<Data>;
@@ -299,21 +279,39 @@
};
template <>
-struct MojomTypeTraits<NativeStructDataView, false> {
- using Data = internal::NativeStruct_Data;
- using DataAsArrayElement = Pointer<Data>;
-
- static const MojomTypeCategory category = MojomTypeCategory::STRUCT;
-};
-
-template <>
-struct MojomTypeTraits<StringDataView, false> {
+struct MojomTypeTraits<String, false> {
using Data = String_Data;
using DataAsArrayElement = Pointer<Data>;
static const MojomTypeCategory category = MojomTypeCategory::STRING;
};
+template <typename T>
+struct MojomTypeTraits<StructPtr<T>, false> {
+ using Data = typename T::Data_;
+ using DataAsArrayElement =
+ typename std::conditional<IsUnionDataType<Data>::value,
+ Data,
+ Pointer<Data>>::type;
+
+ static const MojomTypeCategory category = IsUnionDataType<Data>::value
+ ? MojomTypeCategory::UNION
+ : MojomTypeCategory::STRUCT;
+};
+
+template <typename T>
+struct MojomTypeTraits<InlinedStructPtr<T>, false> {
+ using Data = typename T::Data_;
+ using DataAsArrayElement =
+ typename std::conditional<IsUnionDataType<Data>::value,
+ Data,
+ Pointer<Data>>::type;
+
+ static const MojomTypeCategory category = IsUnionDataType<Data>::value
+ ? MojomTypeCategory::UNION
+ : MojomTypeCategory::STRUCT;
+};
+
template <typename T, MojomTypeCategory categories>
struct BelongsTo {
static const bool value =
diff --git a/mojo/public/cpp/bindings/lib/buffer.h b/mojo/public/cpp/bindings/lib/buffer.h
index 213a445..c3b570e 100644
--- a/mojo/public/cpp/bindings/lib/buffer.h
+++ b/mojo/public/cpp/bindings/lib/buffer.h
@@ -7,61 +7,15 @@
#include <stddef.h>
-#include "base/logging.h"
-#include "base/macros.h"
-#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
-
namespace mojo {
namespace internal {
-// Buffer provides an interface to allocate memory blocks which are 8-byte
-// aligned and zero-initialized. It doesn't own the underlying memory. Users
-// must ensure that the memory stays valid while using the allocated blocks from
-// Buffer.
+// Buffer provides a way to allocate memory. Allocations are 8-byte aligned and
+// zero-initialized. Allocations remain valid for the lifetime of the Buffer.
class Buffer {
public:
- Buffer() {}
-
- // The memory must have been zero-initialized. |data| must be 8-byte
- // aligned.
- void Initialize(void* data, size_t size) {
- DCHECK(IsAligned(data));
-
- data_ = data;
- size_ = size;
- cursor_ = reinterpret_cast<uintptr_t>(data);
- data_end_ = cursor_ + size;
- }
-
- size_t size() const { return size_; }
-
- void* data() const { return data_; }
-
- // Allocates |num_bytes| from the buffer and returns a pointer to the start of
- // the allocated block.
- // The resulting address is 8-byte aligned, and the content of the memory is
- // zero-filled.
- void* Allocate(size_t num_bytes) {
- num_bytes = Align(num_bytes);
- uintptr_t result = cursor_;
- cursor_ += num_bytes;
- if (cursor_ > data_end_ || cursor_ < result) {
- NOTREACHED();
- cursor_ -= num_bytes;
- return nullptr;
- }
-
- return reinterpret_cast<void*>(result);
- }
-
- private:
- void* data_ = nullptr;
- size_t size_ = 0;
-
- uintptr_t cursor_ = 0;
- uintptr_t data_end_ = 0;
-
- DISALLOW_COPY_AND_ASSIGN(Buffer);
+ virtual ~Buffer() {}
+ virtual void* Allocate(size_t num_bytes) = 0;
};
} // namespace internal
diff --git a/mojo/public/cpp/bindings/lib/clone_equals_util.h b/mojo/public/cpp/bindings/lib/clone_equals_util.h
new file mode 100644
index 0000000..f7bd898
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/clone_equals_util.h
@@ -0,0 +1,161 @@
+// 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_CLONE_EQUALS_UTIL_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CLONE_EQUALS_UTIL_H_
+
+#include <type_traits>
+#include <unordered_map>
+#include <vector>
+
+#include "base/optional.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+
+namespace mojo {
+namespace internal {
+
+template <typename T>
+struct HasCloneMethod {
+ template <typename U>
+ static char Test(decltype(&U::Clone));
+ template <typename U>
+ static int Test(...);
+ static const bool value = sizeof(Test<T>(0)) == sizeof(char);
+
+ private:
+ EnsureTypeIsComplete<T> check_t_;
+};
+
+template <typename T, bool has_clone_method = HasCloneMethod<T>::value>
+struct CloneTraits;
+
+template <typename T>
+T Clone(const T& input);
+
+template <typename T>
+struct CloneTraits<T, true> {
+ static T Clone(const T& input) { return input.Clone(); }
+};
+
+template <typename T>
+struct CloneTraits<T, false> {
+ static T Clone(const T& input) { return input; }
+};
+
+template <typename T>
+struct CloneTraits<base::Optional<T>, false> {
+ static base::Optional<T> Clone(const base::Optional<T>& input) {
+ if (!input)
+ return base::nullopt;
+
+ return base::Optional<T>(internal::Clone(*input));
+ }
+};
+
+template <typename T>
+struct CloneTraits<std::vector<T>, false> {
+ static std::vector<T> Clone(const std::vector<T>& input) {
+ std::vector<T> result;
+ result.reserve(input.size());
+ for (const auto& element : input)
+ result.push_back(internal::Clone(element));
+
+ return result;
+ }
+};
+
+template <typename K, typename V>
+struct CloneTraits<std::unordered_map<K, V>, false> {
+ static std::unordered_map<K, V> Clone(const std::unordered_map<K, V>& input) {
+ std::unordered_map<K, V> result;
+ for (const auto& element : input) {
+ result.insert(std::make_pair(internal::Clone(element.first),
+ internal::Clone(element.second)));
+ }
+ return result;
+ }
+};
+
+template <typename T>
+T Clone(const T& input) {
+ return CloneTraits<T>::Clone(input);
+};
+
+template <typename T>
+struct HasEqualsMethod {
+ template <typename U>
+ static char Test(decltype(&U::Equals));
+ template <typename U>
+ static int Test(...);
+ static const bool value = sizeof(Test<T>(0)) == sizeof(char);
+
+ private:
+ EnsureTypeIsComplete<T> check_t_;
+};
+
+template <typename T, bool has_equals_method = HasEqualsMethod<T>::value>
+struct EqualsTraits;
+
+template <typename T>
+bool Equals(const T& a, const T& b);
+
+template <typename T>
+struct EqualsTraits<T, true> {
+ static bool Equals(const T& a, const T& b) { return a.Equals(b); }
+};
+
+template <typename T>
+struct EqualsTraits<T, false> {
+ static bool Equals(const T& a, const T& b) { return a == b; }
+};
+
+template <typename T>
+struct EqualsTraits<base::Optional<T>, false> {
+ static bool Equals(const base::Optional<T>& a, const base::Optional<T>& b) {
+ if (!a && !b)
+ return true;
+ if (!a || !b)
+ return false;
+
+ return internal::Equals(*a, *b);
+ }
+};
+
+template <typename T>
+struct EqualsTraits<std::vector<T>, false> {
+ static bool Equals(const std::vector<T>& a, const std::vector<T>& b) {
+ if (a.size() != b.size())
+ return false;
+ for (size_t i = 0; i < a.size(); ++i) {
+ if (!internal::Equals(a[i], b[i]))
+ return false;
+ }
+ return true;
+ }
+};
+
+template <typename K, typename V>
+struct EqualsTraits<std::unordered_map<K, V>, false> {
+ static bool Equals(const std::unordered_map<K, V>& a,
+ const std::unordered_map<K, V>& b) {
+ if (a.size() != b.size())
+ return false;
+ for (const auto& element : a) {
+ auto iter = b.find(element.first);
+ if (iter == b.end() || !internal::Equals(element.second, iter->second))
+ return false;
+ }
+ return true;
+ }
+};
+
+template <typename T>
+bool Equals(const T& a, const T& b) {
+ return EqualsTraits<T>::Equals(a, b);
+}
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_CLONE_EQUALS_UTIL_H_
diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc
index 4426def..1bb38f0 100644
--- a/mojo/public/cpp/bindings/lib/connector.cc
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -12,20 +12,52 @@
#include "base/logging.h"
#include "base/macros.h"
#include "base/synchronization/lock.h"
-#include "mojo/public/cpp/bindings/lib/may_auto_lock.h"
#include "mojo/public/cpp/bindings/sync_handle_watcher.h"
namespace mojo {
+namespace {
+
+// Similar to base::AutoLock, except that it does nothing if |lock| passed into
+// the constructor is null.
+class MayAutoLock {
+ public:
+ explicit MayAutoLock(base::Lock* lock) : lock_(lock) {
+ if (lock_)
+ lock_->Acquire();
+ }
+
+ ~MayAutoLock() {
+ if (lock_) {
+ lock_->AssertAcquired();
+ lock_->Release();
+ }
+ }
+
+ private:
+ base::Lock* lock_;
+ DISALLOW_COPY_AND_ASSIGN(MayAutoLock);
+};
+
+} // namespace
+
+// ----------------------------------------------------------------------------
+
Connector::Connector(ScopedMessagePipeHandle message_pipe,
ConnectorConfig config,
scoped_refptr<base::SingleThreadTaskRunner> runner)
: message_pipe_(std::move(message_pipe)),
+ incoming_receiver_(nullptr),
task_runner_(std::move(runner)),
+ handle_watcher_(task_runner_),
+ error_(false),
+ drop_writes_(false),
+ enforce_errors_from_incoming_receiver_(true),
+ paused_(false),
+ lock_(config == MULTI_THREADED_SEND ? new base::Lock : nullptr),
+ allow_woken_up_by_others_(false),
+ sync_handle_watcher_callback_count_(0),
weak_factory_(this) {
- if (config == MULTI_THREADED_SEND)
- lock_.emplace();
-
weak_self_ = weak_factory_.GetWeakPtr();
// Even though we don't have an incoming receiver, we still want to monitor
// the message pipe to know if is closed or encounters an error.
@@ -33,34 +65,25 @@
}
Connector::~Connector() {
- {
- // Allow for quick destruction on any thread if the pipe is already closed.
- base::AutoLock lock(connected_lock_);
- if (!connected_)
- return;
- }
-
DCHECK(thread_checker_.CalledOnValidThread());
+
CancelWait();
}
void Connector::CloseMessagePipe() {
- // Throw away the returned message pipe.
- PassMessagePipe();
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ CancelWait();
+ MayAutoLock locker(lock_.get());
+ message_pipe_.reset();
}
ScopedMessagePipeHandle Connector::PassMessagePipe() {
DCHECK(thread_checker_.CalledOnValidThread());
CancelWait();
- internal::MayAutoLock locker(&lock_);
- ScopedMessagePipeHandle message_pipe = std::move(message_pipe_);
- weak_factory_.InvalidateWeakPtrs();
- sync_handle_watcher_callback_count_ = 0;
-
- base::AutoLock lock(connected_lock_);
- connected_ = false;
- return message_pipe;
+ MayAutoLock locker(lock_.get());
+ return std::move(message_pipe_);
}
void Connector::RaiseError() {
@@ -120,7 +143,7 @@
if (error_)
return false;
- internal::MayAutoLock locker(&lock_);
+ MayAutoLock locker(lock_.get());
if (!message_pipe_.is_valid() || drop_writes_)
return true;
@@ -181,13 +204,6 @@
return sync_watcher_->SyncWatch(should_stop);
}
-void Connector::SetWatcherHeapProfilerTag(const char* tag) {
- heap_profiler_tag_ = tag;
- if (handle_watcher_) {
- handle_watcher_->set_heap_profiler_tag(tag);
- }
-}
-
void Connector::OnWatcherHandleReady(MojoResult result) {
OnHandleReadyInternal(result);
}
@@ -198,10 +214,8 @@
sync_handle_watcher_callback_count_++;
OnHandleReadyInternal(result);
// At this point, this object might have been deleted.
- if (weak_self) {
- DCHECK_LT(0u, sync_handle_watcher_callback_count_);
+ if (weak_self)
sync_handle_watcher_callback_count_--;
- }
}
void Connector::OnHandleReadyInternal(MojoResult result) {
@@ -217,14 +231,12 @@
void Connector::WaitToReadMore() {
CHECK(!paused_);
- DCHECK(!handle_watcher_);
+ DCHECK(!handle_watcher_.IsWatching());
- handle_watcher_.reset(new Watcher(FROM_HERE, task_runner_));
- if (heap_profiler_tag_)
- handle_watcher_->set_heap_profiler_tag(heap_profiler_tag_);
- MojoResult rv = handle_watcher_->Start(
+ MojoResult rv = handle_watcher_.Start(
message_pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE,
- base::Bind(&Connector::OnWatcherHandleReady, base::Unretained(this)));
+ base::Bind(&Connector::OnWatcherHandleReady,
+ base::Unretained(this)));
if (rv != MOJO_RESULT_OK) {
// If the watch failed because the handle is invalid or its conditions can
@@ -245,8 +257,8 @@
bool receiver_result = false;
- // Detect if |this| was destroyed or the message pipe was closed/transferred
- // during message dispatch.
+ // Detect if |this| was destroyed during message dispatch. Allow for the
+ // possibility of re-entering ReadMore() through message dispatch.
base::WeakPtr<Connector> weak_self = weak_self_;
Message message;
@@ -280,11 +292,9 @@
while (!error_) {
MojoResult rv;
- if (!ReadSingleMessage(&rv)) {
- // Return immediately without touching any members. |this| may have been
- // destroyed.
+ // Return immediately if |this| was destroyed. Do not touch any members!
+ if (!ReadSingleMessage(&rv))
return;
- }
if (paused_)
return;
@@ -295,7 +305,7 @@
}
void Connector::CancelWait() {
- handle_watcher_.reset();
+ handle_watcher_.Cancel();
sync_watcher_.reset();
}
@@ -315,7 +325,7 @@
if (force_pipe_reset) {
CancelWait();
- internal::MayAutoLock locker(&lock_);
+ MayAutoLock locker(lock_.get());
message_pipe_.reset();
MessagePipe dummy_pipe;
message_pipe_ = std::move(dummy_pipe.handle0);
diff --git a/mojo/public/cpp/bindings/lib/control_message_handler.cc b/mojo/public/cpp/bindings/lib/control_message_handler.cc
index c90aada..9f44e88 100644
--- a/mojo/public/cpp/bindings/lib/control_message_handler.cc
+++ b/mojo/public/cpp/bindings/lib/control_message_handler.cc
@@ -11,53 +11,15 @@
#include "base/logging.h"
#include "mojo/public/cpp/bindings/lib/message_builder.h"
#include "mojo/public/cpp/bindings/lib/serialization.h"
-#include "mojo/public/cpp/bindings/lib/validation_util.h"
#include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h"
namespace mojo {
namespace internal {
-namespace {
-
-bool ValidateControlRequestWithResponse(Message* message) {
- ValidationContext validation_context(message->payload(),
- message->payload_num_bytes(), 0, 0,
- message, "ControlRequestValidator");
- if (!ValidateMessageIsRequestExpectingResponse(message, &validation_context))
- return false;
-
- switch (message->header()->name) {
- case interface_control::kRunMessageId:
- return ValidateMessagePayload<
- interface_control::internal::RunMessageParams_Data>(
- message, &validation_context);
- }
- return false;
-}
-
-bool ValidateControlRequestWithoutResponse(Message* message) {
- ValidationContext validation_context(message->payload(),
- message->payload_num_bytes(), 0, 0,
- message, "ControlRequestValidator");
- if (!ValidateMessageIsRequestWithoutResponse(message, &validation_context))
- return false;
-
- switch (message->header()->name) {
- case interface_control::kRunOrClosePipeMessageId:
- return ValidateMessageIsRequestWithoutResponse(message,
- &validation_context) &&
- ValidateMessagePayload<
- interface_control::internal::RunOrClosePipeMessageParams_Data>(
- message, &validation_context);
- }
- return false;
-}
-
-} // namespace
// static
bool ControlMessageHandler::IsControlMessage(const Message* message) {
- return message->header()->name == interface_control::kRunMessageId ||
- message->header()->name == interface_control::kRunOrClosePipeMessageId;
+ return message->header()->name == kRunMessageId ||
+ message->header()->name == kRunOrClosePipeMessageId;
}
ControlMessageHandler::ControlMessageHandler(uint32_t interface_version)
@@ -68,10 +30,7 @@
}
bool ControlMessageHandler::Accept(Message* message) {
- if (!ValidateControlRequestWithoutResponse(message))
- return false;
-
- if (message->header()->name == interface_control::kRunOrClosePipeMessageId)
+ if (message->header()->name == kRunOrClosePipeMessageId)
return RunOrClosePipe(message);
NOTREACHED();
@@ -81,10 +40,7 @@
bool ControlMessageHandler::AcceptWithResponder(
Message* message,
MessageReceiverWithStatus* responder) {
- if (!ValidateControlRequestWithResponse(message))
- return false;
-
- if (message->header()->name == interface_control::kRunMessageId)
+ if (message->header()->name == kRunMessageId)
return Run(message, responder);
NOTREACHED();
@@ -93,37 +49,20 @@
bool ControlMessageHandler::Run(Message* message,
MessageReceiverWithStatus* responder) {
- interface_control::internal::RunMessageParams_Data* params =
- reinterpret_cast<interface_control::internal::RunMessageParams_Data*>(
- message->mutable_payload());
- interface_control::RunMessageParamsPtr params_ptr;
- Deserialize<interface_control::RunMessageParamsDataView>(params, ¶ms_ptr,
- &context_);
- auto& input = *params_ptr->input;
- interface_control::RunOutputPtr output = interface_control::RunOutput::New();
- if (input.is_query_version()) {
- output->set_query_version_result(
- interface_control::QueryVersionResult::New());
- output->get_query_version_result()->version = interface_version_;
- } else if (input.is_flush_for_testing()) {
- output.reset();
- } else {
- output.reset();
- }
+ RunResponseMessageParamsPtr response_params_ptr(
+ RunResponseMessageParams::New());
+ response_params_ptr->reserved0 = 16u;
+ response_params_ptr->reserved1 = 0u;
+ response_params_ptr->query_version_result = QueryVersionResult::New();
+ response_params_ptr->query_version_result->version = interface_version_;
- auto response_params_ptr = interface_control::RunResponseMessageParams::New();
- response_params_ptr->output = std::move(output);
- size_t size =
- PrepareToSerialize<interface_control::RunResponseMessageParamsDataView>(
- response_params_ptr, &context_);
- MessageBuilder builder(interface_control::kRunMessageId,
- Message::kFlagIsResponse, size, 0);
- builder.message()->set_request_id(message->request_id());
+ size_t size = PrepareToSerialize<RunResponseMessageParamsPtr>(
+ response_params_ptr, &context_);
+ ResponseMessageBuilder builder(kRunMessageId, size, message->request_id());
- interface_control::internal::RunResponseMessageParams_Data* response_params =
- nullptr;
- Serialize<interface_control::RunResponseMessageParamsDataView>(
- response_params_ptr, builder.buffer(), &response_params, &context_);
+ RunResponseMessageParams_Data* response_params = nullptr;
+ Serialize<RunResponseMessageParamsPtr>(response_params_ptr, builder.buffer(),
+ &response_params, &context_);
bool ok = responder->Accept(builder.message());
ALLOW_UNUSED_LOCAL(ok);
delete responder;
@@ -132,18 +71,13 @@
}
bool ControlMessageHandler::RunOrClosePipe(Message* message) {
- interface_control::internal::RunOrClosePipeMessageParams_Data* params =
- reinterpret_cast<
- interface_control::internal::RunOrClosePipeMessageParams_Data*>(
+ RunOrClosePipeMessageParams_Data* params =
+ reinterpret_cast<RunOrClosePipeMessageParams_Data*>(
message->mutable_payload());
- interface_control::RunOrClosePipeMessageParamsPtr params_ptr;
- Deserialize<interface_control::RunOrClosePipeMessageParamsDataView>(
- params, ¶ms_ptr, &context_);
- auto& input = *params_ptr->input;
- if (input.is_require_version())
- return interface_version_ >= input.get_require_version()->version;
+ RunOrClosePipeMessageParamsPtr params_ptr;
+ Deserialize<RunOrClosePipeMessageParamsPtr>(params, ¶ms_ptr, &context_);
- return false;
+ return interface_version_ >= params_ptr->require_version->version;
}
} // namespace internal
diff --git a/mojo/public/cpp/bindings/lib/control_message_handler.h b/mojo/public/cpp/bindings/lib/control_message_handler.h
index 3c385e4..13b5aa6 100644
--- a/mojo/public/cpp/bindings/lib/control_message_handler.h
+++ b/mojo/public/cpp/bindings/lib/control_message_handler.h
@@ -7,9 +7,7 @@
#include <stdint.h>
-#include "base/compiler_specific.h"
#include "base/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/bindings/lib/serialization_context.h"
#include "mojo/public/cpp/bindings/message.h"
@@ -17,8 +15,7 @@
namespace internal {
// Handlers for request messages defined in interface_control_messages.mojom.
-class MOJO_CPP_BINDINGS_EXPORT ControlMessageHandler
- : NON_EXPORTED_BASE(public MessageReceiverWithResponderStatus) {
+class ControlMessageHandler : public MessageReceiverWithResponderStatus {
public:
static bool IsControlMessage(const Message* message);
diff --git a/mojo/public/cpp/bindings/lib/control_message_proxy.cc b/mojo/public/cpp/bindings/lib/control_message_proxy.cc
index 23de991..7af409d 100644
--- a/mojo/public/cpp/bindings/lib/control_message_proxy.cc
+++ b/mojo/public/cpp/bindings/lib/control_message_proxy.cc
@@ -9,12 +9,9 @@
#include <utility>
#include "base/bind.h"
-#include "base/callback_helpers.h"
#include "base/macros.h"
-#include "base/run_loop.h"
#include "mojo/public/cpp/bindings/lib/message_builder.h"
#include "mojo/public/cpp/bindings/lib/serialization.h"
-#include "mojo/public/cpp/bindings/lib/validation_util.h"
#include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h"
@@ -23,28 +20,11 @@
namespace {
-bool ValidateControlResponse(Message* message) {
- ValidationContext validation_context(message->payload(),
- message->payload_num_bytes(), 0, 0,
- message, "ControlResponseValidator");
- if (!ValidateMessageIsResponse(message, &validation_context))
- return false;
-
- switch (message->header()->name) {
- case interface_control::kRunMessageId:
- return ValidateMessagePayload<
- interface_control::internal::RunResponseMessageParams_Data>(
- message, &validation_context);
- }
- return false;
-}
-
-using RunCallback =
- base::Callback<void(interface_control::RunResponseMessageParamsPtr)>;
+using RunCallback = base::Callback<void(QueryVersionResultPtr)>;
class RunResponseForwardToCallback : public MessageReceiver {
public:
- explicit RunResponseForwardToCallback(const RunCallback& callback)
+ RunResponseForwardToCallback(const RunCallback& callback)
: callback_(callback) {}
bool Accept(Message* message) override;
@@ -54,83 +34,59 @@
};
bool RunResponseForwardToCallback::Accept(Message* message) {
- if (!ValidateControlResponse(message))
- return false;
-
- interface_control::internal::RunResponseMessageParams_Data* params =
- reinterpret_cast<
- interface_control::internal::RunResponseMessageParams_Data*>(
+ RunResponseMessageParams_Data* params =
+ reinterpret_cast<RunResponseMessageParams_Data*>(
message->mutable_payload());
- interface_control::RunResponseMessageParamsPtr params_ptr;
+ RunResponseMessageParamsPtr params_ptr;
SerializationContext context;
- Deserialize<interface_control::RunResponseMessageParamsDataView>(
- params, ¶ms_ptr, &context);
+ Deserialize<RunResponseMessageParamsPtr>(params, ¶ms_ptr, &context);
- callback_.Run(std::move(params_ptr));
+ callback_.Run(std::move(params_ptr->query_version_result));
return true;
}
void SendRunMessage(MessageReceiverWithResponder* receiver,
- interface_control::RunInputPtr input_ptr,
- const RunCallback& callback) {
- SerializationContext context;
+ QueryVersionPtr query_version,
+ const RunCallback& callback,
+ SerializationContext* context) {
+ RunMessageParamsPtr params_ptr(RunMessageParams::New());
+ params_ptr->reserved0 = 16u;
+ params_ptr->reserved1 = 0u;
+ params_ptr->query_version = std::move(query_version);
- auto params_ptr = interface_control::RunMessageParams::New();
- params_ptr->input = std::move(input_ptr);
- size_t size = PrepareToSerialize<interface_control::RunMessageParamsDataView>(
- params_ptr, &context);
- MessageBuilder builder(interface_control::kRunMessageId,
- Message::kFlagExpectsResponse, size, 0);
+ size_t size = PrepareToSerialize<RunMessageParamsPtr>(params_ptr, context);
+ RequestMessageBuilder builder(kRunMessageId, size);
- interface_control::internal::RunMessageParams_Data* params = nullptr;
- Serialize<interface_control::RunMessageParamsDataView>(
- params_ptr, builder.buffer(), ¶ms, &context);
+ RunMessageParams_Data* params = nullptr;
+ Serialize<RunMessageParamsPtr>(params_ptr, builder.buffer(), ¶ms,
+ context);
MessageReceiver* responder = new RunResponseForwardToCallback(callback);
if (!receiver->AcceptWithResponder(builder.message(), responder))
delete responder;
}
-Message ConstructRunOrClosePipeMessage(
- interface_control::RunOrClosePipeInputPtr input_ptr) {
- SerializationContext context;
+void SendRunOrClosePipeMessage(MessageReceiverWithResponder* receiver,
+ RequireVersionPtr require_version,
+ SerializationContext* context) {
+ RunOrClosePipeMessageParamsPtr params_ptr(RunOrClosePipeMessageParams::New());
+ params_ptr->reserved0 = 16u;
+ params_ptr->reserved1 = 0u;
+ params_ptr->require_version = std::move(require_version);
- auto params_ptr = interface_control::RunOrClosePipeMessageParams::New();
- params_ptr->input = std::move(input_ptr);
+ size_t size =
+ PrepareToSerialize<RunOrClosePipeMessageParamsPtr>(params_ptr, context);
+ MessageBuilder builder(kRunOrClosePipeMessageId, size);
- size_t size = PrepareToSerialize<
- interface_control::RunOrClosePipeMessageParamsDataView>(params_ptr,
- &context);
- MessageBuilder builder(interface_control::kRunOrClosePipeMessageId, 0, size,
- 0);
-
- interface_control::internal::RunOrClosePipeMessageParams_Data* params =
- nullptr;
- Serialize<interface_control::RunOrClosePipeMessageParamsDataView>(
- params_ptr, builder.buffer(), ¶ms, &context);
- return std::move(*builder.message());
-}
-
-void SendRunOrClosePipeMessage(
- MessageReceiverWithResponder* receiver,
- interface_control::RunOrClosePipeInputPtr input_ptr) {
- Message message(ConstructRunOrClosePipeMessage(std::move(input_ptr)));
-
- bool ok = receiver->Accept(&message);
+ RunOrClosePipeMessageParams_Data* params = nullptr;
+ Serialize<RunOrClosePipeMessageParamsPtr>(params_ptr, builder.buffer(),
+ ¶ms, context);
+ bool ok = receiver->Accept(builder.message());
ALLOW_UNUSED_LOCAL(ok);
}
-void RunVersionCallback(
- const base::Callback<void(uint32_t)>& callback,
- interface_control::RunResponseMessageParamsPtr run_response) {
- uint32_t version = 0u;
- if (run_response->output && run_response->output->is_query_version_result())
- version = run_response->output->get_query_version_result()->version;
- callback.Run(version);
-}
-
-void RunClosure(const base::Closure& callback,
- interface_control::RunResponseMessageParamsPtr run_response) {
- callback.Run();
+void RunVersionCallback(const base::Callback<void(uint32_t)>& callback,
+ QueryVersionResultPtr query_version_result) {
+ callback.Run(query_version_result->version);
}
} // namespace
@@ -139,49 +95,16 @@
: receiver_(receiver) {
}
-ControlMessageProxy::~ControlMessageProxy() = default;
-
void ControlMessageProxy::QueryVersion(
const base::Callback<void(uint32_t)>& callback) {
- auto input_ptr = interface_control::RunInput::New();
- input_ptr->set_query_version(interface_control::QueryVersion::New());
- SendRunMessage(receiver_, std::move(input_ptr),
- base::Bind(&RunVersionCallback, callback));
+ SendRunMessage(receiver_, QueryVersion::New(),
+ base::Bind(&RunVersionCallback, callback), &context_);
}
void ControlMessageProxy::RequireVersion(uint32_t version) {
- auto require_version = interface_control::RequireVersion::New();
+ RequireVersionPtr require_version(RequireVersion::New());
require_version->version = version;
- auto input_ptr = interface_control::RunOrClosePipeInput::New();
- input_ptr->set_require_version(std::move(require_version));
- SendRunOrClosePipeMessage(receiver_, std::move(input_ptr));
-}
-
-void ControlMessageProxy::FlushForTesting() {
- if (encountered_error_)
- return;
-
- auto input_ptr = interface_control::RunInput::New();
- input_ptr->set_flush_for_testing(interface_control::FlushForTesting::New());
- base::RunLoop run_loop;
- run_loop_quit_closure_ = run_loop.QuitClosure();
- SendRunMessage(
- receiver_, std::move(input_ptr),
- base::Bind(&RunClosure,
- base::Bind(&ControlMessageProxy::RunFlushForTestingClosure,
- base::Unretained(this))));
- run_loop.Run();
-}
-
-void ControlMessageProxy::RunFlushForTestingClosure() {
- DCHECK(!run_loop_quit_closure_.is_null());
- base::ResetAndReturn(&run_loop_quit_closure_).Run();
-}
-
-void ControlMessageProxy::OnConnectionError() {
- encountered_error_ = true;
- if (!run_loop_quit_closure_.is_null())
- RunFlushForTestingClosure();
+ SendRunOrClosePipeMessage(receiver_, std::move(require_version), &context_);
}
} // namespace internal
diff --git a/mojo/public/cpp/bindings/lib/control_message_proxy.h b/mojo/public/cpp/bindings/lib/control_message_proxy.h
index 2f9314e..5ec6ddc 100644
--- a/mojo/public/cpp/bindings/lib/control_message_proxy.h
+++ b/mojo/public/cpp/bindings/lib/control_message_proxy.h
@@ -7,9 +7,8 @@
#include <stdint.h>
-#include "base/callback.h"
+#include "base/callback_forward.h"
#include "base/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/bindings/lib/serialization_context.h"
namespace mojo {
@@ -19,26 +18,18 @@
namespace internal {
// Proxy for request messages defined in interface_control_messages.mojom.
-class MOJO_CPP_BINDINGS_EXPORT ControlMessageProxy {
+class ControlMessageProxy {
public:
// Doesn't take ownership of |receiver|. It must outlive this object.
explicit ControlMessageProxy(MessageReceiverWithResponder* receiver);
- ~ControlMessageProxy();
void QueryVersion(const base::Callback<void(uint32_t)>& callback);
void RequireVersion(uint32_t version);
- void FlushForTesting();
- void OnConnectionError();
-
- private:
- void RunFlushForTestingClosure();
-
+ protected:
// Not owned.
MessageReceiverWithResponder* receiver_;
- bool encountered_error_ = false;
-
- base::Closure run_loop_quit_closure_;
+ SerializationContext context_;
DISALLOW_COPY_AND_ASSIGN(ControlMessageProxy);
};
diff --git a/mojo/public/cpp/bindings/lib/equals_traits.h b/mojo/public/cpp/bindings/lib/equals_traits.h
deleted file mode 100644
index 53c7dce..0000000
--- a/mojo/public/cpp/bindings/lib/equals_traits.h
+++ /dev/null
@@ -1,94 +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.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_EQUALS_TRAITS_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_LIB_EQUALS_TRAITS_H_
-
-#include <type_traits>
-#include <unordered_map>
-#include <vector>
-
-#include "base/optional.h"
-#include "mojo/public/cpp/bindings/lib/template_util.h"
-
-namespace mojo {
-namespace internal {
-
-template <typename T>
-struct HasEqualsMethod {
- template <typename U>
- static char Test(decltype(&U::Equals));
- template <typename U>
- static int Test(...);
- static const bool value = sizeof(Test<T>(0)) == sizeof(char);
-
- private:
- EnsureTypeIsComplete<T> check_t_;
-};
-
-template <typename T, bool has_equals_method = HasEqualsMethod<T>::value>
-struct EqualsTraits;
-
-template <typename T>
-bool Equals(const T& a, const T& b);
-
-template <typename T>
-struct EqualsTraits<T, true> {
- static bool Equals(const T& a, const T& b) { return a.Equals(b); }
-};
-
-template <typename T>
-struct EqualsTraits<T, false> {
- static bool Equals(const T& a, const T& b) { return a == b; }
-};
-
-template <typename T>
-struct EqualsTraits<base::Optional<T>, false> {
- static bool Equals(const base::Optional<T>& a, const base::Optional<T>& b) {
- if (!a && !b)
- return true;
- if (!a || !b)
- return false;
-
- return internal::Equals(*a, *b);
- }
-};
-
-template <typename T>
-struct EqualsTraits<std::vector<T>, false> {
- static bool Equals(const std::vector<T>& a, const std::vector<T>& b) {
- if (a.size() != b.size())
- return false;
- for (size_t i = 0; i < a.size(); ++i) {
- if (!internal::Equals(a[i], b[i]))
- return false;
- }
- return true;
- }
-};
-
-template <typename K, typename V>
-struct EqualsTraits<std::unordered_map<K, V>, false> {
- static bool Equals(const std::unordered_map<K, V>& a,
- const std::unordered_map<K, V>& b) {
- if (a.size() != b.size())
- return false;
- for (const auto& element : a) {
- auto iter = b.find(element.first);
- if (iter == b.end() || !internal::Equals(element.second, iter->second))
- return false;
- }
- return true;
- }
-};
-
-template <typename T>
-bool Equals(const T& a, const T& b) {
- return EqualsTraits<T>::Equals(a, b);
-}
-
-} // namespace internal
-} // namespace mojo
-
-#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_EQUALS_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/lib/filter_chain.cc b/mojo/public/cpp/bindings/lib/filter_chain.cc
index 5d919fe..899bac1 100644
--- a/mojo/public/cpp/bindings/lib/filter_chain.cc
+++ b/mojo/public/cpp/bindings/lib/filter_chain.cc
@@ -2,13 +2,14 @@
// 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/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
#include <algorithm>
#include "base/logging.h"
namespace mojo {
+namespace internal {
FilterChain::FilterChain(MessageReceiver* sink) : sink_(sink) {
}
@@ -25,23 +26,24 @@
}
FilterChain::~FilterChain() {
+ for (std::vector<MessageFilter*>::iterator iter = filters_.begin();
+ iter != filters_.end();
+ ++iter) {
+ delete *iter;
+ }
}
void FilterChain::SetSink(MessageReceiver* sink) {
DCHECK(!sink_);
sink_ = sink;
+ if (!filters_.empty())
+ filters_.back()->set_sink(sink);
}
-bool FilterChain::Accept(Message* message) {
+MessageReceiver* FilterChain::GetHead() {
DCHECK(sink_);
- for (auto& filter : filters_)
- if (!filter->Accept(message))
- return false;
- return sink_->Accept(message);
+ return filters_.empty() ? sink_ : filters_.front();
}
-void FilterChain::Append(std::unique_ptr<MessageReceiver> filter) {
- filters_.emplace_back(std::move(filter));
-}
-
+} // namespace internal
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/filter_chain.h b/mojo/public/cpp/bindings/lib/filter_chain.h
new file mode 100644
index 0000000..447be3d
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/filter_chain.h
@@ -0,0 +1,66 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+
+namespace mojo {
+namespace internal {
+
+class FilterChain {
+ public:
+ // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while
+ // this object is alive.
+ explicit FilterChain(MessageReceiver* sink = nullptr);
+
+ FilterChain(FilterChain&& other);
+ FilterChain& operator=(FilterChain&& other);
+ ~FilterChain();
+
+ template <typename FilterType, typename... Args>
+ inline void Append(Args&&... args);
+
+ // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while
+ // this object is alive.
+ void SetSink(MessageReceiver* sink);
+
+ // Returns a receiver to accept messages. Messages flow through all filters in
+ // the same order as they were appended to the chain. If all filters allow a
+ // message to pass, it will be forwarded to |sink_|.
+ // The returned value is invalidated when this object goes away.
+ MessageReceiver* GetHead();
+
+ private:
+ // Owned by this object.
+ // TODO(dcheng): Use unique_ptr.
+ std::vector<MessageFilter*> filters_;
+
+ MessageReceiver* sink_;
+
+ DISALLOW_COPY_AND_ASSIGN(FilterChain);
+};
+
+template <typename FilterType, typename... Args>
+inline void FilterChain::Append(Args&&... args) {
+ FilterType* filter = new FilterType(std::forward<Args>(args)..., sink_);
+ if (!filters_.empty())
+ filters_.back()->set_sink(filter);
+ filters_.push_back(filter);
+}
+
+template <>
+inline void FilterChain::Append<PassThroughFilter>() {
+}
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_
diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.cc b/mojo/public/cpp/bindings/lib/fixed_buffer.cc
index 725a193..50b8a21 100644
--- a/mojo/public/cpp/bindings/lib/fixed_buffer.cc
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.cc
@@ -4,25 +4,56 @@
#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
+#include <stddef.h>
#include <stdlib.h>
+#include <algorithm>
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/lib/serialization_util.h"
+
namespace mojo {
namespace internal {
+FixedBuffer::FixedBuffer() : ptr_(nullptr), cursor_(0), size_(0) {}
+
+void FixedBuffer::Initialize(void* memory, size_t size) {
+ DCHECK(size == internal::Align(size));
+
+ ptr_ = static_cast<char*>(memory);
+ cursor_ = 0;
+ size_ = size;
+}
+
+void* FixedBuffer::Allocate(size_t delta) {
+ delta = internal::Align(delta);
+
+ if (delta == 0 || delta > size_ - cursor_) {
+ NOTREACHED();
+ return nullptr;
+ }
+
+ char* result = ptr_ + cursor_;
+ cursor_ += delta;
+
+ return result;
+}
+
FixedBufferForTesting::FixedBufferForTesting(size_t size) {
- size = internal::Align(size);
+ size_ = internal::Align(size);
// Use calloc here to ensure all message memory is zero'd out.
- void* ptr = calloc(size, 1);
- Initialize(ptr, size);
+ ptr_ = static_cast<char*>(calloc(size_, 1));
}
FixedBufferForTesting::~FixedBufferForTesting() {
- free(data());
+ free(ptr_);
}
void* FixedBufferForTesting::Leak() {
- void* ptr = data();
- Initialize(nullptr, 0);
+ char* ptr = ptr_;
+ ptr_ = nullptr;
+ cursor_ = 0;
+ size_ = 0;
return ptr;
}
diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.h b/mojo/public/cpp/bindings/lib/fixed_buffer.h
index 070b0c8..9a5704b 100644
--- a/mojo/public/cpp/bindings/lib/fixed_buffer.h
+++ b/mojo/public/cpp/bindings/lib/fixed_buffer.h
@@ -7,21 +7,62 @@
#include <stddef.h>
-#include "base/compiler_specific.h"
#include "base/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/bindings/lib/buffer.h"
namespace mojo {
namespace internal {
-// FixedBufferForTesting owns its buffer. The Leak method may be used to steal
-// the underlying memory.
-class MOJO_CPP_BINDINGS_EXPORT FixedBufferForTesting
- : NON_EXPORTED_BASE(public Buffer) {
+// FixedBuffer provides a simple way to allocate objects within a fixed chunk
+// of memory. Objects are allocated by calling the |Allocate| method, which
+// extends the buffer accordingly. Objects allocated in this way are not freed
+// explicitly. Instead, they remain valid so long as the FixedBuffer remains
+// valid. The Leak method may be used to steal the underlying memory from the
+// FixedBuffer.
+//
+// Typical usage:
+//
+// {
+// FixedBuffer buf(8 + 8);
+//
+// int* a = static_cast<int*>(buf->Allocate(sizeof(int)));
+// *a = 2;
+//
+// double* b = static_cast<double*>(buf->Allocate(sizeof(double)));
+// *b = 3.14f;
+//
+// void* data = buf.Leak();
+// Process(data);
+//
+// free(data);
+// }
+
+class FixedBuffer : public Buffer {
+ public:
+ FixedBuffer();
+
+ // |size| should be aligned using internal::Align.
+ void Initialize(void* memory, size_t size);
+
+ size_t size() const { return size_; }
+
+ // Grows the buffer by |num_bytes| and returns a pointer to the start of the
+ // addition. The resulting address is 8-byte aligned, and the content of the
+ // memory is zero-filled.
+ void* Allocate(size_t num_bytes) override;
+
+ protected:
+ char* ptr_;
+ size_t cursor_;
+ size_t size_;
+
+ DISALLOW_COPY_AND_ASSIGN(FixedBuffer);
+};
+
+class FixedBufferForTesting : public FixedBuffer {
public:
explicit FixedBufferForTesting(size_t size);
- ~FixedBufferForTesting();
+ ~FixedBufferForTesting() override;
// Returns the internal memory owned by the Buffer to the caller. The Buffer
// relinquishes its pointer, effectively resetting the state of the Buffer
diff --git a/mojo/public/cpp/bindings/lib/handle_interface_serialization.h b/mojo/public/cpp/bindings/lib/handle_interface_serialization.h
index 14ed21f..344c2ca 100644
--- a/mojo/public/cpp/bindings/lib/handle_interface_serialization.h
+++ b/mojo/public/cpp/bindings/lib/handle_interface_serialization.h
@@ -5,12 +5,9 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_HANDLE_INTERFACE_SERIALIZATION_H_
#define MOJO_PUBLIC_CPP_BINDINGS_LIB_HANDLE_INTERFACE_SERIALIZATION_H_
-#include <type_traits>
-
#include "mojo/public/cpp/bindings/associated_group_controller.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/interface_data_view.h"
#include "mojo/public/cpp/bindings/interface_ptr.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
@@ -21,98 +18,52 @@
namespace mojo {
namespace internal {
-template <typename Base, typename T>
-struct Serializer<AssociatedInterfacePtrInfoDataView<Base>,
+template <typename T>
+struct Serializer<AssociatedInterfacePtrInfo<T>,
AssociatedInterfacePtrInfo<T>> {
- static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch.");
-
- static size_t PrepareToSerialize(const AssociatedInterfacePtrInfo<T>& input,
- SerializationContext* context) {
- if (input.handle().is_valid())
- context->associated_endpoint_count++;
- return 0;
- }
-
static void Serialize(AssociatedInterfacePtrInfo<T>& input,
AssociatedInterface_Data* output,
SerializationContext* context) {
- DCHECK(!input.handle().is_valid() || input.handle().pending_association());
- if (input.handle().is_valid()) {
- // Set to the index of the element pushed to the back of the vector.
- output->handle.value =
- static_cast<uint32_t>(context->associated_endpoint_handles.size());
- context->associated_endpoint_handles.push_back(input.PassHandle());
- } else {
- output->handle.value = kEncodedInvalidHandleValue;
- }
+ DCHECK(!input.handle().is_valid() || !input.handle().is_local());
+ DCHECK_EQ(input.handle().group_controller(),
+ context->group_controller.get());
output->version = input.version();
+ output->interface_id = input.PassHandle().release();
}
static bool Deserialize(AssociatedInterface_Data* input,
AssociatedInterfacePtrInfo<T>* output,
SerializationContext* context) {
- if (input->handle.is_valid()) {
- DCHECK_LT(input->handle.value,
- context->associated_endpoint_handles.size());
- output->set_handle(
- std::move(context->associated_endpoint_handles[input->handle.value]));
- } else {
- output->set_handle(ScopedInterfaceEndpointHandle());
- }
+ output->set_handle(context->group_controller->CreateLocalEndpointHandle(
+ FetchAndReset(&input->interface_id)));
output->set_version(input->version);
return true;
}
};
-template <typename Base, typename T>
-struct Serializer<AssociatedInterfaceRequestDataView<Base>,
+template <typename T>
+struct Serializer<AssociatedInterfaceRequest<T>,
AssociatedInterfaceRequest<T>> {
- static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch.");
-
- static size_t PrepareToSerialize(const AssociatedInterfaceRequest<T>& input,
- SerializationContext* context) {
- if (input.handle().is_valid())
- context->associated_endpoint_count++;
- return 0;
- }
-
static void Serialize(AssociatedInterfaceRequest<T>& input,
- AssociatedEndpointHandle_Data* output,
+ AssociatedInterfaceRequest_Data* output,
SerializationContext* context) {
- DCHECK(!input.handle().is_valid() || input.handle().pending_association());
- if (input.handle().is_valid()) {
- // Set to the index of the element pushed to the back of the vector.
- output->value =
- static_cast<uint32_t>(context->associated_endpoint_handles.size());
- context->associated_endpoint_handles.push_back(input.PassHandle());
- } else {
- output->value = kEncodedInvalidHandleValue;
- }
+ DCHECK(!input.handle().is_valid() || !input.handle().is_local());
+ DCHECK_EQ(input.handle().group_controller(),
+ context->group_controller.get());
+ output->interface_id = input.PassHandle().release();
}
- static bool Deserialize(AssociatedEndpointHandle_Data* input,
+ static bool Deserialize(AssociatedInterfaceRequest_Data* input,
AssociatedInterfaceRequest<T>* output,
SerializationContext* context) {
- if (input->is_valid()) {
- DCHECK_LT(input->value, context->associated_endpoint_handles.size());
- output->Bind(
- std::move(context->associated_endpoint_handles[input->value]));
- } else {
- output->Bind(ScopedInterfaceEndpointHandle());
- }
+ output->Bind(context->group_controller->CreateLocalEndpointHandle(
+ FetchAndReset(&input->interface_id)));
return true;
}
};
-template <typename Base, typename T>
-struct Serializer<InterfacePtrDataView<Base>, InterfacePtr<T>> {
- static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch.");
-
- static size_t PrepareToSerialize(const InterfacePtr<T>& input,
- SerializationContext* context) {
- return 0;
- }
-
+template <typename T>
+struct Serializer<InterfacePtr<T>, InterfacePtr<T>> {
static void Serialize(InterfacePtr<T>& input,
Interface_Data* output,
SerializationContext* context) {
@@ -131,15 +82,8 @@
}
};
-template <typename Base, typename T>
-struct Serializer<InterfaceRequestDataView<Base>, InterfaceRequest<T>> {
- static_assert(std::is_base_of<Base, T>::value, "Interface type mismatch.");
-
- static size_t PrepareToSerialize(const InterfaceRequest<T>& input,
- SerializationContext* context) {
- return 0;
- }
-
+template <typename T>
+struct Serializer<InterfaceRequest<T>, InterfaceRequest<T>> {
static void Serialize(InterfaceRequest<T>& input,
Handle_Data* output,
SerializationContext* context) {
@@ -156,11 +100,6 @@
template <typename T>
struct Serializer<ScopedHandleBase<T>, ScopedHandleBase<T>> {
- static size_t PrepareToSerialize(const ScopedHandleBase<T>& input,
- SerializationContext* context) {
- return 0;
- }
-
static void Serialize(ScopedHandleBase<T>& input,
Handle_Data* output,
SerializationContext* context) {
diff --git a/mojo/public/cpp/bindings/lib/hash_util.h b/mojo/public/cpp/bindings/lib/hash_util.h
deleted file mode 100644
index 93280d6..0000000
--- a/mojo/public/cpp/bindings/lib/hash_util.h
+++ /dev/null
@@ -1,84 +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.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_HASH_UTIL_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_LIB_HASH_UTIL_H_
-
-#include <cstring>
-#include <functional>
-#include <type_traits>
-#include <vector>
-
-#include "base/optional.h"
-#include "mojo/public/cpp/bindings/lib/template_util.h"
-
-namespace mojo {
-namespace internal {
-
-template <typename T>
-size_t HashCombine(size_t seed, const T& value) {
- // Based on proposal in:
- // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1756.pdf
- return seed ^ (std::hash<T>()(value) + (seed << 6) + (seed >> 2));
-}
-
-template <typename T>
-struct HasHashMethod {
- template <typename U>
- static char Test(decltype(&U::Hash));
- template <typename U>
- static int Test(...);
- static const bool value = sizeof(Test<T>(0)) == sizeof(char);
-
- private:
- EnsureTypeIsComplete<T> check_t_;
-};
-
-template <typename T, bool has_hash_method = HasHashMethod<T>::value>
-struct HashTraits;
-
-template <typename T>
-size_t Hash(size_t seed, const T& value);
-
-template <typename T>
-struct HashTraits<T, true> {
- static size_t Hash(size_t seed, const T& value) { return value.Hash(seed); }
-};
-
-template <typename T>
-struct HashTraits<T, false> {
- static size_t Hash(size_t seed, const T& value) {
- return HashCombine(seed, value);
- }
-};
-
-template <typename T>
-struct HashTraits<std::vector<T>, false> {
- static size_t Hash(size_t seed, const std::vector<T>& value) {
- for (const auto& element : value) {
- seed = HashCombine(seed, element);
- }
- return seed;
- }
-};
-
-template <typename T>
-struct HashTraits<base::Optional<std::vector<T>>, false> {
- static size_t Hash(size_t seed, const base::Optional<std::vector<T>>& value) {
- if (!value)
- return HashCombine(seed, 0);
-
- return Hash(seed, *value);
- }
-};
-
-template <typename T>
-size_t Hash(size_t seed, const T& value) {
- return HashTraits<T>::Hash(seed, value);
-}
-
-} // namespace internal
-} // namespace mojo
-
-#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_HASH_UTIL_H_
diff --git a/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc b/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc
index 3eca5a1..e1f388a 100644
--- a/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc
+++ b/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc
@@ -10,7 +10,6 @@
#include "base/bind.h"
#include "base/location.h"
-#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/single_thread_task_runner.h"
@@ -18,7 +17,6 @@
#include "mojo/public/cpp/bindings/associated_group.h"
#include "mojo/public/cpp/bindings/associated_group_controller.h"
#include "mojo/public/cpp/bindings/interface_endpoint_controller.h"
-#include "mojo/public/cpp/bindings/lib/validation_util.h"
#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
namespace mojo {
@@ -47,7 +45,7 @@
task_runner_(std::move(runner)) {}
~ResponderThunk() override {
if (!accept_was_invoked_) {
- // The Service handled a message that was expecting a response
+ // The Mojo application handled a message that was expecting a response
// but did not send a response.
// We raise an error to signal the calling application that an error
// condition occurred. Without this the calling application would have no
@@ -134,47 +132,47 @@
InterfaceEndpointClient::InterfaceEndpointClient(
ScopedInterfaceEndpointHandle handle,
MessageReceiverWithResponderStatus* receiver,
- std::unique_ptr<MessageReceiver> payload_validator,
+ std::unique_ptr<MessageFilter> payload_validator,
bool expect_sync_requests,
- scoped_refptr<base::SingleThreadTaskRunner> runner,
- uint32_t interface_version)
- : expect_sync_requests_(expect_sync_requests),
- handle_(std::move(handle)),
+ scoped_refptr<base::SingleThreadTaskRunner> runner)
+ : handle_(std::move(handle)),
incoming_receiver_(receiver),
+ payload_validator_(std::move(payload_validator)),
thunk_(this),
- filters_(&thunk_),
+ next_request_id_(1),
+ encountered_error_(false),
task_runner_(std::move(runner)),
- control_message_proxy_(this),
- control_message_handler_(interface_version),
weak_ptr_factory_(this) {
DCHECK(handle_.is_valid());
+ DCHECK(handle_.is_local());
// TODO(yzshen): the way to use validator (or message filter in general)
// directly is a little awkward.
- if (payload_validator)
- filters_.Append(std::move(payload_validator));
+ payload_validator_->set_sink(&thunk_);
- if (handle_.pending_association()) {
- handle_.SetAssociationEventHandler(base::Bind(
- &InterfaceEndpointClient::OnAssociationEvent, base::Unretained(this)));
- } else {
- InitControllerIfNecessary();
- }
+ controller_ = handle_.group_controller()->AttachEndpointClient(
+ handle_, this, task_runner_);
+ if (expect_sync_requests)
+ controller_->AllowWokenUpBySyncWatchOnSameThread();
}
InterfaceEndpointClient::~InterfaceEndpointClient() {
DCHECK(thread_checker_.CalledOnValidThread());
- if (controller_)
- handle_.group_controller()->DetachEndpointClient(handle_);
+ handle_.group_controller()->DetachEndpointClient(handle_);
}
AssociatedGroup* InterfaceEndpointClient::associated_group() {
if (!associated_group_)
- associated_group_ = base::MakeUnique<AssociatedGroup>(handle_);
+ associated_group_ = handle_.group_controller()->CreateAssociatedGroup();
return associated_group_.get();
}
+uint32_t InterfaceEndpointClient::interface_id() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return handle_.id();
+}
+
ScopedInterfaceEndpointHandle InterfaceEndpointClient::PassHandle() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!has_pending_responders());
@@ -182,73 +180,38 @@
if (!handle_.is_valid())
return ScopedInterfaceEndpointHandle();
- handle_.SetAssociationEventHandler(
- ScopedInterfaceEndpointHandle::AssociationEventCallback());
-
- if (controller_) {
- controller_ = nullptr;
- handle_.group_controller()->DetachEndpointClient(handle_);
- }
+ controller_ = nullptr;
+ handle_.group_controller()->DetachEndpointClient(handle_);
return std::move(handle_);
}
-void InterfaceEndpointClient::AddFilter(
- std::unique_ptr<MessageReceiver> filter) {
- filters_.Append(std::move(filter));
-}
-
void InterfaceEndpointClient::RaiseError() {
DCHECK(thread_checker_.CalledOnValidThread());
- if (!handle_.pending_association())
- handle_.group_controller()->RaiseError();
-}
-
-void InterfaceEndpointClient::CloseWithReason(uint32_t custom_reason,
- const std::string& description) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- auto handle = PassHandle();
- handle.ResetWithReason(custom_reason, description);
+ handle_.group_controller()->RaiseError();
}
bool InterfaceEndpointClient::Accept(Message* message) {
DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(controller_);
DCHECK(!message->has_flag(Message::kFlagExpectsResponse));
- DCHECK(!handle_.pending_association());
-
- // This has to been done even if connection error has occurred. For example,
- // the message contains a pending associated request. The user may try to use
- // the corresponding associated interface pointer after sending this message.
- // That associated interface pointer has to join an associated group in order
- // to work properly.
- if (!message->associated_endpoint_handles()->empty())
- message->SerializeAssociatedEndpointHandles(handle_.group_controller());
if (encountered_error_)
return false;
- InitControllerIfNecessary();
-
return controller_->SendMessage(message);
}
bool InterfaceEndpointClient::AcceptWithResponder(Message* message,
MessageReceiver* responder) {
DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(controller_);
DCHECK(message->has_flag(Message::kFlagExpectsResponse));
- DCHECK(!handle_.pending_association());
-
- // Please see comments in Accept().
- if (!message->associated_endpoint_handles()->empty())
- message->SerializeAssociatedEndpointHandles(handle_.group_controller());
if (encountered_error_)
return false;
- InitControllerIfNecessary();
-
// Reserve 0 in case we want it to convey special meaning in the future.
uint64_t request_id = next_request_id_++;
if (request_id == 0)
@@ -271,18 +234,20 @@
bool response_received = false;
std::unique_ptr<MessageReceiver> sync_responder(responder);
sync_responses_.insert(std::make_pair(
- request_id, base::MakeUnique<SyncResponseInfo>(&response_received)));
+ request_id, base::WrapUnique(new SyncResponseInfo(&response_received))));
base::WeakPtr<InterfaceEndpointClient> weak_self =
weak_ptr_factory_.GetWeakPtr();
controller_->SyncWatch(&response_received);
// Make sure that this instance hasn't been destroyed.
if (weak_self) {
- DCHECK(base::ContainsKey(sync_responses_, request_id));
+ DCHECK(ContainsKey(sync_responses_, request_id));
auto iter = sync_responses_.find(request_id);
DCHECK_EQ(&response_received, iter->second->response_received);
- if (response_received)
- ignore_result(sync_responder->Accept(&iter->second->response));
+ if (response_received) {
+ std::unique_ptr<Message> response = std::move(iter->second->response);
+ ignore_result(sync_responder->Accept(response.get()));
+ }
sync_responses_.erase(iter);
}
@@ -292,97 +257,30 @@
bool InterfaceEndpointClient::HandleIncomingMessage(Message* message) {
DCHECK(thread_checker_.CalledOnValidThread());
- return filters_.Accept(message);
+
+ return payload_validator_->Accept(message);
}
-void InterfaceEndpointClient::NotifyError(
- const base::Optional<DisconnectReason>& reason) {
+void InterfaceEndpointClient::NotifyError() {
DCHECK(thread_checker_.CalledOnValidThread());
if (encountered_error_)
return;
encountered_error_ = true;
-
- // Response callbacks may hold on to resource, and there's no need to keep
- // them alive any longer. Note that it's allowed that a pending response
- // callback may own this endpoint, so we simply move the responders onto the
- // stack here and let them be destroyed when the stack unwinds.
- AsyncResponderMap responders = std::move(async_responders_);
-
- control_message_proxy_.OnConnectionError();
-
- if (!error_handler_.is_null()) {
- base::Closure error_handler = std::move(error_handler_);
- error_handler.Run();
- } else if (!error_with_reason_handler_.is_null()) {
- ConnectionErrorWithReasonCallback error_with_reason_handler =
- std::move(error_with_reason_handler_);
- if (reason) {
- error_with_reason_handler.Run(reason->custom_reason, reason->description);
- } else {
- error_with_reason_handler.Run(0, std::string());
- }
- }
-}
-
-void InterfaceEndpointClient::QueryVersion(
- const base::Callback<void(uint32_t)>& callback) {
- control_message_proxy_.QueryVersion(callback);
-}
-
-void InterfaceEndpointClient::RequireVersion(uint32_t version) {
- control_message_proxy_.RequireVersion(version);
-}
-
-void InterfaceEndpointClient::FlushForTesting() {
- control_message_proxy_.FlushForTesting();
-}
-
-void InterfaceEndpointClient::InitControllerIfNecessary() {
- if (controller_ || handle_.pending_association())
- return;
-
- controller_ = handle_.group_controller()->AttachEndpointClient(handle_, this,
- task_runner_);
- if (expect_sync_requests_)
- controller_->AllowWokenUpBySyncWatchOnSameThread();
-}
-
-void InterfaceEndpointClient::OnAssociationEvent(
- ScopedInterfaceEndpointHandle::AssociationEvent event) {
- if (event == ScopedInterfaceEndpointHandle::ASSOCIATED) {
- InitControllerIfNecessary();
- } else if (event ==
- ScopedInterfaceEndpointHandle::PEER_CLOSED_BEFORE_ASSOCIATION) {
- task_runner_->PostTask(FROM_HERE,
- base::Bind(&InterfaceEndpointClient::NotifyError,
- weak_ptr_factory_.GetWeakPtr(),
- handle_.disconnect_reason()));
- }
+ if (!error_handler_.is_null())
+ error_handler_.Run();
}
bool InterfaceEndpointClient::HandleValidatedMessage(Message* message) {
DCHECK_EQ(handle_.id(), message->interface_id());
- if (encountered_error_) {
- // This message is received after error has been encountered. For associated
- // interfaces, this means the remote side sends a
- // PeerAssociatedEndpointClosed event but continues to send more messages
- // for the same interface. Close the pipe because this shouldn't happen.
- DVLOG(1) << "A message is received for an interface after it has been "
- << "disconnected. Closing the pipe.";
- return false;
- }
-
if (message->has_flag(Message::kFlagExpectsResponse)) {
+ if (!incoming_receiver_)
+ return false;
+
MessageReceiverWithStatus* responder =
new ResponderThunk(weak_ptr_factory_.GetWeakPtr(), task_runner_);
- bool ok = false;
- if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) {
- ok = control_message_handler_.AcceptWithResponder(message, responder);
- } else {
- ok = incoming_receiver_->AcceptWithResponder(message, responder);
- }
+ bool ok = incoming_receiver_->AcceptWithResponder(message, responder);
if (!ok)
delete responder;
return ok;
@@ -393,7 +291,8 @@
auto it = sync_responses_.find(request_id);
if (it == sync_responses_.end())
return false;
- it->second->response = std::move(*message);
+ it->second->response.reset(new Message());
+ message->MoveTo(it->second->response.get());
*it->second->response_received = true;
return true;
}
@@ -405,8 +304,8 @@
async_responders_.erase(it);
return responder->Accept(message);
} else {
- if (mojo::internal::ControlMessageHandler::IsControlMessage(message))
- return control_message_handler_.Accept(message);
+ if (!incoming_receiver_)
+ return false;
return incoming_receiver_->Accept(message);
}
diff --git a/mojo/public/cpp/bindings/lib/interface_ptr_state.h b/mojo/public/cpp/bindings/lib/interface_ptr_state.h
index 8f5b4ff..584933e 100644
--- a/mojo/public/cpp/bindings/lib/interface_ptr_state.h
+++ b/mojo/public/cpp/bindings/lib/interface_ptr_state.h
@@ -9,7 +9,6 @@
#include <algorithm> // For |std::swap()|.
#include <memory>
-#include <string>
#include <utility>
#include "base/bind.h"
@@ -20,20 +19,174 @@
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "mojo/public/cpp/bindings/associated_group.h"
-#include "mojo/public/cpp/bindings/connection_error_callback.h"
-#include "mojo/public/cpp/bindings/filter_chain.h"
#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
#include "mojo/public/cpp/bindings/interface_id.h"
#include "mojo/public/cpp/bindings/interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/lib/control_message_proxy.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
#include "mojo/public/cpp/bindings/lib/multiplex_router.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
#include "mojo/public/cpp/bindings/message_header_validator.h"
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
namespace mojo {
namespace internal {
+template <typename Interface, bool use_multiplex_router>
+class InterfacePtrState;
+
+// Uses a single-threaded, dedicated router. If |Interface| doesn't have any
+// methods to pass associated interface pointers or requests, there won't be
+// multiple interfaces running on the underlying message pipe. In that case, we
+// can use this specialization to reduce cost.
template <typename Interface>
-class InterfacePtrState {
+class InterfacePtrState<Interface, false> {
+ public:
+ InterfacePtrState() : proxy_(nullptr), router_(nullptr), version_(0u) {}
+
+ ~InterfacePtrState() {
+ // Destruction order matters here. We delete |proxy_| first, even though
+ // |router_| may have a reference to it, so that destructors for any request
+ // callbacks still pending can interact with the InterfacePtr.
+ delete proxy_;
+ delete router_;
+ }
+
+ Interface* instance() {
+ ConfigureProxyIfNecessary();
+
+ // This will be null if the object is not bound.
+ return proxy_;
+ }
+
+ uint32_t version() const { return version_; }
+
+ void QueryVersion(const base::Callback<void(uint32_t)>& callback) {
+ ConfigureProxyIfNecessary();
+
+ // Do a static cast in case the interface contains methods with the same
+ // name. It is safe to capture |this| because the callback won't be run
+ // after this object goes away.
+ static_cast<ControlMessageProxy*>(proxy_)->QueryVersion(
+ base::Bind(&InterfacePtrState::OnQueryVersion, base::Unretained(this),
+ callback));
+ }
+
+ void RequireVersion(uint32_t version) {
+ ConfigureProxyIfNecessary();
+
+ if (version <= version_)
+ return;
+
+ version_ = version;
+ // Do a static cast in case the interface contains methods with the same
+ // name.
+ static_cast<ControlMessageProxy*>(proxy_)->RequireVersion(version);
+ }
+
+ void Swap(InterfacePtrState* other) {
+ using std::swap;
+ swap(other->proxy_, proxy_);
+ swap(other->router_, router_);
+ handle_.swap(other->handle_);
+ runner_.swap(other->runner_);
+ swap(other->version_, version_);
+ }
+
+ void Bind(InterfacePtrInfo<Interface> info,
+ scoped_refptr<base::SingleThreadTaskRunner> runner) {
+ DCHECK(!proxy_);
+ DCHECK(!router_);
+ DCHECK(!handle_.is_valid());
+ DCHECK_EQ(0u, version_);
+ DCHECK(info.is_valid());
+
+ handle_ = info.PassHandle();
+ version_ = info.version();
+ runner_ = std::move(runner);
+ }
+
+ bool HasAssociatedInterfaces() const { return false; }
+
+ // After this method is called, the object is in an invalid state and
+ // shouldn't be reused.
+ InterfacePtrInfo<Interface> PassInterface() {
+ return InterfacePtrInfo<Interface>(
+ router_ ? router_->PassMessagePipe() : std::move(handle_), version_);
+ }
+
+ bool is_bound() const { return handle_.is_valid() || router_; }
+
+ bool encountered_error() const {
+ return router_ ? router_->encountered_error() : false;
+ }
+
+ void set_connection_error_handler(const base::Closure& error_handler) {
+ ConfigureProxyIfNecessary();
+
+ DCHECK(router_);
+ router_->set_connection_error_handler(error_handler);
+ }
+
+ // Returns true if bound and awaiting a response to a message.
+ bool has_pending_callbacks() const {
+ return router_ && router_->has_pending_responders();
+ }
+
+ AssociatedGroup* associated_group() { return nullptr; }
+
+ void EnableTestingMode() {
+ ConfigureProxyIfNecessary();
+ router_->EnableTestingMode();
+ }
+
+ private:
+ using Proxy = typename Interface::Proxy_;
+
+ void ConfigureProxyIfNecessary() {
+ // The proxy has been configured.
+ if (proxy_) {
+ DCHECK(router_);
+ return;
+ }
+ // The object hasn't been bound.
+ if (!handle_.is_valid())
+ return;
+
+ FilterChain filters;
+ filters.Append<MessageHeaderValidator>(Interface::Name_);
+ filters.Append<typename Interface::ResponseValidator_>();
+
+ router_ = new Router(std::move(handle_), std::move(filters), false,
+ std::move(runner_));
+
+ proxy_ = new Proxy(router_);
+ }
+
+ void OnQueryVersion(const base::Callback<void(uint32_t)>& callback,
+ uint32_t version) {
+ version_ = version;
+ callback.Run(version);
+ }
+
+ Proxy* proxy_;
+ Router* router_;
+
+ // |proxy_| and |router_| are not initialized until read/write with the
+ // message pipe handle is needed. |handle_| is valid between the Bind() call
+ // and the initialization of |proxy_| and |router_|.
+ ScopedMessagePipeHandle handle_;
+ scoped_refptr<base::SingleThreadTaskRunner> runner_;
+
+ uint32_t version_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterfacePtrState);
+};
+
+// Uses a multiplexing router. If |Interface| has methods to pass associated
+// interface pointers or requests, this specialization should be used.
+template <typename Interface>
+class InterfacePtrState<Interface, true> {
public:
InterfacePtrState() : version_(0u) {}
@@ -56,10 +209,13 @@
void QueryVersion(const base::Callback<void(uint32_t)>& callback) {
ConfigureProxyIfNecessary();
- // It is safe to capture |this| because the callback won't be run after this
- // object goes away.
- endpoint_client_->QueryVersion(base::Bind(
- &InterfacePtrState::OnQueryVersion, base::Unretained(this), callback));
+
+ // Do a static cast in case the interface contains methods with the same
+ // name. It is safe to capture |this| because the callback won't be run
+ // after this object goes away.
+ static_cast<ControlMessageProxy*>(proxy_.get())->QueryVersion(
+ base::Bind(&InterfacePtrState::OnQueryVersion, base::Unretained(this),
+ callback));
}
void RequireVersion(uint32_t version) {
@@ -69,17 +225,9 @@
return;
version_ = version;
- endpoint_client_->RequireVersion(version);
- }
-
- void FlushForTesting() {
- ConfigureProxyIfNecessary();
- endpoint_client_->FlushForTesting();
- }
-
- void CloseWithReason(uint32_t custom_reason, const std::string& description) {
- ConfigureProxyIfNecessary();
- endpoint_client_->CloseWithReason(custom_reason, description);
+ // Do a static cast in case the interface contains methods with the same
+ // name.
+ static_cast<ControlMessageProxy*>(proxy_.get())->RequireVersion(version);
}
void Swap(InterfacePtrState* other) {
@@ -132,14 +280,6 @@
endpoint_client_->set_connection_error_handler(error_handler);
}
- void set_connection_error_with_reason_handler(
- const ConnectionErrorWithReasonCallback& error_handler) {
- ConfigureProxyIfNecessary();
-
- DCHECK(endpoint_client_);
- endpoint_client_->set_connection_error_with_reason_handler(error_handler);
- }
-
// Returns true if bound and awaiting a response to a message.
bool has_pending_callbacks() const {
return endpoint_client_ && endpoint_client_->has_pending_responders();
@@ -155,17 +295,6 @@
router_->EnableTestingMode();
}
- void ForwardMessage(Message message) {
- ConfigureProxyIfNecessary();
- endpoint_client_->Accept(&message);
- }
-
- void ForwardMessageWithResponder(Message message,
- std::unique_ptr<MessageReceiver> responder) {
- ConfigureProxyIfNecessary();
- endpoint_client_->AcceptWithResponder(&message, responder.release());
- }
-
private:
using Proxy = typename Interface::Proxy_;
@@ -180,22 +309,15 @@
if (!handle_.is_valid())
return;
- MultiplexRouter::Config config =
- Interface::PassesAssociatedKinds_
- ? MultiplexRouter::MULTI_INTERFACE
- : (Interface::HasSyncMethods_
- ? MultiplexRouter::SINGLE_INTERFACE_WITH_SYNC_METHODS
- : MultiplexRouter::SINGLE_INTERFACE);
- router_ = new MultiplexRouter(std::move(handle_), config, true, runner_);
+ router_ = new MultiplexRouter(true, std::move(handle_), runner_);
router_->SetMasterInterfaceName(Interface::Name_);
endpoint_client_.reset(new InterfaceEndpointClient(
router_->CreateLocalEndpointHandle(kMasterInterfaceId), nullptr,
base::WrapUnique(new typename Interface::ResponseValidator_()), false,
- std::move(runner_),
- // The version is only queried from the client so the value passed here
- // will not be used.
- 0u));
+ std::move(runner_)));
proxy_.reset(new Proxy(endpoint_client_.get()));
+ proxy_->serialization_context()->group_controller =
+ endpoint_client_->group_controller();
}
void OnQueryVersion(const base::Callback<void(uint32_t)>& callback,
diff --git a/mojo/public/cpp/bindings/lib/map_serialization.h b/mojo/public/cpp/bindings/lib/map_serialization.h
index 718a763..c28b835 100644
--- a/mojo/public/cpp/bindings/lib/map_serialization.h
+++ b/mojo/public/cpp/bindings/lib/map_serialization.h
@@ -8,11 +8,11 @@
#include <type_traits>
#include <vector>
-#include "mojo/public/cpp/bindings/array_data_view.h"
+#include "mojo/public/cpp/bindings/array.h"
#include "mojo/public/cpp/bindings/lib/array_serialization.h"
#include "mojo/public/cpp/bindings/lib/map_data_internal.h"
#include "mojo/public/cpp/bindings/lib/serialization_forward.h"
-#include "mojo/public/cpp/bindings/map_data_view.h"
+#include "mojo/public/cpp/bindings/map.h"
namespace mojo {
namespace internal {
@@ -46,15 +46,12 @@
public:
using Base = MapReaderBase<MaybeConstUserType>;
using Traits = typename Base::Traits;
- using MaybeConstIterator = typename Base::MaybeConstIterator;
explicit MapKeyReader(MaybeConstUserType& input) : Base(input) {}
~MapKeyReader() {}
- using GetNextResult =
- decltype(Traits::GetKey(std::declval<MaybeConstIterator&>()));
- GetNextResult GetNext() {
- GetNextResult key = Traits::GetKey(this->iter_);
+ const typename Traits::Key& GetNext() {
+ const typename Traits::Key& key = Traits::GetKey(this->iter_);
Traits::AdvanceIterator(this->iter_);
return key;
}
@@ -81,17 +78,17 @@
};
template <typename Key, typename Value, typename MaybeConstUserType>
-struct Serializer<MapDataView<Key, Value>, MaybeConstUserType> {
+struct Serializer<Map<Key, Value>, MaybeConstUserType> {
using UserType = typename std::remove_const<MaybeConstUserType>::type;
using Traits = MapTraits<UserType>;
using UserKey = typename Traits::Key;
using UserValue = typename Traits::Value;
- using Data = typename MojomTypeTraits<MapDataView<Key, Value>>::Data;
- using KeyArraySerializer = ArraySerializer<ArrayDataView<Key>,
+ using Data = typename MojomTypeTraits<Map<Key, Value>>::Data;
+ using KeyArraySerializer = ArraySerializer<Array<Key>,
std::vector<UserKey>,
MapKeyReader<MaybeConstUserType>>;
using ValueArraySerializer =
- ArraySerializer<ArrayDataView<Value>,
+ ArraySerializer<Array<Value>,
std::vector<UserValue>,
MapValueReader<MaybeConstUserType>>;
@@ -125,8 +122,8 @@
auto result = Data::New(buf);
if (result) {
- auto keys_ptr = MojomTypeTraits<ArrayDataView<Key>>::Data::New(
- Traits::GetSize(input), buf);
+ auto keys_ptr =
+ MojomTypeTraits<Array<Key>>::Data::New(Traits::GetSize(input), buf);
if (keys_ptr) {
MapKeyReader<MaybeConstUserType> key_reader(input);
KeyArraySerializer::SerializeElements(
@@ -135,8 +132,8 @@
result->keys.Set(keys_ptr);
}
- auto values_ptr = MojomTypeTraits<ArrayDataView<Value>>::Data::New(
- Traits::GetSize(input), buf);
+ auto values_ptr =
+ MojomTypeTraits<Array<Value>>::Data::New(Traits::GetSize(input), buf);
if (values_ptr) {
MapValueReader<MaybeConstUserType> value_reader(input);
ValueArraySerializer::SerializeElements(
diff --git a/mojo/public/cpp/bindings/lib/may_auto_lock.h b/mojo/public/cpp/bindings/lib/may_auto_lock.h
deleted file mode 100644
index 06091fe..0000000
--- a/mojo/public/cpp/bindings/lib/may_auto_lock.h
+++ /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.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MAY_AUTO_LOCK_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MAY_AUTO_LOCK_H_
-
-#include "base/macros.h"
-#include "base/optional.h"
-#include "base/synchronization/lock.h"
-
-namespace mojo {
-namespace internal {
-
-// Similar to base::AutoLock, except that it does nothing if |lock| passed into
-// the constructor is null.
-class MayAutoLock {
- public:
- explicit MayAutoLock(base::Optional<base::Lock>* lock)
- : lock_(lock->has_value() ? &lock->value() : nullptr) {
- if (lock_)
- lock_->Acquire();
- }
-
- ~MayAutoLock() {
- if (lock_) {
- lock_->AssertAcquired();
- lock_->Release();
- }
- }
-
- private:
- base::Lock* lock_;
- DISALLOW_COPY_AND_ASSIGN(MayAutoLock);
-};
-
-// Similar to base::AutoUnlock, except that it does nothing if |lock| passed
-// into the constructor is null.
-class MayAutoUnlock {
- public:
- explicit MayAutoUnlock(base::Optional<base::Lock>* lock)
- : lock_(lock->has_value() ? &lock->value() : nullptr) {
- if (lock_) {
- lock_->AssertAcquired();
- lock_->Release();
- }
- }
-
- ~MayAutoUnlock() {
- if (lock_)
- lock_->Acquire();
- }
-
- private:
- base::Lock* lock_;
- DISALLOW_COPY_AND_ASSIGN(MayAutoUnlock);
-};
-
-} // namespace internal
-} // namespace mojo
-
-#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MAY_AUTO_LOCK_H_
diff --git a/mojo/public/cpp/bindings/lib/message.cc b/mojo/public/cpp/bindings/lib/message.cc
index e5f3808..939e064 100644
--- a/mojo/public/cpp/bindings/lib/message.cc
+++ b/mojo/public/cpp/bindings/lib/message.cc
@@ -11,58 +11,18 @@
#include <algorithm>
#include <utility>
-#include "base/bind.h"
-#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
-#include "base/threading/thread_local.h"
-#include "mojo/public/cpp/bindings/associated_group_controller.h"
-#include "mojo/public/cpp/bindings/lib/array_internal.h"
namespace mojo {
-namespace {
-
-base::LazyInstance<base::ThreadLocalPointer<internal::MessageDispatchContext>>::
- DestructorAtExit g_tls_message_dispatch_context = LAZY_INSTANCE_INITIALIZER;
-
-base::LazyInstance<base::ThreadLocalPointer<SyncMessageResponseContext>>::
- DestructorAtExit g_tls_sync_response_context = LAZY_INSTANCE_INITIALIZER;
-
-void DoNotifyBadMessage(Message message, const std::string& error) {
- message.NotifyBadMessage(error);
-}
-
-} // namespace
-
Message::Message() {
}
-Message::Message(Message&& other)
- : buffer_(std::move(other.buffer_)),
- handles_(std::move(other.handles_)),
- associated_endpoint_handles_(
- std::move(other.associated_endpoint_handles_)) {}
-
Message::~Message() {
CloseHandles();
}
-Message& Message::operator=(Message&& other) {
- Reset();
- std::swap(other.buffer_, buffer_);
- std::swap(other.handles_, handles_);
- std::swap(other.associated_endpoint_handles_, associated_endpoint_handles_);
- return *this;
-}
-
-void Message::Reset() {
- CloseHandles();
- handles_.clear();
- associated_endpoint_handles_.clear();
- buffer_.reset();
-}
-
void Message::Initialize(size_t capacity, bool zero_initialized) {
DCHECK(!buffer_);
buffer_.reset(new internal::MessageBuffer(capacity, zero_initialized));
@@ -76,52 +36,19 @@
handles_.swap(*handles);
}
-const uint8_t* Message::payload() const {
- if (version() < 2)
- return data() + header()->num_bytes;
+void Message::MoveTo(Message* destination) {
+ DCHECK(this != destination);
- return static_cast<const uint8_t*>(header_v2()->payload.Get());
-}
+ // No copy needed.
+ std::swap(destination->buffer_, buffer_);
+ std::swap(destination->handles_, handles_);
-uint32_t Message::payload_num_bytes() const {
- DCHECK_GE(data_num_bytes(), header()->num_bytes);
- size_t num_bytes;
- if (version() < 2) {
- num_bytes = data_num_bytes() - header()->num_bytes;
- } else {
- auto payload = reinterpret_cast<uintptr_t>(header_v2()->payload.Get());
- if (!payload) {
- num_bytes = 0;
- } else {
- auto payload_end =
- reinterpret_cast<uintptr_t>(header_v2()->payload_interface_ids.Get());
- if (!payload_end)
- payload_end = reinterpret_cast<uintptr_t>(data() + data_num_bytes());
- DCHECK_GE(payload_end, payload);
- num_bytes = payload_end - payload;
- }
- }
- DCHECK_LE(num_bytes, std::numeric_limits<uint32_t>::max());
- return static_cast<uint32_t>(num_bytes);
-}
-
-uint32_t Message::payload_num_interface_ids() const {
- auto* array_pointer =
- version() < 2 ? nullptr : header_v2()->payload_interface_ids.Get();
- return array_pointer ? static_cast<uint32_t>(array_pointer->size()) : 0;
-}
-
-const uint32_t* Message::payload_interface_ids() const {
- auto* array_pointer =
- version() < 2 ? nullptr : header_v2()->payload_interface_ids.Get();
- return array_pointer ? array_pointer->storage() : nullptr;
+ CloseHandles();
+ handles_.clear();
+ buffer_.reset();
}
ScopedMessageHandle Message::TakeMojoMessage() {
- // If there are associated endpoints transferred,
- // SerializeAssociatedEndpointHandles() must be called before this method.
- DCHECK(associated_endpoint_handles_.empty());
-
if (handles_.empty()) // Fast path for the common case: No handles.
return buffer_->TakeMessage();
@@ -153,7 +80,6 @@
}
void Message::NotifyBadMessage(const std::string& error) {
- DCHECK(buffer_);
buffer_->NotifyBadMessage(error);
}
@@ -165,88 +91,6 @@
}
}
-void Message::SerializeAssociatedEndpointHandles(
- AssociatedGroupController* group_controller) {
- if (associated_endpoint_handles_.empty())
- return;
-
- DCHECK_GE(version(), 2u);
- DCHECK(header_v2()->payload_interface_ids.is_null());
-
- size_t size = associated_endpoint_handles_.size();
- auto* data = internal::Array_Data<uint32_t>::New(size, buffer());
- header_v2()->payload_interface_ids.Set(data);
-
- for (size_t i = 0; i < size; ++i) {
- ScopedInterfaceEndpointHandle& handle = associated_endpoint_handles_[i];
-
- DCHECK(handle.pending_association());
- data->storage()[i] =
- group_controller->AssociateInterface(std::move(handle));
- }
- associated_endpoint_handles_.clear();
-}
-
-bool Message::DeserializeAssociatedEndpointHandles(
- AssociatedGroupController* group_controller) {
- associated_endpoint_handles_.clear();
-
- uint32_t num_ids = payload_num_interface_ids();
- if (num_ids == 0)
- return true;
-
- associated_endpoint_handles_.reserve(num_ids);
- uint32_t* ids = header_v2()->payload_interface_ids.Get()->storage();
- bool result = true;
- for (uint32_t i = 0; i < num_ids; ++i) {
- auto handle = group_controller->CreateLocalEndpointHandle(ids[i]);
- if (IsValidInterfaceId(ids[i]) && !handle.is_valid()) {
- // |ids[i]| itself is valid but handle creation failed. In that case, mark
- // deserialization as failed but continue to deserialize the rest of
- // handles.
- result = false;
- }
-
- associated_endpoint_handles_.push_back(std::move(handle));
- ids[i] = kInvalidInterfaceId;
- }
- return result;
-}
-
-PassThroughFilter::PassThroughFilter() {}
-
-PassThroughFilter::~PassThroughFilter() {}
-
-bool PassThroughFilter::Accept(Message* message) { return true; }
-
-SyncMessageResponseContext::SyncMessageResponseContext()
- : outer_context_(current()) {
- g_tls_sync_response_context.Get().Set(this);
-}
-
-SyncMessageResponseContext::~SyncMessageResponseContext() {
- DCHECK_EQ(current(), this);
- g_tls_sync_response_context.Get().Set(outer_context_);
-}
-
-// static
-SyncMessageResponseContext* SyncMessageResponseContext::current() {
- return g_tls_sync_response_context.Get().Get();
-}
-
-void SyncMessageResponseContext::ReportBadMessage(const std::string& error) {
- GetBadMessageCallback().Run(error);
-}
-
-const ReportBadMessageCallback&
-SyncMessageResponseContext::GetBadMessageCallback() {
- if (bad_message_callback_.is_null()) {
- bad_message_callback_ =
- base::Bind(&DoNotifyBadMessage, base::Passed(&response_));
- }
- return bad_message_callback_;
-}
-
MojoResult ReadMessage(MessagePipeHandle handle, Message* message) {
MojoResult rv;
@@ -278,55 +122,4 @@
return MOJO_RESULT_OK;
}
-void ReportBadMessage(const std::string& error) {
- internal::MessageDispatchContext* context =
- internal::MessageDispatchContext::current();
- DCHECK(context);
- context->GetBadMessageCallback().Run(error);
-}
-
-ReportBadMessageCallback GetBadMessageCallback() {
- internal::MessageDispatchContext* context =
- internal::MessageDispatchContext::current();
- DCHECK(context);
- return context->GetBadMessageCallback();
-}
-
-namespace internal {
-
-MessageHeaderV2::MessageHeaderV2() = default;
-
-MessageDispatchContext::MessageDispatchContext(Message* message)
- : outer_context_(current()), message_(message) {
- g_tls_message_dispatch_context.Get().Set(this);
-}
-
-MessageDispatchContext::~MessageDispatchContext() {
- DCHECK_EQ(current(), this);
- g_tls_message_dispatch_context.Get().Set(outer_context_);
-}
-
-// static
-MessageDispatchContext* MessageDispatchContext::current() {
- return g_tls_message_dispatch_context.Get().Get();
-}
-
-const ReportBadMessageCallback&
-MessageDispatchContext::GetBadMessageCallback() {
- if (bad_message_callback_.is_null()) {
- bad_message_callback_ =
- base::Bind(&DoNotifyBadMessage, base::Passed(message_));
- }
- return bad_message_callback_;
-}
-
-// static
-void SyncMessageResponseSetup::SetCurrentSyncResponseMessage(Message* message) {
- SyncMessageResponseContext* context = SyncMessageResponseContext::current();
- if (context)
- context->response_ = std::move(*message);
-}
-
-} // namespace internal
-
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_buffer.cc b/mojo/public/cpp/bindings/lib/message_buffer.cc
index cc12ef6..af79cfd 100644
--- a/mojo/public/cpp/bindings/lib/message_buffer.cc
+++ b/mojo/public/cpp/bindings/lib/message_buffer.cc
@@ -13,35 +13,54 @@
MessageBuffer::MessageBuffer(size_t capacity, bool zero_initialized) {
DCHECK_LE(capacity, std::numeric_limits<uint32_t>::max());
+ data_num_bytes_ = static_cast<uint32_t>(capacity);
MojoResult rv = AllocMessage(capacity, nullptr, 0,
MOJO_ALLOC_MESSAGE_FLAG_NONE, &message_);
CHECK_EQ(rv, MOJO_RESULT_OK);
- void* buffer = nullptr;
- if (capacity != 0) {
- rv = GetMessageBuffer(message_.get(), &buffer);
+ if (capacity == 0) {
+ buffer_ = nullptr;
+ } else {
+ rv = GetMessageBuffer(message_.get(), &buffer_);
CHECK_EQ(rv, MOJO_RESULT_OK);
if (zero_initialized)
- memset(buffer, 0, capacity);
+ memset(buffer_, 0, capacity);
}
- Initialize(buffer, capacity);
}
MessageBuffer::MessageBuffer(ScopedMessageHandle message, uint32_t num_bytes) {
message_ = std::move(message);
+ data_num_bytes_ = num_bytes;
- void* buffer = nullptr;
- if (num_bytes != 0) {
- MojoResult rv = GetMessageBuffer(message_.get(), &buffer);
+ if (num_bytes == 0) {
+ buffer_ = nullptr;
+ } else {
+ MojoResult rv = GetMessageBuffer(message_.get(), &buffer_);
CHECK_EQ(rv, MOJO_RESULT_OK);
}
- Initialize(buffer, num_bytes);
}
MessageBuffer::~MessageBuffer() {}
+void* MessageBuffer::Allocate(size_t delta) {
+ delta = internal::Align(delta);
+
+ DCHECK_LE(delta, static_cast<size_t>(data_num_bytes_));
+ DCHECK_GT(bytes_claimed_ + static_cast<uint32_t>(delta), bytes_claimed_);
+
+ uint32_t new_bytes_claimed = bytes_claimed_ + static_cast<uint32_t>(delta);
+ if (new_bytes_claimed > data_num_bytes_) {
+ NOTREACHED();
+ return nullptr;
+ }
+
+ char* start = static_cast<char*>(buffer_) + bytes_claimed_;
+ bytes_claimed_ = new_bytes_claimed;
+ return static_cast<void*>(start);
+}
+
void MessageBuffer::NotifyBadMessage(const std::string& error) {
DCHECK(message_.is_valid());
MojoResult result = mojo::NotifyBadMessage(message_.get(), error);
diff --git a/mojo/public/cpp/bindings/lib/message_buffer.h b/mojo/public/cpp/bindings/lib/message_buffer.h
index 96d5140..0382131 100644
--- a/mojo/public/cpp/bindings/lib/message_buffer.h
+++ b/mojo/public/cpp/bindings/lib/message_buffer.h
@@ -5,6 +5,7 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_LIB_MESSAGE_BUFFER_H_
#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_LIB_MESSAGE_BUFFER_H_
+#include <stddef.h>
#include <stdint.h>
#include <utility>
@@ -16,7 +17,7 @@
namespace mojo {
namespace internal {
-// A fixed-size Buffer using a Mojo message object for storage.
+// A fixed-size Buffer implementation using a Mojo message object for storage.
class MessageBuffer : public Buffer {
public:
// Initializes this buffer to carry a fixed byte capacity and no handles.
@@ -25,14 +26,24 @@
// Initializes this buffer from an existing Mojo MessageHandle.
MessageBuffer(ScopedMessageHandle message, uint32_t num_bytes);
- ~MessageBuffer();
+ ~MessageBuffer() override;
+
+ void* data() const { return buffer_; }
+ uint32_t data_num_bytes() const { return data_num_bytes_; }
+
+ // Buffer:
+ void* Allocate(size_t delta) override;
ScopedMessageHandle TakeMessage() { return std::move(message_); }
void NotifyBadMessage(const std::string& error);
private:
+ uint32_t data_num_bytes_ = 0;
ScopedMessageHandle message_;
+ void* buffer_;
+
+ uint32_t bytes_claimed_ = 0;
DISALLOW_COPY_AND_ASSIGN(MessageBuffer);
};
diff --git a/mojo/public/cpp/bindings/lib/message_builder.cc b/mojo/public/cpp/bindings/lib/message_builder.cc
index 6806a73..4ffa180 100644
--- a/mojo/public/cpp/bindings/lib/message_builder.cc
+++ b/mojo/public/cpp/bindings/lib/message_builder.cc
@@ -4,10 +4,11 @@
#include "mojo/public/cpp/bindings/lib/message_builder.h"
-#include "mojo/public/cpp/bindings/lib/array_internal.h"
-#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
-#include "mojo/public/cpp/bindings/lib/buffer.h"
-#include "mojo/public/cpp/bindings/lib/message_internal.h"
+#include <stddef.h>
+#include <stdint.h>
+
+#include "mojo/public/cpp/bindings/lib/serialization_util.h"
+#include "mojo/public/cpp/bindings/message.h"
namespace mojo {
namespace internal {
@@ -18,52 +19,38 @@
(*header)->num_bytes = sizeof(Header);
}
-MessageBuilder::MessageBuilder(uint32_t name,
- uint32_t flags,
- size_t payload_size,
- size_t payload_interface_id_count) {
- if (payload_interface_id_count > 0) {
- // Version 2
- InitializeMessage(
- sizeof(MessageHeaderV2) + Align(payload_size) +
- ArrayDataTraits<uint32_t>::GetStorageSize(
- static_cast<uint32_t>(payload_interface_id_count)));
+MessageBuilder::MessageBuilder(uint32_t name, size_t payload_size) {
+ InitializeMessage(sizeof(MessageHeader) + payload_size);
- MessageHeaderV2* header;
- Allocate(message_.buffer(), &header);
- header->version = 2;
- header->name = name;
- header->flags = flags;
- // The payload immediately follows the header.
- header->payload.Set(header + 1);
- } else if (flags &
- (Message::kFlagExpectsResponse | Message::kFlagIsResponse)) {
- // Version 1
- InitializeMessage(sizeof(MessageHeaderV1) + payload_size);
-
- MessageHeaderV1* header;
- Allocate(message_.buffer(), &header);
- header->version = 1;
- header->name = name;
- header->flags = flags;
- } else {
- InitializeMessage(sizeof(MessageHeader) + payload_size);
-
- MessageHeader* header;
- Allocate(message_.buffer(), &header);
- header->version = 0;
- header->name = name;
- header->flags = flags;
- }
+ MessageHeader* header;
+ Allocate(message_.buffer(), &header);
+ header->version = 0;
+ header->name = name;
}
MessageBuilder::~MessageBuilder() {
}
+MessageBuilder::MessageBuilder() {}
+
void MessageBuilder::InitializeMessage(size_t size) {
message_.Initialize(static_cast<uint32_t>(Align(size)),
true /* zero_initialized */);
}
+MessageWithRequestIDBuilder::MessageWithRequestIDBuilder(uint32_t name,
+ size_t payload_size,
+ uint32_t flags,
+ uint64_t request_id) {
+ InitializeMessage(sizeof(MessageHeaderWithRequestID) + payload_size);
+
+ MessageHeaderWithRequestID* header;
+ Allocate(message_.buffer(), &header);
+ header->version = 1;
+ header->name = name;
+ header->flags = flags;
+ header->request_id = request_id;
+}
+
} // namespace internal
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_builder.h b/mojo/public/cpp/bindings/lib/message_builder.h
index 8a4d5c4..a5a050f 100644
--- a/mojo/public/cpp/bindings/lib/message_builder.h
+++ b/mojo/public/cpp/bindings/lib/message_builder.h
@@ -8,30 +8,24 @@
#include <stddef.h>
#include <stdint.h>
-#include "base/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
+#include "mojo/public/cpp/bindings/lib/message_internal.h"
#include "mojo/public/cpp/bindings/message.h"
namespace mojo {
-
class Message;
namespace internal {
-class Buffer;
-
-class MOJO_CPP_BINDINGS_EXPORT MessageBuilder {
+class MessageBuilder {
public:
- MessageBuilder(uint32_t name,
- uint32_t flags,
- size_t payload_size,
- size_t payload_interface_id_count);
+ MessageBuilder(uint32_t name, size_t payload_size);
~MessageBuilder();
Buffer* buffer() { return message_.buffer(); }
Message* message() { return &message_; }
- private:
+ protected:
+ MessageBuilder();
void InitializeMessage(size_t size);
Message message_;
@@ -39,6 +33,51 @@
DISALLOW_COPY_AND_ASSIGN(MessageBuilder);
};
+class MessageWithRequestIDBuilder : public MessageBuilder {
+ public:
+ MessageWithRequestIDBuilder(uint32_t name,
+ size_t payload_size,
+ uint32_t flags,
+ uint64_t request_id);
+};
+
+class RequestMessageBuilder : public MessageWithRequestIDBuilder {
+ public:
+ RequestMessageBuilder(uint32_t name, size_t payload_size)
+ : MessageWithRequestIDBuilder(name,
+ payload_size,
+ Message::kFlagExpectsResponse,
+ 0) {}
+
+ RequestMessageBuilder(uint32_t name,
+ size_t payload_size,
+ uint32_t extra_flags)
+ : MessageWithRequestIDBuilder(name,
+ payload_size,
+ Message::kFlagExpectsResponse | extra_flags,
+ 0) {}
+};
+
+class ResponseMessageBuilder : public MessageWithRequestIDBuilder {
+ public:
+ ResponseMessageBuilder(uint32_t name,
+ size_t payload_size,
+ uint64_t request_id)
+ : MessageWithRequestIDBuilder(name,
+ payload_size,
+ Message::kFlagIsResponse,
+ request_id) {}
+
+ ResponseMessageBuilder(uint32_t name,
+ size_t payload_size,
+ uint64_t request_id,
+ uint32_t extra_flags)
+ : MessageWithRequestIDBuilder(name,
+ payload_size,
+ Message::kFlagIsResponse | extra_flags,
+ request_id) {}
+};
+
} // namespace internal
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_filter.cc b/mojo/public/cpp/bindings/lib/message_filter.cc
new file mode 100644
index 0000000..b09f40d
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/message_filter.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/message_filter.h"
+
+namespace mojo {
+
+MessageFilter::MessageFilter(MessageReceiver* sink) : sink_(sink) {
+}
+
+MessageFilter::~MessageFilter() {
+}
+
+PassThroughFilter::PassThroughFilter(MessageReceiver* sink)
+ : MessageFilter(sink) {
+}
+
+bool PassThroughFilter::Accept(Message* message) {
+ return sink_->Accept(message);
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_header_validator.cc b/mojo/public/cpp/bindings/lib/message_header_validator.cc
index 9f8c627..10f7774 100644
--- a/mojo/public/cpp/bindings/lib/message_header_validator.cc
+++ b/mojo/public/cpp/bindings/lib/message_header_validator.cc
@@ -4,8 +4,6 @@
#include "mojo/public/cpp/bindings/message_header_validator.h"
-#include "mojo/public/cpp/bindings/lib/array_internal.h"
-#include "mojo/public/cpp/bindings/lib/validate_params.h"
#include "mojo/public/cpp/bindings/lib/validation_context.h"
#include "mojo/public/cpp/bindings/lib/validation_errors.h"
#include "mojo/public/cpp/bindings/lib/validation_util.h"
@@ -13,40 +11,40 @@
namespace mojo {
namespace {
-// TODO(yzshen): Define a mojom struct for message header and use the generated
-// validation and data view code.
bool IsValidMessageHeader(const internal::MessageHeader* header,
internal::ValidationContext* validation_context) {
// NOTE: Our goal is to preserve support for future extension of the message
// header. If we encounter fields we do not understand, we must ignore them.
// Extra validation of the struct header:
- do {
- if (header->version == 0) {
- if (header->num_bytes == sizeof(internal::MessageHeader))
- break;
- } else if (header->version == 1) {
- if (header->num_bytes == sizeof(internal::MessageHeaderV1))
- break;
- } else if (header->version == 2) {
- if (header->num_bytes == sizeof(internal::MessageHeaderV2))
- break;
- } else if (header->version > 2) {
- if (header->num_bytes >= sizeof(internal::MessageHeaderV2))
- break;
+ if (header->version == 0) {
+ if (header->num_bytes != sizeof(internal::MessageHeader)) {
+ internal::ReportValidationError(
+ validation_context,
+ internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+ return false;
}
- internal::ReportValidationError(
- validation_context,
- internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
- return false;
- } while (false);
+ } else if (header->version == 1) {
+ if (header->num_bytes != sizeof(internal::MessageHeaderWithRequestID)) {
+ internal::ReportValidationError(
+ validation_context,
+ internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+ return false;
+ }
+ } else if (header->version > 1) {
+ if (header->num_bytes < sizeof(internal::MessageHeaderWithRequestID)) {
+ internal::ReportValidationError(
+ validation_context,
+ internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER);
+ return false;
+ }
+ }
// Validate flags (allow unknown bits):
// These flags require a RequestID.
- constexpr uint32_t kRequestIdFlags =
- Message::kFlagExpectsResponse | Message::kFlagIsResponse;
- if (header->version == 0 && (header->flags & kRequestIdFlags)) {
+ if (header->version < 1 && ((header->flags & Message::kFlagExpectsResponse) ||
+ (header->flags & Message::kFlagIsResponse))) {
internal::ReportValidationError(
validation_context,
internal::VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID);
@@ -54,60 +52,25 @@
}
// These flags are mutually exclusive.
- if ((header->flags & kRequestIdFlags) == kRequestIdFlags) {
+ if ((header->flags & Message::kFlagExpectsResponse) &&
+ (header->flags & Message::kFlagIsResponse)) {
internal::ReportValidationError(
validation_context,
internal::VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS);
return false;
}
- if (header->version < 2)
- return true;
-
- auto* header_v2 = static_cast<const internal::MessageHeaderV2*>(header);
- // For the payload pointer:
- // - Check that the pointer can be safely decoded.
- // - Claim one byte that the pointer points to. It makes sure not only the
- // address is within the message, but also the address precedes the array
- // storing interface IDs (which is important for safely calculating the
- // payload size).
- // - Validation of the payload contents will be done separately based on the
- // payload type.
- if (!header_v2->payload.is_null() &&
- (!internal::ValidatePointer(header_v2->payload, validation_context) ||
- !validation_context->ClaimMemory(header_v2->payload.Get(), 1))) {
- return false;
- }
-
- const internal::ContainerValidateParams validate_params(0, false, nullptr);
- if (!internal::ValidateContainer(header_v2->payload_interface_ids,
- validation_context, &validate_params)) {
- return false;
- }
-
- if (!header_v2->payload_interface_ids.is_null()) {
- size_t num_ids = header_v2->payload_interface_ids.Get()->size();
- const uint32_t* ids = header_v2->payload_interface_ids.Get()->storage();
- for (size_t i = 0; i < num_ids; ++i) {
- if (!IsValidInterfaceId(ids[i]) || IsMasterInterfaceId(ids[i])) {
- internal::ReportValidationError(
- validation_context,
- internal::VALIDATION_ERROR_ILLEGAL_INTERFACE_ID);
- return false;
- }
- }
- }
-
return true;
}
} // namespace
-MessageHeaderValidator::MessageHeaderValidator()
- : MessageHeaderValidator("MessageHeaderValidator") {}
+MessageHeaderValidator::MessageHeaderValidator(MessageReceiver* sink)
+ : MessageHeaderValidator("MessageHeaderValidator", sink) {}
-MessageHeaderValidator::MessageHeaderValidator(const std::string& description)
- : description_(description) {
+MessageHeaderValidator::MessageHeaderValidator(const std::string& description,
+ MessageReceiver* sink)
+ : MessageFilter(sink), description_(description) {
}
void MessageHeaderValidator::SetDescription(const std::string& description) {
@@ -115,10 +78,10 @@
}
bool MessageHeaderValidator::Accept(Message* message) {
- // Pass 0 as number of handles and associated endpoint handles because we
- // don't expect any in the header, even if |message| contains handles.
+ // Pass 0 as number of handles because we don't expect any in the header, even
+ // if |message| contains handles.
internal::ValidationContext validation_context(
- message->data(), message->data_num_bytes(), 0, 0, message, description_);
+ message->data(), message->data_num_bytes(), 0, message, description_);
if (!internal::ValidateStructHeaderAndClaimMemory(message->data(),
&validation_context))
@@ -127,7 +90,7 @@
if (!IsValidMessageHeader(message->header(), &validation_context))
return false;
- return true;
+ return sink_->Accept(message);
}
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_internal.h b/mojo/public/cpp/bindings/lib/message_internal.h
index 6693198..63edffd 100644
--- a/mojo/public/cpp/bindings/lib/message_internal.h
+++ b/mojo/public/cpp/bindings/lib/message_internal.h
@@ -7,22 +7,11 @@
#include <stdint.h>
-#include <string>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
namespace mojo {
-
-class Message;
-
namespace internal {
-template <typename T>
-class Array_Data;
-
#pragma pack(push, 1)
struct MessageHeader : internal::StructHeader {
@@ -38,44 +27,16 @@
};
static_assert(sizeof(MessageHeader) == 24, "Bad sizeof(MessageHeader)");
-struct MessageHeaderV1 : MessageHeader {
+struct MessageHeaderWithRequestID : MessageHeader {
// Only used if either kFlagExpectsResponse or kFlagIsResponse is set in
// order to match responses with corresponding requests.
uint64_t request_id;
};
-static_assert(sizeof(MessageHeaderV1) == 32, "Bad sizeof(MessageHeaderV1)");
-
-struct MessageHeaderV2 : MessageHeaderV1 {
- MessageHeaderV2();
- GenericPointer payload;
- Pointer<Array_Data<uint32_t>> payload_interface_ids;
-};
-static_assert(sizeof(MessageHeaderV2) == 48, "Bad sizeof(MessageHeaderV2)");
+static_assert(sizeof(MessageHeaderWithRequestID) == 32,
+ "Bad sizeof(MessageHeaderWithRequestID)");
#pragma pack(pop)
-class MOJO_CPP_BINDINGS_EXPORT MessageDispatchContext {
- public:
- explicit MessageDispatchContext(Message* message);
- ~MessageDispatchContext();
-
- static MessageDispatchContext* current();
-
- const base::Callback<void(const std::string&)>& GetBadMessageCallback();
-
- private:
- MessageDispatchContext* outer_context_;
- Message* message_;
- base::Callback<void(const std::string&)> bad_message_callback_;
-
- DISALLOW_COPY_AND_ASSIGN(MessageDispatchContext);
-};
-
-class MOJO_CPP_BINDINGS_EXPORT SyncMessageResponseSetup {
- public:
- static void SetCurrentSyncResponseMessage(Message* message);
-};
-
} // namespace internal
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.cc b/mojo/public/cpp/bindings/lib/multiplex_router.cc
index 2da459a..dcfbab1 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.cc
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.cc
@@ -15,9 +15,9 @@
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "mojo/public/cpp/bindings/associated_group.h"
#include "mojo/public/cpp/bindings/interface_endpoint_client.h"
#include "mojo/public/cpp/bindings/interface_endpoint_controller.h"
-#include "mojo/public/cpp/bindings/lib/may_auto_lock.h"
#include "mojo/public/cpp/bindings/sync_handle_watcher.h"
namespace mojo {
@@ -36,7 +36,6 @@
id_(id),
closed_(false),
peer_closed_(false),
- handle_created_(false),
client_(nullptr),
event_signalled_(false) {}
@@ -51,31 +50,16 @@
bool closed() const { return closed_; }
void set_closed() {
- router_->AssertLockAcquired();
+ router_->lock_.AssertAcquired();
closed_ = true;
}
bool peer_closed() const { return peer_closed_; }
void set_peer_closed() {
- router_->AssertLockAcquired();
+ router_->lock_.AssertAcquired();
peer_closed_ = true;
}
- bool handle_created() const { return handle_created_; }
- void set_handle_created() {
- router_->AssertLockAcquired();
- handle_created_ = true;
- }
-
- const base::Optional<DisconnectReason>& disconnect_reason() const {
- return disconnect_reason_;
- }
- void set_disconnect_reason(
- const base::Optional<DisconnectReason>& disconnect_reason) {
- router_->AssertLockAcquired();
- disconnect_reason_ = disconnect_reason;
- }
-
base::SingleThreadTaskRunner* task_runner() const {
return task_runner_.get();
}
@@ -84,7 +68,7 @@
void AttachClient(InterfaceEndpointClient* client,
scoped_refptr<base::SingleThreadTaskRunner> runner) {
- router_->AssertLockAcquired();
+ router_->lock_.AssertAcquired();
DCHECK(!client_);
DCHECK(!closed_);
DCHECK(runner->BelongsToCurrentThread());
@@ -96,7 +80,7 @@
// This method must be called on the same thread as the corresponding
// AttachClient() call.
void DetachClient() {
- router_->AssertLockAcquired();
+ router_->lock_.AssertAcquired();
DCHECK(client_);
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(!closed_);
@@ -107,36 +91,18 @@
}
void SignalSyncMessageEvent() {
- router_->AssertLockAcquired();
+ router_->lock_.AssertAcquired();
if (event_signalled_)
return;
+ EnsureEventMessagePipeExists();
event_signalled_ = true;
- if (!sync_message_event_sender_.is_valid())
- return;
-
MojoResult result =
WriteMessageRaw(sync_message_event_sender_.get(), nullptr, 0, nullptr,
0, MOJO_WRITE_MESSAGE_FLAG_NONE);
DCHECK_EQ(MOJO_RESULT_OK, result);
}
- void ResetSyncMessageSignal() {
- router_->AssertLockAcquired();
-
- if (!event_signalled_)
- return;
-
- event_signalled_ = false;
- if (!sync_message_event_receiver_.is_valid())
- return;
-
- MojoResult result =
- ReadMessageRaw(sync_message_event_receiver_.get(), nullptr, nullptr,
- nullptr, nullptr, MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
- DCHECK_EQ(MOJO_RESULT_OK, result);
- }
-
// ---------------------------------------------------------------------------
// The following public methods (i.e., InterfaceEndpointController
// implementation) are called by the client on the same thread as the
@@ -166,7 +132,7 @@
friend class base::RefCounted<InterfaceEndpoint>;
~InterfaceEndpoint() override {
- router_->AssertLockAcquired();
+ router_->lock_.AssertAcquired();
DCHECK(!client_);
DCHECK(closed_);
@@ -176,23 +142,26 @@
void OnHandleReady(MojoResult result) {
DCHECK(task_runner_->BelongsToCurrentThread());
+ scoped_refptr<InterfaceEndpoint> self_protector(this);
scoped_refptr<MultiplexRouter> router_protector(router_);
// Because we never close |sync_message_event_{sender,receiver}_| before
// destruction or set a deadline, |result| should always be MOJO_RESULT_OK.
DCHECK_EQ(MOJO_RESULT_OK, result);
+ bool reset_sync_watcher = false;
+ {
+ base::AutoLock locker(router_->lock_);
- MayAutoLock locker(&router_->lock_);
- scoped_refptr<InterfaceEndpoint> self_protector(this);
+ bool more_to_process = router_->ProcessFirstSyncMessageForEndpoint(id_);
- bool more_to_process = router_->ProcessFirstSyncMessageForEndpoint(id_);
+ if (!more_to_process)
+ ResetSyncMessageSignal();
- if (!more_to_process)
- ResetSyncMessageSignal();
-
- // Currently there are no queued sync messages and the peer has closed so
- // there won't be incoming sync messages in the future.
- if (!more_to_process && peer_closed_) {
+ // Currently there are no queued sync messages and the peer has closed so
+ // there won't be incoming sync messages in the future.
+ reset_sync_watcher = !more_to_process && peer_closed_;
+ }
+ if (reset_sync_watcher) {
// If a SyncWatch() call (or multiple ones) of this interface endpoint is
// on the call stack, resetting the sync watcher will allow it to exit
// when the call stack unwinds to that frame.
@@ -206,21 +175,12 @@
return;
{
- MayAutoLock locker(&router_->lock_);
+ base::AutoLock locker(router_->lock_);
+ EnsureEventMessagePipeExists();
- if (!sync_message_event_sender_.is_valid()) {
- MojoResult result =
- CreateMessagePipe(nullptr, &sync_message_event_sender_,
- &sync_message_event_receiver_);
- DCHECK_EQ(MOJO_RESULT_OK, result);
-
- if (event_signalled_) {
- // Reset the flag so that SignalSyncMessageEvent() will actually
- // signal using the newly-created message pipe.
- event_signalled_ = false;
- SignalSyncMessageEvent();
- }
- }
+ auto iter = router_->sync_message_tasks_.find(id_);
+ if (iter != router_->sync_message_tasks_.end() && !iter->second.empty())
+ SignalSyncMessageEvent();
}
sync_watcher_.reset(new SyncHandleWatcher(
@@ -228,6 +188,31 @@
base::Bind(&InterfaceEndpoint::OnHandleReady, base::Unretained(this))));
}
+ void EnsureEventMessagePipeExists() {
+ router_->lock_.AssertAcquired();
+
+ if (sync_message_event_receiver_.is_valid())
+ return;
+
+ MojoResult result = CreateMessagePipe(nullptr, &sync_message_event_sender_,
+ &sync_message_event_receiver_);
+ DCHECK_EQ(MOJO_RESULT_OK, result);
+ }
+
+ void ResetSyncMessageSignal() {
+ router_->lock_.AssertAcquired();
+
+ if (!event_signalled_)
+ return;
+
+ DCHECK(sync_message_event_receiver_.is_valid());
+ MojoResult result = ReadMessageRaw(sync_message_event_receiver_.get(),
+ nullptr, nullptr, nullptr, nullptr,
+ MOJO_READ_MESSAGE_FLAG_MAY_DISCARD);
+ DCHECK_EQ(MOJO_RESULT_OK, result);
+ event_signalled_ = false;
+ }
+
// ---------------------------------------------------------------------------
// The following members are safe to access from any threads.
@@ -242,12 +227,6 @@
// Whether the peer endpoint has been closed.
bool peer_closed_;
- // Whether there is already a ScopedInterfaceEndpointHandle created for this
- // endpoint.
- bool handle_created_;
-
- base::Optional<DisconnectReason> disconnect_reason_;
-
// The task runner on which |client_|'s methods can be called.
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
// Not owned. It is null if no client is attached to this endpoint.
@@ -271,53 +250,13 @@
DISALLOW_COPY_AND_ASSIGN(InterfaceEndpoint);
};
-// MessageWrapper objects are always destroyed under the router's lock. On
-// destruction, if the message it wrappers contains
-// ScopedInterfaceEndpointHandles (which cannot be destructed under the
-// router's lock), the wrapper unlocks to clean them up.
-class MultiplexRouter::MessageWrapper {
- public:
- MessageWrapper() = default;
-
- MessageWrapper(MultiplexRouter* router, Message message)
- : router_(router), value_(std::move(message)) {}
-
- MessageWrapper(MessageWrapper&& other)
- : router_(other.router_), value_(std::move(other.value_)) {}
-
- ~MessageWrapper() {
- if (value_.associated_endpoint_handles()->empty())
- return;
-
- router_->AssertLockAcquired();
- {
- MayAutoUnlock unlocker(&router_->lock_);
- value_.mutable_associated_endpoint_handles()->clear();
- }
- }
-
- MessageWrapper& operator=(MessageWrapper&& other) {
- router_ = other.router_;
- value_ = std::move(other.value_);
- return *this;
- }
-
- Message& value() { return value_; }
-
- private:
- MultiplexRouter* router_ = nullptr;
- Message value_;
-
- DISALLOW_COPY_AND_ASSIGN(MessageWrapper);
-};
-
struct MultiplexRouter::Task {
public:
// Doesn't take ownership of |message| but takes its contents.
- static std::unique_ptr<Task> CreateMessageTask(
- MessageWrapper message_wrapper) {
+ static std::unique_ptr<Task> CreateMessageTask(Message* message) {
Task* task = new Task(MESSAGE);
- task->message_wrapper = std::move(message_wrapper);
+ task->message.reset(new Message);
+ message->MoveTo(task->message.get());
return base::WrapUnique(task);
}
static std::unique_ptr<Task> CreateNotifyErrorTask(
@@ -332,7 +271,7 @@
bool IsMessageTask() const { return type == MESSAGE; }
bool IsNotifyErrorTask() const { return type == NOTIFY_ERROR; }
- MessageWrapper message_wrapper;
+ std::unique_ptr<Message> message;
scoped_refptr<InterfaceEndpoint> endpoint_to_notify;
enum Type { MESSAGE, NOTIFY_ERROR };
@@ -340,56 +279,36 @@
private:
explicit Task(Type in_type) : type(in_type) {}
-
- DISALLOW_COPY_AND_ASSIGN(Task);
};
MultiplexRouter::MultiplexRouter(
- ScopedMessagePipeHandle message_pipe,
- Config config,
bool set_interface_id_namesapce_bit,
+ ScopedMessagePipeHandle message_pipe,
scoped_refptr<base::SingleThreadTaskRunner> runner)
- : set_interface_id_namespace_bit_(set_interface_id_namesapce_bit),
- task_runner_(runner),
- header_validator_(nullptr),
- filters_(this),
+ : AssociatedGroupController(base::ThreadTaskRunnerHandle::Get()),
+ set_interface_id_namespace_bit_(set_interface_id_namesapce_bit),
+ header_validator_(this),
connector_(std::move(message_pipe),
- config == MULTI_INTERFACE ? Connector::MULTI_THREADED_SEND
- : Connector::SINGLE_THREADED_SEND,
+ Connector::MULTI_THREADED_SEND,
std::move(runner)),
control_message_handler_(this),
control_message_proxy_(&connector_),
next_interface_id_value_(1),
posted_to_process_tasks_(false),
encountered_error_(false),
- paused_(false),
testing_mode_(false) {
- DCHECK(task_runner_->BelongsToCurrentThread());
-
- if (config == MULTI_INTERFACE)
- lock_.emplace();
-
- if (config == SINGLE_INTERFACE_WITH_SYNC_METHODS ||
- config == MULTI_INTERFACE) {
- // Always participate in sync handle watching in multi-interface mode,
- // because even if it doesn't expect sync requests during sync handle
- // watching, it may still need to dispatch messages to associated endpoints
- // on a different thread.
- connector_.AllowWokenUpBySyncWatchOnSameThread();
- }
- connector_.set_incoming_receiver(&filters_);
+ // Always participate in sync handle watching, because even if it doesn't
+ // expect sync requests during sync handle watching, it may still need to
+ // dispatch messages to associated endpoints on a different thread.
+ connector_.AllowWokenUpBySyncWatchOnSameThread();
+ connector_.set_incoming_receiver(&header_validator_);
connector_.set_connection_error_handler(
base::Bind(&MultiplexRouter::OnPipeConnectionError,
base::Unretained(this)));
-
- std::unique_ptr<MessageHeaderValidator> header_validator =
- base::MakeUnique<MessageHeaderValidator>();
- header_validator_ = header_validator.get();
- filters_.Append(std::move(header_validator));
}
MultiplexRouter::~MultiplexRouter() {
- MayAutoLock locker(&lock_);
+ base::AutoLock locker(lock_);
sync_message_tasks_.clear();
tasks_.clear();
@@ -400,66 +319,40 @@
// because it may remove the corresponding value from the map.
++iter;
- if (!endpoint->closed()) {
- // This happens when a NotifyPeerEndpointClosed message been received, but
- // the interface ID hasn't been used to create local endpoint handle.
- DCHECK(!endpoint->client());
- DCHECK(endpoint->peer_closed());
- UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED);
- } else {
- UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED);
- }
+ DCHECK(endpoint->closed());
+ UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED);
}
DCHECK(endpoints_.empty());
}
-void MultiplexRouter::SetMasterInterfaceName(const char* name) {
+void MultiplexRouter::SetMasterInterfaceName(const std::string& name) {
DCHECK(thread_checker_.CalledOnValidThread());
- header_validator_->SetDescription(
- std::string(name) + " [master] MessageHeaderValidator");
+ header_validator_.SetDescription(name + " [master] MessageHeaderValidator");
control_message_handler_.SetDescription(
- std::string(name) + " [master] PipeControlMessageHandler");
- connector_.SetWatcherHeapProfilerTag(name);
+ name + " [master] PipeControlMessageHandler");
}
-InterfaceId MultiplexRouter::AssociateInterface(
- ScopedInterfaceEndpointHandle handle_to_send) {
- if (!handle_to_send.pending_association())
- return kInvalidInterfaceId;
-
+void MultiplexRouter::CreateEndpointHandlePair(
+ ScopedInterfaceEndpointHandle* local_endpoint,
+ ScopedInterfaceEndpointHandle* remote_endpoint) {
+ base::AutoLock locker(lock_);
uint32_t id = 0;
- {
- MayAutoLock locker(&lock_);
- do {
- if (next_interface_id_value_ >= kInterfaceIdNamespaceMask)
- next_interface_id_value_ = 1;
- id = next_interface_id_value_++;
- if (set_interface_id_namespace_bit_)
- id |= kInterfaceIdNamespaceMask;
- } while (base::ContainsKey(endpoints_, id));
+ do {
+ if (next_interface_id_value_ >= kInterfaceIdNamespaceMask)
+ next_interface_id_value_ = 1;
+ id = next_interface_id_value_++;
+ if (set_interface_id_namespace_bit_)
+ id |= kInterfaceIdNamespaceMask;
+ } while (ContainsKey(endpoints_, id));
- InterfaceEndpoint* endpoint = new InterfaceEndpoint(this, id);
- endpoints_[id] = endpoint;
- if (encountered_error_)
- UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED);
- endpoint->set_handle_created();
- }
+ InterfaceEndpoint* endpoint = new InterfaceEndpoint(this, id);
+ endpoints_[id] = endpoint;
+ if (encountered_error_)
+ UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED);
- if (!NotifyAssociation(&handle_to_send, id)) {
- // The peer handle of |handle_to_send|, which is supposed to join this
- // associated group, has been closed.
- {
- MayAutoLock locker(&lock_);
- InterfaceEndpoint* endpoint = FindEndpoint(id);
- if (endpoint)
- UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED);
- }
-
- control_message_proxy_.NotifyPeerEndpointClosed(
- id, handle_to_send.disconnect_reason());
- }
- return id;
+ *local_endpoint = CreateScopedInterfaceEndpointHandle(id, true);
+ *remote_endpoint = CreateScopedInterfaceEndpointHandle(id, false);
}
ScopedInterfaceEndpointHandle MultiplexRouter::CreateLocalEndpointHandle(
@@ -467,12 +360,10 @@
if (!IsValidInterfaceId(id))
return ScopedInterfaceEndpointHandle();
- MayAutoLock locker(&lock_);
+ base::AutoLock locker(lock_);
bool inserted = false;
InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, &inserted);
if (inserted) {
- DCHECK(!endpoint->handle_created());
-
if (encountered_error_)
UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED);
} else {
@@ -480,32 +371,34 @@
// notification that the peer endpoint has closed.
CHECK(!endpoint->closed());
CHECK(endpoint->peer_closed());
-
- if (endpoint->handle_created())
- return ScopedInterfaceEndpointHandle();
}
-
- endpoint->set_handle_created();
- return CreateScopedInterfaceEndpointHandle(id);
+ return CreateScopedInterfaceEndpointHandle(id, true);
}
-void MultiplexRouter::CloseEndpointHandle(
- InterfaceId id,
- const base::Optional<DisconnectReason>& reason) {
+void MultiplexRouter::CloseEndpointHandle(InterfaceId id, bool is_local) {
if (!IsValidInterfaceId(id))
return;
- MayAutoLock locker(&lock_);
- DCHECK(base::ContainsKey(endpoints_, id));
+ base::AutoLock locker(lock_);
+
+ if (!is_local) {
+ DCHECK(ContainsKey(endpoints_, id));
+ DCHECK(!IsMasterInterfaceId(id));
+
+ // We will receive a NotifyPeerEndpointClosed message from the other side.
+ control_message_proxy_.NotifyEndpointClosedBeforeSent(id);
+
+ return;
+ }
+
+ DCHECK(ContainsKey(endpoints_, id));
InterfaceEndpoint* endpoint = endpoints_[id].get();
DCHECK(!endpoint->client());
DCHECK(!endpoint->closed());
UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED);
- if (!IsMasterInterfaceId(id) || reason) {
- MayAutoUnlock unlocker(&lock_);
- control_message_proxy_.NotifyPeerEndpointClosed(id, reason);
- }
+ if (!IsMasterInterfaceId(id))
+ control_message_proxy_.NotifyPeerEndpointClosed(id);
ProcessTasks(NO_DIRECT_CLIENT_CALLS, nullptr);
}
@@ -519,8 +412,8 @@
DCHECK(IsValidInterfaceId(id));
DCHECK(client);
- MayAutoLock locker(&lock_);
- DCHECK(base::ContainsKey(endpoints_, id));
+ base::AutoLock locker(lock_);
+ DCHECK(ContainsKey(endpoints_, id));
InterfaceEndpoint* endpoint = endpoints_[id].get();
endpoint->AttachClient(client, std::move(runner));
@@ -538,8 +431,8 @@
DCHECK(IsValidInterfaceId(id));
- MayAutoLock locker(&lock_);
- DCHECK(base::ContainsKey(endpoints_, id));
+ base::AutoLock locker(lock_);
+ DCHECK(ContainsKey(endpoints_, id));
InterfaceEndpoint* endpoint = endpoints_[id].get();
endpoint->DetachClient();
@@ -563,51 +456,21 @@
OnPipeConnectionError();
}
-void MultiplexRouter::PauseIncomingMethodCallProcessing() {
- DCHECK(thread_checker_.CalledOnValidThread());
- connector_.PauseIncomingMethodCallProcessing();
-
- MayAutoLock locker(&lock_);
- paused_ = true;
-
- for (auto iter = endpoints_.begin(); iter != endpoints_.end(); ++iter)
- iter->second->ResetSyncMessageSignal();
-}
-
-void MultiplexRouter::ResumeIncomingMethodCallProcessing() {
- DCHECK(thread_checker_.CalledOnValidThread());
- connector_.ResumeIncomingMethodCallProcessing();
-
- MayAutoLock locker(&lock_);
- paused_ = false;
-
- for (auto iter = endpoints_.begin(); iter != endpoints_.end(); ++iter) {
- auto sync_iter = sync_message_tasks_.find(iter->first);
- if (iter->second->peer_closed() ||
- (sync_iter != sync_message_tasks_.end() &&
- !sync_iter->second.empty())) {
- iter->second->SignalSyncMessageEvent();
- }
- }
-
- ProcessTasks(NO_DIRECT_CLIENT_CALLS, nullptr);
-}
-
bool MultiplexRouter::HasAssociatedEndpoints() const {
DCHECK(thread_checker_.CalledOnValidThread());
- MayAutoLock locker(&lock_);
+ base::AutoLock locker(lock_);
if (endpoints_.size() > 1)
return true;
if (endpoints_.size() == 0)
return false;
- return !base::ContainsKey(endpoints_, kMasterInterfaceId);
+ return !ContainsKey(endpoints_, kMasterInterfaceId);
}
void MultiplexRouter::EnableTestingMode() {
DCHECK(thread_checker_.CalledOnValidThread());
- MayAutoLock locker(&lock_);
+ base::AutoLock locker(lock_);
testing_mode_ = true;
connector_.set_enforce_errors_from_incoming_receiver(false);
@@ -616,13 +479,8 @@
bool MultiplexRouter::Accept(Message* message) {
DCHECK(thread_checker_.CalledOnValidThread());
- if (!message->DeserializeAssociatedEndpointHandles(this))
- return false;
-
scoped_refptr<MultiplexRouter> protector(this);
- MayAutoLock locker(&lock_);
-
- DCHECK(!paused_);
+ base::AutoLock locker(lock_);
ClientCallBehavior client_call_behavior =
connector_.during_sync_handle_watcher_callback()
@@ -636,16 +494,15 @@
if (!processed) {
// Either the task queue is not empty or we cannot process the message
// directly. In both cases, there is no need to call ProcessTasks().
- tasks_.push_back(
- Task::CreateMessageTask(MessageWrapper(this, std::move(*message))));
+ tasks_.push_back(Task::CreateMessageTask(message));
Task* task = tasks_.back().get();
- if (task->message_wrapper.value().has_flag(Message::kFlagIsSync)) {
- InterfaceId id = task->message_wrapper.value().interface_id();
+ if (task->message->has_flag(Message::kFlagIsSync)) {
+ InterfaceId id = task->message->interface_id();
sync_message_tasks_[id].push_back(task);
- InterfaceEndpoint* endpoint = FindEndpoint(id);
- if (endpoint)
- endpoint->SignalSyncMessageEvent();
+ auto iter = endpoints_.find(id);
+ if (iter != endpoints_.end())
+ iter->second->SignalSyncMessageEvent();
}
} else if (!tasks_.empty()) {
// Processing the message may result in new tasks (for error notification)
@@ -659,17 +516,14 @@
return true;
}
-bool MultiplexRouter::OnPeerAssociatedEndpointClosed(
- InterfaceId id,
- const base::Optional<DisconnectReason>& reason) {
- DCHECK(!IsMasterInterfaceId(id) || reason);
+bool MultiplexRouter::OnPeerAssociatedEndpointClosed(InterfaceId id) {
+ lock_.AssertAcquired();
- MayAutoLock locker(&lock_);
+ if (IsMasterInterfaceId(id))
+ return false;
+
InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, nullptr);
- if (reason)
- endpoint->set_disconnect_reason(reason);
-
// It is possible that this endpoint has been set as peer closed. That is
// because when the message pipe is closed, all the endpoints are updated with
// PEER_ENDPOINT_CLOSED. We continue to process remaining tasks in the queue,
@@ -687,11 +541,26 @@
return true;
}
+bool MultiplexRouter::OnAssociatedEndpointClosedBeforeSent(InterfaceId id) {
+ lock_.AssertAcquired();
+
+ if (IsMasterInterfaceId(id))
+ return false;
+
+ InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, nullptr);
+ DCHECK(!endpoint->closed());
+ UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED);
+
+ control_message_proxy_.NotifyPeerEndpointClosed(id);
+
+ return true;
+}
+
void MultiplexRouter::OnPipeConnectionError() {
DCHECK(thread_checker_.CalledOnValidThread());
scoped_refptr<MultiplexRouter> protector(this);
- MayAutoLock locker(&lock_);
+ base::AutoLock locker(lock_);
encountered_error_ = true;
@@ -716,21 +585,20 @@
void MultiplexRouter::ProcessTasks(
ClientCallBehavior client_call_behavior,
base::SingleThreadTaskRunner* current_task_runner) {
- AssertLockAcquired();
+ lock_.AssertAcquired();
if (posted_to_process_tasks_)
return;
- while (!tasks_.empty() && !paused_) {
+ while (!tasks_.empty()) {
std::unique_ptr<Task> task(std::move(tasks_.front()));
tasks_.pop_front();
InterfaceId id = kInvalidInterfaceId;
- bool sync_message =
- task->IsMessageTask() && !task->message_wrapper.value().IsNull() &&
- task->message_wrapper.value().has_flag(Message::kFlagIsSync);
+ bool sync_message = task->IsMessageTask() && task->message &&
+ task->message->has_flag(Message::kFlagIsSync);
if (sync_message) {
- id = task->message_wrapper.value().interface_id();
+ id = task->message->interface_id();
auto& sync_message_queue = sync_message_tasks_[id];
DCHECK_EQ(task.get(), sync_message_queue.front());
sync_message_queue.pop_front();
@@ -740,8 +608,8 @@
task->IsNotifyErrorTask()
? ProcessNotifyErrorTask(task.get(), client_call_behavior,
current_task_runner)
- : ProcessIncomingMessage(&task->message_wrapper.value(),
- client_call_behavior, current_task_runner);
+ : ProcessIncomingMessage(task->message.get(), client_call_behavior,
+ current_task_runner);
if (!processed) {
if (sync_message) {
@@ -761,25 +629,21 @@
}
bool MultiplexRouter::ProcessFirstSyncMessageForEndpoint(InterfaceId id) {
- AssertLockAcquired();
+ lock_.AssertAcquired();
auto iter = sync_message_tasks_.find(id);
if (iter == sync_message_tasks_.end())
return false;
- if (paused_)
- return true;
-
MultiplexRouter::Task* task = iter->second.front();
iter->second.pop_front();
DCHECK(task->IsMessageTask());
- MessageWrapper message_wrapper = std::move(task->message_wrapper);
+ std::unique_ptr<Message> message(std::move(task->message));
- // Note: after this call, |task| and |iter| may be invalidated.
+ // Note: after this call, |task| and |iter| may be invalidated.
bool processed = ProcessIncomingMessage(
- &message_wrapper.value(), ALLOW_DIRECT_CLIENT_CALLS_FOR_SYNC_MESSAGES,
- nullptr);
+ message.get(), ALLOW_DIRECT_CLIENT_CALLS_FOR_SYNC_MESSAGES, nullptr);
DCHECK(processed);
iter = sync_message_tasks_.find(id);
@@ -799,9 +663,7 @@
ClientCallBehavior client_call_behavior,
base::SingleThreadTaskRunner* current_task_runner) {
DCHECK(!current_task_runner || current_task_runner->BelongsToCurrentThread());
- DCHECK(!paused_);
-
- AssertLockAcquired();
+ lock_.AssertAcquired();
InterfaceEndpoint* endpoint = task->endpoint_to_notify.get();
if (!endpoint->client())
return true;
@@ -815,17 +677,14 @@
DCHECK(endpoint->task_runner()->BelongsToCurrentThread());
InterfaceEndpointClient* client = endpoint->client();
- base::Optional<DisconnectReason> disconnect_reason(
- endpoint->disconnect_reason());
-
{
// We must unlock before calling into |client| because it may call this
// object within NotifyError(). Holding the lock will lead to deadlock.
//
// It is safe to call into |client| without the lock. Because |client| is
// always accessed on the same thread, including DetachEndpointClient().
- MayAutoUnlock unlocker(&lock_);
- client->NotifyError(disconnect_reason);
+ base::AutoUnlock unlocker(lock_);
+ client->NotifyError();
}
return true;
}
@@ -835,35 +694,47 @@
ClientCallBehavior client_call_behavior,
base::SingleThreadTaskRunner* current_task_runner) {
DCHECK(!current_task_runner || current_task_runner->BelongsToCurrentThread());
- DCHECK(!paused_);
- DCHECK(message);
- AssertLockAcquired();
+ lock_.AssertAcquired();
- if (message->IsNull()) {
+ if (!message) {
// This is a sync message and has been processed during sync handle
// watching.
return true;
}
if (PipeControlMessageHandler::IsPipeControlMessage(message)) {
- bool result = false;
-
- {
- MayAutoUnlock unlocker(&lock_);
- result = control_message_handler_.Accept(message);
- }
-
- if (!result)
+ if (!control_message_handler_.Accept(message))
RaiseErrorInNonTestingMode();
-
return true;
}
InterfaceId id = message->interface_id();
DCHECK(IsValidInterfaceId(id));
- InterfaceEndpoint* endpoint = FindEndpoint(id);
- if (!endpoint || endpoint->closed())
+ bool inserted = false;
+ InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, &inserted);
+ if (inserted) {
+ // Currently, it is legitimate to receive messages for an endpoint
+ // that is not registered. For example, the endpoint is transferred in
+ // a message that is discarded. Once we add support to specify all
+ // enclosing endpoints in message header, we should be able to remove
+ // this.
+ UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED);
+
+ // It is also possible that this newly-inserted endpoint is the master
+ // endpoint. When the master InterfacePtr/Binding goes away, the message
+ // pipe is closed and we explicitly trigger a pipe connection error. The
+ // error updates all the endpoints, including the master endpoint, with
+ // PEER_ENDPOINT_CLOSED and removes the master endpoint from the
+ // registration. We continue to process remaining tasks in the queue, as
+ // long as there are refs keeping the router alive. If there are remaining
+ // messages for the master endpoint, we will get here.
+ if (!IsMasterInterfaceId(id))
+ control_message_proxy_.NotifyPeerEndpointClosed(id);
+ return true;
+ }
+
+ if (endpoint->closed())
return true;
if (!endpoint->client()) {
@@ -897,7 +768,7 @@
//
// It is safe to call into |client| without the lock. Because |client| is
// always accessed on the same thread, including DetachEndpointClient().
- MayAutoUnlock unlocker(&lock_);
+ base::AutoUnlock unlocker(lock_);
result = client->HandleIncomingMessage(message);
}
if (!result)
@@ -908,7 +779,7 @@
void MultiplexRouter::MaybePostToProcessTasks(
base::SingleThreadTaskRunner* task_runner) {
- AssertLockAcquired();
+ lock_.AssertAcquired();
if (posted_to_process_tasks_)
return;
@@ -921,7 +792,7 @@
void MultiplexRouter::LockAndCallProcessTasks() {
// There is no need to hold a ref to this class in this case because this is
// always called using base::Bind(), which holds a ref.
- MayAutoLock locker(&lock_);
+ base::AutoLock locker(lock_);
posted_to_process_tasks_ = false;
scoped_refptr<base::SingleThreadTaskRunner> runner(
std::move(posted_to_task_runner_));
@@ -931,20 +802,23 @@
void MultiplexRouter::UpdateEndpointStateMayRemove(
InterfaceEndpoint* endpoint,
EndpointStateUpdateType type) {
- if (type == ENDPOINT_CLOSED) {
- endpoint->set_closed();
- } else {
- endpoint->set_peer_closed();
- // If the interface endpoint is performing a sync watch, this makes sure
- // it is notified and eventually exits the sync watch.
- endpoint->SignalSyncMessageEvent();
+ switch (type) {
+ case ENDPOINT_CLOSED:
+ endpoint->set_closed();
+ break;
+ case PEER_ENDPOINT_CLOSED:
+ endpoint->set_peer_closed();
+ // If the interface endpoint is performing a sync watch, this makes sure
+ // it is notified and eventually exits the sync watch.
+ endpoint->SignalSyncMessageEvent();
+ break;
}
if (endpoint->closed() && endpoint->peer_closed())
endpoints_.erase(endpoint->id());
}
void MultiplexRouter::RaiseErrorInNonTestingMode() {
- AssertLockAcquired();
+ lock_.AssertAcquired();
if (!testing_mode_)
RaiseError();
}
@@ -952,35 +826,24 @@
MultiplexRouter::InterfaceEndpoint* MultiplexRouter::FindOrInsertEndpoint(
InterfaceId id,
bool* inserted) {
- AssertLockAcquired();
+ lock_.AssertAcquired();
// Either |inserted| is nullptr or it points to a boolean initialized as
// false.
DCHECK(!inserted || !*inserted);
- InterfaceEndpoint* endpoint = FindEndpoint(id);
- if (!endpoint) {
+ auto iter = endpoints_.find(id);
+ InterfaceEndpoint* endpoint;
+ if (iter == endpoints_.end()) {
endpoint = new InterfaceEndpoint(this, id);
endpoints_[id] = endpoint;
if (inserted)
*inserted = true;
+ } else {
+ endpoint = iter->second.get();
}
return endpoint;
}
-MultiplexRouter::InterfaceEndpoint* MultiplexRouter::FindEndpoint(
- InterfaceId id) {
- AssertLockAcquired();
- auto iter = endpoints_.find(id);
- return iter != endpoints_.end() ? iter->second.get() : nullptr;
-}
-
-void MultiplexRouter::AssertLockAcquired() {
-#if DCHECK_IS_ON()
- if (lock_)
- lock_->AssertAcquired();
-#endif
-}
-
} // namespace internal
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.h b/mojo/public/cpp/bindings/lib/multiplex_router.h
index cac138b..dc66e8e 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.h
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.h
@@ -12,19 +12,15 @@
#include <memory>
#include <string>
-#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "mojo/public/cpp/bindings/associated_group_controller.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/bindings/connector.h"
-#include "mojo/public/cpp/bindings/filter_chain.h"
#include "mojo/public/cpp/bindings/interface_id.h"
#include "mojo/public/cpp/bindings/message_header_validator.h"
#include "mojo/public/cpp/bindings/pipe_control_message_handler.h"
@@ -38,6 +34,8 @@
namespace mojo {
+class AssociatedGroup;
+
namespace internal {
// MultiplexRouter supports routing messages for multiple interfaces over a
@@ -49,51 +47,31 @@
// Some public methods are only allowed to be called on the creating thread;
// while the others are safe to call from any threads. Please see the method
// comments for more details.
-//
-// NOTE: CloseMessagePipe() or PassMessagePipe() MUST be called on |runner|'s
-// thread before this object is destroyed.
-class MOJO_CPP_BINDINGS_EXPORT MultiplexRouter
- : NON_EXPORTED_BASE(public MessageReceiver),
+class MultiplexRouter
+ : public MessageReceiver,
public AssociatedGroupController,
- NON_EXPORTED_BASE(public PipeControlMessageHandlerDelegate) {
+ public PipeControlMessageHandlerDelegate {
public:
- enum Config {
- // There is only the master interface running on this router. Please note
- // that because of interface versioning, the other side of the message pipe
- // may use a newer master interface definition which passes associated
- // interfaces. In that case, this router may still receive pipe control
- // messages or messages targetting associated interfaces.
- SINGLE_INTERFACE,
- // Similar to the mode above, there is only the master interface running on
- // this router. Besides, the master interface has sync methods.
- SINGLE_INTERFACE_WITH_SYNC_METHODS,
- // There may be associated interfaces running on this router.
- MULTI_INTERFACE
- };
-
// If |set_interface_id_namespace_bit| is true, the interface IDs generated by
// this router will have the highest bit set.
- MultiplexRouter(ScopedMessagePipeHandle message_pipe,
- Config config,
- bool set_interface_id_namespace_bit,
+ MultiplexRouter(bool set_interface_id_namespace_bit,
+ ScopedMessagePipeHandle message_pipe,
scoped_refptr<base::SingleThreadTaskRunner> runner);
// Sets the master interface name for this router. Only used when reporting
// message header or control message validation errors.
- // |name| must be a string literal.
- void SetMasterInterfaceName(const char* name);
+ void SetMasterInterfaceName(const std::string& name);
// ---------------------------------------------------------------------------
// The following public methods are safe to call from any threads.
// AssociatedGroupController implementation:
- InterfaceId AssociateInterface(
- ScopedInterfaceEndpointHandle handle_to_send) override;
+ void CreateEndpointHandlePair(
+ ScopedInterfaceEndpointHandle* local_endpoint,
+ ScopedInterfaceEndpointHandle* remote_endpoint) override;
ScopedInterfaceEndpointHandle CreateLocalEndpointHandle(
InterfaceId id) override;
- void CloseEndpointHandle(
- InterfaceId id,
- const base::Optional<DisconnectReason>& reason) override;
+ void CloseEndpointHandle(InterfaceId id, bool is_local) override;
InterfaceEndpointController* AttachEndpointClient(
const ScopedInterfaceEndpointHandle& handle,
InterfaceEndpointClient* endpoint_client,
@@ -124,8 +102,14 @@
}
// See Binding for details of pause/resume.
- void PauseIncomingMethodCallProcessing();
- void ResumeIncomingMethodCallProcessing();
+ void PauseIncomingMethodCallProcessing() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ connector_.PauseIncomingMethodCallProcessing();
+ }
+ void ResumeIncomingMethodCallProcessing() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ connector_.ResumeIncomingMethodCallProcessing();
+ }
// Whether there are any associated interfaces running currently.
bool HasAssociatedEndpoints() const;
@@ -147,13 +131,8 @@
return connector_.handle();
}
- bool SimulateReceivingMessageForTesting(Message* message) {
- return filters_.Accept(message);
- }
-
private:
class InterfaceEndpoint;
- class MessageWrapper;
struct Task;
~MultiplexRouter() override;
@@ -162,9 +141,8 @@
bool Accept(Message* message) override;
// PipeControlMessageHandlerDelegate implementation:
- bool OnPeerAssociatedEndpointClosed(
- InterfaceId id,
- const base::Optional<DisconnectReason>& reason) override;
+ bool OnPeerAssociatedEndpointClosed(InterfaceId id) override;
+ bool OnAssociatedEndpointClosedBeforeSent(InterfaceId id) override;
void OnPipeConnectionError();
@@ -224,30 +202,19 @@
void RaiseErrorInNonTestingMode();
InterfaceEndpoint* FindOrInsertEndpoint(InterfaceId id, bool* inserted);
- InterfaceEndpoint* FindEndpoint(InterfaceId id);
-
- void AssertLockAcquired();
// Whether to set the namespace bit when generating interface IDs. Please see
// comments of kInterfaceIdNamespaceMask.
const bool set_interface_id_namespace_bit_;
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-
- // Owned by |filters_| below.
- MessageHeaderValidator* header_validator_;
-
- FilterChain filters_;
+ MessageHeaderValidator header_validator_;
Connector connector_;
base::ThreadChecker thread_checker_;
// Protects the following members.
- // Not set in Config::SINGLE_INTERFACE* mode.
- mutable base::Optional<base::Lock> lock_;
+ mutable base::Lock lock_;
PipeControlMessageHandler control_message_handler_;
-
- // NOTE: It is unsafe to call into this object while holding |lock_|.
PipeControlMessageProxy control_message_proxy_;
std::map<InterfaceId, scoped_refptr<InterfaceEndpoint>> endpoints_;
@@ -262,8 +229,6 @@
bool encountered_error_;
- bool paused_;
-
bool testing_mode_;
DISALLOW_COPY_AND_ASSIGN(MultiplexRouter);
diff --git a/mojo/public/cpp/bindings/lib/native_struct.cc b/mojo/public/cpp/bindings/lib/native_struct.cc
index 7b1a1a6..837b75a 100644
--- a/mojo/public/cpp/bindings/lib/native_struct.cc
+++ b/mojo/public/cpp/bindings/lib/native_struct.cc
@@ -4,31 +4,27 @@
#include "mojo/public/cpp/bindings/native_struct.h"
-#include "mojo/public/cpp/bindings/lib/hash_util.h"
-
namespace mojo {
// static
NativeStructPtr NativeStruct::New() {
- return NativeStructPtr(base::in_place);
+ NativeStructPtr rv;
+ internal::StructHelper<NativeStruct>::Initialize(&rv);
+ return rv;
}
-NativeStruct::NativeStruct() {}
+NativeStruct::NativeStruct() : data(nullptr) {}
NativeStruct::~NativeStruct() {}
NativeStructPtr NativeStruct::Clone() const {
NativeStructPtr rv(New());
- rv->data = data;
+ rv->data = data.Clone();
return rv;
}
bool NativeStruct::Equals(const NativeStruct& other) const {
- return data == other.data;
-}
-
-size_t NativeStruct::Hash(size_t seed) const {
- return internal::Hash(seed, data);
+ return data.Equals(other.data);
}
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/native_struct_data.h b/mojo/public/cpp/bindings/lib/native_struct_data.h
index 1c7cd81..5c58774 100644
--- a/mojo/public/cpp/bindings/lib/native_struct_data.h
+++ b/mojo/public/cpp/bindings/lib/native_struct_data.h
@@ -7,16 +7,16 @@
#include <vector>
-#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/bindings/lib/array_internal.h"
#include "mojo/public/cpp/system/handle.h"
namespace mojo {
namespace internal {
+class Buffer;
class ValidationContext;
-class MOJO_CPP_BINDINGS_EXPORT NativeStruct_Data {
+class NativeStruct_Data {
public:
static bool Validate(const void* data, ValidationContext* validation_context);
diff --git a/mojo/public/cpp/bindings/lib/native_struct_serialization.cc b/mojo/public/cpp/bindings/lib/native_struct_serialization.cc
index fa0dbf3..ac06059 100644
--- a/mojo/public/cpp/bindings/lib/native_struct_serialization.cc
+++ b/mojo/public/cpp/bindings/lib/native_struct_serialization.cc
@@ -15,8 +15,7 @@
SerializationContext* context) {
if (!input)
return 0;
- return internal::PrepareToSerialize<ArrayDataView<uint8_t>>(input->data,
- context);
+ return internal::PrepareToSerialize<Array<uint8_t>>(input->data, context);
}
// static
@@ -32,8 +31,8 @@
Array_Data<uint8_t>* data = nullptr;
const ContainerValidateParams params(0, false, nullptr);
- internal::Serialize<ArrayDataView<uint8_t>>(input->data, buffer, &data,
- ¶ms, context);
+ internal::Serialize<Array<uint8_t>>(input->data, buffer, &data, ¶ms,
+ context);
*output = reinterpret_cast<NativeStruct_Data*>(data);
}
@@ -45,8 +44,7 @@
Array_Data<uint8_t>* data = reinterpret_cast<Array_Data<uint8_t>*>(input);
NativeStructPtr result(NativeStruct::New());
- if (!internal::Deserialize<ArrayDataView<uint8_t>>(data, &result->data,
- context)) {
+ if (!internal::Deserialize<Array<uint8_t>>(data, &result->data, context)) {
output = nullptr;
return false;
}
diff --git a/mojo/public/cpp/bindings/lib/native_struct_serialization.h b/mojo/public/cpp/bindings/lib/native_struct_serialization.h
index 457435b..e64b862 100644
--- a/mojo/public/cpp/bindings/lib/native_struct_serialization.h
+++ b/mojo/public/cpp/bindings/lib/native_struct_serialization.h
@@ -13,14 +13,12 @@
#include "base/logging.h"
#include "base/pickle.h"
#include "ipc/ipc_param_traits.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/bindings/lib/array_internal.h"
#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
#include "mojo/public/cpp/bindings/lib/native_struct_data.h"
#include "mojo/public/cpp/bindings/lib/serialization_forward.h"
#include "mojo/public/cpp/bindings/lib/serialization_util.h"
#include "mojo/public/cpp/bindings/native_struct.h"
-#include "mojo/public/cpp/bindings/native_struct_data_view.h"
namespace mojo {
namespace internal {
@@ -104,7 +102,7 @@
}
};
-struct MOJO_CPP_BINDINGS_EXPORT UnmappedNativeStructSerializerImpl {
+struct UnmappedNativeStructSerializerImpl {
static size_t PrepareToSerialize(const NativeStructPtr& input,
SerializationContext* context);
static void Serialize(const NativeStructPtr& input,
@@ -125,7 +123,7 @@
: public UnmappedNativeStructSerializerImpl {};
template <typename MaybeConstUserType>
-struct Serializer<NativeStructDataView, MaybeConstUserType>
+struct Serializer<NativeStructPtr, MaybeConstUserType>
: public NativeStructSerializerImpl<MaybeConstUserType> {};
} // namespace internal
diff --git a/mojo/public/cpp/bindings/lib/no_interface.cc b/mojo/public/cpp/bindings/lib/no_interface.cc
new file mode 100644
index 0000000..9e0945c
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/no_interface.cc
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/no_interface.h"
+
+namespace mojo {
+
+const char* NoInterface::Name_ = "mojo::NoInterface";
+
+bool NoInterfaceStub::Accept(Message* message) {
+ return false;
+}
+
+bool NoInterfaceStub::AcceptWithResponder(Message* message,
+ MessageReceiver* responder) {
+ return false;
+}
+
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc b/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc
index d451c05..7ee9f8a 100644
--- a/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc
+++ b/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc
@@ -5,10 +5,8 @@
#include "mojo/public/cpp/bindings/pipe_control_message_handler.h"
#include "base/logging.h"
-#include "mojo/public/cpp/bindings/interface_id.h"
#include "mojo/public/cpp/bindings/lib/message_builder.h"
#include "mojo/public/cpp/bindings/lib/serialization.h"
-#include "mojo/public/cpp/bindings/lib/serialization_context.h"
#include "mojo/public/cpp/bindings/lib/validation_context.h"
#include "mojo/public/cpp/bindings/lib/validation_util.h"
#include "mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h"
@@ -43,9 +41,8 @@
}
bool PipeControlMessageHandler::Validate(Message* message) {
- internal::ValidationContext validation_context(message->payload(),
- message->payload_num_bytes(),
- 0, 0, message, description_);
+ internal::ValidationContext validation_context(
+ message->data(), message->data_num_bytes(), 0, message, description_);
if (message->name() == pipe_control::kRunOrClosePipeMessageId) {
if (!internal::ValidateMessageIsRequestWithoutResponse(
@@ -61,25 +58,22 @@
}
bool PipeControlMessageHandler::RunOrClosePipe(Message* message) {
- internal::SerializationContext context;
pipe_control::internal::RunOrClosePipeMessageParams_Data* params =
reinterpret_cast<
pipe_control::internal::RunOrClosePipeMessageParams_Data*>(
message->mutable_payload());
pipe_control::RunOrClosePipeMessageParamsPtr params_ptr;
- internal::Deserialize<pipe_control::RunOrClosePipeMessageParamsDataView>(
- params, ¶ms_ptr, &context);
+ internal::Deserialize<pipe_control::RunOrClosePipeMessageParamsPtr>(
+ params, ¶ms_ptr, &context_);
if (params_ptr->input->is_peer_associated_endpoint_closed_event()) {
- const auto& event =
- params_ptr->input->get_peer_associated_endpoint_closed_event();
-
- base::Optional<DisconnectReason> reason;
- if (event->disconnect_reason) {
- reason.emplace(event->disconnect_reason->custom_reason,
- event->disconnect_reason->description);
- }
- return delegate_->OnPeerAssociatedEndpointClosed(event->id, reason);
+ return delegate_->OnPeerAssociatedEndpointClosed(
+ params_ptr->input->get_peer_associated_endpoint_closed_event()->id);
+ }
+ if (params_ptr->input->is_associated_endpoint_closed_before_sent_event()) {
+ return delegate_->OnAssociatedEndpointClosedBeforeSent(
+ params_ptr->input->get_associated_endpoint_closed_before_sent_event()
+ ->id);
}
DVLOG(1) << "Unsupported command in a RunOrClosePipe message pipe control "
diff --git a/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc b/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc
index 701108e..55ee64b 100644
--- a/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc
+++ b/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc
@@ -11,28 +11,33 @@
#include "base/logging.h"
#include "mojo/public/cpp/bindings/lib/message_builder.h"
#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/cpp/bindings/message.h"
#include "mojo/public/interfaces/bindings/pipe_control_messages.mojom.h"
namespace mojo {
namespace {
-Message ConstructRunOrClosePipeMessage(
- pipe_control::RunOrClosePipeInputPtr input_ptr) {
- internal::SerializationContext context;
+void SendRunOrClosePipeMessage(MessageReceiver* receiver,
+ pipe_control::RunOrClosePipeInputPtr input,
+ internal::SerializationContext* context) {
+ pipe_control::RunOrClosePipeMessageParamsPtr params_ptr(
+ pipe_control::RunOrClosePipeMessageParams::New());
+ params_ptr->input = std::move(input);
- auto params_ptr = pipe_control::RunOrClosePipeMessageParams::New();
- params_ptr->input = std::move(input_ptr);
-
- size_t size = internal::PrepareToSerialize<
- pipe_control::RunOrClosePipeMessageParamsDataView>(params_ptr, &context);
- internal::MessageBuilder builder(pipe_control::kRunOrClosePipeMessageId, 0,
- size, 0);
+ size_t size =
+ internal::PrepareToSerialize<
+ pipe_control::RunOrClosePipeMessageParamsPtr>(params_ptr, context);
+ internal::MessageBuilder builder(pipe_control::kRunOrClosePipeMessageId,
+ size);
pipe_control::internal::RunOrClosePipeMessageParams_Data* params = nullptr;
- internal::Serialize<pipe_control::RunOrClosePipeMessageParamsDataView>(
- params_ptr, builder.buffer(), ¶ms, &context);
+ internal::Serialize<pipe_control::RunOrClosePipeMessageParamsPtr>(
+ params_ptr, builder.buffer(), ¶ms, context);
builder.message()->set_interface_id(kInvalidInterfaceId);
- return std::move(*builder.message());
+ bool ok = receiver->Accept(builder.message());
+ // This return value may be ignored as !ok implies the underlying message pipe
+ // has encountered an error, which will be visible through other means.
+ ALLOW_UNUSED_LOCAL(ok);
}
} // namespace
@@ -40,30 +45,30 @@
PipeControlMessageProxy::PipeControlMessageProxy(MessageReceiver* receiver)
: receiver_(receiver) {}
-void PipeControlMessageProxy::NotifyPeerEndpointClosed(
- InterfaceId id,
- const base::Optional<DisconnectReason>& reason) {
- Message message(ConstructPeerEndpointClosedMessage(id, reason));
- bool ok = receiver_->Accept(&message);
- ALLOW_UNUSED_LOCAL(ok);
-}
-
-// static
-Message PipeControlMessageProxy::ConstructPeerEndpointClosedMessage(
- InterfaceId id,
- const base::Optional<DisconnectReason>& reason) {
- auto event = pipe_control::PeerAssociatedEndpointClosedEvent::New();
+void PipeControlMessageProxy::NotifyPeerEndpointClosed(InterfaceId id) {
+ DCHECK(!IsMasterInterfaceId(id));
+ pipe_control::PeerAssociatedEndpointClosedEventPtr event(
+ pipe_control::PeerAssociatedEndpointClosedEvent::New());
event->id = id;
- if (reason) {
- event->disconnect_reason = pipe_control::DisconnectReason::New();
- event->disconnect_reason->custom_reason = reason->custom_reason;
- event->disconnect_reason->description = reason->description;
- }
- auto input = pipe_control::RunOrClosePipeInput::New();
+ pipe_control::RunOrClosePipeInputPtr input(
+ pipe_control::RunOrClosePipeInput::New());
input->set_peer_associated_endpoint_closed_event(std::move(event));
- return ConstructRunOrClosePipeMessage(std::move(input));
+ SendRunOrClosePipeMessage(receiver_, std::move(input), &context_);
+}
+
+void PipeControlMessageProxy::NotifyEndpointClosedBeforeSent(InterfaceId id) {
+ DCHECK(!IsMasterInterfaceId(id));
+ pipe_control::AssociatedEndpointClosedBeforeSentEventPtr event(
+ pipe_control::AssociatedEndpointClosedBeforeSentEvent::New());
+ event->id = id;
+
+ pipe_control::RunOrClosePipeInputPtr input(
+ pipe_control::RunOrClosePipeInput::New());
+ input->set_associated_endpoint_closed_before_sent_event(std::move(event));
+
+ SendRunOrClosePipeMessage(receiver_, std::move(input), &context_);
}
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/router.cc b/mojo/public/cpp/bindings/lib/router.cc
new file mode 100644
index 0000000..8c1b77d
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/router.cc
@@ -0,0 +1,323 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/public/cpp/bindings/lib/router.h"
+
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/stl_util.h"
+#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
+
+namespace mojo {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+
+namespace {
+
+void DCheckIfInvalid(const base::WeakPtr<Router>& router,
+ const std::string& message) {
+ bool is_valid = router && !router->encountered_error() && router->is_valid();
+ DCHECK(!is_valid) << message;
+}
+
+class ResponderThunk : public MessageReceiverWithStatus {
+ public:
+ explicit ResponderThunk(const base::WeakPtr<Router>& router,
+ scoped_refptr<base::SingleThreadTaskRunner> runner)
+ : router_(router),
+ accept_was_invoked_(false),
+ task_runner_(std::move(runner)) {}
+ ~ResponderThunk() override {
+ if (!accept_was_invoked_) {
+ // The Mojo application handled a message that was expecting a response
+ // but did not send a response.
+ // We raise an error to signal the calling application that an error
+ // condition occurred. Without this the calling application would have no
+ // way of knowing it should stop waiting for a response.
+ if (task_runner_->RunsTasksOnCurrentThread()) {
+ // Please note that even if this code is run from a different task
+ // runner on the same thread as |task_runner_|, it is okay to directly
+ // call Router::RaiseError(), because it will raise error from the
+ // correct task runner asynchronously.
+ if (router_)
+ router_->RaiseError();
+ } else {
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&Router::RaiseError, router_));
+ }
+ }
+ }
+
+ // MessageReceiver implementation:
+ bool Accept(Message* message) override {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ accept_was_invoked_ = true;
+ DCHECK(message->has_flag(Message::kFlagIsResponse));
+
+ bool result = false;
+
+ if (router_)
+ result = router_->Accept(message);
+
+ return result;
+ }
+
+ // MessageReceiverWithStatus implementation:
+ bool IsValid() override {
+ DCHECK(task_runner_->RunsTasksOnCurrentThread());
+ return router_ && !router_->encountered_error() && router_->is_valid();
+ }
+
+ void DCheckInvalid(const std::string& message) override {
+ if (task_runner_->RunsTasksOnCurrentThread()) {
+ DCheckIfInvalid(router_, message);
+ } else {
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&DCheckIfInvalid, router_, message));
+ }
+ }
+
+ private:
+ base::WeakPtr<Router> router_;
+ bool accept_was_invoked_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+};
+
+} // namespace
+
+// ----------------------------------------------------------------------------
+
+Router::SyncResponseInfo::SyncResponseInfo(bool* in_response_received)
+ : response_received(in_response_received) {}
+
+Router::SyncResponseInfo::~SyncResponseInfo() {}
+
+// ----------------------------------------------------------------------------
+
+Router::HandleIncomingMessageThunk::HandleIncomingMessageThunk(Router* router)
+ : router_(router) {
+}
+
+Router::HandleIncomingMessageThunk::~HandleIncomingMessageThunk() {
+}
+
+bool Router::HandleIncomingMessageThunk::Accept(Message* message) {
+ return router_->HandleIncomingMessage(message);
+}
+
+// ----------------------------------------------------------------------------
+
+Router::Router(ScopedMessagePipeHandle message_pipe,
+ FilterChain filters,
+ bool expects_sync_requests,
+ scoped_refptr<base::SingleThreadTaskRunner> runner)
+ : thunk_(this),
+ filters_(std::move(filters)),
+ connector_(std::move(message_pipe),
+ Connector::SINGLE_THREADED_SEND,
+ std::move(runner)),
+ incoming_receiver_(nullptr),
+ next_request_id_(0),
+ testing_mode_(false),
+ pending_task_for_messages_(false),
+ encountered_error_(false),
+ weak_factory_(this) {
+ filters_.SetSink(&thunk_);
+ if (expects_sync_requests)
+ connector_.AllowWokenUpBySyncWatchOnSameThread();
+ connector_.set_incoming_receiver(filters_.GetHead());
+ connector_.set_connection_error_handler(
+ base::Bind(&Router::OnConnectionError, base::Unretained(this)));
+}
+
+Router::~Router() {}
+
+bool Router::Accept(Message* message) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!message->has_flag(Message::kFlagExpectsResponse));
+ return connector_.Accept(message);
+}
+
+bool Router::AcceptWithResponder(Message* message, MessageReceiver* responder) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(message->has_flag(Message::kFlagExpectsResponse));
+
+ // Reserve 0 in case we want it to convey special meaning in the future.
+ uint64_t request_id = next_request_id_++;
+ if (request_id == 0)
+ request_id = next_request_id_++;
+
+ bool is_sync = message->has_flag(Message::kFlagIsSync);
+ message->set_request_id(request_id);
+ if (!connector_.Accept(message))
+ return false;
+
+ if (!is_sync) {
+ // We assume ownership of |responder|.
+ async_responders_[request_id] = base::WrapUnique(responder);
+ return true;
+ }
+
+ SyncCallRestrictions::AssertSyncCallAllowed();
+
+ bool response_received = false;
+ std::unique_ptr<MessageReceiver> sync_responder(responder);
+ sync_responses_.insert(std::make_pair(
+ request_id, base::WrapUnique(new SyncResponseInfo(&response_received))));
+
+ base::WeakPtr<Router> weak_self = weak_factory_.GetWeakPtr();
+ connector_.SyncWatch(&response_received);
+ // Make sure that this instance hasn't been destroyed.
+ if (weak_self) {
+ DCHECK(ContainsKey(sync_responses_, request_id));
+ auto iter = sync_responses_.find(request_id);
+ DCHECK_EQ(&response_received, iter->second->response_received);
+ if (response_received) {
+ std::unique_ptr<Message> response = std::move(iter->second->response);
+ ignore_result(sync_responder->Accept(response.get()));
+ }
+ sync_responses_.erase(iter);
+ }
+
+ // Return true means that we take ownership of |responder|.
+ return true;
+}
+
+void Router::EnableTestingMode() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ testing_mode_ = true;
+ connector_.set_enforce_errors_from_incoming_receiver(false);
+}
+
+bool Router::HandleIncomingMessage(Message* message) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ const bool during_sync_call =
+ connector_.during_sync_handle_watcher_callback();
+ if (!message->has_flag(Message::kFlagIsSync) &&
+ (during_sync_call || !pending_messages_.empty())) {
+ std::unique_ptr<Message> pending_message(new Message);
+ message->MoveTo(pending_message.get());
+ pending_messages_.push(std::move(pending_message));
+
+ if (!pending_task_for_messages_) {
+ pending_task_for_messages_ = true;
+ connector_.task_runner()->PostTask(
+ FROM_HERE, base::Bind(&Router::HandleQueuedMessages,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ return true;
+ }
+
+ return HandleMessageInternal(message);
+}
+
+void Router::HandleQueuedMessages() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(pending_task_for_messages_);
+
+ base::WeakPtr<Router> weak_self = weak_factory_.GetWeakPtr();
+ while (!pending_messages_.empty()) {
+ std::unique_ptr<Message> message(std::move(pending_messages_.front()));
+ pending_messages_.pop();
+
+ bool result = HandleMessageInternal(message.get());
+ if (!weak_self)
+ return;
+
+ if (!result && !testing_mode_) {
+ connector_.RaiseError();
+ break;
+ }
+ }
+
+ pending_task_for_messages_ = false;
+
+ // We may have already seen a connection error from the connector, but
+ // haven't notified the user because we want to process all the queued
+ // messages first. We should do it now.
+ if (connector_.encountered_error() && !encountered_error_)
+ OnConnectionError();
+}
+
+bool Router::HandleMessageInternal(Message* message) {
+ if (message->has_flag(Message::kFlagExpectsResponse)) {
+ if (!incoming_receiver_)
+ return false;
+
+ MessageReceiverWithStatus* responder = new ResponderThunk(
+ weak_factory_.GetWeakPtr(), connector_.task_runner());
+ bool ok = incoming_receiver_->AcceptWithResponder(message, responder);
+ if (!ok)
+ delete responder;
+ return ok;
+
+ } else if (message->has_flag(Message::kFlagIsResponse)) {
+ uint64_t request_id = message->request_id();
+
+ if (message->has_flag(Message::kFlagIsSync)) {
+ auto it = sync_responses_.find(request_id);
+ if (it == sync_responses_.end()) {
+ DCHECK(testing_mode_);
+ return false;
+ }
+ it->second->response.reset(new Message());
+ message->MoveTo(it->second->response.get());
+ *it->second->response_received = true;
+ return true;
+ }
+
+ auto it = async_responders_.find(request_id);
+ if (it == async_responders_.end()) {
+ DCHECK(testing_mode_);
+ return false;
+ }
+ std::unique_ptr<MessageReceiver> responder = std::move(it->second);
+ async_responders_.erase(it);
+ return responder->Accept(message);
+ } else {
+ if (!incoming_receiver_)
+ return false;
+
+ return incoming_receiver_->Accept(message);
+ }
+}
+
+void Router::OnConnectionError() {
+ if (encountered_error_)
+ return;
+
+ if (!pending_messages_.empty()) {
+ // After all the pending messages are processed, we will check whether an
+ // error has been encountered and run the user's connection error handler
+ // if necessary.
+ DCHECK(pending_task_for_messages_);
+ return;
+ }
+
+ if (connector_.during_sync_handle_watcher_callback()) {
+ // We don't want the error handler to reenter an ongoing sync call.
+ connector_.task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&Router::OnConnectionError, weak_factory_.GetWeakPtr()));
+ return;
+ }
+
+ encountered_error_ = true;
+ if (!error_handler_.is_null())
+ error_handler_.Run();
+}
+
+// ----------------------------------------------------------------------------
+
+} // namespace internal
+} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/router.h b/mojo/public/cpp/bindings/lib/router.h
new file mode 100644
index 0000000..6dbe08d
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/router.h
@@ -0,0 +1,177 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <queue>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/public/cpp/bindings/connector.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+
+namespace mojo {
+namespace internal {
+
+// TODO(yzshen): Consider removing this class and use MultiplexRouter in all
+// cases. crbug.com/594244
+class Router : public MessageReceiverWithResponder {
+ public:
+ Router(ScopedMessagePipeHandle message_pipe,
+ FilterChain filters,
+ bool expects_sync_requests,
+ scoped_refptr<base::SingleThreadTaskRunner> runner);
+ ~Router() override;
+
+ // Sets the receiver to handle messages read from the message pipe that do
+ // not have the Message::kFlagIsResponse flag set.
+ void set_incoming_receiver(MessageReceiverWithResponderStatus* receiver) {
+ incoming_receiver_ = receiver;
+ }
+
+ // Sets the error handler to receive notifications when an error is
+ // encountered while reading from the pipe or waiting to read from the pipe.
+ void set_connection_error_handler(const base::Closure& error_handler) {
+ error_handler_ = error_handler;
+ }
+
+ // Returns true if an error was encountered while reading from the pipe or
+ // waiting to read from the pipe.
+ bool encountered_error() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return encountered_error_;
+ }
+
+ // Is the router bound to a MessagePipe handle?
+ bool is_valid() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return connector_.is_valid();
+ }
+
+ // Please note that this method shouldn't be called unless it results from an
+ // explicit request of the user of bindings (e.g., the user sets an
+ // InterfacePtr to null or closes a Binding).
+ void CloseMessagePipe() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ connector_.CloseMessagePipe();
+ }
+
+ ScopedMessagePipeHandle PassMessagePipe() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return connector_.PassMessagePipe();
+ }
+
+ void RaiseError() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ connector_.RaiseError();
+ }
+
+ // MessageReceiver implementation:
+ bool Accept(Message* message) override;
+ bool AcceptWithResponder(Message* message,
+ MessageReceiver* responder) override;
+
+ // Blocks the current thread until the first incoming method call, i.e.,
+ // either a call to a client method or a callback method, or |deadline|.
+ bool WaitForIncomingMessage(MojoDeadline deadline) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return connector_.WaitForIncomingMessage(deadline);
+ }
+
+ // See Binding for details of pause/resume.
+ void PauseIncomingMethodCallProcessing() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ connector_.PauseIncomingMethodCallProcessing();
+ }
+ void ResumeIncomingMethodCallProcessing() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ connector_.ResumeIncomingMethodCallProcessing();
+ }
+
+ // Sets this object to testing mode.
+ // In testing mode:
+ // - the object is more tolerant of unrecognized response messages;
+ // - the connector continues working after seeing errors from its incoming
+ // receiver.
+ void EnableTestingMode();
+
+ MessagePipeHandle handle() const { return connector_.handle(); }
+
+ // Returns true if this Router has any pending callbacks.
+ bool has_pending_responders() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return !async_responders_.empty() || !sync_responses_.empty();
+ }
+
+ private:
+ // Maps from the id of a response to the MessageReceiver that handles the
+ // response.
+ using AsyncResponderMap =
+ std::map<uint64_t, std::unique_ptr<MessageReceiver>>;
+
+ struct SyncResponseInfo {
+ public:
+ explicit SyncResponseInfo(bool* in_response_received);
+ ~SyncResponseInfo();
+
+ std::unique_ptr<Message> response;
+
+ // Points to a stack-allocated variable.
+ bool* response_received;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SyncResponseInfo);
+ };
+
+ using SyncResponseMap = std::map<uint64_t, std::unique_ptr<SyncResponseInfo>>;
+
+ class HandleIncomingMessageThunk : public MessageReceiver {
+ public:
+ HandleIncomingMessageThunk(Router* router);
+ ~HandleIncomingMessageThunk() override;
+
+ // MessageReceiver implementation:
+ bool Accept(Message* message) override;
+
+ private:
+ Router* router_;
+ };
+
+ bool HandleIncomingMessage(Message* message);
+ void HandleQueuedMessages();
+ bool HandleMessageInternal(Message* message);
+
+ void OnConnectionError();
+
+ HandleIncomingMessageThunk thunk_;
+ FilterChain filters_;
+ Connector connector_;
+ MessageReceiverWithResponderStatus* incoming_receiver_;
+ AsyncResponderMap async_responders_;
+ SyncResponseMap sync_responses_;
+ uint64_t next_request_id_;
+ bool testing_mode_;
+ std::queue<std::unique_ptr<Message>> pending_messages_;
+ // Whether a task has been posted to trigger processing of
+ // |pending_messages_|.
+ bool pending_task_for_messages_;
+ bool encountered_error_;
+ base::Closure error_handler_;
+ base::ThreadChecker thread_checker_;
+ base::WeakPtrFactory<Router> weak_factory_;
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_
diff --git a/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc b/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc
index c134507..f54c3f7 100644
--- a/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc
+++ b/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc
@@ -4,379 +4,69 @@
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
-#include "base/bind.h"
#include "base/logging.h"
-#include "base/synchronization/lock.h"
#include "mojo/public/cpp/bindings/associated_group_controller.h"
-#include "mojo/public/cpp/bindings/lib/may_auto_lock.h"
namespace mojo {
-// ScopedInterfaceEndpointHandle::State ----------------------------------------
-
-// State could be called from multiple threads.
-class ScopedInterfaceEndpointHandle::State
- : public base::RefCountedThreadSafe<State> {
- public:
- State() = default;
-
- State(InterfaceId id,
- scoped_refptr<AssociatedGroupController> group_controller)
- : id_(id), group_controller_(group_controller) {}
-
- void InitPendingState(scoped_refptr<State> peer) {
- DCHECK(!lock_);
- DCHECK(!pending_association_);
-
- lock_.emplace();
- pending_association_ = true;
- peer_state_ = std::move(peer);
- }
-
- void Close(const base::Optional<DisconnectReason>& reason) {
- scoped_refptr<AssociatedGroupController> cached_group_controller;
- InterfaceId cached_id = kInvalidInterfaceId;
- scoped_refptr<State> cached_peer_state;
-
- {
- internal::MayAutoLock locker(&lock_);
-
- if (!association_event_handler_.is_null()) {
- association_event_handler_.Reset();
- runner_ = nullptr;
- }
-
- if (!pending_association_) {
- if (IsValidInterfaceId(id_)) {
- // Intentionally keep |group_controller_| unchanged.
- // That is because the callback created by
- // CreateGroupControllerGetter() could still be used after this point,
- // potentially from another thread. We would like it to continue
- // returning the same group controller.
- //
- // Imagine there is a ThreadSafeForwarder A:
- // (1) On the IO thread, A's underlying associated interface pointer
- // is closed.
- // (2) On the proxy thread, the user makes a call on A to pass an
- // associated request B_asso_req. The callback returned by
- // CreateGroupControllerGetter() is used to associate B_asso_req.
- // (3) On the proxy thread, the user immediately binds B_asso_ptr_info
- // to B_asso_ptr and makes calls on it.
- //
- // If we reset |group_controller_| in step (1), step (2) won't be able
- // to associate B_asso_req. Therefore, in step (3) B_asso_ptr won't be
- // able to serialize associated endpoints or send message because it
- // is still in "pending_association" state and doesn't have a group
- // controller.
- //
- // We could "address" this issue by ignoring messages if there isn't a
- // group controller. But the side effect is that we cannot detect
- // programming errors of "using associated interface pointer before
- // sending associated request".
-
- cached_group_controller = group_controller_;
- cached_id = id_;
- id_ = kInvalidInterfaceId;
- }
- } else {
- pending_association_ = false;
- cached_peer_state = std::move(peer_state_);
- }
- }
-
- if (cached_group_controller) {
- cached_group_controller->CloseEndpointHandle(cached_id, reason);
- } else if (cached_peer_state) {
- cached_peer_state->OnPeerClosedBeforeAssociation(reason);
- }
- }
-
- void SetAssociationEventHandler(AssociationEventCallback handler) {
- internal::MayAutoLock locker(&lock_);
-
- if (!pending_association_ && !IsValidInterfaceId(id_))
- return;
-
- association_event_handler_ = std::move(handler);
- if (association_event_handler_.is_null()) {
- runner_ = nullptr;
- return;
- }
-
- runner_ = base::ThreadTaskRunnerHandle::Get();
- if (!pending_association_) {
- runner_->PostTask(
- FROM_HERE,
- base::Bind(
- &ScopedInterfaceEndpointHandle::State::RunAssociationEventHandler,
- this, runner_, ASSOCIATED));
- } else if (!peer_state_) {
- runner_->PostTask(
- FROM_HERE,
- base::Bind(
- &ScopedInterfaceEndpointHandle::State::RunAssociationEventHandler,
- this, runner_, PEER_CLOSED_BEFORE_ASSOCIATION));
- }
- }
-
- bool NotifyAssociation(
- InterfaceId id,
- scoped_refptr<AssociatedGroupController> peer_group_controller) {
- scoped_refptr<State> cached_peer_state;
- {
- internal::MayAutoLock locker(&lock_);
-
- DCHECK(pending_association_);
- pending_association_ = false;
- cached_peer_state = std::move(peer_state_);
- }
-
- if (cached_peer_state) {
- cached_peer_state->OnAssociated(id, std::move(peer_group_controller));
- return true;
- }
- return false;
- }
-
- bool is_valid() const {
- internal::MayAutoLock locker(&lock_);
- return pending_association_ || IsValidInterfaceId(id_);
- }
-
- bool pending_association() const {
- internal::MayAutoLock locker(&lock_);
- return pending_association_;
- }
-
- InterfaceId id() const {
- internal::MayAutoLock locker(&lock_);
- return id_;
- }
-
- AssociatedGroupController* group_controller() const {
- internal::MayAutoLock locker(&lock_);
- return group_controller_.get();
- }
-
- const base::Optional<DisconnectReason>& disconnect_reason() const {
- internal::MayAutoLock locker(&lock_);
- return disconnect_reason_;
- }
-
- private:
- friend class base::RefCountedThreadSafe<State>;
-
- ~State() {
- DCHECK(!pending_association_);
- DCHECK(!IsValidInterfaceId(id_));
- }
-
- // Called by the peer, maybe from a different thread.
- void OnAssociated(InterfaceId id,
- scoped_refptr<AssociatedGroupController> group_controller) {
- AssociationEventCallback handler;
- {
- internal::MayAutoLock locker(&lock_);
-
- // There may be race between Close() of endpoint A and
- // NotifyPeerAssociation() of endpoint A_peer on different threads.
- // Therefore, it is possible that endpoint A has been closed but it
- // still gets OnAssociated() call from its peer.
- if (!pending_association_)
- return;
-
- pending_association_ = false;
- peer_state_ = nullptr;
- id_ = id;
- group_controller_ = std::move(group_controller);
-
- if (!association_event_handler_.is_null()) {
- if (runner_->BelongsToCurrentThread()) {
- handler = std::move(association_event_handler_);
- runner_ = nullptr;
- } else {
- runner_->PostTask(FROM_HERE,
- base::Bind(&ScopedInterfaceEndpointHandle::State::
- RunAssociationEventHandler,
- this, runner_, ASSOCIATED));
- }
- }
- }
-
- if (!handler.is_null())
- std::move(handler).Run(ASSOCIATED);
- }
-
- // Called by the peer, maybe from a different thread.
- void OnPeerClosedBeforeAssociation(
- const base::Optional<DisconnectReason>& reason) {
- AssociationEventCallback handler;
- {
- internal::MayAutoLock locker(&lock_);
-
- // There may be race between Close()/NotifyPeerAssociation() of endpoint
- // A and Close() of endpoint A_peer on different threads.
- // Therefore, it is possible that endpoint A is not in pending association
- // state but still gets OnPeerClosedBeforeAssociation() call from its
- // peer.
- if (!pending_association_)
- return;
-
- disconnect_reason_ = reason;
- // NOTE: This handle itself is still pending.
- peer_state_ = nullptr;
-
- if (!association_event_handler_.is_null()) {
- if (runner_->BelongsToCurrentThread()) {
- handler = std::move(association_event_handler_);
- runner_ = nullptr;
- } else {
- runner_->PostTask(
- FROM_HERE,
- base::Bind(&ScopedInterfaceEndpointHandle::State::
- RunAssociationEventHandler,
- this, runner_, PEER_CLOSED_BEFORE_ASSOCIATION));
- }
- }
- }
-
- if (!handler.is_null())
- std::move(handler).Run(PEER_CLOSED_BEFORE_ASSOCIATION);
- }
-
- void RunAssociationEventHandler(
- scoped_refptr<base::SingleThreadTaskRunner> posted_to_runner,
- AssociationEvent event) {
- AssociationEventCallback handler;
-
- {
- internal::MayAutoLock locker(&lock_);
- if (posted_to_runner == runner_) {
- runner_ = nullptr;
- handler = std::move(association_event_handler_);
- }
- }
-
- if (!handler.is_null())
- std::move(handler).Run(event);
- }
-
- // Protects the following members if the handle is initially set to pending
- // association.
- mutable base::Optional<base::Lock> lock_;
-
- bool pending_association_ = false;
- base::Optional<DisconnectReason> disconnect_reason_;
-
- scoped_refptr<State> peer_state_;
-
- AssociationEventCallback association_event_handler_;
- scoped_refptr<base::SingleThreadTaskRunner> runner_;
-
- InterfaceId id_ = kInvalidInterfaceId;
- scoped_refptr<AssociatedGroupController> group_controller_;
-
- DISALLOW_COPY_AND_ASSIGN(State);
-};
-
-// ScopedInterfaceEndpointHandle -----------------------------------------------
-
-// static
-void ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(
- ScopedInterfaceEndpointHandle* handle0,
- ScopedInterfaceEndpointHandle* handle1) {
- ScopedInterfaceEndpointHandle result0;
- ScopedInterfaceEndpointHandle result1;
- result0.state_->InitPendingState(result1.state_);
- result1.state_->InitPendingState(result0.state_);
-
- *handle0 = std::move(result0);
- *handle1 = std::move(result1);
-}
-
ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle()
- : state_(new State) {}
+ : ScopedInterfaceEndpointHandle(kInvalidInterfaceId, true, nullptr) {}
ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle(
ScopedInterfaceEndpointHandle&& other)
- : state_(new State) {
- state_.swap(other.state_);
+ : id_(other.id_), is_local_(other.is_local_) {
+ group_controller_.swap(other.group_controller_);
+ other.id_ = kInvalidInterfaceId;
}
ScopedInterfaceEndpointHandle::~ScopedInterfaceEndpointHandle() {
- state_->Close(base::nullopt);
+ reset();
}
ScopedInterfaceEndpointHandle& ScopedInterfaceEndpointHandle::operator=(
ScopedInterfaceEndpointHandle&& other) {
reset();
- state_.swap(other.state_);
+ swap(other);
+
return *this;
}
-bool ScopedInterfaceEndpointHandle::is_valid() const {
- return state_->is_valid();
-}
-
-bool ScopedInterfaceEndpointHandle::pending_association() const {
- return state_->pending_association();
-}
-
-InterfaceId ScopedInterfaceEndpointHandle::id() const {
- return state_->id();
-}
-
-AssociatedGroupController* ScopedInterfaceEndpointHandle::group_controller()
- const {
- return state_->group_controller();
-}
-
-const base::Optional<DisconnectReason>&
-ScopedInterfaceEndpointHandle::disconnect_reason() const {
- return state_->disconnect_reason();
-}
-
-void ScopedInterfaceEndpointHandle::SetAssociationEventHandler(
- AssociationEventCallback handler) {
- state_->SetAssociationEventHandler(std::move(handler));
-}
-
void ScopedInterfaceEndpointHandle::reset() {
- ResetInternal(base::nullopt);
+ if (!IsValidInterfaceId(id_))
+ return;
+
+ group_controller_->CloseEndpointHandle(id_, is_local_);
+
+ id_ = kInvalidInterfaceId;
+ is_local_ = true;
+ group_controller_ = nullptr;
}
-void ScopedInterfaceEndpointHandle::ResetWithReason(
- uint32_t custom_reason,
- const std::string& description) {
- ResetInternal(DisconnectReason(custom_reason, description));
+void ScopedInterfaceEndpointHandle::swap(ScopedInterfaceEndpointHandle& other) {
+ using std::swap;
+ swap(other.id_, id_);
+ swap(other.is_local_, is_local_);
+ swap(other.group_controller_, group_controller_);
+}
+
+InterfaceId ScopedInterfaceEndpointHandle::release() {
+ InterfaceId result = id_;
+
+ id_ = kInvalidInterfaceId;
+ is_local_ = true;
+ group_controller_ = nullptr;
+
+ return result;
}
ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle(
InterfaceId id,
+ bool is_local,
scoped_refptr<AssociatedGroupController> group_controller)
- : state_(new State(id, std::move(group_controller))) {
- DCHECK(!IsValidInterfaceId(state_->id()) || state_->group_controller());
-}
-
-bool ScopedInterfaceEndpointHandle::NotifyAssociation(
- InterfaceId id,
- scoped_refptr<AssociatedGroupController> peer_group_controller) {
- return state_->NotifyAssociation(id, peer_group_controller);
-}
-
-void ScopedInterfaceEndpointHandle::ResetInternal(
- const base::Optional<DisconnectReason>& reason) {
- scoped_refptr<State> new_state(new State);
- state_->Close(reason);
- state_.swap(new_state);
-}
-
-base::Callback<AssociatedGroupController*()>
-ScopedInterfaceEndpointHandle::CreateGroupControllerGetter() const {
- // We allow this callback to be run on any thread. If this handle is created
- // in non-pending state, we don't have a lock but it should still be safe
- // because the group controller never changes.
- return base::Bind(&State::group_controller, state_);
+ : id_(id),
+ is_local_(is_local),
+ group_controller_(std::move(group_controller)) {
+ DCHECK(!IsValidInterfaceId(id) || group_controller_);
}
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/serialization.h b/mojo/public/cpp/bindings/lib/serialization.h
index 359b02b..6d7dd8e 100644
--- a/mojo/public/cpp/bindings/lib/serialization.h
+++ b/mojo/public/cpp/bindings/lib/serialization.h
@@ -8,16 +8,19 @@
#include <string.h>
#include "mojo/public/cpp/bindings/array_traits_carray.h"
+#include "mojo/public/cpp/bindings/array_traits_standard.h"
#include "mojo/public/cpp/bindings/array_traits_stl.h"
#include "mojo/public/cpp/bindings/lib/array_serialization.h"
-#include "mojo/public/cpp/bindings/lib/buffer.h"
+#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
#include "mojo/public/cpp/bindings/lib/handle_interface_serialization.h"
#include "mojo/public/cpp/bindings/lib/map_serialization.h"
#include "mojo/public/cpp/bindings/lib/native_enum_serialization.h"
#include "mojo/public/cpp/bindings/lib/native_struct_serialization.h"
#include "mojo/public/cpp/bindings/lib/string_serialization.h"
#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/map_traits_standard.h"
#include "mojo/public/cpp/bindings/map_traits_stl.h"
+#include "mojo/public/cpp/bindings/string_traits_standard.h"
#include "mojo/public/cpp/bindings/string_traits_stl.h"
#include "mojo/public/cpp/bindings/string_traits_string16.h"
#include "mojo/public/cpp/bindings/string_traits_string_piece.h"
@@ -41,7 +44,7 @@
void* result_buffer = &result.front();
// The serialization logic requires that the buffer is 8-byte aligned. If the
// result buffer is not properly aligned, we have to do an extra copy. In
- // practice, this should never happen for std::vector.
+ // practice, this should never happen for mojo::Array (backed by std::vector).
bool need_copy = !IsAligned(result_buffer);
if (need_copy) {
@@ -50,9 +53,9 @@
DCHECK(IsAligned(result_buffer));
}
- Buffer buffer;
+ FixedBuffer buffer;
buffer.Initialize(result_buffer, size);
- typename MojomTypeTraits<MojomType>::Data* data = nullptr;
+ typename MojomType::Struct::Data_* data = nullptr;
Serialize<MojomType>(*input, &buffer, &data, &context);
if (need_copy) {
@@ -67,11 +70,13 @@
bool StructDeserializeImpl(const DataArrayType& input, UserType* output) {
static_assert(BelongsTo<MojomType, MojomTypeCategory::STRUCT>::value,
"Unexpected type.");
- using DataType = typename MojomTypeTraits<MojomType>::Data;
+ using DataType = typename MojomType::Struct::Data_;
- // TODO(sammc): Use DataArrayType::empty() once WTF::Vector::empty() exists.
+ if (input.is_null())
+ return false;
+
void* input_buffer =
- input.size() == 0
+ input.empty()
? nullptr
: const_cast<void*>(reinterpret_cast<const void*>(&input.front()));
@@ -84,7 +89,7 @@
memcpy(input_buffer, &input.front(), input.size());
}
- ValidationContext validation_context(input_buffer, input.size(), 0, 0);
+ ValidationContext validation_context(input_buffer, input.size(), 0);
bool result = false;
if (DataType::Validate(input_buffer, &validation_context)) {
auto data = reinterpret_cast<DataType*>(input_buffer);
diff --git a/mojo/public/cpp/bindings/lib/serialization_context.cc b/mojo/public/cpp/bindings/lib/serialization_context.cc
index e2fd5c6..7fd80be 100644
--- a/mojo/public/cpp/bindings/lib/serialization_context.cc
+++ b/mojo/public/cpp/bindings/lib/serialization_context.cc
@@ -7,6 +7,7 @@
#include <limits>
#include "base/logging.h"
+#include "mojo/public/cpp/bindings/associated_group_controller.h"
#include "mojo/public/cpp/system/core.h"
namespace mojo {
@@ -49,6 +50,10 @@
SerializationContext::SerializationContext() {}
+SerializationContext::SerializationContext(
+ scoped_refptr<AssociatedGroupController> in_group_controller)
+ : group_controller(std::move(in_group_controller)) {}
+
SerializationContext::~SerializationContext() {
DCHECK(!custom_contexts || custom_contexts->empty());
}
diff --git a/mojo/public/cpp/bindings/lib/serialization_context.h b/mojo/public/cpp/bindings/lib/serialization_context.h
index a34fe3d..64d2a1a 100644
--- a/mojo/public/cpp/bindings/lib/serialization_context.h
+++ b/mojo/public/cpp/bindings/lib/serialization_context.h
@@ -12,16 +12,18 @@
#include <vector>
#include "base/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
+#include "base/memory/ref_counted.h"
#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
-#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
#include "mojo/public/cpp/system/handle.h"
namespace mojo {
+
+class AssociatedGroupController;
+
namespace internal {
// A container for handles during serialization/deserialization.
-class MOJO_CPP_BINDINGS_EXPORT SerializedHandleVector {
+class SerializedHandleVector {
public:
SerializedHandleVector();
~SerializedHandleVector();
@@ -52,23 +54,21 @@
};
// Context information for serialization/deserialization routines.
-struct MOJO_CPP_BINDINGS_EXPORT SerializationContext {
+struct SerializationContext {
SerializationContext();
+ explicit SerializationContext(
+ scoped_refptr<AssociatedGroupController> in_group_controller);
~SerializationContext();
+ // Used to serialize/deserialize associated interface pointers and requests.
+ scoped_refptr<AssociatedGroupController> group_controller;
+
// Opaque context pointers returned by StringTraits::SetUpContext().
std::unique_ptr<std::queue<void*>> custom_contexts;
// Stashes handles encoded in a message by index.
SerializedHandleVector handles;
-
- // The number of ScopedInterfaceEndpointHandles that need to be serialized.
- // It is calculated by PrepareToSerialize().
- uint32_t associated_endpoint_count = 0;
-
- // Stashes ScopedInterfaceEndpointHandles encoded in a message by index.
- std::vector<ScopedInterfaceEndpointHandle> associated_endpoint_handles;
};
} // namespace internal
diff --git a/mojo/public/cpp/bindings/lib/serialization_forward.h b/mojo/public/cpp/bindings/lib/serialization_forward.h
index 55c9982..5bed126 100644
--- a/mojo/public/cpp/bindings/lib/serialization_forward.h
+++ b/mojo/public/cpp/bindings/lib/serialization_forward.h
@@ -12,7 +12,6 @@
#include "mojo/public/cpp/bindings/map_traits.h"
#include "mojo/public/cpp/bindings/string_traits.h"
#include "mojo/public/cpp/bindings/struct_traits.h"
-#include "mojo/public/cpp/bindings/union_traits.h"
// This file is included by serialization implementation files to avoid circular
// includes.
diff --git a/mojo/public/cpp/bindings/lib/string_serialization.h b/mojo/public/cpp/bindings/lib/string_serialization.h
index 6e0c758..5e65891 100644
--- a/mojo/public/cpp/bindings/lib/string_serialization.h
+++ b/mojo/public/cpp/bindings/lib/string_serialization.h
@@ -11,14 +11,14 @@
#include "mojo/public/cpp/bindings/lib/array_internal.h"
#include "mojo/public/cpp/bindings/lib/serialization_forward.h"
#include "mojo/public/cpp/bindings/lib/serialization_util.h"
-#include "mojo/public/cpp/bindings/string_data_view.h"
+#include "mojo/public/cpp/bindings/string.h"
#include "mojo/public/cpp/bindings/string_traits.h"
namespace mojo {
namespace internal {
template <typename MaybeConstUserType>
-struct Serializer<StringDataView, MaybeConstUserType> {
+struct Serializer<String, MaybeConstUserType> {
using UserType = typename std::remove_const<MaybeConstUserType>::type;
using Traits = StringTraits<UserType>;
@@ -60,7 +60,7 @@
SerializationContext* context) {
if (!input)
return CallSetToNullIfExists<Traits>(output);
- return Traits::Read(StringDataView(input, context), output);
+ return Traits::Read(StringDataView(input), output);
}
};
diff --git a/mojo/public/cpp/bindings/lib/string_traits_wtf.cc b/mojo/public/cpp/bindings/lib/string_traits_wtf.cc
index 203f6f5..19fa907 100644
--- a/mojo/public/cpp/bindings/lib/string_traits_wtf.cc
+++ b/mojo/public/cpp/bindings/lib/string_traits_wtf.cc
@@ -16,7 +16,7 @@
struct UTF8AdaptorInfo {
explicit UTF8AdaptorInfo(const WTF::String& input) : utf8_adaptor(input) {
#if DCHECK_IS_ON()
- original_size_in_bytes = input.charactersSizeInBytes();
+ original_size_in_bytes = static_cast<size_t>(input.sizeInBytes());
#endif
}
@@ -34,7 +34,8 @@
UTF8AdaptorInfo* adaptor = static_cast<UTF8AdaptorInfo*>(context);
#if DCHECK_IS_ON()
- DCHECK_EQ(adaptor->original_size_in_bytes, input.charactersSizeInBytes());
+ DCHECK_EQ(adaptor->original_size_in_bytes,
+ static_cast<size_t>(input.sizeInBytes()));
#endif
return adaptor;
}
diff --git a/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc b/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc
index 585a8f0..3d864af 100644
--- a/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc
+++ b/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc
@@ -6,7 +6,6 @@
#if ENABLE_SYNC_CALL_RESTRICTIONS
-#include "base/debug/leak_annotations.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/threading/thread_local.h"
@@ -38,7 +37,7 @@
size_t scoped_allow_count_ = 0;
};
-base::LazyInstance<base::ThreadLocalPointer<SyncCallSettings>>::DestructorAtExit
+base::LazyInstance<base::ThreadLocalPointer<SyncCallSettings>>
g_sync_call_settings = LAZY_INSTANCE_INITIALIZER;
// static
@@ -46,7 +45,6 @@
SyncCallSettings* result = g_sync_call_settings.Pointer()->Get();
if (!result) {
result = new SyncCallSettings();
- ANNOTATE_LEAKING_OBJECT_PTR(result);
DCHECK_EQ(result, g_sync_call_settings.Pointer()->Get());
}
return result;
diff --git a/mojo/public/cpp/bindings/lib/sync_handle_registry.cc b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
index 5ae763b..f6372d9 100644
--- a/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
+++ b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
@@ -13,7 +13,7 @@
namespace mojo {
namespace {
-base::LazyInstance<base::ThreadLocalPointer<SyncHandleRegistry>>::Leaky
+base::LazyInstance<base::ThreadLocalPointer<SyncHandleRegistry>>
g_current_sync_handle_watcher = LAZY_INSTANCE_INITIALIZER;
} // namespace
@@ -34,7 +34,7 @@
const HandleCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
- if (base::ContainsKey(handles_, handle))
+ if (ContainsKey(handles_, handle))
return false;
MojoResult result = MojoAddHandle(wait_set_handle_.get().value(),
@@ -48,7 +48,7 @@
void SyncHandleRegistry::UnregisterHandle(const Handle& handle) {
DCHECK(thread_checker_.CalledOnValidThread());
- if (!base::ContainsKey(handles_, handle))
+ if (!ContainsKey(handles_, handle))
return;
MojoResult result =
@@ -107,19 +107,6 @@
SyncHandleRegistry::~SyncHandleRegistry() {
DCHECK(thread_checker_.CalledOnValidThread());
-
- // This object may be destructed after the thread local storage slot used by
- // |g_current_sync_handle_watcher| is reset during thread shutdown.
- // For example, another slot in the thread local storage holds a referrence to
- // this object, and that slot is cleaned up after
- // |g_current_sync_handle_watcher|.
- if (!g_current_sync_handle_watcher.Pointer()->Get())
- return;
-
- // If this breaks, it is likely that the global variable is bulit into and
- // accessed from multiple modules.
- DCHECK_EQ(this, g_current_sync_handle_watcher.Pointer()->Get());
-
g_current_sync_handle_watcher.Pointer()->Set(nullptr);
}
diff --git a/mojo/public/cpp/bindings/lib/sync_handle_registry.h b/mojo/public/cpp/bindings/lib/sync_handle_registry.h
new file mode 100644
index 0000000..d6b8c38
--- /dev/null
+++ b/mojo/public/cpp/bindings/lib/sync_handle_registry.h
@@ -0,0 +1,67 @@
+// 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_SYNC_HANDLE_REGISTRY_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_LIB_SYNC_HANDLE_REGISTRY_H_
+
+#include <unordered_map>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/threading/thread_checker.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+namespace internal {
+
+// SyncHandleRegistry is a thread-local storage to register handles that want to
+// be watched together.
+//
+// This class is not thread safe.
+class SyncHandleRegistry : public base::RefCounted<SyncHandleRegistry> {
+ public:
+ // Returns a thread-local object.
+ static scoped_refptr<SyncHandleRegistry> current();
+
+ using HandleCallback = base::Callback<void(MojoResult)>;
+ bool RegisterHandle(const Handle& handle,
+ MojoHandleSignals handle_signals,
+ const HandleCallback& callback);
+
+ void UnregisterHandle(const Handle& handle);
+
+ // Waits on all the registered handles and runs callbacks synchronously for
+ // those ready handles.
+ // The method:
+ // - returns true when any element of |should_stop| is set to true;
+ // - returns false when any error occurs.
+ bool WatchAllHandles(const bool* should_stop[], size_t count);
+
+ private:
+ friend class base::RefCounted<SyncHandleRegistry>;
+
+ struct HandleHasher {
+ size_t operator()(const Handle& handle) const {
+ return std::hash<uint32_t>()(static_cast<uint32_t>(handle.value()));
+ }
+ };
+ using HandleMap = std::unordered_map<Handle, HandleCallback, HandleHasher>;
+
+ SyncHandleRegistry();
+ ~SyncHandleRegistry();
+
+ HandleMap handles_;
+
+ ScopedHandle wait_set_handle_;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncHandleRegistry);
+};
+
+} // namespace internal
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_SYNC_HANDLE_REGISTRY_H_
diff --git a/mojo/public/cpp/bindings/lib/validation_context.cc b/mojo/public/cpp/bindings/lib/validation_context.cc
index ad0a364..a95e07e 100644
--- a/mojo/public/cpp/bindings/lib/validation_context.cc
+++ b/mojo/public/cpp/bindings/lib/validation_context.cc
@@ -4,7 +4,13 @@
#include "mojo/public/cpp/bindings/lib/validation_context.h"
+#include <stddef.h>
+#include <stdint.h>
+
#include "base/logging.h"
+#include "mojo/public/cpp/bindings/lib/serialization_util.h"
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/system/handle.h"
namespace mojo {
namespace internal {
@@ -12,39 +18,69 @@
ValidationContext::ValidationContext(const void* data,
size_t data_num_bytes,
size_t num_handles,
- size_t num_associated_endpoint_handles,
Message* message,
- const base::StringPiece& description,
- int stack_depth)
+ const base::StringPiece& description)
: message_(message),
description_(description),
data_begin_(reinterpret_cast<uintptr_t>(data)),
data_end_(data_begin_ + data_num_bytes),
handle_begin_(0),
- handle_end_(static_cast<uint32_t>(num_handles)),
- associated_endpoint_handle_begin_(0),
- associated_endpoint_handle_end_(
- static_cast<uint32_t>(num_associated_endpoint_handles)),
- stack_depth_(stack_depth) {
- // Check whether the calculation of |data_end_| or static_cast from size_t to
- // uint32_t causes overflow.
- // They shouldn't happen but they do, set the corresponding range to empty.
+ handle_end_(static_cast<uint32_t>(num_handles)) {
if (data_end_ < data_begin_) {
+ // The calculation of |data_end_| overflowed.
+ // It shouldn't happen but if it does, set the range to empty so
+ // IsValidRange() and ClaimMemory() always fail.
NOTREACHED();
data_end_ = data_begin_;
}
if (handle_end_ < num_handles) {
+ // Assigning |num_handles| to |handle_end_| overflowed.
+ // It shouldn't happen but if it does, set the handle index range to empty.
NOTREACHED();
handle_end_ = 0;
}
- if (associated_endpoint_handle_end_ < num_associated_endpoint_handles) {
- NOTREACHED();
- associated_endpoint_handle_end_ = 0;
- }
}
ValidationContext::~ValidationContext() {
}
+bool ValidationContext::ClaimMemory(const void* position, uint32_t num_bytes) {
+ uintptr_t begin = reinterpret_cast<uintptr_t>(position);
+ uintptr_t end = begin + num_bytes;
+
+ if (!InternalIsValidRange(begin, end))
+ return false;
+
+ data_begin_ = end;
+ return true;
+}
+
+bool ValidationContext::ClaimHandle(const Handle_Data& encoded_handle) {
+ uint32_t index = encoded_handle.value;
+ if (index == kEncodedInvalidHandleValue)
+ return true;
+
+ if (index < handle_begin_ || index >= handle_end_)
+ return false;
+
+ // |index| + 1 shouldn't overflow, because |index| is not the max value of
+ // uint32_t (it is less than |handle_end_|).
+ handle_begin_ = index + 1;
+ return true;
+}
+
+bool ValidationContext::IsValidRange(const void* position,
+ uint32_t num_bytes) const {
+ uintptr_t begin = reinterpret_cast<uintptr_t>(position);
+ uintptr_t end = begin + num_bytes;
+
+ return InternalIsValidRange(begin, end);
+}
+
+bool ValidationContext::InternalIsValidRange(uintptr_t begin,
+ uintptr_t end) const {
+ return end > begin && begin >= data_begin_ && end <= data_end_;
+}
+
} // namespace internal
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/validation_context.h b/mojo/public/cpp/bindings/lib/validation_context.h
index ed6c654..6045ca8 100644
--- a/mojo/public/cpp/bindings/lib/validation_context.h
+++ b/mojo/public/cpp/bindings/lib/validation_context.h
@@ -8,28 +8,23 @@
#include <stddef.h>
#include <stdint.h>
-#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/strings/string_piece.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
-static const int kMaxRecursionDepth = 100;
-
namespace mojo {
+class Handle;
class Message;
namespace internal {
// ValidationContext is used when validating object sizes, pointers and handle
// indices in the payload of incoming messages.
-class MOJO_CPP_BINDINGS_EXPORT ValidationContext {
+class ValidationContext {
public:
// [data, data + data_num_bytes) specifies the initial valid memory range.
// [0, num_handles) specifies the initial valid range of handle indices.
- // [0, num_associated_endpoint_handles) specifies the initial valid range of
- // associated endpoint handle indices.
//
// If provided, |message| and |description| provide additional information
// to use when reporting validation errors. In addition if |message| is
@@ -38,10 +33,8 @@
ValidationContext(const void* data,
size_t data_num_bytes,
size_t num_handles,
- size_t num_associated_endpoint_handles,
Message* message = nullptr,
- const base::StringPiece& description = "",
- int stack_depth = 0);
+ const base::StringPiece& description = "");
~ValidationContext();
@@ -50,97 +43,24 @@
// the comments for IsValidRange().)
// On success, the valid memory range is shrinked to begin right after the end
// of the claimed range.
- bool ClaimMemory(const void* position, uint32_t num_bytes) {
- uintptr_t begin = reinterpret_cast<uintptr_t>(position);
- uintptr_t end = begin + num_bytes;
-
- if (!InternalIsValidRange(begin, end))
- return false;
-
- data_begin_ = end;
- return true;
- }
+ bool ClaimMemory(const void* position, uint32_t num_bytes);
// Claims the specified encoded handle (which is basically a handle index).
// The method succeeds if:
// - |encoded_handle|'s value is |kEncodedInvalidHandleValue|.
// - the handle is contained inside the valid range of handle indices. In this
// case, the valid range is shinked to begin right after the claimed handle.
- bool ClaimHandle(const Handle_Data& encoded_handle) {
- uint32_t index = encoded_handle.value;
- if (index == kEncodedInvalidHandleValue)
- return true;
-
- if (index < handle_begin_ || index >= handle_end_)
- return false;
-
- // |index| + 1 shouldn't overflow, because |index| is not the max value of
- // uint32_t (it is less than |handle_end_|).
- handle_begin_ = index + 1;
- return true;
- }
-
- // Claims the specified encoded associated endpoint handle.
- // The method succeeds if:
- // - |encoded_handle|'s value is |kEncodedInvalidHandleValue|.
- // - the handle is contained inside the valid range of associated endpoint
- // handle indices. In this case, the valid range is shinked to begin right
- // after the claimed handle.
- bool ClaimAssociatedEndpointHandle(
- const AssociatedEndpointHandle_Data& encoded_handle) {
- uint32_t index = encoded_handle.value;
- if (index == kEncodedInvalidHandleValue)
- return true;
-
- if (index < associated_endpoint_handle_begin_ ||
- index >= associated_endpoint_handle_end_)
- return false;
-
- // |index| + 1 shouldn't overflow, because |index| is not the max value of
- // uint32_t (it is less than |associated_endpoint_handle_end_|).
- associated_endpoint_handle_begin_ = index + 1;
- return true;
- }
+ bool ClaimHandle(const Handle_Data& encoded_handle);
// Returns true if the specified range is not empty, and the range is
// contained inside the valid memory range.
- bool IsValidRange(const void* position, uint32_t num_bytes) const {
- uintptr_t begin = reinterpret_cast<uintptr_t>(position);
- uintptr_t end = begin + num_bytes;
-
- return InternalIsValidRange(begin, end);
- }
-
- // This object should be created on the stack once every time we recurse down
- // into a subfield during validation to make sure we don't recurse too deep
- // and blow the stack.
- class ScopedDepthTracker {
- public:
- // |ctx| must outlive this object.
- explicit ScopedDepthTracker(ValidationContext* ctx) : ctx_(ctx) {
- ++ctx_->stack_depth_;
- }
-
- ~ScopedDepthTracker() { --ctx_->stack_depth_; }
-
- private:
- ValidationContext* ctx_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedDepthTracker);
- };
-
- // Returns true if the recursion depth limit has been reached.
- bool ExceedsMaxDepth() WARN_UNUSED_RESULT {
- return stack_depth_ > kMaxRecursionDepth;
- }
+ bool IsValidRange(const void* position, uint32_t num_bytes) const;
Message* message() const { return message_; }
const base::StringPiece& description() const { return description_; }
private:
- bool InternalIsValidRange(uintptr_t begin, uintptr_t end) const {
- return end > begin && begin >= data_begin_ && end <= data_end_;
- }
+ bool InternalIsValidRange(uintptr_t begin, uintptr_t end) const;
Message* const message_;
const base::StringPiece description_;
@@ -153,13 +73,6 @@
uint32_t handle_begin_;
uint32_t handle_end_;
- // [associated_endpoint_handle_begin_, associated_endpoint_handle_end_) is the
- // valid associated endpoint handle index range.
- uint32_t associated_endpoint_handle_begin_;
- uint32_t associated_endpoint_handle_end_;
-
- int stack_depth_;
-
DISALLOW_COPY_AND_ASSIGN(ValidationContext);
};
diff --git a/mojo/public/cpp/bindings/lib/validation_errors.cc b/mojo/public/cpp/bindings/lib/validation_errors.cc
index 904f5e4..90652de 100644
--- a/mojo/public/cpp/bindings/lib/validation_errors.cc
+++ b/mojo/public/cpp/bindings/lib/validation_errors.cc
@@ -14,7 +14,6 @@
ValidationErrorObserverForTesting* g_validation_error_observer = nullptr;
SerializationWarningObserverForTesting* g_serialization_warning_observer =
nullptr;
-bool g_suppress_logging = false;
} // namespace
@@ -56,8 +55,6 @@
return "VALIDATION_ERROR_UNKNOWN_ENUM_VALUE";
case VALIDATION_ERROR_DESERIALIZATION_FAILED:
return "VALIDATION_ERROR_DESERIALIZATION_FAILED";
- case VALIDATION_ERROR_MAX_RECURSION_DEPTH:
- return "VALIDATION_ERROR_MAX_RECURSION_DEPTH";
}
return "Unknown error";
@@ -72,10 +69,8 @@
}
if (description) {
- if (!g_suppress_logging) {
- LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error)
- << " (" << description << ")";
- }
+ LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error) << " ("
+ << description << ")";
if (context->message()) {
context->message()->NotifyBadMessage(
base::StringPrintf("Validation failed for %s [%s (%s)]",
@@ -83,8 +78,7 @@
ValidationErrorToString(error), description));
}
} else {
- if (!g_suppress_logging)
- LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error);
+ LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error);
if (context->message()) {
context->message()->NotifyBadMessage(
base::StringPrintf("Validation failed for %s [%s]",
@@ -94,25 +88,6 @@
}
}
-void ReportValidationErrorForMessage(
- mojo::Message* message,
- ValidationError error,
- const char* description) {
- ValidationContext validation_context(nullptr, 0, 0, 0, message, description);
- ReportValidationError(&validation_context, error);
-}
-
-ScopedSuppressValidationErrorLoggingForTests
- ::ScopedSuppressValidationErrorLoggingForTests()
- : was_suppressed_(g_suppress_logging) {
- g_suppress_logging = true;
-}
-
-ScopedSuppressValidationErrorLoggingForTests
- ::~ScopedSuppressValidationErrorLoggingForTests() {
- g_suppress_logging = was_suppressed_;
-}
-
ValidationErrorObserverForTesting::ValidationErrorObserverForTesting(
const base::Closure& callback)
: last_error_(VALIDATION_ERROR_NONE), callback_(callback) {
diff --git a/mojo/public/cpp/bindings/lib/validation_errors.h b/mojo/public/cpp/bindings/lib/validation_errors.h
index 122418d..ec0aa27 100644
--- a/mojo/public/cpp/bindings/lib/validation_errors.h
+++ b/mojo/public/cpp/bindings/lib/validation_errors.h
@@ -8,13 +8,9 @@
#include "base/callback.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/bindings/lib/validation_context.h"
namespace mojo {
-
-class Message;
-
namespace internal {
enum ValidationError {
@@ -71,41 +67,17 @@
// Message deserialization failure, for example due to rejection by custom
// validation logic.
VALIDATION_ERROR_DESERIALIZATION_FAILED,
- // The message contains a too deeply nested value, for example a recursively
- // defined field which runtime value is too large.
- VALIDATION_ERROR_MAX_RECURSION_DEPTH,
};
-MOJO_CPP_BINDINGS_EXPORT const char* ValidationErrorToString(
- ValidationError error);
+const char* ValidationErrorToString(ValidationError error);
-MOJO_CPP_BINDINGS_EXPORT void ReportValidationError(
- ValidationContext* context,
- ValidationError error,
- const char* description = nullptr);
-
-MOJO_CPP_BINDINGS_EXPORT void ReportValidationErrorForMessage(
- mojo::Message* message,
- ValidationError error,
- const char* description = nullptr);
-
-// This class may be used by tests to suppress validation error logging. This is
-// not thread-safe and must only be instantiated on the main thread with no
-// other threads using Mojo bindings at the time of construction or destruction.
-class MOJO_CPP_BINDINGS_EXPORT ScopedSuppressValidationErrorLoggingForTests {
- public:
- ScopedSuppressValidationErrorLoggingForTests();
- ~ScopedSuppressValidationErrorLoggingForTests();
-
- private:
- const bool was_suppressed_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedSuppressValidationErrorLoggingForTests);
-};
+void ReportValidationError(ValidationContext* context,
+ ValidationError error,
+ const char* description = nullptr);
// Only used by validation tests and when there is only one thread doing message
// validation.
-class MOJO_CPP_BINDINGS_EXPORT ValidationErrorObserverForTesting {
+class ValidationErrorObserverForTesting {
public:
explicit ValidationErrorObserverForTesting(const base::Closure& callback);
~ValidationErrorObserverForTesting();
@@ -127,11 +99,11 @@
//
// The function returns true if the error is recorded (by a
// SerializationWarningObserverForTesting object), false otherwise.
-MOJO_CPP_BINDINGS_EXPORT bool ReportSerializationWarning(ValidationError error);
+bool ReportSerializationWarning(ValidationError error);
// Only used by serialization tests and when there is only one thread doing
// message serialization.
-class MOJO_CPP_BINDINGS_EXPORT SerializationWarningObserverForTesting {
+class SerializationWarningObserverForTesting {
public:
SerializationWarningObserverForTesting();
~SerializationWarningObserverForTesting();
diff --git a/mojo/public/cpp/bindings/lib/validation_util.cc b/mojo/public/cpp/bindings/lib/validation_util.cc
index 7614df5..9e63521 100644
--- a/mojo/public/cpp/bindings/lib/validation_util.cc
+++ b/mojo/public/cpp/bindings/lib/validation_util.cc
@@ -16,6 +16,16 @@
namespace mojo {
namespace internal {
+bool ValidateEncodedPointer(const uint64_t* offset) {
+ // - Make sure |*offset| is no more than 32-bits.
+ // - Cast |offset| to uintptr_t so overflow behavior is well defined across
+ // 32-bit and 64-bit systems.
+ return *offset <= std::numeric_limits<uint32_t>::max() &&
+ (reinterpret_cast<uintptr_t>(offset) +
+ static_cast<uint32_t>(*offset) >=
+ reinterpret_cast<uintptr_t>(offset));
+}
+
bool ValidateStructHeaderAndClaimMemory(const void* data,
ValidationContext* validation_context) {
if (!IsAligned(data)) {
@@ -46,17 +56,20 @@
return true;
}
-bool ValidateNonInlinedUnionHeaderAndClaimMemory(
- const void* data,
- ValidationContext* validation_context) {
+bool ValidateUnionHeaderAndClaimMemory(const void* data,
+ bool inlined,
+ ValidationContext* validation_context) {
if (!IsAligned(data)) {
ReportValidationError(validation_context,
VALIDATION_ERROR_MISALIGNED_OBJECT);
return false;
}
- if (!validation_context->ClaimMemory(data, kUnionDataSize) ||
- *static_cast<const uint32_t*>(data) != kUnionDataSize) {
+ // If the union is inlined in another structure its memory was already
+ // claimed.
+ // This ONLY applies to the union itself, NOT anything which the union points
+ // to.
+ if (!inlined && !validation_context->ClaimMemory(data, kUnionDataSize)) {
ReportValidationError(validation_context,
VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE);
return false;
@@ -100,12 +113,41 @@
return true;
}
-bool IsHandleOrInterfaceValid(const AssociatedInterface_Data& input) {
- return input.handle.is_valid();
+bool ValidateControlRequest(const Message* message,
+ ValidationContext* validation_context) {
+ switch (message->header()->name) {
+ case kRunMessageId:
+ return ValidateMessageIsRequestExpectingResponse(message,
+ validation_context) &&
+ ValidateMessagePayload<RunMessageParams_Data>(message,
+ validation_context);
+ case kRunOrClosePipeMessageId:
+ return ValidateMessageIsRequestWithoutResponse(message,
+ validation_context) &&
+ ValidateMessagePayload<RunOrClosePipeMessageParams_Data>(
+ message, validation_context);
+ }
+ return false;
}
-bool IsHandleOrInterfaceValid(const AssociatedEndpointHandle_Data& input) {
- return input.is_valid();
+bool ValidateControlResponse(const Message* message,
+ ValidationContext* validation_context) {
+ if (!ValidateMessageIsResponse(message, validation_context))
+ return false;
+ switch (message->header()->name) {
+ case kRunMessageId:
+ return ValidateMessagePayload<RunResponseMessageParams_Data>(
+ message, validation_context);
+ }
+ return false;
+}
+
+bool IsHandleOrInterfaceValid(const AssociatedInterface_Data& input) {
+ return IsValidInterfaceId(input.interface_id);
+}
+
+bool IsHandleOrInterfaceValid(const AssociatedInterfaceRequest_Data& input) {
+ return IsValidInterfaceId(input.interface_id);
}
bool IsHandleOrInterfaceValid(const Interface_Data& input) {
@@ -130,7 +172,7 @@
}
bool ValidateHandleOrInterfaceNonNullable(
- const AssociatedEndpointHandle_Data& input,
+ const AssociatedInterfaceRequest_Data& input,
const char* error_message,
ValidationContext* validation_context) {
if (IsHandleOrInterfaceValid(input))
@@ -170,7 +212,7 @@
bool ValidateHandleOrInterface(const AssociatedInterface_Data& input,
ValidationContext* validation_context) {
- if (validation_context->ClaimAssociatedEndpointHandle(input.handle))
+ if (!IsMasterInterfaceId(input.interface_id))
return true;
ReportValidationError(validation_context,
@@ -178,9 +220,9 @@
return false;
}
-bool ValidateHandleOrInterface(const AssociatedEndpointHandle_Data& input,
+bool ValidateHandleOrInterface(const AssociatedInterfaceRequest_Data& input,
ValidationContext* validation_context) {
- if (validation_context->ClaimAssociatedEndpointHandle(input))
+ if (!IsMasterInterfaceId(input.interface_id))
return true;
ReportValidationError(validation_context,
diff --git a/mojo/public/cpp/bindings/lib/validation_util.h b/mojo/public/cpp/bindings/lib/validation_util.h
index ea5a991..c883392 100644
--- a/mojo/public/cpp/bindings/lib/validation_util.h
+++ b/mojo/public/cpp/bindings/lib/validation_util.h
@@ -7,7 +7,6 @@
#include <stdint.h>
-#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
#include "mojo/public/cpp/bindings/lib/serialization_util.h"
#include "mojo/public/cpp/bindings/lib/validate_params.h"
@@ -20,15 +19,7 @@
// Checks whether decoding the pointer will overflow and produce a pointer
// smaller than |offset|.
-inline bool ValidateEncodedPointer(const uint64_t* offset) {
- // - Make sure |*offset| is no more than 32-bits.
- // - Cast |offset| to uintptr_t so overflow behavior is well defined across
- // 32-bit and 64-bit systems.
- return *offset <= std::numeric_limits<uint32_t>::max() &&
- (reinterpret_cast<uintptr_t>(offset) +
- static_cast<uint32_t>(*offset) >=
- reinterpret_cast<uintptr_t>(offset));
-}
+bool ValidateEncodedPointer(const uint64_t* offset);
template <typename T>
bool ValidatePointer(const Pointer<T>& input,
@@ -47,32 +38,30 @@
// |validation_context|. On success, the memory range is marked as occupied.
// Note: Does not verify |version| or that |num_bytes| is correct for the
// claimed version.
-MOJO_CPP_BINDINGS_EXPORT bool ValidateStructHeaderAndClaimMemory(
- const void* data,
- ValidationContext* validation_context);
+bool ValidateStructHeaderAndClaimMemory(const void* data,
+ ValidationContext* validation_context);
// Validates that |data| contains a valid union header, in terms of alignment
-// and size. It checks that the memory range [data, data + kUnionDataSize) is
-// not marked as occupied by other objects in |validation_context|. On success,
-// the memory range is marked as occupied.
-MOJO_CPP_BINDINGS_EXPORT bool ValidateNonInlinedUnionHeaderAndClaimMemory(
- const void* data,
- ValidationContext* validation_context);
+// and size. If not inlined, it checks that the memory range
+// [data, data + num_bytes) is not marked as occupied by other objects in
+// |validation_context|. On success, the memory range is marked as occupied.
+bool ValidateUnionHeaderAndClaimMemory(const void* data,
+ bool inlined,
+ ValidationContext* validation_context);
// Validates that the message is a request which doesn't expect a response.
-MOJO_CPP_BINDINGS_EXPORT bool ValidateMessageIsRequestWithoutResponse(
+bool ValidateMessageIsRequestWithoutResponse(
const Message* message,
ValidationContext* validation_context);
// Validates that the message is a request expecting a response.
-MOJO_CPP_BINDINGS_EXPORT bool ValidateMessageIsRequestExpectingResponse(
+bool ValidateMessageIsRequestExpectingResponse(
const Message* message,
ValidationContext* validation_context);
// Validates that the message is a response.
-MOJO_CPP_BINDINGS_EXPORT bool ValidateMessageIsResponse(
- const Message* message,
- ValidationContext* validation_context);
+bool ValidateMessageIsResponse(const Message* message,
+ ValidationContext* validation_context);
// Validates that the message payload is a valid struct of type ParamsType.
template <typename ParamsType>
@@ -81,6 +70,13 @@
return ParamsType::Validate(message->payload(), validation_context);
}
+// The following methods validate control messages defined in
+// interface_control_messages.mojom.
+bool ValidateControlRequest(const Message* message,
+ ValidationContext* validation_context);
+bool ValidateControlResponse(const Message* message,
+ ValidationContext* validation_context);
+
// The following Validate.*NonNullable() functions validate that the given
// |input| is not null/invalid.
template <typename T>
@@ -109,28 +105,24 @@
return false;
}
-MOJO_CPP_BINDINGS_EXPORT bool IsHandleOrInterfaceValid(
- const AssociatedInterface_Data& input);
-MOJO_CPP_BINDINGS_EXPORT bool IsHandleOrInterfaceValid(
- const AssociatedEndpointHandle_Data& input);
-MOJO_CPP_BINDINGS_EXPORT bool IsHandleOrInterfaceValid(
- const Interface_Data& input);
-MOJO_CPP_BINDINGS_EXPORT bool IsHandleOrInterfaceValid(
- const Handle_Data& input);
+bool IsHandleOrInterfaceValid(const AssociatedInterface_Data& input);
+bool IsHandleOrInterfaceValid(const AssociatedInterfaceRequest_Data& input);
+bool IsHandleOrInterfaceValid(const Interface_Data& input);
+bool IsHandleOrInterfaceValid(const Handle_Data& input);
-MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable(
+bool ValidateHandleOrInterfaceNonNullable(
const AssociatedInterface_Data& input,
const char* error_message,
ValidationContext* validation_context);
-MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable(
- const AssociatedEndpointHandle_Data& input,
+bool ValidateHandleOrInterfaceNonNullable(
+ const AssociatedInterfaceRequest_Data& input,
const char* error_message,
ValidationContext* validation_context);
-MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable(
+bool ValidateHandleOrInterfaceNonNullable(
const Interface_Data& input,
const char* error_message,
ValidationContext* validation_context);
-MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable(
+bool ValidateHandleOrInterfaceNonNullable(
const Handle_Data& input,
const char* error_message,
ValidationContext* validation_context);
@@ -139,12 +131,6 @@
bool ValidateContainer(const Pointer<T>& input,
ValidationContext* validation_context,
const ContainerValidateParams* validate_params) {
- ValidationContext::ScopedDepthTracker depth_tracker(validation_context);
- if (validation_context->ExceedsMaxDepth()) {
- ReportValidationError(validation_context,
- VALIDATION_ERROR_MAX_RECURSION_DEPTH);
- return false;
- }
return ValidatePointer(input, validation_context) &&
T::Validate(input.Get(), validation_context, validate_params);
}
@@ -152,12 +138,6 @@
template <typename T>
bool ValidateStruct(const Pointer<T>& input,
ValidationContext* validation_context) {
- ValidationContext::ScopedDepthTracker depth_tracker(validation_context);
- if (validation_context->ExceedsMaxDepth()) {
- ReportValidationError(validation_context,
- VALIDATION_ERROR_MAX_RECURSION_DEPTH);
- return false;
- }
return ValidatePointer(input, validation_context) &&
T::Validate(input.Get(), validation_context);
}
@@ -165,40 +145,24 @@
template <typename T>
bool ValidateInlinedUnion(const T& input,
ValidationContext* validation_context) {
- ValidationContext::ScopedDepthTracker depth_tracker(validation_context);
- if (validation_context->ExceedsMaxDepth()) {
- ReportValidationError(validation_context,
- VALIDATION_ERROR_MAX_RECURSION_DEPTH);
- return false;
- }
return T::Validate(&input, validation_context, true);
}
template <typename T>
bool ValidateNonInlinedUnion(const Pointer<T>& input,
ValidationContext* validation_context) {
- ValidationContext::ScopedDepthTracker depth_tracker(validation_context);
- if (validation_context->ExceedsMaxDepth()) {
- ReportValidationError(validation_context,
- VALIDATION_ERROR_MAX_RECURSION_DEPTH);
- return false;
- }
return ValidatePointer(input, validation_context) &&
T::Validate(input.Get(), validation_context, false);
}
-MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterface(
- const AssociatedInterface_Data& input,
- ValidationContext* validation_context);
-MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterface(
- const AssociatedEndpointHandle_Data& input,
- ValidationContext* validation_context);
-MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterface(
- const Interface_Data& input,
- ValidationContext* validation_context);
-MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterface(
- const Handle_Data& input,
- ValidationContext* validation_context);
+bool ValidateHandleOrInterface(const AssociatedInterface_Data& input,
+ ValidationContext* validation_context);
+bool ValidateHandleOrInterface(const AssociatedInterfaceRequest_Data& input,
+ ValidationContext* validation_context);
+bool ValidateHandleOrInterface(const Interface_Data& input,
+ ValidationContext* validation_context);
+bool ValidateHandleOrInterface(const Handle_Data& input,
+ ValidationContext* validation_context);
} // namespace internal
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h b/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h
index cb24bc4..edbf27b 100644
--- a/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h
+++ b/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h
@@ -7,14 +7,14 @@
#include <type_traits>
-#include "mojo/public/cpp/bindings/clone_traits.h"
-#include "mojo/public/cpp/bindings/lib/equals_traits.h"
+#include "mojo/public/cpp/bindings/lib/clone_equals_util.h"
#include "third_party/WebKit/Source/wtf/HashMap.h"
#include "third_party/WebKit/Source/wtf/Optional.h"
#include "third_party/WebKit/Source/wtf/Vector.h"
#include "third_party/WebKit/Source/wtf/text/WTFString.h"
namespace mojo {
+namespace internal {
template <typename T>
struct CloneTraits<WTF::Vector<T>, false> {
@@ -22,7 +22,7 @@
WTF::Vector<T> result;
result.reserveCapacity(input.size());
for (const auto& element : input)
- result.push_back(mojo::Clone(element));
+ result.append(internal::Clone(element));
return result;
}
@@ -34,13 +34,11 @@
WTF::HashMap<K, V> result;
auto input_end = input.end();
for (auto it = input.begin(); it != input_end; ++it)
- result.add(mojo::Clone(it->key), mojo::Clone(it->value));
+ result.add(internal::Clone(it->key), internal::Clone(it->value));
return result;
}
};
-namespace internal {
-
template <typename T>
struct EqualsTraits<WTF::Vector<T>, false> {
static bool Equals(const WTF::Vector<T>& a, const WTF::Vector<T>& b) {
diff --git a/mojo/public/cpp/bindings/lib/wtf_serialization.h b/mojo/public/cpp/bindings/lib/wtf_serialization.h
index 0f112b9..132e19c 100644
--- a/mojo/public/cpp/bindings/lib/wtf_serialization.h
+++ b/mojo/public/cpp/bindings/lib/wtf_serialization.h
@@ -5,7 +5,9 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_WTF_SERIALIZATION_H_
#define MOJO_PUBLIC_CPP_BINDINGS_LIB_WTF_SERIALIZATION_H_
+#include "mojo/public/cpp/bindings/array_traits_wtf.h"
#include "mojo/public/cpp/bindings/array_traits_wtf_vector.h"
+#include "mojo/public/cpp/bindings/map_traits_wtf.h"
#include "mojo/public/cpp/bindings/map_traits_wtf_hash_map.h"
#include "mojo/public/cpp/bindings/string_traits_wtf.h"
diff --git a/mojo/public/cpp/bindings/map.h b/mojo/public/cpp/bindings/map.h
index c1ba075..d4c7952 100644
--- a/mojo/public/cpp/bindings/map.h
+++ b/mojo/public/cpp/bindings/map.h
@@ -5,37 +5,299 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_MAP_H_
#define MOJO_PUBLIC_CPP_BINDINGS_MAP_H_
+#include <stddef.h>
#include <map>
#include <unordered_map>
#include <utility>
+#include "base/logging.h"
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/lib/map_data_internal.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
namespace mojo {
-// TODO(yzshen): These conversion functions should be removed and callsites
-// should be revisited and changed to use the same map type.
-template <typename Key, typename Value>
-std::unordered_map<Key, Value> MapToUnorderedMap(
- const std::map<Key, Value>& input) {
- return std::unordered_map<Key, Value>(input.begin(), input.end());
-}
+// A move-only map that can handle move-only values. Map has the following
+// characteristics:
+// - The map itself can be null, and this is distinct from empty.
+// - Keys must not be move-only.
+// - The Key-type's "<" operator is used to sort the entries, and also is
+// used to determine equality of the key values.
+// - There can only be one entry per unique key.
+// - Values of move-only types will be moved into the Map when they are added
+// using the insert() method.
+template <typename K, typename V>
+class Map {
+ public:
+ using Key = K;
+ using Value = V;
-template <typename Key, typename Value>
-std::unordered_map<Key, Value> MapToUnorderedMap(std::map<Key, Value>&& input) {
- return std::unordered_map<Key, Value>(std::make_move_iterator(input.begin()),
- std::make_move_iterator(input.end()));
-}
+ // Map keys cannot be move only classes.
+ static_assert(!internal::IsMoveOnlyType<Key>::value,
+ "Map keys cannot be move only types.");
-template <typename Key, typename Value>
-std::map<Key, Value> UnorderedMapToMap(
- const std::unordered_map<Key, Value>& input) {
- return std::map<Key, Value>(input.begin(), input.end());
-}
+ using Iterator = typename std::map<Key, Value>::iterator;
+ using ConstIterator = typename std::map<Key, Value>::const_iterator;
-template <typename Key, typename Value>
-std::map<Key, Value> UnorderedMapToMap(std::unordered_map<Key, Value>&& input) {
- return std::map<Key, Value>(std::make_move_iterator(input.begin()),
- std::make_move_iterator(input.end()));
-}
+ // Constructs an empty map.
+ Map() : is_null_(false) {}
+ // Constructs a null map.
+ Map(std::nullptr_t null_pointer) : is_null_(true) {}
+
+ // Constructs a non-null Map containing the specified |keys| mapped to the
+ // corresponding |values|.
+ Map(mojo::Array<Key> keys, mojo::Array<Value> values) : is_null_(false) {
+ DCHECK(keys.size() == values.size());
+ for (size_t i = 0; i < keys.size(); ++i)
+ map_.insert(std::make_pair(keys[i], std::move(values[i])));
+ }
+
+ ~Map() {}
+
+ Map(std::map<Key, Value>&& other) : map_(std::move(other)), is_null_(false) {}
+ Map(Map&& other) : is_null_(true) { Take(&other); }
+
+ Map& operator=(std::map<Key, Value>&& other) {
+ is_null_ = false;
+ map_ = std::move(other);
+ return *this;
+ }
+ Map& operator=(Map&& other) {
+ Take(&other);
+ return *this;
+ }
+
+ Map& operator=(std::nullptr_t null_pointer) {
+ is_null_ = true;
+ map_.clear();
+ return *this;
+ }
+
+ // Copies the contents of some other type of map into a new Map using a
+ // TypeConverter. A TypeConverter for std::map to Map is defined below.
+ template <typename U>
+ static Map From(const U& other) {
+ return TypeConverter<Map, U>::Convert(other);
+ }
+
+ // Copies the contents of the Map into some other type of map. A TypeConverter
+ // for Map to std::map is defined below.
+ template <typename U>
+ U To() const {
+ return TypeConverter<U, Map>::Convert(*this);
+ }
+
+ // Indicates whether the map is null (which is distinct from empty).
+ bool is_null() const { return is_null_; }
+
+ // Indicates whether the map is empty (which is distinct from null).
+ bool empty() const { return map_.empty() && !is_null_; }
+
+ // Indicates the number of keys in the map, which will be zero if the map is
+ // null.
+ size_t size() const { return map_.size(); }
+
+ // Inserts a key-value pair into the map. Like std::map, this does not insert
+ // |value| if |key| is already a member of the map.
+ void insert(const Key& key, const Value& value) {
+ is_null_ = false;
+ map_.insert(std::make_pair(key, value));
+ }
+ void insert(const Key& key, Value&& value) {
+ is_null_ = false;
+ map_.insert(std::make_pair(key, std::move(value)));
+ }
+
+ // Returns a reference to the value associated with the specified key,
+ // crashing the process if the key is not present in the map.
+ Value& at(const Key& key) { return map_.at(key); }
+ const Value& at(const Key& key) const { return map_.at(key); }
+
+ // Returns a reference to the value associated with the specified key,
+ // creating a new entry if the key is not already present in the map. A
+ // newly-created value will be value-initialized (meaning that it will be
+ // initialized by the default constructor of the value type, if any, or else
+ // will be zero-initialized).
+ Value& operator[](const Key& key) {
+ is_null_ = false;
+ return map_[key];
+ }
+
+ // Sets the map to empty (even if previously it was null).
+ void SetToEmpty() {
+ is_null_ = false;
+ map_.clear();
+ }
+
+ // Returns a const reference to the std::map managed by this class. If this
+ // object is null, the return value will be an empty map.
+ const std::map<Key, Value>& storage() const { return map_; }
+
+ // Passes the underlying storage and resets this map to null.
+ std::map<Key, Value> PassStorage() {
+ is_null_ = true;
+ return std::move(map_);
+ }
+
+ operator const std::map<Key, Value>&() const { return map_; }
+
+ // Swaps the contents of this Map with another Map of the same type (including
+ // nullness).
+ void Swap(Map<Key, Value>* other) {
+ std::swap(is_null_, other->is_null_);
+ map_.swap(other->map_);
+ }
+
+ // Swaps the contents of this Map with an std::map containing keys and values
+ // of the same type. Since std::map cannot represent the null state, the
+ // std::map will be empty if Map is null. The Map will always be left in a
+ // non-null state.
+ void Swap(std::map<Key, Value>* other) {
+ is_null_ = false;
+ map_.swap(*other);
+ }
+
+ // Removes all contents from the Map and places them into parallel key/value
+ // arrays. Each key will be copied from the source to the destination, and
+ // values will be copied unless their type is designated move-only, in which
+ // case they will be moved. Either way, the Map will be left in a null state.
+ void DecomposeMapTo(mojo::Array<Key>* keys, mojo::Array<Value>* values) {
+ std::vector<Key> key_vector;
+ key_vector.reserve(map_.size());
+ std::vector<Value> value_vector;
+ value_vector.reserve(map_.size());
+
+ for (auto& entry : map_) {
+ key_vector.push_back(entry.first);
+ value_vector.push_back(std::move(entry.second));
+ }
+
+ map_.clear();
+ is_null_ = true;
+
+ keys->Swap(&key_vector);
+ values->Swap(&value_vector);
+ }
+
+ // Returns a new Map that contains a copy of the contents of this map. If the
+ // key/value type defines a Clone() method, it will be used; otherwise copy
+ // constructor/assignment will be used.
+ //
+ // Please note that calling this method will fail compilation if the key/value
+ // type cannot be cloned (which usually means that it is a Mojo handle type or
+ // a type containing Mojo handles).
+ Map Clone() const {
+ Map result;
+ result.is_null_ = is_null_;
+ for (auto it = map_.begin(); it != map_.end(); ++it) {
+ result.map_.insert(std::make_pair(internal::Clone(it->first),
+ internal::Clone(it->second)));
+ }
+ return result;
+ }
+
+ // Indicates whether the contents of this map are equal to those of another
+ // Map (including nullness). If the key/value type defines an Equals() method,
+ // it will be used; otherwise == operator will be used.
+ bool Equals(const Map& other) const {
+ if (is_null() != other.is_null())
+ return false;
+ if (size() != other.size())
+ return false;
+ auto i = begin();
+ auto j = other.begin();
+ while (i != end()) {
+ if (!internal::Equals(i->first, j->first))
+ return false;
+ if (!internal::Equals(i->second, j->second))
+ return false;
+ ++i;
+ ++j;
+ }
+ return true;
+ }
+
+ // Provide read-only iteration over map members in a way similar to STL
+ // collections.
+ ConstIterator begin() const { return map_.begin(); }
+ Iterator begin() { return map_.begin(); }
+
+ ConstIterator end() const { return map_.end(); }
+ Iterator end() { return map_.end(); }
+
+ // Returns the iterator pointing to the entry for |key|, if present, or else
+ // returns end().
+ ConstIterator find(const Key& key) const { return map_.find(key); }
+ Iterator find(const Key& key) { return map_.find(key); }
+
+ private:
+ typedef std::map<Key, Value> Map::*Testable;
+
+ public:
+ // The Map may be used in boolean expressions to determine if it is non-null,
+ // but is not implicitly convertible to an actual bool value (which would be
+ // dangerous).
+ operator Testable() const { return is_null_ ? 0 : &Map::map_; }
+
+ private:
+ // Forbid the == and != operators explicitly, otherwise Map will be converted
+ // to Testable to do == or != comparison.
+ template <typename T, typename U>
+ bool operator==(const Map<T, U>& other) const = delete;
+ template <typename T, typename U>
+ bool operator!=(const Map<T, U>& other) const = delete;
+
+ void Take(Map* other) {
+ operator=(nullptr);
+ Swap(other);
+ }
+
+ std::map<Key, Value> map_;
+ bool is_null_;
+
+ DISALLOW_COPY_AND_ASSIGN(Map);
+};
+
+// Copies the contents of an std::map to a new Map, optionally changing the
+// types of the keys and values along the way using TypeConverter.
+template <typename MojoKey,
+ typename MojoValue,
+ typename STLKey,
+ typename STLValue>
+struct TypeConverter<Map<MojoKey, MojoValue>, std::map<STLKey, STLValue>> {
+ static Map<MojoKey, MojoValue> Convert(
+ const std::map<STLKey, STLValue>& input) {
+ Map<MojoKey, MojoValue> result;
+ for (auto& pair : input) {
+ result.insert(TypeConverter<MojoKey, STLKey>::Convert(pair.first),
+ TypeConverter<MojoValue, STLValue>::Convert(pair.second));
+ }
+ return result;
+ }
+};
+
+// Copies the contents of a Map to an std::map, optionally changing the types of
+// the keys and values along the way using TypeConverter.
+template <typename MojoKey,
+ typename MojoValue,
+ typename STLKey,
+ typename STLValue>
+struct TypeConverter<std::map<STLKey, STLValue>, Map<MojoKey, MojoValue>> {
+ static std::map<STLKey, STLValue> Convert(
+ const Map<MojoKey, MojoValue>& input) {
+ std::map<STLKey, STLValue> result;
+ if (!input.is_null()) {
+ for (auto it = input.begin(); it != input.end(); ++it) {
+ result.insert(std::make_pair(
+ TypeConverter<STLKey, MojoKey>::Convert(it->first),
+ TypeConverter<STLValue, MojoValue>::Convert(it->second)));
+ }
+ }
+ return result;
+ }
+};
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/map_data_view.h b/mojo/public/cpp/bindings/map_data_view.h
deleted file mode 100644
index a65bb9e..0000000
--- a/mojo/public/cpp/bindings/map_data_view.h
+++ /dev/null
@@ -1,63 +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.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_MAP_DATA_VIEW_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_MAP_DATA_VIEW_H_
-
-#include "base/logging.h"
-#include "mojo/public/cpp/bindings/array_data_view.h"
-#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
-#include "mojo/public/cpp/bindings/lib/map_data_internal.h"
-#include "mojo/public/cpp/bindings/lib/serialization_context.h"
-#include "mojo/public/cpp/bindings/lib/serialization_forward.h"
-
-namespace mojo {
-
-template <typename K, typename V>
-class MapDataView {
- public:
- using Data_ = typename internal::MojomTypeTraits<MapDataView<K, V>>::Data;
-
- MapDataView() {}
-
- MapDataView(Data_* data, internal::SerializationContext* context)
- : keys_(data ? data->keys.Get() : nullptr, context),
- values_(data ? data->values.Get() : nullptr, context) {}
-
- bool is_null() const {
- DCHECK_EQ(keys_.is_null(), values_.is_null());
- return keys_.is_null();
- }
-
- size_t size() const {
- DCHECK_EQ(keys_.size(), values_.size());
- return keys_.size();
- }
-
- ArrayDataView<K>& keys() { return keys_; }
- const ArrayDataView<K>& keys() const { return keys_; }
-
- template <typename U>
- bool ReadKeys(U* output) {
- return internal::Deserialize<ArrayDataView<K>>(keys_.data_, output,
- keys_.context_);
- }
-
- ArrayDataView<V>& values() { return values_; }
- const ArrayDataView<V>& values() const { return values_; }
-
- template <typename U>
- bool ReadValues(U* output) {
- return internal::Deserialize<ArrayDataView<V>>(values_.data_, output,
- values_.context_);
- }
-
- private:
- ArrayDataView<K> keys_;
- ArrayDataView<V> values_;
-};
-
-} // namespace mojo
-
-#endif // MOJO_PUBLIC_CPP_BINDINGS_MAP_DATA_VIEW_H_
diff --git a/mojo/public/cpp/bindings/map_traits.h b/mojo/public/cpp/bindings/map_traits.h
index 5c0d8b2..01dd66d 100644
--- a/mojo/public/cpp/bindings/map_traits.h
+++ b/mojo/public/cpp/bindings/map_traits.h
@@ -37,13 +37,13 @@
// static const V& GetValue(CustomConstIterator& iterator);
//
// // Returning false results in deserialization failure and causes the
-// // message pipe receiving it to be disconnected. |IK| and |IV| are
-// // separate input key/value template parameters that allows for the
-// // the key/value types to be forwarded.
-// template <typename IK, typename IV>
+// // message pipe receiving it to be disconnected.
// static bool Insert(CustomMap<K, V>& input,
-// IK&& key,
-// IV&& value);
+// const K& key,
+// V&& value);
+// static bool Insert(CustomMap<K, V>& input,
+// const K& key,
+// const V& value);
//
// static void SetToEmpty(CustomMap<K, V>* output);
// };
diff --git a/mojo/public/cpp/bindings/map_traits_standard.h b/mojo/public/cpp/bindings/map_traits_standard.h
new file mode 100644
index 0000000..0c76890
--- /dev/null
+++ b/mojo/public/cpp/bindings/map_traits_standard.h
@@ -0,0 +1,53 @@
+// 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_MAP_TRAITS_STANDARD_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_STANDARD_H_
+
+#include "mojo/public/cpp/bindings/map.h"
+#include "mojo/public/cpp/bindings/map_traits.h"
+
+namespace mojo {
+
+template <typename K, typename V>
+struct MapTraits<Map<K, V>> {
+ using Key = K;
+ using Value = V;
+ using Iterator = typename Map<K, V>::Iterator;
+ using ConstIterator = typename Map<K, V>::ConstIterator;
+
+ static bool IsNull(const Map<K, V>& input) { return input.is_null(); }
+ static void SetToNull(Map<K, V>* output) { *output = nullptr; }
+
+ static size_t GetSize(const Map<K, V>& input) { return input.size(); }
+
+ static ConstIterator GetBegin(const Map<K, V>& input) {
+ return input.begin();
+ }
+ static Iterator GetBegin(Map<K, V>& input) { return input.begin(); }
+
+ static void AdvanceIterator(ConstIterator& iterator) { iterator++; }
+ static void AdvanceIterator(Iterator& iterator) { iterator++; }
+
+ static const K& GetKey(Iterator& iterator) { return iterator->first; }
+ static const K& GetKey(ConstIterator& iterator) { return iterator->first; }
+
+ static V& GetValue(Iterator& iterator) { return iterator->second; }
+ static const V& GetValue(ConstIterator& iterator) { return iterator->second; }
+
+ static bool Insert(Map<K, V>& input, const K& key, V&& value) {
+ input.insert(key, std::forward<V>(value));
+ return true;
+ }
+ static bool Insert(Map<K, V>& input, const K& key, const V& value) {
+ input.insert(key, value);
+ return true;
+ }
+
+ static void SetToEmpty(Map<K, V>* output) { output->SetToEmpty(); }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_STANDARD_H_
diff --git a/mojo/public/cpp/bindings/map_traits_stl.h b/mojo/public/cpp/bindings/map_traits_stl.h
index 83a4399..ff79a20 100644
--- a/mojo/public/cpp/bindings/map_traits_stl.h
+++ b/mojo/public/cpp/bindings/map_traits_stl.h
@@ -94,10 +94,14 @@
static V& GetValue(Iterator& iterator) { return iterator->second; }
static const V& GetValue(ConstIterator& iterator) { return iterator->second; }
- template <typename IK, typename IV>
- static bool Insert(std::unordered_map<K, V>& input, IK&& key, IV&& value) {
- input.insert(
- std::make_pair(std::forward<IK>(key), std::forward<IV>(value)));
+ static bool Insert(std::unordered_map<K, V>& input, const K& key, V&& value) {
+ input.insert(std::make_pair(key, std::forward<V>(value)));
+ return true;
+ }
+ static bool Insert(std::unordered_map<K, V>& input,
+ const K& key,
+ const V& value) {
+ input.insert(std::make_pair(key, value));
return true;
}
diff --git a/mojo/public/cpp/bindings/map_traits_wtf.h b/mojo/public/cpp/bindings/map_traits_wtf.h
new file mode 100644
index 0000000..805e4c9
--- /dev/null
+++ b/mojo/public/cpp/bindings/map_traits_wtf.h
@@ -0,0 +1,62 @@
+// 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_MAP_TRAITS_WTF_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_WTF_H_
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/map_traits.h"
+#include "mojo/public/cpp/bindings/wtf_map.h"
+
+namespace mojo {
+
+template <typename K, typename V>
+struct MapTraits<WTFMap<K, V>> {
+ using Key = K;
+ using Value = V;
+ using Iterator = typename WTFMap<K, V>::Iterator;
+ using ConstIterator = typename WTFMap<K, V>::ConstIterator;
+
+ static bool IsNull(const WTFMap<K, V>& input) { return input.is_null(); }
+ static void SetToNull(WTFMap<K, V>* output) { *output = nullptr; }
+
+ static size_t GetSize(const WTFMap<K, V>& input) { return input.size(); }
+
+ static ConstIterator GetBegin(const WTFMap<K, V>& input) {
+ return input.begin();
+ }
+ static Iterator GetBegin(WTFMap<K, V>& input) { return input.begin(); }
+
+ static void AdvanceIterator(ConstIterator& iterator) { ++iterator; }
+ static void AdvanceIterator(Iterator& iterator) { ++iterator; }
+
+ static const K& GetKey(Iterator& iterator) { return iterator->key; }
+ static const K& GetKey(ConstIterator& iterator) { return iterator->key; }
+
+ static V& GetValue(Iterator& iterator) { return iterator->value; }
+ static const V& GetValue(ConstIterator& iterator) { return iterator->value; }
+
+ static bool Insert(WTFMap<K, V>& input, const K& key, V&& value) {
+ if (!WTFMap<K, V>::IsValidKey(key)) {
+ LOG(ERROR) << "The key value is disallowed by WTF::HashMap: " << key;
+ return false;
+ }
+ input.insert(key, std::forward<V>(value));
+ return true;
+ }
+ static bool Insert(WTFMap<K, V>& input, const K& key, const V& value) {
+ if (!WTFMap<K, V>::IsValidKey(key)) {
+ LOG(ERROR) << "The key value is disallowed by WTF::HashMap: " << key;
+ return false;
+ }
+ input.insert(key, value);
+ return true;
+ }
+
+ static void SetToEmpty(WTFMap<K, V>* output) { output->SetToEmpty(); }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_WTF_H_
diff --git a/mojo/public/cpp/bindings/map_traits_wtf_hash_map.h b/mojo/public/cpp/bindings/map_traits_wtf_hash_map.h
index dd68b36..4392201 100644
--- a/mojo/public/cpp/bindings/map_traits_wtf_hash_map.h
+++ b/mojo/public/cpp/bindings/map_traits_wtf_hash_map.h
@@ -46,13 +46,20 @@
static V& GetValue(Iterator& iterator) { return iterator->value; }
static const V& GetValue(ConstIterator& iterator) { return iterator->value; }
- template <typename IK, typename IV>
- static bool Insert(WTF::HashMap<K, V>& input, IK&& key, IV&& value) {
+ static bool Insert(WTF::HashMap<K, V>& input, const K& key, V&& value) {
if (!WTF::HashMap<K, V>::isValidKey(key)) {
- LOG(ERROR) << "The key value is disallowed by WTF::HashMap";
+ LOG(ERROR) << "The key value is disallowed by WTF::HashMap: " << key;
return false;
}
- input.insert(std::forward<IK>(key), std::forward<IV>(value));
+ input.add(key, std::forward<V>(value));
+ return true;
+ }
+ static bool Insert(WTF::HashMap<K, V>& input, const K& key, const V& value) {
+ if (!WTF::HashMap<K, V>::isValidKey(key)) {
+ LOG(ERROR) << "The key value is disallowed by WTF::HashMap: " << key;
+ return false;
+ }
+ input.add(key, value);
return true;
}
diff --git a/mojo/public/cpp/bindings/message.h b/mojo/public/cpp/bindings/message.h
index 65d6cec..e758432 100644
--- a/mojo/public/cpp/bindings/message.h
+++ b/mojo/public/cpp/bindings/message.h
@@ -13,46 +13,26 @@
#include <string>
#include <vector>
-#include "base/callback.h"
-#include "base/compiler_specific.h"
#include "base/logging.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/bindings/lib/message_buffer.h"
#include "mojo/public/cpp/bindings/lib/message_internal.h"
-#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
#include "mojo/public/cpp/system/message.h"
namespace mojo {
-class AssociatedGroupController;
-
-using ReportBadMessageCallback = base::Callback<void(const std::string& error)>;
-
// Message is a holder for the data and handles to be sent over a MessagePipe.
// Message owns its data and handles, but a consumer of Message is free to
// mutate the data and handles. The message's data is comprised of a header
// followed by payload.
-class MOJO_CPP_BINDINGS_EXPORT Message {
+class Message {
public:
static const uint32_t kFlagExpectsResponse = 1 << 0;
static const uint32_t kFlagIsResponse = 1 << 1;
static const uint32_t kFlagIsSync = 1 << 2;
Message();
- Message(Message&& other);
-
~Message();
- Message& operator=(Message&& other);
-
- // Resets the Message to an uninitialized state. Upon reset, the Message
- // exists as if it were default-constructed: it has no data buffer and owns no
- // handles.
- void Reset();
-
- // Indicates whether this Message is uninitialized.
- bool IsNull() const { return !buffer_; }
-
// Initializes a Message with enough space for |capacity| bytes.
void Initialize(size_t capacity, bool zero_initialized);
@@ -61,9 +41,10 @@
uint32_t num_bytes,
std::vector<Handle>* handles);
- uint32_t data_num_bytes() const {
- return static_cast<uint32_t>(buffer_->size());
- }
+ // Transfers data and handles to |destination|.
+ void MoveTo(Message* destination);
+
+ uint32_t data_num_bytes() const { return buffer_->data_num_bytes(); }
// Access the raw bytes of the message.
const uint8_t* data() const {
@@ -76,30 +57,12 @@
const internal::MessageHeader* header() const {
return static_cast<const internal::MessageHeader*>(buffer_->data());
}
+
internal::MessageHeader* header() {
- return static_cast<internal::MessageHeader*>(buffer_->data());
+ return const_cast<internal::MessageHeader*>(
+ static_cast<const Message*>(this)->header());
}
- const internal::MessageHeaderV1* header_v1() const {
- DCHECK_GE(version(), 1u);
- return static_cast<const internal::MessageHeaderV1*>(buffer_->data());
- }
- internal::MessageHeaderV1* header_v1() {
- DCHECK_GE(version(), 1u);
- return static_cast<internal::MessageHeaderV1*>(buffer_->data());
- }
-
- const internal::MessageHeaderV2* header_v2() const {
- DCHECK_GE(version(), 2u);
- return static_cast<const internal::MessageHeaderV2*>(buffer_->data());
- }
- internal::MessageHeaderV2* header_v2() {
- DCHECK_GE(version(), 2u);
- return static_cast<internal::MessageHeaderV2*>(buffer_->data());
- }
-
- uint32_t version() const { return header()->version; }
-
uint32_t interface_id() const { return header()->interface_id; }
void set_interface_id(uint32_t id) { header()->interface_id = id; }
@@ -107,32 +70,32 @@
bool has_flag(uint32_t flag) const { return !!(header()->flags & flag); }
// Access the request_id field (if present).
- uint64_t request_id() const { return header_v1()->request_id; }
+ bool has_request_id() const { return header()->version >= 1; }
+ uint64_t request_id() const {
+ DCHECK(has_request_id());
+ return static_cast<const internal::MessageHeaderWithRequestID*>(
+ header())->request_id;
+ }
void set_request_id(uint64_t request_id) {
- header_v1()->request_id = request_id;
+ DCHECK(has_request_id());
+ static_cast<internal::MessageHeaderWithRequestID*>(header())
+ ->request_id = request_id;
}
// Access the payload.
- const uint8_t* payload() const;
+ const uint8_t* payload() const { return data() + header()->num_bytes; }
uint8_t* mutable_payload() { return const_cast<uint8_t*>(payload()); }
- uint32_t payload_num_bytes() const;
-
- uint32_t payload_num_interface_ids() const;
- const uint32_t* payload_interface_ids() const;
+ uint32_t payload_num_bytes() const {
+ DCHECK(buffer_->data_num_bytes() >= header()->num_bytes);
+ size_t num_bytes = buffer_->data_num_bytes() - header()->num_bytes;
+ DCHECK(num_bytes <= std::numeric_limits<uint32_t>::max());
+ return static_cast<uint32_t>(num_bytes);
+ }
// Access the handles.
const std::vector<Handle>* handles() const { return &handles_; }
std::vector<Handle>* mutable_handles() { return &handles_; }
- const std::vector<ScopedInterfaceEndpointHandle>*
- associated_endpoint_handles() const {
- return &associated_endpoint_handles_;
- }
- std::vector<ScopedInterfaceEndpointHandle>*
- mutable_associated_endpoint_handles() {
- return &associated_endpoint_handles_;
- }
-
// Access the underlying Buffer interface.
internal::Buffer* buffer() { return buffer_.get(); }
@@ -145,22 +108,11 @@
// rejected by bindings validation code.
void NotifyBadMessage(const std::string& error);
- // Serializes |associated_endpoint_handles_| into the payload_interface_ids
- // field.
- void SerializeAssociatedEndpointHandles(
- AssociatedGroupController* group_controller);
-
- // Deserializes |associated_endpoint_handles_| from the payload_interface_ids
- // field.
- bool DeserializeAssociatedEndpointHandles(
- AssociatedGroupController* group_controller);
-
private:
void CloseHandles();
std::unique_ptr<internal::MessageBuffer> buffer_;
std::vector<Handle> handles_;
- std::vector<ScopedInterfaceEndpointHandle> associated_endpoint_handles_;
DISALLOW_COPY_AND_ASSIGN(Message);
};
@@ -234,57 +186,6 @@
WARN_UNUSED_RESULT = 0;
};
-class MOJO_CPP_BINDINGS_EXPORT PassThroughFilter
- : NON_EXPORTED_BASE(public MessageReceiver) {
- public:
- PassThroughFilter();
- ~PassThroughFilter() override;
-
- // MessageReceiver:
- bool Accept(Message* message) override;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(PassThroughFilter);
-};
-
-namespace internal {
-class SyncMessageResponseSetup;
-}
-
-// An object which should be constructed on the stack immediately before making
-// a sync request for which the caller wishes to perform custom validation of
-// the response value(s). It is illegal to make more than one sync call during
-// the lifetime of the topmost SyncMessageResponseContext, but it is legal to
-// nest contexts to support reentrancy.
-//
-// Usage should look something like:
-//
-// SyncMessageResponseContext response_context;
-// foo_interface->SomeSyncCall(&response_value);
-// if (response_value.IsBad())
-// response_context.ReportBadMessage("Bad response_value!");
-//
-class MOJO_CPP_BINDINGS_EXPORT SyncMessageResponseContext {
- public:
- SyncMessageResponseContext();
- ~SyncMessageResponseContext();
-
- static SyncMessageResponseContext* current();
-
- void ReportBadMessage(const std::string& error);
-
- const ReportBadMessageCallback& GetBadMessageCallback();
-
- private:
- friend class internal::SyncMessageResponseSetup;
-
- SyncMessageResponseContext* outer_context_;
- Message response_;
- ReportBadMessageCallback bad_message_callback_;
-
- DISALLOW_COPY_AND_ASSIGN(SyncMessageResponseContext);
-};
-
// Read a single message from the pipe. The caller should have created the
// Message, but not called Initialize(). Returns MOJO_RESULT_SHOULD_WAIT if
// the caller should wait on the handle to become readable. Returns
@@ -294,22 +195,6 @@
// NOTE: The message hasn't been validated and may be malformed!
MojoResult ReadMessage(MessagePipeHandle handle, Message* message);
-// Reports the currently dispatching Message as bad. Note that this is only
-// legal to call from directly within the stack frame of a message dispatch. If
-// you need to do asynchronous work before you can determine the legitimacy of
-// a message, use TakeBadMessageCallback() and retain its result until you're
-// ready to invoke or discard it.
-MOJO_CPP_BINDINGS_EXPORT
-void ReportBadMessage(const std::string& error);
-
-// Acquires a callback which may be run to report the currently dispatching
-// Message as bad. Note that this is only legal to call from directly within the
-// stack frame of a message dispatch, but the returned callback may be called
-// exactly once any time thereafter to report the message as bad. This may only
-// be called once per message.
-MOJO_CPP_BINDINGS_EXPORT
-ReportBadMessageCallback GetBadMessageCallback();
-
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_H_
diff --git a/mojo/public/cpp/bindings/message_filter.h b/mojo/public/cpp/bindings/message_filter.h
new file mode 100644
index 0000000..638c53b
--- /dev/null
+++ b/mojo/public/cpp/bindings/message_filter.h
@@ -0,0 +1,38 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_
+
+#include "mojo/public/cpp/bindings/message.h"
+
+namespace mojo {
+
+// This class is the base class for message filters. Subclasses should
+// implement the pure virtual method Accept() inherited from MessageReceiver to
+// process messages and/or forward them to |sink_|.
+class MessageFilter : public MessageReceiver {
+ public:
+ // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while
+ // this object is alive.
+ explicit MessageFilter(MessageReceiver* sink = nullptr);
+ ~MessageFilter() override;
+
+ void set_sink(MessageReceiver* sink) { sink_ = sink; }
+
+ protected:
+ MessageReceiver* sink_;
+};
+
+// A trivial filter that simply forwards every message it receives to |sink_|.
+class PassThroughFilter : public MessageFilter {
+ public:
+ explicit PassThroughFilter(MessageReceiver* sink = nullptr);
+
+ bool Accept(Message* message) override;
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_
diff --git a/mojo/public/cpp/bindings/message_header_validator.h b/mojo/public/cpp/bindings/message_header_validator.h
index 50c19db..3bcbd0a 100644
--- a/mojo/public/cpp/bindings/message_header_validator.h
+++ b/mojo/public/cpp/bindings/message_header_validator.h
@@ -5,17 +5,16 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_HEADER_VALIDATOR_H_
#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_HEADER_VALIDATOR_H_
-#include "base/compiler_specific.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
namespace mojo {
-class MOJO_CPP_BINDINGS_EXPORT MessageHeaderValidator
- : NON_EXPORTED_BASE(public MessageReceiver) {
+class MessageHeaderValidator : public MessageFilter {
public:
- MessageHeaderValidator();
- explicit MessageHeaderValidator(const std::string& description);
+ explicit MessageHeaderValidator(MessageReceiver* sink = nullptr);
+ MessageHeaderValidator(const std::string& description,
+ MessageReceiver* sink = nullptr);
// Sets the description associated with this validator. Used for reporting
// more detailed validation errors.
diff --git a/mojo/public/cpp/bindings/native_struct.h b/mojo/public/cpp/bindings/native_struct.h
index ac27250..882c970 100644
--- a/mojo/public/cpp/bindings/native_struct.h
+++ b/mojo/public/cpp/bindings/native_struct.h
@@ -5,10 +5,7 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_H_
#define MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_H_
-#include <vector>
-
-#include "base/optional.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
+#include "mojo/public/cpp/bindings/array.h"
#include "mojo/public/cpp/bindings/lib/native_struct_data.h"
#include "mojo/public/cpp/bindings/struct_ptr.h"
#include "mojo/public/cpp/bindings/type_converter.h"
@@ -20,7 +17,7 @@
// Native-only structs correspond to "[Native] struct Foo;" definitions in
// mojom.
-class MOJO_CPP_BINDINGS_EXPORT NativeStruct {
+class NativeStruct {
public:
using Data_ = internal::NativeStruct_Data;
@@ -41,9 +38,8 @@
NativeStructPtr Clone() const;
bool Equals(const NativeStruct& other) const;
- size_t Hash(size_t seed) const;
- base::Optional<std::vector<uint8_t>> data;
+ Array<uint8_t> data;
};
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/native_struct_data_view.h b/mojo/public/cpp/bindings/native_struct_data_view.h
deleted file mode 100644
index 613bd7a..0000000
--- a/mojo/public/cpp/bindings/native_struct_data_view.h
+++ /dev/null
@@ -1,36 +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.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_DATA_VIEW_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_DATA_VIEW_H_
-
-#include "mojo/public/cpp/bindings/lib/native_struct_data.h"
-#include "mojo/public/cpp/bindings/lib/serialization_context.h"
-
-namespace mojo {
-
-class NativeStructDataView {
- public:
- using Data_ = internal::NativeStruct_Data;
-
- NativeStructDataView() {}
-
- NativeStructDataView(Data_* data, internal::SerializationContext* context)
- : data_(data) {}
-
- bool is_null() const { return !data_; }
-
- size_t size() const { return data_->data.size(); }
-
- uint8_t operator[](size_t index) const { return data_->data.at(index); }
-
- const uint8_t* data() const { return data_->data.storage(); }
-
- private:
- Data_* data_ = nullptr;
-};
-
-} // namespace mojo
-
-#endif // MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_DATA_VIEW_H_
diff --git a/mojo/public/cpp/bindings/no_interface.h b/mojo/public/cpp/bindings/no_interface.h
new file mode 100644
index 0000000..d8915cd
--- /dev/null
+++ b/mojo/public/cpp/bindings/no_interface.h
@@ -0,0 +1,52 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_NO_INTERFACE_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_NO_INTERFACE_H_
+
+#include "mojo/public/cpp/bindings/message.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+#include "mojo/public/cpp/system/core.h"
+
+namespace mojo {
+
+// NoInterface is for use in cases when a non-existent or empty interface is
+// needed.
+
+class NoInterfaceProxy;
+class NoInterfaceStub;
+
+class NoInterface {
+ public:
+ static const char* Name_;
+ typedef NoInterfaceProxy Proxy_;
+ typedef NoInterfaceStub Stub_;
+ typedef PassThroughFilter RequestValidator_;
+ typedef PassThroughFilter ResponseValidator_;
+ virtual ~NoInterface() {}
+};
+
+class NoInterfaceProxy : public NoInterface {
+ public:
+ explicit NoInterfaceProxy(MessageReceiver* receiver) {}
+};
+
+class NoInterfaceStub : public MessageReceiverWithResponder {
+ public:
+ NoInterfaceStub() {}
+ void set_sink(NoInterface* sink) {}
+ NoInterface* sink() { return nullptr; }
+ bool Accept(Message* message) override;
+ bool AcceptWithResponder(Message* message,
+ MessageReceiver* responder) override;
+};
+
+// AnyInterface is for use in cases where any interface would do (e.g., see the
+// Shell::Connect method).
+
+typedef NoInterface AnyInterface;
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_NO_INTERFACE_H_
diff --git a/mojo/public/cpp/bindings/pipe_control_message_handler.h b/mojo/public/cpp/bindings/pipe_control_message_handler.h
index a5c04da..b387b06 100644
--- a/mojo/public/cpp/bindings/pipe_control_message_handler.h
+++ b/mojo/public/cpp/bindings/pipe_control_message_handler.h
@@ -5,11 +5,9 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_H_
#define MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_H_
-#include <string>
-
-#include "base/compiler_specific.h"
#include "base/macros.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
+#include "mojo/public/cpp/bindings/interface_id.h"
+#include "mojo/public/cpp/bindings/lib/serialization_context.h"
#include "mojo/public/cpp/bindings/message.h"
namespace mojo {
@@ -17,8 +15,7 @@
class PipeControlMessageHandlerDelegate;
// Handler for messages defined in pipe_control_messages.mojom.
-class MOJO_CPP_BINDINGS_EXPORT PipeControlMessageHandler
- : NON_EXPORTED_BASE(public MessageReceiver) {
+class PipeControlMessageHandler : public MessageReceiver {
public:
explicit PipeControlMessageHandler(
PipeControlMessageHandlerDelegate* delegate);
@@ -46,6 +43,7 @@
std::string description_;
PipeControlMessageHandlerDelegate* const delegate_;
+ internal::SerializationContext context_;
DISALLOW_COPY_AND_ASSIGN(PipeControlMessageHandler);
};
diff --git a/mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h b/mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h
index 16fd918..06f0695 100644
--- a/mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h
+++ b/mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h
@@ -5,8 +5,6 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_DELEGATE_H_
#define MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_DELEGATE_H_
-#include "base/optional.h"
-#include "mojo/public/cpp/bindings/disconnect_reason.h"
#include "mojo/public/cpp/bindings/interface_id.h"
namespace mojo {
@@ -16,9 +14,8 @@
// The implementation of the following methods should return false if the
// notification is unexpected. In that case, the user of this delegate is
// expected to close the message pipe.
- virtual bool OnPeerAssociatedEndpointClosed(
- InterfaceId id,
- const base::Optional<DisconnectReason>& reason) = 0;
+ virtual bool OnPeerAssociatedEndpointClosed(InterfaceId id) = 0;
+ virtual bool OnAssociatedEndpointClosedBeforeSent(InterfaceId id) = 0;
protected:
virtual ~PipeControlMessageHandlerDelegate() {}
diff --git a/mojo/public/cpp/bindings/pipe_control_message_proxy.h b/mojo/public/cpp/bindings/pipe_control_message_proxy.h
index 52c408f..7f3e006 100644
--- a/mojo/public/cpp/bindings/pipe_control_message_proxy.h
+++ b/mojo/public/cpp/bindings/pipe_control_message_proxy.h
@@ -6,36 +6,26 @@
#define MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_PROXY_H_
#include "base/macros.h"
-#include "base/optional.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
-#include "mojo/public/cpp/bindings/disconnect_reason.h"
#include "mojo/public/cpp/bindings/interface_id.h"
#include "mojo/public/cpp/bindings/lib/serialization_context.h"
-#include "mojo/public/cpp/bindings/message.h"
namespace mojo {
class MessageReceiver;
// Proxy for request messages defined in pipe_control_messages.mojom.
-//
-// NOTE: This object may be used from multiple threads.
-class MOJO_CPP_BINDINGS_EXPORT PipeControlMessageProxy {
+class PipeControlMessageProxy {
public:
- // Doesn't take ownership of |receiver|. If This PipeControlMessageProxy will
- // be used from multiple threads, |receiver| must be thread-safe.
+ // Doesn't take ownership of |receiver|. It must outlive this object.
explicit PipeControlMessageProxy(MessageReceiver* receiver);
- void NotifyPeerEndpointClosed(InterfaceId id,
- const base::Optional<DisconnectReason>& reason);
-
- static Message ConstructPeerEndpointClosedMessage(
- InterfaceId id,
- const base::Optional<DisconnectReason>& reason);
+ void NotifyPeerEndpointClosed(InterfaceId id);
+ void NotifyEndpointClosedBeforeSent(InterfaceId id);
private:
// Not owned.
MessageReceiver* receiver_;
+ internal::SerializationContext context_;
DISALLOW_COPY_AND_ASSIGN(PipeControlMessageProxy);
};
diff --git a/mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h b/mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h
deleted file mode 100644
index 4d40cdf..0000000
--- a/mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h
+++ /dev/null
@@ -1,22 +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.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_RAW_PTR_IMPL_REF_TRAITS_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_RAW_PTR_IMPL_REF_TRAITS_H_
-
-namespace mojo {
-
-// Default traits for a binding's implementation reference type. This
-// corresponds to a raw pointer.
-template <typename Interface>
-struct RawPtrImplRefTraits {
- using PointerType = Interface*;
-
- static bool IsNull(PointerType ptr) { return !ptr; }
- static Interface* GetRawPointer(PointerType* ptr) { return *ptr; }
-};
-
-} // namespace mojo
-
-#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_RAW_PTR_IMPL_REF_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h b/mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h
index 16527cf..1f45b0c 100644
--- a/mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h
+++ b/mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h
@@ -5,16 +5,8 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_SCOPED_INTERFACE_ENDPOINT_HANDLE_H_
#define MOJO_PUBLIC_CPP_BINDINGS_SCOPED_INTERFACE_ENDPOINT_HANDLE_H_
-#include <string>
-
-#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "base/optional.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
-#include "mojo/public/cpp/bindings/disconnect_reason.h"
#include "mojo/public/cpp/bindings/interface_id.h"
namespace mojo {
@@ -23,16 +15,8 @@
// ScopedInterfaceEndpointHandle refers to one end of an interface, either the
// implementation side or the client side.
-// Threading: At any given time, a ScopedInterfaceEndpointHandle should only
-// be accessed from a single thread.
-class MOJO_CPP_BINDINGS_EXPORT ScopedInterfaceEndpointHandle {
+class ScopedInterfaceEndpointHandle {
public:
- // Creates a pair of handles representing the two endpoints of an interface,
- // which are not yet associated with a message pipe.
- static void CreatePairPendingAssociation(
- ScopedInterfaceEndpointHandle* handle0,
- ScopedInterfaceEndpointHandle* handle1);
-
// Creates an invalid endpoint handle.
ScopedInterfaceEndpointHandle();
@@ -43,77 +27,39 @@
ScopedInterfaceEndpointHandle& operator=(
ScopedInterfaceEndpointHandle&& other);
- bool is_valid() const;
+ bool is_valid() const { return IsValidInterfaceId(id_); }
- // Returns true if the interface hasn't associated with a message pipe.
- bool pending_association() const;
-
- // Returns kInvalidInterfaceId when in pending association state or the handle
- // is invalid.
- InterfaceId id() const;
-
- // Returns null when in pending association state or the handle is invalid.
- AssociatedGroupController* group_controller() const;
-
- // Returns the disconnect reason if the peer handle is closed before
- // association and specifies a custom disconnect reason.
- const base::Optional<DisconnectReason>& disconnect_reason() const;
-
- enum AssociationEvent {
- // The interface has been associated with a message pipe.
- ASSOCIATED,
- // The peer of this object has been closed before association.
- PEER_CLOSED_BEFORE_ASSOCIATION
- };
-
- using AssociationEventCallback = base::OnceCallback<void(AssociationEvent)>;
- // Note:
- // - |handler| won't run if the handle is invalid. Otherwise, |handler| is run
- // on the calling thread asynchronously, even if the interface has already
- // been associated or the peer has been closed before association.
- // - |handler| won't be called after this object is destroyed or reset.
- // - A null |handler| can be used to cancel the previous callback.
- void SetAssociationEventHandler(AssociationEventCallback handler);
+ bool is_local() const { return is_local_; }
void reset();
- void ResetWithReason(uint32_t custom_reason, const std::string& description);
+ void swap(ScopedInterfaceEndpointHandle& other);
+
+ // DO NOT USE METHODS BELOW THIS LINE. These are for internal use and testing.
+
+ InterfaceId id() const { return id_; }
+
+ AssociatedGroupController* group_controller() const {
+ return group_controller_.get();
+ }
+
+ // Releases the handle without closing it.
+ InterfaceId release();
private:
friend class AssociatedGroupController;
- friend class AssociatedGroup;
- class State;
-
- // Used by AssociatedGroupController.
+ // This is supposed to be used by AssociatedGroupController only.
+ // |id| is the corresponding interface ID.
+ // If |is_local| is false, this handle is meant to be passed over a message
+ // pipe the remote side of the associated group.
ScopedInterfaceEndpointHandle(
InterfaceId id,
+ bool is_local,
scoped_refptr<AssociatedGroupController> group_controller);
- // Used by AssociatedGroupController.
- // The peer of this handle will join |peer_group_controller|.
- bool NotifyAssociation(
- InterfaceId id,
- scoped_refptr<AssociatedGroupController> peer_group_controller);
-
- void ResetInternal(const base::Optional<DisconnectReason>& reason);
-
- // Used by AssociatedGroup.
- // It is safe to run the returned callback on any thread, or after this handle
- // is destroyed.
- // The return value of the getter:
- // - If the getter is retrieved when the handle is invalid, the return value
- // of the getter will always be null.
- // - If the getter is retrieved when the handle is valid and non-pending,
- // the return value of the getter will be non-null and remain unchanged
- // even if the handle is later reset.
- // - If the getter is retrieved when the handle is valid but pending
- // asssociation, the return value of the getter will initially be null,
- // change to non-null when the handle is associated, and remain unchanged
- // ever since.
- base::Callback<AssociatedGroupController*()> CreateGroupControllerGetter()
- const;
-
- scoped_refptr<State> state_;
+ InterfaceId id_;
+ bool is_local_;
+ scoped_refptr<AssociatedGroupController> group_controller_;
DISALLOW_COPY_AND_ASSIGN(ScopedInterfaceEndpointHandle);
};
diff --git a/mojo/public/cpp/bindings/stl_converters.h b/mojo/public/cpp/bindings/stl_converters.h
new file mode 100644
index 0000000..92b2924
--- /dev/null
+++ b/mojo/public/cpp/bindings/stl_converters.h
@@ -0,0 +1,245 @@
+// 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_STL_CONVERTERS_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STL_CONVERTERS_H_
+
+#include <map>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/map.h"
+#include "mojo/public/cpp/bindings/string.h"
+
+// Two functions are defined to facilitate conversion between
+// mojo::Array/Map/String and std::vector/map/string: mojo::UnwrapToSTLType()
+// recursively convert mojo types to STL types; mojo::WrapSTLType() does the
+// opposite. For example:
+// mojo::Array<mojo::Map<mojo::String, mojo::Array<int32_t>>> mojo_obj;
+//
+// std::vector<std::map<std::string, std::vector<int32_t>>> stl_obj =
+// mojo::UnwrapToSTLType(std::move(mojo_obj));
+//
+// mojo_obj = mojo::WrapSTLType(std::move(stl_obj));
+//
+// Notes:
+// - The conversion moves as much contents as possible. The two functions both
+// take an rvalue ref as input in order to avoid accidental copies.
+// - Because std::vector/map/string cannot express null, UnwrapToSTLType()
+// converts null mojo::Array/Map/String to empty.
+// - The recursive conversion stops at any types that are not the types listed
+// above. For example, unwrapping mojo::Array<StructContainingMojoMap> will
+// result in std::vector<StructContainingMojoMap>. It won't convert
+// mojo::Map inside the struct.
+
+namespace mojo {
+namespace internal {
+
+template <typename T>
+struct UnwrapTraits;
+
+template <typename T>
+struct UnwrapShouldGoDeeper {
+ public:
+ static const bool value =
+ !std::is_same<T, typename UnwrapTraits<T>::Type>::value;
+};
+
+template <typename T>
+struct UnwrapTraits {
+ public:
+ using Type = T;
+ static Type Unwrap(T input) { return input; }
+};
+
+template <typename T>
+struct UnwrapTraits<Array<T>> {
+ public:
+ using Type = std::vector<typename UnwrapTraits<T>::Type>;
+
+ static Type Unwrap(Array<T> input) {
+ return Helper<T>::Run(std::move(input));
+ }
+
+ private:
+ template <typename U, bool should_go_deeper = UnwrapShouldGoDeeper<U>::value>
+ struct Helper {};
+
+ template <typename U>
+ struct Helper<U, true> {
+ public:
+ static Type Run(Array<T> input) {
+ Type output;
+ output.reserve(input.size());
+ for (size_t i = 0; i < input.size(); ++i)
+ output.push_back(UnwrapTraits<T>::Unwrap(std::move(input[i])));
+ return output;
+ }
+ };
+
+ template <typename U>
+ struct Helper<U, false> {
+ public:
+ static Type Run(Array<T> input) { return input.PassStorage(); }
+ };
+};
+
+template <typename K, typename V>
+struct UnwrapTraits<Map<K, V>> {
+ public:
+ using Type =
+ std::map<typename UnwrapTraits<K>::Type, typename UnwrapTraits<V>::Type>;
+
+ static Type Unwrap(Map<K, V> input) {
+ return Helper<K, V>::Run(std::move(input));
+ }
+
+ private:
+ template <typename X,
+ typename Y,
+ bool should_go_deeper = UnwrapShouldGoDeeper<X>::value ||
+ UnwrapShouldGoDeeper<Y>::value>
+ struct Helper {};
+
+ template <typename X, typename Y>
+ struct Helper<X, Y, true> {
+ public:
+ static Type Run(Map<K, V> input) {
+ std::map<K, V> input_storage = input.PassStorage();
+ Type output;
+ for (auto& pair : input_storage) {
+ output.insert(
+ std::make_pair(UnwrapTraits<K>::Unwrap(pair.first),
+ UnwrapTraits<V>::Unwrap(std::move(pair.second))));
+ }
+ return output;
+ }
+ };
+
+ template <typename X, typename Y>
+ struct Helper<X, Y, false> {
+ public:
+ static Type Run(Map<K, V> input) { return input.PassStorage(); }
+ };
+};
+
+template <>
+struct UnwrapTraits<String> {
+ public:
+ using Type = std::string;
+
+ static std::string Unwrap(const String& input) { return input; }
+};
+
+template <typename T>
+struct WrapTraits;
+
+template <typename T>
+struct WrapShouldGoDeeper {
+ public:
+ static const bool value =
+ !std::is_same<T, typename WrapTraits<T>::Type>::value;
+};
+
+template <typename T>
+struct WrapTraits {
+ public:
+ using Type = T;
+
+ static T Wrap(T input) { return input; }
+};
+
+template <typename T>
+struct WrapTraits<std::vector<T>> {
+ public:
+ using Type = Array<typename WrapTraits<T>::Type>;
+
+ static Type Wrap(std::vector<T> input) {
+ return Helper<T>::Run(std::move(input));
+ }
+
+ private:
+ template <typename U, bool should_go_deeper = WrapShouldGoDeeper<U>::value>
+ struct Helper {};
+
+ template <typename U>
+ struct Helper<U, true> {
+ public:
+ static Type Run(std::vector<T> input) {
+ std::vector<typename WrapTraits<T>::Type> output_storage;
+ output_storage.reserve(input.size());
+ for (auto& element : input)
+ output_storage.push_back(WrapTraits<T>::Wrap(std::move(element)));
+ return Type(std::move(output_storage));
+ }
+ };
+
+ template <typename U>
+ struct Helper<U, false> {
+ public:
+ static Type Run(std::vector<T> input) { return Type(std::move(input)); }
+ };
+};
+
+template <typename K, typename V>
+struct WrapTraits<std::map<K, V>> {
+ public:
+ using Type = Map<typename WrapTraits<K>::Type, typename WrapTraits<V>::Type>;
+
+ static Type Wrap(std::map<K, V> input) {
+ return Helper<K, V>::Run(std::move(input));
+ }
+
+ private:
+ template <typename X,
+ typename Y,
+ bool should_go_deeper =
+ WrapShouldGoDeeper<X>::value || WrapShouldGoDeeper<Y>::value>
+ struct Helper {};
+
+ template <typename X, typename Y>
+ struct Helper<X, Y, true> {
+ public:
+ static Type Run(std::map<K, V> input) {
+ Type output;
+ for (auto& pair : input) {
+ output.insert(WrapTraits<K>::Wrap(pair.first),
+ WrapTraits<V>::Wrap(std::move(pair.second)));
+ }
+ return output;
+ }
+ };
+
+ template <typename X, typename Y>
+ struct Helper<X, Y, false> {
+ public:
+ static Type Run(std::map<K, V> input) { return Type(std::move(input)); }
+ };
+};
+
+template <>
+struct WrapTraits<std::string> {
+ public:
+ using Type = String;
+
+ static String Wrap(const std::string& input) { return input; }
+};
+
+} // namespace internal
+
+template <typename T>
+typename internal::UnwrapTraits<T>::Type UnwrapToSTLType(T&& input) {
+ return internal::UnwrapTraits<T>::Unwrap(std::move(input));
+}
+
+template <typename T>
+typename internal::WrapTraits<T>::Type WrapSTLType(T&& input) {
+ return internal::WrapTraits<T>::Wrap(std::move(input));
+}
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_STL_CONVERTERS_H_
diff --git a/mojo/public/cpp/bindings/string.h b/mojo/public/cpp/bindings/string.h
new file mode 100644
index 0000000..7cfd713
--- /dev/null
+++ b/mojo/public/cpp/bindings/string.h
@@ -0,0 +1,196 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRING_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRING_H_
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+
+namespace mojo {
+
+// A UTF-8 encoded character string that can be null. Provides functions that
+// are similar to std::string, along with access to the underlying std::string
+// object.
+class String {
+ public:
+ // Constructs an empty string.
+ String() : is_null_(false) {}
+ String(const std::string& str) : value_(str), is_null_(false) {}
+ String(const char* chars) : is_null_(!chars) {
+ if (chars)
+ value_ = chars;
+ }
+ String(const char* chars, size_t num_chars)
+ : value_(chars, num_chars), is_null_(false) {}
+ String(const mojo::String& str)
+ : value_(str.value_), is_null_(str.is_null_) {}
+
+ template <size_t N>
+ String(const char chars[N])
+ : value_(chars, N - 1), is_null_(false) {}
+
+ String(std::string&& other) : value_(std::move(other)), is_null_(false) {}
+ String(String&& other) : is_null_(true) { Swap(&other); }
+
+ template <typename U>
+ static String From(const U& other) {
+ return TypeConverter<String, U>::Convert(other);
+ }
+
+ template <typename U>
+ U To() const {
+ return TypeConverter<U, String>::Convert(*this);
+ }
+
+ String& operator=(const mojo::String& str) {
+ value_ = str.value_;
+ is_null_ = str.is_null_;
+ return *this;
+ }
+ String& operator=(const std::string& str) {
+ value_ = str;
+ is_null_ = false;
+ return *this;
+ }
+ String& operator=(const char* chars) {
+ is_null_ = !chars;
+ if (chars) {
+ value_ = chars;
+ } else {
+ value_.clear();
+ }
+ return *this;
+ }
+
+ String& operator=(std::string&& other) {
+ value_ = std::move(other);
+ is_null_ = false;
+ return *this;
+ }
+ String& operator=(String&& other) {
+ is_null_ = true;
+ value_.clear();
+ Swap(&other);
+ return *this;
+ }
+
+ bool is_null() const { return is_null_; }
+
+ size_t size() const { return value_.size(); }
+
+ const char* data() const { return value_.data(); }
+
+ const char& at(size_t offset) const { return value_.at(offset); }
+ const char& operator[](size_t offset) const { return value_[offset]; }
+
+ const std::string& get() const { return value_; }
+ operator const std::string&() const { return value_; }
+
+ // Returns a const reference to the |std::string| managed by this class. If
+ // the string is null, this will be an empty std::string.
+ const std::string& storage() const { return value_; }
+
+ // Passes the underlying storage and resets this string to null.
+ std::string PassStorage() {
+ is_null_ = true;
+ return std::move(value_);
+ }
+
+ void Swap(String* other) {
+ std::swap(is_null_, other->is_null_);
+ value_.swap(other->value_);
+ }
+
+ void Swap(std::string* other) {
+ is_null_ = false;
+ value_.swap(*other);
+ }
+
+ private:
+ typedef std::string String::*Testable;
+
+ public:
+ operator Testable() const { return is_null_ ? 0 : &String::value_; }
+
+ private:
+ std::string value_;
+ bool is_null_;
+};
+
+inline bool operator==(const String& a, const String& b) {
+ return a.is_null() == b.is_null() && a.get() == b.get();
+}
+inline bool operator==(const char* a, const String& b) {
+ return !b.is_null() && a == b.get();
+}
+inline bool operator==(const String& a, const char* b) {
+ return !a.is_null() && a.get() == b;
+}
+inline bool operator!=(const String& a, const String& b) {
+ return !(a == b);
+}
+inline bool operator!=(const char* a, const String& b) {
+ return !(a == b);
+}
+inline bool operator!=(const String& a, const char* b) {
+ return !(a == b);
+}
+
+inline std::ostream& operator<<(std::ostream& out, const String& s) {
+ return out << s.get();
+}
+
+inline bool operator<(const String& a, const String& b) {
+ if (a.is_null())
+ return !b.is_null();
+ if (b.is_null())
+ return false;
+
+ return a.get() < b.get();
+}
+
+// TODO(darin): Add similar variants of operator<,<=,>,>=
+
+template <>
+struct TypeConverter<String, std::string> {
+ static String Convert(const std::string& input) { return String(input); }
+};
+
+template <>
+struct TypeConverter<std::string, String> {
+ static std::string Convert(const String& input) { return input; }
+};
+
+template <size_t N>
+struct TypeConverter<String, char[N]> {
+ static String Convert(const char input[N]) {
+ DCHECK(input);
+ return String(input, N - 1);
+ }
+};
+
+// Appease MSVC.
+template <size_t N>
+struct TypeConverter<String, const char[N]> {
+ static String Convert(const char input[N]) {
+ DCHECK(input);
+ return String(input, N - 1);
+ }
+};
+
+template <>
+struct TypeConverter<String, const char*> {
+ // |input| may be null, in which case a null String will be returned.
+ static String Convert(const char* input) { return String(input); }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_STRING_H_
diff --git a/mojo/public/cpp/bindings/string_data_view.h b/mojo/public/cpp/bindings/string_data_view.h
deleted file mode 100644
index 2b091b4..0000000
--- a/mojo/public/cpp/bindings/string_data_view.h
+++ /dev/null
@@ -1,34 +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.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRING_DATA_VIEW_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_STRING_DATA_VIEW_H_
-
-#include "mojo/public/cpp/bindings/lib/array_internal.h"
-#include "mojo/public/cpp/bindings/lib/serialization_context.h"
-
-namespace mojo {
-
-// Access to the contents of a serialized string.
-class StringDataView {
- public:
- StringDataView() {}
-
- StringDataView(internal::String_Data* data,
- internal::SerializationContext* context)
- : data_(data) {}
-
- bool is_null() const { return !data_; }
-
- const char* storage() const { return data_->storage(); }
-
- size_t size() const { return data_->size(); }
-
- private:
- internal::String_Data* data_ = nullptr;
-};
-
-} // namespace mojo
-
-#endif // MOJO_PUBLIC_CPP_BINDINGS_STRING_DATA_VIEW_H_
diff --git a/mojo/public/cpp/bindings/string_traits.h b/mojo/public/cpp/bindings/string_traits.h
index 7d3075a..a6ade6f 100644
--- a/mojo/public/cpp/bindings/string_traits.h
+++ b/mojo/public/cpp/bindings/string_traits.h
@@ -5,10 +5,26 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_H_
#define MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_H_
-#include "mojo/public/cpp/bindings/string_data_view.h"
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
namespace mojo {
+// Access to the contents of a serialized string.
+class StringDataView {
+ public:
+ explicit StringDataView(internal::String_Data* data) : data_(data) {
+ DCHECK(data_);
+ }
+
+ const char* storage() const { return data_->storage(); }
+
+ size_t size() const { return data_->size(); }
+
+ private:
+ internal::String_Data* data_;
+};
+
// This must be specialized for any type |T| to be serialized/deserialized as
// a mojom string.
//
@@ -24,7 +40,6 @@
// static size_t GetSize(const CustomString& input);
// static const char* GetData(const CustomString& input);
//
-// // The caller guarantees that |!input.is_null()|.
// static bool Read(StringDataView input, CustomString* output);
// };
//
diff --git a/mojo/public/cpp/bindings/string_traits_standard.h b/mojo/public/cpp/bindings/string_traits_standard.h
new file mode 100644
index 0000000..9b78d24
--- /dev/null
+++ b/mojo/public/cpp/bindings/string_traits_standard.h
@@ -0,0 +1,31 @@
+// 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_STRING_TRAITS_STANDARD_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_STANDARD_H_
+
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/cpp/bindings/string_traits.h"
+
+namespace mojo {
+
+template <>
+struct StringTraits<String> {
+ static bool IsNull(const String& input) { return input.is_null(); }
+ static void SetToNull(String* output) { *output = nullptr; }
+
+ static size_t GetSize(const String& input) { return input.size(); }
+
+ static const char* GetData(const String& input) { return input.data(); }
+
+ static bool Read(StringDataView input, String* output) {
+ String result(input.storage(), input.size());
+ result.Swap(output);
+ return true;
+ }
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_STANDARD_H_
diff --git a/mojo/public/cpp/bindings/string_traits_string16.h b/mojo/public/cpp/bindings/string_traits_string16.h
index f96973a..5a08908 100644
--- a/mojo/public/cpp/bindings/string_traits_string16.h
+++ b/mojo/public/cpp/bindings/string_traits_string16.h
@@ -6,13 +6,12 @@
#define MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_STRING16_H_
#include "base/strings/string16.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/bindings/string_traits.h"
namespace mojo {
template <>
-struct MOJO_CPP_BINDINGS_EXPORT StringTraits<base::string16> {
+struct StringTraits<base::string16> {
static bool IsNull(const base::string16& input) {
// base::string16 is always converted to non-null mojom string.
return false;
diff --git a/mojo/public/cpp/bindings/strong_binding.h b/mojo/public/cpp/bindings/strong_binding.h
index f4b4a06..7fb7eea 100644
--- a/mojo/public/cpp/bindings/strong_binding.h
+++ b/mojo/public/cpp/bindings/strong_binding.h
@@ -5,54 +5,94 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_H_
#define MOJO_PUBLIC_CPP_BINDINGS_STRONG_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/binding.h"
-#include "mojo/public/cpp/bindings/connection_error_callback.h"
-#include "mojo/public/cpp/bindings/filter_chain.h"
#include "mojo/public/cpp/bindings/interface_ptr.h"
#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/lib/filter_chain.h"
+#include "mojo/public/cpp/bindings/lib/router.h"
#include "mojo/public/cpp/bindings/message_header_validator.h"
#include "mojo/public/cpp/system/core.h"
namespace mojo {
-template <typename Interface>
-class StrongBinding;
-
-template <typename Interface>
-using StrongBindingPtr = base::WeakPtr<StrongBinding<Interface>>;
-
// This connects an interface implementation strongly to a pipe. When a
-// connection error is detected the implementation is deleted. If the task
-// runner that a StrongBinding is bound on is stopped, the connection error
-// handler will not be invoked and the implementation will not be deleted.
+// connection error is detected the implementation is deleted. Deleting the
+// connector also closes the pipe.
//
-// To use, call StrongBinding<T>::Create() (see below) or the helper
-// MakeStrongBinding function:
+// Example of an implementation that is always bound strongly to a pipe
//
-// mojo::MakeStrongBinding(base::MakeUnique<FooImpl>(),
-// std::move(foo_request));
+// class StronglyBound : public Foo {
+// public:
+// explicit StronglyBound(InterfaceRequest<Foo> request)
+// : binding_(this, std::move(request)) {}
//
+// // Foo implementation here
+//
+// private:
+// StrongBinding<Foo> binding_;
+// };
+//
+// class MyFooFactory : public InterfaceFactory<Foo> {
+// public:
+// void Create(..., InterfaceRequest<Foo> request) override {
+// new StronglyBound(std::move(request)); // The binding now owns the
+// // instance of StronglyBound.
+// }
+// };
+//
+// This class is thread hostile once it is bound to a message pipe. Until it is
+// bound, it may be bound or destroyed on any thread.
template <typename Interface>
class StrongBinding {
public:
- // Create a new StrongBinding instance. The instance owns itself, cleaning up
- // only in the event of a pipe connection error. Returns a WeakPtr to the new
- // StrongBinding instance.
- static StrongBindingPtr<Interface> Create(
- std::unique_ptr<Interface> impl,
- InterfaceRequest<Interface> request) {
- StrongBinding* binding =
- new StrongBinding(std::move(impl), std::move(request));
- return binding->weak_factory_.GetWeakPtr();
+ explicit StrongBinding(Interface* impl) : binding_(impl) {}
+
+ StrongBinding(Interface* impl, ScopedMessagePipeHandle handle)
+ : StrongBinding(impl) {
+ Bind(std::move(handle));
+ }
+
+ StrongBinding(Interface* impl, InterfacePtr<Interface>* ptr)
+ : StrongBinding(impl) {
+ Bind(ptr);
+ }
+
+ StrongBinding(Interface* impl, InterfaceRequest<Interface> request)
+ : StrongBinding(impl) {
+ Bind(std::move(request));
+ }
+
+ ~StrongBinding() {}
+
+ void Bind(ScopedMessagePipeHandle handle) {
+ DCHECK(!binding_.is_bound());
+ binding_.Bind(std::move(handle));
+ binding_.set_connection_error_handler(
+ base::Bind(&StrongBinding::OnConnectionError, base::Unretained(this)));
+ }
+
+ void Bind(InterfacePtr<Interface>* ptr) {
+ DCHECK(!binding_.is_bound());
+ binding_.Bind(ptr);
+ binding_.set_connection_error_handler(
+ base::Bind(&StrongBinding::OnConnectionError, base::Unretained(this)));
+ }
+
+ void Bind(InterfaceRequest<Interface> request) {
+ DCHECK(!binding_.is_bound());
+ binding_.Bind(std::move(request));
+ binding_.set_connection_error_handler(
+ base::Bind(&StrongBinding::OnConnectionError, base::Unretained(this)));
+ }
+
+ bool WaitForIncomingMethodCall() {
+ return binding_.WaitForIncomingMethodCall();
}
// Note: The error handler must not delete the interface implementation.
@@ -62,64 +102,25 @@
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();
- }
+ Interface* impl() { return binding_.impl(); }
+ // Exposed for testing, should not generally be used.
+ internal::Router* internal_router() { return binding_.internal_router(); }
- // 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:
- StrongBinding(std::unique_ptr<Interface> impl,
- InterfaceRequest<Interface> request)
- : impl_(std::move(impl)),
- binding_(impl_.get(), std::move(request)),
- weak_factory_(this) {
- binding_.set_connection_error_with_reason_handler(
- base::Bind(&StrongBinding::OnConnectionError, base::Unretained(this)));
- }
-
- ~StrongBinding() {}
-
- void OnConnectionError(uint32_t custom_reason,
- const std::string& description) {
+ void OnConnectionError() {
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();
+ delete binding_.impl();
}
- std::unique_ptr<Interface> impl_;
+ private:
base::Closure connection_error_handler_;
- ConnectionErrorWithReasonCallback connection_error_with_reason_handler_;
Binding<Interface> binding_;
- base::WeakPtrFactory<StrongBinding> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(StrongBinding);
};
-template <typename Interface, typename Impl>
-StrongBindingPtr<Interface> MakeStrongBinding(
- std::unique_ptr<Impl> impl,
- InterfaceRequest<Interface> request) {
- return StrongBinding<Interface>::Create(std::move(impl), std::move(request));
-}
-
} // namespace mojo
#endif // MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_H_
diff --git a/mojo/public/cpp/bindings/struct_ptr.h b/mojo/public/cpp/bindings/struct_ptr.h
index b135312..92f2728 100644
--- a/mojo/public/cpp/bindings/struct_ptr.h
+++ b/mojo/public/cpp/bindings/struct_ptr.h
@@ -5,26 +5,23 @@
#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
#define MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
-#include <functional>
-#include <memory>
#include <new>
#include "base/logging.h"
#include "base/macros.h"
-#include "base/optional.h"
-#include "mojo/public/cpp/bindings/lib/hash_util.h"
#include "mojo/public/cpp/bindings/type_converter.h"
namespace mojo {
namespace internal {
-constexpr size_t kHashSeed = 31;
-
template <typename Struct>
-class StructPtrWTFHelper;
-
-template <typename Struct>
-class InlinedStructPtrWTFHelper;
+class StructHelper {
+ public:
+ template <typename Ptr>
+ static void Initialize(Ptr* ptr) {
+ ptr->Initialize();
+ }
+};
} // namespace internal
@@ -34,34 +31,35 @@
public:
using Struct = S;
- StructPtr() = default;
- StructPtr(decltype(nullptr)) {}
+ StructPtr() : ptr_(nullptr) {}
+ StructPtr(decltype(nullptr)) : ptr_(nullptr) {}
- ~StructPtr() = default;
+ ~StructPtr() { delete ptr_; }
StructPtr& operator=(decltype(nullptr)) {
reset();
return *this;
}
- StructPtr(StructPtr&& other) { Take(&other); }
+ StructPtr(StructPtr&& other) : ptr_(nullptr) { Take(&other); }
StructPtr& operator=(StructPtr&& other) {
Take(&other);
return *this;
}
- template <typename... Args>
- StructPtr(base::in_place_t, Args&&... args)
- : ptr_(new Struct(std::forward<Args>(args)...)) {}
-
template <typename U>
U To() const {
return TypeConverter<U, StructPtr>::Convert(*this);
}
- void reset() { ptr_.reset(); }
+ void reset() {
+ if (ptr_) {
+ delete ptr_;
+ ptr_ = nullptr;
+ }
+ }
- bool is_null() const { return !ptr_; }
+ bool is_null() const { return ptr_ == nullptr; }
Struct& operator*() const {
DCHECK(ptr_);
@@ -69,9 +67,9 @@
}
Struct* operator->() const {
DCHECK(ptr_);
- return ptr_.get();
+ return ptr_;
}
- Struct* get() const { return ptr_.get(); }
+ Struct* get() const { return ptr_; }
void Swap(StructPtr* other) { std::swap(ptr_, other->ptr_); }
@@ -80,52 +78,52 @@
// that it contains Mojo handles).
StructPtr Clone() const { return is_null() ? StructPtr() : ptr_->Clone(); }
- // Compares the pointees (which might both be null).
- // TODO(tibell): Get rid of Equals in favor of the operator. Same for Hash.
bool Equals(const StructPtr& other) const {
if (is_null() || other.is_null())
return is_null() && other.is_null();
return ptr_->Equals(*other.ptr_);
}
- // Hashes based on the pointee (which might be null).
- size_t Hash(size_t seed) const {
- if (is_null())
- return internal::HashCombine(seed, 0);
- return ptr_->Hash(seed);
- }
+ private:
+ // TODO(dcheng): Use an explicit conversion operator.
+ typedef Struct* StructPtr::*Testable;
- explicit operator bool() const { return !is_null(); }
+ public:
+ operator Testable() const { return ptr_ ? &StructPtr::ptr_ : 0; }
private:
- friend class internal::StructPtrWTFHelper<Struct>;
+ friend class internal::StructHelper<Struct>;
+
+ // Forbid the == and != operators explicitly, otherwise StructPtr will be
+ // converted to Testable to do == or != comparison.
+ template <typename T>
+ bool operator==(const StructPtr<T>& other) const = delete;
+ template <typename T>
+ bool operator!=(const StructPtr<T>& other) const = delete;
+
+ void Initialize() {
+ DCHECK(!ptr_);
+ ptr_ = new Struct();
+ }
+
void Take(StructPtr* other) {
reset();
Swap(other);
}
- std::unique_ptr<Struct> ptr_;
+ Struct* ptr_;
DISALLOW_COPY_AND_ASSIGN(StructPtr);
};
-template <typename T>
-bool operator==(const StructPtr<T>& lhs, const StructPtr<T>& rhs) {
- return lhs.Equals(rhs);
-}
-template <typename T>
-bool operator!=(const StructPtr<T>& lhs, const StructPtr<T>& rhs) {
- return !(lhs == rhs);
-}
-
// Designed to be used when Struct is small and copyable.
template <typename S>
class InlinedStructPtr {
public:
using Struct = S;
- InlinedStructPtr() : state_(NIL) {}
- InlinedStructPtr(decltype(nullptr)) : state_(NIL) {}
+ InlinedStructPtr() : is_null_(true) {}
+ InlinedStructPtr(decltype(nullptr)) : is_null_(true) {}
~InlinedStructPtr() {}
@@ -134,150 +132,79 @@
return *this;
}
- InlinedStructPtr(InlinedStructPtr&& other) : state_(NIL) { Take(&other); }
+ InlinedStructPtr(InlinedStructPtr&& other) : is_null_(true) { Take(&other); }
InlinedStructPtr& operator=(InlinedStructPtr&& other) {
Take(&other);
return *this;
}
- template <typename... Args>
- InlinedStructPtr(base::in_place_t, Args&&... args)
- : value_(std::forward<Args>(args)...), state_(VALID) {}
-
template <typename U>
U To() const {
return TypeConverter<U, InlinedStructPtr>::Convert(*this);
}
void reset() {
- state_ = NIL;
+ is_null_ = true;
value_. ~Struct();
new (&value_) Struct();
}
- bool is_null() const { return state_ == NIL; }
+ bool is_null() const { return is_null_; }
Struct& operator*() const {
- DCHECK(state_ == VALID);
+ DCHECK(!is_null_);
return value_;
}
Struct* operator->() const {
- DCHECK(state_ == VALID);
+ DCHECK(!is_null_);
return &value_;
}
Struct* get() const { return &value_; }
void Swap(InlinedStructPtr* other) {
std::swap(value_, other->value_);
- std::swap(state_, other->state_);
+ std::swap(is_null_, other->is_null_);
}
InlinedStructPtr Clone() const {
return is_null() ? InlinedStructPtr() : value_.Clone();
}
-
- // Compares the pointees (which might both be null).
bool Equals(const InlinedStructPtr& other) const {
if (is_null() || other.is_null())
return is_null() && other.is_null();
return value_.Equals(other.value_);
}
- // Hashes based on the pointee (which might be null).
- size_t Hash(size_t seed) const {
- if (is_null())
- return internal::HashCombine(seed, 0);
- return value_.Hash(seed);
- }
+ private:
+ // TODO(dcheng): Use an explicit conversion operator.
+ typedef Struct InlinedStructPtr::*Testable;
- explicit operator bool() const { return !is_null(); }
+ public:
+ operator Testable() const { return is_null_ ? 0 : &InlinedStructPtr::value_; }
private:
- friend class internal::InlinedStructPtrWTFHelper<Struct>;
+ friend class internal::StructHelper<Struct>;
+
+ // Forbid the == and != operators explicitly, otherwise InlinedStructPtr will
+ // be converted to Testable to do == or != comparison.
+ template <typename T>
+ bool operator==(const InlinedStructPtr<T>& other) const = delete;
+ template <typename T>
+ bool operator!=(const InlinedStructPtr<T>& other) const = delete;
+
+ void Initialize() { is_null_ = false; }
+
void Take(InlinedStructPtr* other) {
reset();
Swap(other);
}
- enum State {
- VALID,
- NIL,
- DELETED, // For use in WTF::HashMap only
- };
-
mutable Struct value_;
- State state_;
+ bool is_null_;
DISALLOW_COPY_AND_ASSIGN(InlinedStructPtr);
};
-template <typename T>
-bool operator==(const InlinedStructPtr<T>& lhs,
- const InlinedStructPtr<T>& rhs) {
- return lhs.Equals(rhs);
-}
-template <typename T>
-bool operator!=(const InlinedStructPtr<T>& lhs,
- const InlinedStructPtr<T>& rhs) {
- return !(lhs == rhs);
-}
-
-namespace internal {
-
-template <typename Struct>
-class StructPtrWTFHelper {
- public:
- static bool IsHashTableDeletedValue(const StructPtr<Struct>& value) {
- return value.ptr_.get() == reinterpret_cast<Struct*>(1u);
- }
-
- static void ConstructDeletedValue(mojo::StructPtr<Struct>& slot) {
- // |slot| refers to a previous, real value that got deleted and had its
- // destructor run, so this is the first time the "deleted value" has its
- // constructor called.
- //
- // Dirty trick: implant an invalid pointer in |ptr_|. Destructor isn't
- // called for deleted buckets, so this is okay.
- new (&slot) StructPtr<Struct>();
- slot.ptr_.reset(reinterpret_cast<Struct*>(1u));
- }
-};
-
-template <typename Struct>
-class InlinedStructPtrWTFHelper {
- public:
- static bool IsHashTableDeletedValue(const InlinedStructPtr<Struct>& value) {
- return value.state_ == InlinedStructPtr<Struct>::DELETED;
- }
-
- static void ConstructDeletedValue(mojo::InlinedStructPtr<Struct>& slot) {
- // |slot| refers to a previous, real value that got deleted and had its
- // destructor run, so this is the first time the "deleted value" has its
- // constructor called.
- new (&slot) InlinedStructPtr<Struct>();
- slot.state_ = InlinedStructPtr<Struct>::DELETED;
- }
-};
-
-} // namespace internal
} // namespace mojo
-namespace std {
-
-template <typename T>
-struct hash<mojo::StructPtr<T>> {
- size_t operator()(const mojo::StructPtr<T>& value) const {
- return value.Hash(mojo::internal::kHashSeed);
- }
-};
-
-template <typename T>
-struct hash<mojo::InlinedStructPtr<T>> {
- size_t operator()(const mojo::InlinedStructPtr<T>& value) const {
- return value.Hash(mojo::internal::kHashSeed);
- }
-};
-
-} // namespace std
-
#endif // MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
diff --git a/mojo/public/cpp/bindings/struct_traits.h b/mojo/public/cpp/bindings/struct_traits.h
index 6cc070f..0f0bea7 100644
--- a/mojo/public/cpp/bindings/struct_traits.h
+++ b/mojo/public/cpp/bindings/struct_traits.h
@@ -8,11 +8,7 @@
namespace mojo {
// This must be specialized for any type |T| to be serialized/deserialized as
-// a mojom struct. |DataViewType| is the corresponding data view type of the
-// mojom struct. For example, if the mojom struct is example.Foo,
-// |DataViewType| will be example::FooDataView, which can also be referred to by
-// example::Foo::DataView (in chromium) and example::blink::Foo::DataView (in
-// blink).
+// a mojom struct of type |MojomType|.
//
// Each specialization needs to implement a few things:
// 1. Static getters for each field in the Mojom type. These should be
@@ -24,21 +20,19 @@
// from |input|.
//
// Serializable form of a field:
-// Value or reference of the same type used in the generated stuct
-// wrapper type, or the following alternatives:
+// Value or reference of the same type used in |MojomType|, or the
+// following alternatives:
// - string:
// Value or reference of any type that has a StringTraits defined.
-// Supported by default: base::StringPiece, std::string,
-// WTF::String (in blink).
+// Supported by default: base::StringPiece, std::string.
//
// - array:
// Value or reference of any type that has an ArrayTraits defined.
-// Supported by default: std::vector, CArray, WTF::Vector (in blink)
+// Supported by default: std::vector, WTF::Vector (in blink), CArray.
//
// - map:
// Value or reference of any type that has a MapTraits defined.
-// Supported by default: std::map, std::unordered_map,
-// WTF::HashMap (in blink).
+// Supported by default: std::map.
//
// - struct:
// Value or reference of any type that has a StructTraits defined.
@@ -46,10 +40,6 @@
// - enum:
// Value of any type that has an EnumTraits defined.
//
-// For any nullable string/struct/array/map/union field you could also
-// return value or reference of base::Optional<T>/WTF::Optional<T>, if T
-// has the right *Traits defined.
-//
// During serialization, getters for string/struct/array/map/union fields
// are called twice (one for size calculation and one for actual
// serialization). If you want to return a value (as opposed to a
@@ -59,13 +49,13 @@
// Getters for fields of other types are called once.
//
// 2. A static Read() method to set the contents of a |T| instance from a
-// DataViewType.
+// |MojomType|DataView (e.g., if |MojomType| is test::Example, the data
+// view will be test::ExampleDataView).
//
-// static bool Read(DataViewType data, T* output);
+// static bool Read(|MojomType|DataView data, T* output);
//
-// The generated DataViewType provides a convenient, inexpensive view of a
-// serialized struct's field data. The caller guarantees that
-// |!data.is_null()|.
+// The generated |MojomType|DataView type provides a convenient,
+// inexpensive view of a serialized struct's field data.
//
// Returning false indicates invalid incoming data and causes the message
// pipe receiving it to be disconnected. Therefore, you can do custom
@@ -121,12 +111,9 @@
// reference/value to the Mojo bindings for serialization:
// - if T is used in the "type_mappings" section of a typemap config file,
// you need to declare it as pass-by-value:
-// type_mappings = [ "MojomType=T[move_only]" ]
-// or
-// type_mappings = [ "MojomType=T[copyable_pass_by_value]" ]
-//
-// - if another type U's StructTraits/UnionTraits has a getter for T, it
-// needs to return non-const reference/value.
+// type_mappings = [ "MojomType=T(pass_by_value)" ]
+// - if another type U's StructTraits has a getter for T, it needs to return
+// non-const reference/value.
//
// EXAMPLE:
//
@@ -141,7 +128,7 @@
//
// StructTraits for Foo:
// template <>
-// struct StructTraits<FooDataView, CustomFoo> {
+// struct StructTraits<Foo, CustomFoo> {
// // Optional methods dealing with null:
// static bool IsNull(const CustomFoo& input);
// static void SetToNull(CustomFoo* output);
@@ -157,7 +144,7 @@
// static bool Read(FooDataView data, CustomFoo* output);
// };
//
-template <typename DataViewType, typename T>
+template <typename MojomType, typename T>
struct StructTraits;
} // namespace mojo
diff --git a/mojo/public/cpp/bindings/sync_call_restrictions.h b/mojo/public/cpp/bindings/sync_call_restrictions.h
index 39a77a8..e8fc1c4 100644
--- a/mojo/public/cpp/bindings/sync_call_restrictions.h
+++ b/mojo/public/cpp/bindings/sync_call_restrictions.h
@@ -7,7 +7,6 @@
#include "base/macros.h"
#include "base/threading/thread_restrictions.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
#define ENABLE_SYNC_CALL_RESTRICTIONS 1
@@ -15,12 +14,8 @@
#define ENABLE_SYNC_CALL_RESTRICTIONS 0
#endif
-namespace leveldb {
-class LevelDBMojoProxy;
-}
-
namespace ui {
-class Gpu;
+class GpuService;
}
namespace views {
@@ -41,7 +36,7 @@
// a very compelling reason to disregard that (which should be very very rare),
// you can override it by constructing a ScopedAllowSyncCall object, which
// allows making sync calls on the current thread during its lifetime.
-class MOJO_CPP_BINDINGS_EXPORT SyncCallRestrictions {
+class SyncCallRestrictions {
public:
#if ENABLE_SYNC_CALL_RESTRICTIONS
// Checks whether the current thread is allowed to make sync calls, and causes
@@ -55,9 +50,7 @@
private:
// DO NOT ADD ANY OTHER FRIEND STATEMENTS, talk to mojo/OWNERS first.
// BEGIN ALLOWED USAGE.
- friend class ui::Gpu; // http://crbug.com/620058
- // LevelDBMojoProxy makes same-process sync calls from the DB thread.
- friend class leveldb::LevelDBMojoProxy;
+ friend class ui::GpuService; // http://crbug.com/620058
// END ALLOWED USAGE.
// BEGIN USAGE THAT NEEDS TO BE FIXED.
diff --git a/mojo/public/cpp/bindings/sync_handle_registry.h b/mojo/public/cpp/bindings/sync_handle_registry.h
index b5415af..6c0701e 100644
--- a/mojo/public/cpp/bindings/sync_handle_registry.h
+++ b/mojo/public/cpp/bindings/sync_handle_registry.h
@@ -11,7 +11,6 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/threading/thread_checker.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/system/core.h"
namespace mojo {
@@ -20,8 +19,7 @@
// be watched together.
//
// This class is not thread safe.
-class MOJO_CPP_BINDINGS_EXPORT SyncHandleRegistry
- : public base::RefCounted<SyncHandleRegistry> {
+class SyncHandleRegistry : public base::RefCounted<SyncHandleRegistry> {
public:
// Returns a thread-local object.
static scoped_refptr<SyncHandleRegistry> current();
diff --git a/mojo/public/cpp/bindings/sync_handle_watcher.h b/mojo/public/cpp/bindings/sync_handle_watcher.h
index eff73dd..36b796b 100644
--- a/mojo/public/cpp/bindings/sync_handle_watcher.h
+++ b/mojo/public/cpp/bindings/sync_handle_watcher.h
@@ -8,7 +8,6 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/threading/thread_checker.h"
-#include "mojo/public/cpp/bindings/bindings_export.h"
#include "mojo/public/cpp/bindings/sync_handle_registry.h"
#include "mojo/public/cpp/system/core.h"
@@ -26,7 +25,7 @@
// associated endpoints on different threads.
//
// This class is not thread safe.
-class MOJO_CPP_BINDINGS_EXPORT SyncHandleWatcher {
+class SyncHandleWatcher {
public:
// Note: |handle| must outlive this object.
SyncHandleWatcher(const Handle& handle,
diff --git a/mojo/public/cpp/bindings/thread_safe_interface_ptr.h b/mojo/public/cpp/bindings/thread_safe_interface_ptr.h
deleted file mode 100644
index bab6d22..0000000
--- a/mojo/public/cpp/bindings/thread_safe_interface_ptr.h
+++ /dev/null
@@ -1,278 +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.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_THREAD_SAFE_INTERFACE_PTR_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_THREAD_SAFE_INTERFACE_PTR_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/memory/ref_counted.h"
-#include "base/task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "mojo/public/cpp/bindings/associated_group.h"
-#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
-#include "mojo/public/cpp/bindings/interface_ptr.h"
-#include "mojo/public/cpp/bindings/message.h"
-
-namespace mojo {
-
-// Instances of this class may be used from any thread to serialize |Interface|
-// messages and forward them elsewhere. In general you should use one of the
-// ThreadSafeInterfacePtrBase helper aliases defined below, but this type may be
-// useful if you need/want to manually manage the lifetime of the underlying
-// proxy object which will be used to ultimately send messages.
-template <typename Interface>
-class ThreadSafeForwarder : public MessageReceiverWithResponder {
- public:
- using ProxyType = typename Interface::Proxy_;
- using ForwardMessageCallback = base::Callback<void(Message)>;
- using ForwardMessageWithResponderCallback =
- base::Callback<void(Message, std::unique_ptr<MessageReceiver>)>;
-
- // Constructs a ThreadSafeForwarder through which Messages are forwarded to
- // |forward| or |forward_with_responder| by posting to |task_runner|.
- //
- // Any message sent through this forwarding interface will dispatch its reply,
- // if any, back to the thread which called the corresponding interface method.
- ThreadSafeForwarder(
- const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
- const ForwardMessageCallback& forward,
- const ForwardMessageWithResponderCallback& forward_with_responder,
- const AssociatedGroup& associated_group)
- : proxy_(this),
- task_runner_(task_runner),
- forward_(forward),
- forward_with_responder_(forward_with_responder),
- associated_group_(associated_group) {}
-
- ~ThreadSafeForwarder() override {}
-
- ProxyType& proxy() { return proxy_; }
-
- private:
- // MessageReceiverWithResponder implementation:
- bool Accept(Message* message) override {
- if (!message->associated_endpoint_handles()->empty()) {
- // If this DCHECK fails, it is likely because:
- // - This is a non-associated interface pointer setup using
- // PtrWrapper::BindOnTaskRunner(
- // InterfacePtrInfo<InterfaceType> ptr_info);
- // Please see the TODO in that method.
- // - This is an associated interface which hasn't been associated with a
- // message pipe. In other words, the corresponding
- // AssociatedInterfaceRequest hasn't been sent.
- DCHECK(associated_group_.GetController());
- message->SerializeAssociatedEndpointHandles(
- associated_group_.GetController());
- }
- task_runner_->PostTask(FROM_HERE,
- base::Bind(forward_, base::Passed(message)));
- return true;
- }
-
- bool AcceptWithResponder(Message* message,
- MessageReceiver* response_receiver) override {
- if (!message->associated_endpoint_handles()->empty()) {
- // Please see comment for the DCHECK in the previous method.
- DCHECK(associated_group_.GetController());
- message->SerializeAssociatedEndpointHandles(
- associated_group_.GetController());
- }
- auto responder = base::MakeUnique<ForwardToCallingThread>(
- base::WrapUnique(response_receiver));
- task_runner_->PostTask(
- FROM_HERE, base::Bind(forward_with_responder_, base::Passed(message),
- base::Passed(&responder)));
- return true;
- }
-
- class ForwardToCallingThread : public MessageReceiver {
- public:
- explicit ForwardToCallingThread(std::unique_ptr<MessageReceiver> responder)
- : responder_(std::move(responder)),
- caller_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
- }
-
- private:
- bool Accept(Message* message) {
- // The current instance will be deleted when this method returns, so we
- // have to relinquish the responder's ownership so it does not get
- // deleted.
- caller_task_runner_->PostTask(FROM_HERE,
- base::Bind(&ForwardToCallingThread::CallAcceptAndDeleteResponder,
- base::Passed(std::move(responder_)),
- base::Passed(std::move(*message))));
- return true;
- }
-
- static void CallAcceptAndDeleteResponder(
- std::unique_ptr<MessageReceiver> responder,
- Message message) {
- ignore_result(responder->Accept(&message));
- }
-
- std::unique_ptr<MessageReceiver> responder_;
- scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
- };
-
- ProxyType proxy_;
- const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
- const ForwardMessageCallback forward_;
- const ForwardMessageWithResponderCallback forward_with_responder_;
- AssociatedGroup associated_group_;
-
- DISALLOW_COPY_AND_ASSIGN(ThreadSafeForwarder);
-};
-
-template <typename InterfacePtrType>
-class ThreadSafeInterfacePtrBase
- : public base::RefCountedThreadSafe<
- ThreadSafeInterfacePtrBase<InterfacePtrType>> {
- public:
- using InterfaceType = typename InterfacePtrType::InterfaceType;
- using PtrInfoType = typename InterfacePtrType::PtrInfoType;
-
- explicit ThreadSafeInterfacePtrBase(
- std::unique_ptr<ThreadSafeForwarder<InterfaceType>> forwarder)
- : forwarder_(std::move(forwarder)) {}
-
- // Creates a ThreadSafeInterfacePtrBase wrapping an underlying non-thread-safe
- // InterfacePtrType which is bound to the calling thread. All messages sent
- // via this thread-safe proxy will internally be sent by first posting to this
- // (the calling) thread's TaskRunner.
- static scoped_refptr<ThreadSafeInterfacePtrBase> Create(
- InterfacePtrType interface_ptr) {
- scoped_refptr<PtrWrapper> wrapper =
- new PtrWrapper(std::move(interface_ptr));
- return new ThreadSafeInterfacePtrBase(wrapper->CreateForwarder());
- }
-
- // Creates a ThreadSafeInterfacePtrBase which binds the underlying
- // non-thread-safe InterfacePtrType on the specified TaskRunner. All messages
- // sent via this thread-safe proxy will internally be sent by first posting to
- // that TaskRunner.
- static scoped_refptr<ThreadSafeInterfacePtrBase> Create(
- PtrInfoType ptr_info,
- const scoped_refptr<base::SingleThreadTaskRunner>& bind_task_runner) {
- scoped_refptr<PtrWrapper> wrapper = new PtrWrapper(bind_task_runner);
- wrapper->BindOnTaskRunner(std::move(ptr_info));
- return new ThreadSafeInterfacePtrBase(wrapper->CreateForwarder());
- }
-
- InterfaceType* get() { return &forwarder_->proxy(); }
- InterfaceType* operator->() { return get(); }
- InterfaceType& operator*() { return *get(); }
-
- private:
- friend class base::RefCountedThreadSafe<
- ThreadSafeInterfacePtrBase<InterfacePtrType>>;
-
- struct PtrWrapperDeleter;
-
- // Helper class which owns an |InterfacePtrType| instance on an appropriate
- // thread. This is kept alive as long its bound within some
- // ThreadSafeForwarder's callbacks.
- class PtrWrapper
- : public base::RefCountedThreadSafe<PtrWrapper, PtrWrapperDeleter> {
- public:
- explicit PtrWrapper(InterfacePtrType ptr)
- : PtrWrapper(base::ThreadTaskRunnerHandle::Get()) {
- ptr_ = std::move(ptr);
- associated_group_ = *ptr_.internal_state()->associated_group();
- }
-
- explicit PtrWrapper(
- const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
- : task_runner_(task_runner) {}
-
- void BindOnTaskRunner(AssociatedInterfacePtrInfo<InterfaceType> ptr_info) {
- associated_group_ = AssociatedGroup(ptr_info.handle());
- task_runner_->PostTask(FROM_HERE, base::Bind(&PtrWrapper::Bind, this,
- base::Passed(&ptr_info)));
- }
-
- void BindOnTaskRunner(InterfacePtrInfo<InterfaceType> ptr_info) {
- // TODO(yzhsen): At the momment we don't have a group controller
- // available. That means the user won't be able to pass associated
- // endpoints on this interface (at least not immediately). In order to fix
- // this, we need to create a MultiplexRouter immediately and bind it to
- // the interface pointer on the |task_runner_|. Therefore, MultiplexRouter
- // should be able to be created on a thread different than the one that it
- // is supposed to listen on. crbug.com/682334
- task_runner_->PostTask(FROM_HERE, base::Bind(&PtrWrapper::Bind, this,
- base::Passed(&ptr_info)));
- }
-
- std::unique_ptr<ThreadSafeForwarder<InterfaceType>> CreateForwarder() {
- return base::MakeUnique<ThreadSafeForwarder<InterfaceType>>(
- task_runner_, base::Bind(&PtrWrapper::Accept, this),
- base::Bind(&PtrWrapper::AcceptWithResponder, this),
- associated_group_);
- }
-
- private:
- friend struct PtrWrapperDeleter;
-
- ~PtrWrapper() {}
-
- void Bind(PtrInfoType ptr_info) {
- DCHECK(task_runner_->RunsTasksOnCurrentThread());
- ptr_.Bind(std::move(ptr_info));
- }
-
- void Accept(Message message) {
- ptr_.internal_state()->ForwardMessage(std::move(message));
- }
-
- void AcceptWithResponder(Message message,
- std::unique_ptr<MessageReceiver> responder) {
- ptr_.internal_state()->ForwardMessageWithResponder(std::move(message),
- std::move(responder));
- }
-
- void DeleteOnCorrectThread() const {
- if (!task_runner_->RunsTasksOnCurrentThread()) {
- // NOTE: This is only called when there are no more references to
- // |this|, so binding it unretained is both safe and necessary.
- task_runner_->PostTask(FROM_HERE,
- base::Bind(&PtrWrapper::DeleteOnCorrectThread,
- base::Unretained(this)));
- } else {
- delete this;
- }
- }
-
- InterfacePtrType ptr_;
- const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
- AssociatedGroup associated_group_;
-
- DISALLOW_COPY_AND_ASSIGN(PtrWrapper);
- };
-
- struct PtrWrapperDeleter {
- static void Destruct(const PtrWrapper* interface_ptr) {
- interface_ptr->DeleteOnCorrectThread();
- }
- };
-
- ~ThreadSafeInterfacePtrBase() {}
-
- const std::unique_ptr<ThreadSafeForwarder<InterfaceType>> forwarder_;
-
- DISALLOW_COPY_AND_ASSIGN(ThreadSafeInterfacePtrBase);
-};
-
-template <typename Interface>
-using ThreadSafeAssociatedInterfacePtr =
- ThreadSafeInterfacePtrBase<AssociatedInterfacePtr<Interface>>;
-
-template <typename Interface>
-using ThreadSafeInterfacePtr =
- ThreadSafeInterfacePtrBase<InterfacePtr<Interface>>;
-
-} // namespace mojo
-
-#endif // MOJO_PUBLIC_CPP_BINDINGS_THREAD_SAFE_INTERFACE_PTR_H_
diff --git a/mojo/public/cpp/bindings/type_converter.h b/mojo/public/cpp/bindings/type_converter.h
index 395eeb4..1446ab3 100644
--- a/mojo/public/cpp/bindings/type_converter.h
+++ b/mojo/public/cpp/bindings/type_converter.h
@@ -7,15 +7,8 @@
#include <stdint.h>
-#include <vector>
-
namespace mojo {
-// NOTE: TypeConverter is deprecated. Please consider StructTraits /
-// UnionTraits / EnumTraits / ArrayTraits / MapTraits / StringTraits if you
-// would like to convert between custom types and the wire format of mojom
-// types.
-//
// Specialize the following class:
// template <typename T, typename U> struct TypeConverter;
// to perform type conversion for Mojom-defined structs and arrays. Here, T is
@@ -81,9 +74,6 @@
template <typename T, typename U>
struct TypeConverter;
-template <typename T, typename U>
-inline T ConvertTo(const U& obj);
-
// The following specialization is useful when you are converting between
// Array<POD> and std::vector<POD>.
template <typename T>
@@ -91,18 +81,6 @@
static T Convert(const T& obj) { return obj; }
};
-template <typename T, typename Container>
-struct TypeConverter<std::vector<T>, Container> {
- static std::vector<T> Convert(const Container& container) {
- std::vector<T> output;
- output.reserve(container.size());
- for (const auto& obj : container) {
- output.push_back(ConvertTo<T>(obj));
- }
- return output;
- }
-};
-
// The following helper function is useful for shorthand. The compiler can infer
// the input type, so you can write:
// OutputType out = ConvertTo<OutputType>(input);
diff --git a/mojo/public/cpp/bindings/union_traits.h b/mojo/public/cpp/bindings/union_traits.h
deleted file mode 100644
index 292ee58..0000000
--- a/mojo/public/cpp/bindings/union_traits.h
+++ /dev/null
@@ -1,39 +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.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_UNION_TRAITS_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_UNION_TRAITS_H_
-
-namespace mojo {
-
-// This must be specialized for any type |T| to be serialized/deserialized as
-// a mojom union. |DataViewType| is the corresponding data view type of the
-// mojom union. For example, if the mojom union is example.Foo, |DataViewType|
-// will be example::FooDataView, which can also be referred to by
-// example::Foo::DataView (in chromium) and example::blink::Foo::DataView (in
-// blink).
-//
-// Similar to StructTraits, each specialization of UnionTraits implements the
-// following methods:
-// 1. Getters for each field in the Mojom type.
-// 2. Read() method.
-// 3. [Optional] IsNull() and SetToNull().
-// 4. [Optional] SetUpContext() and TearDownContext().
-// Please see the documentation of StructTraits for details of these methods.
-//
-// Unlike StructTraits, there is one more method to implement:
-// 5. A static GetTag() method indicating which field is the current active
-// field for serialization:
-//
-// static DataViewType::Tag GetTag(const T& input);
-//
-// During serialization, only the field getter corresponding to this tag
-// will be called.
-//
-template <typename DataViewType, typename T>
-struct UnionTraits;
-
-} // namespace mojo
-
-#endif // MOJO_PUBLIC_CPP_BINDINGS_UNION_TRAITS_H_
diff --git a/mojo/public/cpp/bindings/wtf_array.h b/mojo/public/cpp/bindings/wtf_array.h
new file mode 100644
index 0000000..46d9a69
--- /dev/null
+++ b/mojo/public/cpp/bindings/wtf_array.h
@@ -0,0 +1,197 @@
+// 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_WTF_ARRAY_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_WTF_ARRAY_H_
+
+#include <stddef.h>
+#include <utility>
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/lib/array_internal.h"
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+#include "third_party/WebKit/Source/wtf/Vector.h"
+
+namespace mojo {
+
+// Represents an array backed by WTF::Vector. Comparing with WTF::Vector,
+// mojo::WTFArray is move-only and can be null.
+// It is easy to convert between WTF::Vector<T> and mojo::WTFArray<T>:
+// - constructor WTFArray(WTF::Vector<T>&&) takes the contents of a
+// WTF::Vector<T>;
+// - method PassStorage() passes the underlying WTF::Vector.
+template <typename T>
+class WTFArray {
+ public:
+ // Constructs an empty array.
+ WTFArray() : is_null_(false) {}
+ // Constructs a null array.
+ WTFArray(std::nullptr_t null_pointer) : is_null_(true) {}
+
+ // Constructs a new non-null array of the specified size. The elements will
+ // be value-initialized (meaning that they will be initialized by their
+ // default constructor, if any, or else zero-initialized).
+ explicit WTFArray(size_t size) : vec_(size), is_null_(false) {}
+ ~WTFArray() {}
+
+ // Moves the contents of |other| into this array.
+ WTFArray(WTF::Vector<T>&& other) : vec_(std::move(other)), is_null_(false) {}
+ WTFArray(WTFArray&& other) : is_null_(true) { Take(&other); }
+
+ WTFArray& operator=(WTF::Vector<T>&& other) {
+ vec_ = std::move(other);
+ is_null_ = false;
+ return *this;
+ }
+ WTFArray& operator=(WTFArray&& other) {
+ Take(&other);
+ return *this;
+ }
+
+ WTFArray& operator=(std::nullptr_t null_pointer) {
+ is_null_ = true;
+ vec_.clear();
+ return *this;
+ }
+
+ // Creates a non-null array of the specified size. The elements will be
+ // value-initialized (meaning that they will be initialized by their default
+ // constructor, if any, or else zero-initialized).
+ static WTFArray New(size_t size) { return WTFArray(size); }
+
+ // Creates a new array with a copy of the contents of |other|.
+ template <typename U>
+ static WTFArray From(const U& other) {
+ return TypeConverter<WTFArray, U>::Convert(other);
+ }
+
+ // Copies the contents of this array to a new object of type |U|.
+ template <typename U>
+ U To() const {
+ return TypeConverter<U, WTFArray>::Convert(*this);
+ }
+
+ // Indicates whether the array is null (which is distinct from empty).
+ bool is_null() const {
+ // When the array is set to null, the underlying storage |vec_| shouldn't
+ // contain any elements.
+ DCHECK(!is_null_ || vec_.isEmpty());
+ return is_null_;
+ }
+
+ // Indicates whether the array is empty (which is distinct from null).
+ bool empty() const { return vec_.isEmpty() && !is_null_; }
+
+ // Returns a reference to the first element of the array. Calling this on a
+ // null or empty array causes undefined behavior.
+ const T& front() const { return vec_.first(); }
+ T& front() { return vec_.first(); }
+
+ // Returns the size of the array, which will be zero if the array is null.
+ size_t size() const { return vec_.size(); }
+
+ // Returns a reference to the element at zero-based |offset|. Calling this on
+ // an array with size less than |offset|+1 causes undefined behavior.
+ const T& at(size_t offset) const { return vec_.at(offset); }
+ const T& operator[](size_t offset) const { return at(offset); }
+ T& at(size_t offset) { return vec_.at(offset); }
+ T& operator[](size_t offset) { return at(offset); }
+
+ // Resizes the array to |size| and makes it non-null. Otherwise, works just
+ // like the resize method of |WTF::Vector|.
+ void resize(size_t size) {
+ is_null_ = false;
+ vec_.resize(size);
+ }
+
+ // Sets the array to empty (even if previously it was null.)
+ void SetToEmpty() { resize(0); }
+
+ // Returns a const reference to the |WTF::Vector| managed by this class. If
+ // the array is null, this will be an empty vector.
+ const WTF::Vector<T>& storage() const { return vec_; }
+
+ // Passes the underlying storage and resets this array to null.
+ //
+ // TODO(yzshen): Consider changing this to a rvalue-ref-qualified conversion
+ // to WTF::Vector<T> after we move to MSVC 2015.
+ WTF::Vector<T> PassStorage() {
+ is_null_ = true;
+ return std::move(vec_);
+ }
+
+ void Swap(WTFArray* other) {
+ std::swap(is_null_, other->is_null_);
+ vec_.swap(other->vec_);
+ }
+
+ // Swaps the contents of this array with the specified vector, making this
+ // array non-null. Since the vector cannot represent null, it will just be
+ // made empty if this array is null.
+ void Swap(WTF::Vector<T>* other) {
+ is_null_ = false;
+ vec_.swap(*other);
+ }
+
+ // Returns a copy of the array where each value of the new array has been
+ // "cloned" from the corresponding value of this array. If the element type
+ // defines a Clone() method, it will be used; otherwise copy
+ // constructor/assignment will be used.
+ //
+ // Please note that calling this method will fail compilation if the element
+ // type cannot be cloned (which usually means that it is a Mojo handle type or
+ // a type containing Mojo handles).
+ WTFArray Clone() const {
+ WTFArray result;
+ result.is_null_ = is_null_;
+ result.vec_ = internal::Clone(vec_);
+ return result;
+ }
+
+ // Indicates whether the contents of this array are equal to |other|. A null
+ // array is only equal to another null array. If the element type defines an
+ // Equals() method, it will be used; otherwise == operator will be used.
+ bool Equals(const WTFArray& other) const {
+ if (is_null() != other.is_null())
+ return false;
+ return internal::Equals(vec_, other.vec_);
+ }
+
+ private:
+ // TODO(dcheng): Use an explicit conversion operator.
+ typedef WTF::Vector<T> WTFArray::*Testable;
+
+ public:
+ operator Testable() const {
+ // When the array is set to null, the underlying storage |vec_| shouldn't
+ // contain any elements.
+ DCHECK(!is_null_ || vec_.isEmpty());
+ return is_null_ ? 0 : &WTFArray::vec_;
+ }
+
+ private:
+ // Forbid the == and != operators explicitly, otherwise WTFArray will be
+ // converted to Testable to do == or != comparison.
+ template <typename U>
+ bool operator==(const WTFArray<U>& other) const = delete;
+ template <typename U>
+ bool operator!=(const WTFArray<U>& other) const = delete;
+
+ void Take(WTFArray* other) {
+ operator=(nullptr);
+ Swap(other);
+ }
+
+ WTF::Vector<T> vec_;
+ bool is_null_;
+
+ DISALLOW_COPY_AND_ASSIGN(WTFArray);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_WTF_ARRAY_H_
diff --git a/mojo/public/cpp/bindings/wtf_map.h b/mojo/public/cpp/bindings/wtf_map.h
new file mode 100644
index 0000000..0aba959
--- /dev/null
+++ b/mojo/public/cpp/bindings/wtf_map.h
@@ -0,0 +1,200 @@
+// 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_WTF_MAP_H_
+#define MOJO_PUBLIC_CPP_BINDINGS_WTF_MAP_H_
+
+#include <stddef.h>
+#include <utility>
+
+#include "base/macros.h"
+#include "mojo/public/cpp/bindings/lib/template_util.h"
+#include "mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+#include "third_party/WebKit/Source/wtf/HashMap.h"
+#include "third_party/WebKit/Source/wtf/text/StringHash.h"
+
+namespace mojo {
+
+// Represents a map backed by WTF::HashMap. Comparing with WTF::HashMap,
+// mojo::WTFMap is move-only and can be null.
+//
+// It is easy to convert between WTF::HashMap<K, V> and mojo::WTFMap<K, V>:
+// - constructor WTFMap(WTF::HashMap<K, V>&&) takes the contents of a
+// WTF::HashMap<K, V>;
+// - method PassStorage() passes the underlying WTF::HashMap.
+//
+// NOTE: WTF::HashMap disallows certain key values. For integer types, those are
+// 0 and -1 (max value instead of -1 for unsigned). For string, that is null.
+template <typename Key, typename Value>
+class WTFMap {
+ public:
+ using Iterator = typename WTF::HashMap<Key, Value>::iterator;
+ using ConstIterator = typename WTF::HashMap<Key, Value>::const_iterator;
+
+ // Constructs an empty map.
+ WTFMap() : is_null_(false) {}
+ // Constructs a null map.
+ WTFMap(std::nullptr_t null_pointer) : is_null_(true) {}
+
+ ~WTFMap() {}
+
+ WTFMap(WTF::HashMap<Key, Value>&& other)
+ : map_(std::move(other)), is_null_(false) {}
+ WTFMap(WTFMap&& other) : is_null_(true) { Take(&other); }
+
+ WTFMap& operator=(WTF::HashMap<Key, Value>&& other) {
+ is_null_ = false;
+ map_ = std::move(other);
+ return *this;
+ }
+ WTFMap& operator=(WTFMap&& other) {
+ Take(&other);
+ return *this;
+ }
+
+ WTFMap& operator=(std::nullptr_t null_pointer) {
+ is_null_ = true;
+ map_.clear();
+ return *this;
+ }
+
+ static bool IsValidKey(const Key& key) {
+ return WTF::HashMap<Key, Value>::isValidKey(key);
+ }
+
+ // Copies the contents of some other type of map into a new WTFMap using a
+ // TypeConverter.
+ template <typename U>
+ static WTFMap From(const U& other) {
+ return TypeConverter<WTFMap, U>::Convert(other);
+ }
+
+ // Copies the contents of the WTFMap into some other type of map.
+ template <typename U>
+ U To() const {
+ return TypeConverter<U, WTFMap>::Convert(*this);
+ }
+
+ // Indicates whether the map is null (which is distinct from empty).
+ bool is_null() const { return is_null_; }
+
+ // Indicates whether the map is empty (which is distinct from null).
+ bool empty() const { return map_.isEmpty() && !is_null_; }
+
+ // Indicates the number of keys in the map, which will be zero if the map is
+ // null.
+ size_t size() const { return map_.size(); }
+
+ // Inserts a key-value pair into the map. Like WTF::HashMap::add(), this does
+ // not insert |value| if |key| is already a member of the map.
+ void insert(const Key& key, const Value& value) {
+ is_null_ = false;
+ map_.add(key, value);
+ }
+ void insert(const Key& key, Value&& value) {
+ is_null_ = false;
+ map_.add(key, std::move(value));
+ }
+
+ // Returns a reference to the value associated with the specified key,
+ // crashing the process if the key is not present in the map.
+ Value& at(const Key& key) { return map_.find(key)->value; }
+ const Value& at(const Key& key) const { return map_.find(key)->value; }
+
+ // Returns a reference to the value associated with the specified key,
+ // creating a new entry if the key is not already present in the map. A
+ // newly-created value will be value-initialized (meaning that it will be
+ // initialized by the default constructor of the value type, if any, or else
+ // will be zero-initialized).
+ Value& operator[](const Key& key) {
+ is_null_ = false;
+ if (!map_.contains(key))
+ map_.add(key, Value());
+ return at(key);
+ }
+
+ // Sets the map to empty (even if previously it was null).
+ void SetToEmpty() {
+ is_null_ = false;
+ map_.clear();
+ }
+
+ // Returns a const reference to the WTF::HashMap managed by this class. If
+ // this object is null, the return value will be an empty map.
+ const WTF::HashMap<Key, Value>& storage() const { return map_; }
+
+ // Passes the underlying storage and resets this map to null.
+ WTF::HashMap<Key, Value> PassStorage() {
+ is_null_ = true;
+ return std::move(map_);
+ }
+
+ // Swaps the contents of this WTFMap with another WTFMap of the same type
+ // (including nullness).
+ void Swap(WTFMap<Key, Value>* other) {
+ std::swap(is_null_, other->is_null_);
+ map_.swap(other->map_);
+ }
+
+ // Swaps the contents of this WTFMap with an WTF::HashMap containing keys and
+ // values of the same type. Since WTF::HashMap cannot represent the null
+ // state, the WTF::HashMap will be empty if WTFMap is null. The WTFMap will
+ // always be left in a non-null state.
+ void Swap(WTF::HashMap<Key, Value>* other) {
+ is_null_ = false;
+ map_.swap(*other);
+ }
+
+ // Returns a new WTFMap that contains a copy of the contents of this map. If
+ // the key/value type defines a Clone() method, it will be used; otherwise
+ // copy constructor/assignment will be used.
+ //
+ // Please note that calling this method will fail compilation if the key/value
+ // type cannot be cloned (which usually means that it is a Mojo handle type or
+ // a type containing Mojo handles).
+ WTFMap Clone() const {
+ WTFMap result;
+ result.is_null_ = is_null_;
+ result.map_ = internal::Clone(map_);
+ return result;
+ }
+
+ // Indicates whether the contents of this map are equal to those of another
+ // WTFMap (including nullness). If the key/value type defines an Equals()
+ // method, it will be used; otherwise == operator will be used.
+ bool Equals(const WTFMap& other) const {
+ if (is_null() != other.is_null())
+ return false;
+ return internal::Equals(map_, other.map_);
+ }
+
+ ConstIterator begin() const { return map_.begin(); }
+ Iterator begin() { return map_.begin(); }
+
+ ConstIterator end() const { return map_.end(); }
+ Iterator end() { return map_.end(); }
+
+ // Returns the iterator pointing to the entry for |key|, if present, or else
+ // returns end().
+ ConstIterator find(const Key& key) const { return map_.find(key); }
+ Iterator find(const Key& key) { return map_.find(key); }
+
+ explicit operator bool() const { return !is_null_; }
+
+ private:
+ void Take(WTFMap* other) {
+ operator=(nullptr);
+ Swap(other);
+ }
+
+ WTF::HashMap<Key, Value> map_;
+ bool is_null_;
+
+ DISALLOW_COPY_AND_ASSIGN(WTFMap);
+};
+
+} // namespace mojo
+
+#endif // MOJO_PUBLIC_CPP_BINDINGS_WTF_MAP_H_
diff --git a/mojo/public/cpp/system/BUILD.gn b/mojo/public/cpp/system/BUILD.gn
index 0dc7af9..8dcec71 100644
--- a/mojo/public/cpp/system/BUILD.gn
+++ b/mojo/public/cpp/system/BUILD.gn
@@ -2,26 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-# Deletes libsystem.dylib from the build dir, since it shadows
-# /usr/lib/libSystem.dylib on macOS.
-# TODO(thakis): Remove this after a while.
-action("clean_up_old_dylib") {
- script = "//build/rm.py"
- stamp = "$target_gen_dir/clean_up_stamp"
- outputs = [
- stamp,
- ]
- args = [
- "--stamp",
- rebase_path(stamp, root_build_dir),
- "-f",
- "libsystem.dylib",
- ]
-}
-
-component("system") {
- output_name = "mojo_public_system_cpp"
-
+source_set("system") {
sources = [
"buffer.cc",
"buffer.h",
@@ -33,7 +14,6 @@
"message_pipe.h",
"platform_handle.cc",
"platform_handle.h",
- "system_export.h",
"watcher.cc",
"watcher.h",
]
@@ -42,9 +22,4 @@
"//base",
"//mojo/public/c/system",
]
- deps = [
- ":clean_up_old_dylib",
- ]
-
- defines = [ "MOJO_CPP_SYSTEM_IMPLEMENTATION" ]
}
diff --git a/mojo/public/cpp/system/buffer.h b/mojo/public/cpp/system/buffer.h
index 1ae923c..449c6ce 100644
--- a/mojo/public/cpp/system/buffer.h
+++ b/mojo/public/cpp/system/buffer.h
@@ -20,7 +20,6 @@
#include "base/logging.h"
#include "mojo/public/c/system/buffer.h"
#include "mojo/public/cpp/system/handle.h"
-#include "mojo/public/cpp/system/system_export.h"
namespace mojo {
namespace internal {
@@ -42,8 +41,7 @@
// A strongly-typed representation of a |MojoHandle| referring to a shared
// buffer.
-class MOJO_CPP_SYSTEM_EXPORT SharedBufferHandle
- : NON_EXPORTED_BASE(public Handle) {
+class SharedBufferHandle : public Handle {
public:
enum class AccessMode {
READ_WRITE,
diff --git a/mojo/public/cpp/system/platform_handle.cc b/mojo/public/cpp/system/platform_handle.cc
index 42e4aba..e4a6088 100644
--- a/mojo/public/cpp/system/platform_handle.cc
+++ b/mojo/public/cpp/system/platform_handle.cc
@@ -4,11 +4,6 @@
#include "mojo/public/cpp/system/platform_handle.h"
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-#include <mach/mach.h>
-#include "base/mac/mach_logging.h"
-#endif
-
namespace mojo {
namespace {
@@ -66,13 +61,6 @@
const base::SharedMemoryHandle& memory_handle,
size_t size,
bool read_only) {
-#if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS))
- if (memory_handle.fd == base::kInvalidPlatformFile)
- return ScopedSharedBufferHandle();
-#else
- if (!memory_handle.IsValid())
- return ScopedSharedBufferHandle();
-#endif
MojoPlatformHandle platform_handle;
platform_handle.struct_size = sizeof(MojoPlatformHandle);
platform_handle.type = kPlatformSharedBufferHandleType;
@@ -103,8 +91,6 @@
base::SharedMemoryHandle* memory_handle,
size_t* size,
bool* read_only) {
- if (!handle.is_valid())
- return MOJO_RESULT_INVALID_ARGUMENT;
MojoPlatformHandle platform_handle;
platform_handle.struct_size = sizeof(MojoPlatformHandle);
@@ -140,39 +126,4 @@
return MOJO_RESULT_OK;
}
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-ScopedHandle WrapMachPort(mach_port_t port) {
- kern_return_t kr =
- mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, 1);
- MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)
- << "MachPortAttachmentMac mach_port_mod_refs";
- if (kr != KERN_SUCCESS)
- return ScopedHandle();
-
- MojoPlatformHandle platform_handle;
- platform_handle.struct_size = sizeof(MojoPlatformHandle);
- platform_handle.type = MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT;
- platform_handle.value = static_cast<uint64_t>(port);
-
- MojoHandle mojo_handle;
- MojoResult result = MojoWrapPlatformHandle(&platform_handle, &mojo_handle);
- CHECK_EQ(result, MOJO_RESULT_OK);
-
- return ScopedHandle(Handle(mojo_handle));
-}
-
-MojoResult UnwrapMachPort(ScopedHandle handle, mach_port_t* port) {
- MojoPlatformHandle platform_handle;
- platform_handle.struct_size = sizeof(MojoPlatformHandle);
- MojoResult result =
- MojoUnwrapPlatformHandle(handle.release().value(), &platform_handle);
- if (result != MOJO_RESULT_OK)
- return result;
-
- CHECK_EQ(platform_handle.type, MOJO_PLATFORM_HANDLE_TYPE_MACH_PORT);
- *port = static_cast<mach_port_t>(platform_handle.value);
- return MOJO_RESULT_OK;
-}
-#endif // defined(OS_MACOSX) && !defined(OS_IOS)
-
} // namespace mojo
diff --git a/mojo/public/cpp/system/platform_handle.h b/mojo/public/cpp/system/platform_handle.h
index 801264e..2a81734 100644
--- a/mojo/public/cpp/system/platform_handle.h
+++ b/mojo/public/cpp/system/platform_handle.h
@@ -22,7 +22,6 @@
#include "mojo/public/c/system/platform_handle.h"
#include "mojo/public/cpp/system/buffer.h"
#include "mojo/public/cpp/system/handle.h"
-#include "mojo/public/cpp/system/system_export.h"
#if defined(OS_WIN)
#include <windows.h>
@@ -51,18 +50,15 @@
#endif // defined(OS_POSIX)
// Wraps a PlatformFile as a Mojo handle. Takes ownership of the file object.
-MOJO_CPP_SYSTEM_EXPORT
ScopedHandle WrapPlatformFile(base::PlatformFile platform_file);
// Unwraps a PlatformFile from a Mojo handle.
-MOJO_CPP_SYSTEM_EXPORT
MojoResult UnwrapPlatformFile(ScopedHandle handle, base::PlatformFile* file);
// Wraps a base::SharedMemoryHandle as a Mojo handle. Takes ownership of the
// SharedMemoryHandle. Note that |read_only| is only an indicator of whether
// |memory_handle| only supports read-only mapping. It does NOT have any
// influence on the access control of the shared buffer object.
-MOJO_CPP_SYSTEM_EXPORT
ScopedSharedBufferHandle WrapSharedMemoryHandle(
const base::SharedMemoryHandle& memory_handle,
size_t size,
@@ -70,22 +66,10 @@
// Unwraps a base::SharedMemoryHandle from a Mojo handle. The caller assumes
// responsibility for the lifetime of the SharedMemoryHandle.
-MOJO_CPP_SYSTEM_EXPORT MojoResult
-UnwrapSharedMemoryHandle(ScopedSharedBufferHandle handle,
- base::SharedMemoryHandle* memory_handle,
- size_t* size,
- bool* read_only);
-
-#if defined(OS_MACOSX) && !defined(OS_IOS)
-// Wraps a mach_port_t as a Mojo handle. This takes a reference to the
-// Mach port.
-MOJO_CPP_SYSTEM_EXPORT ScopedHandle WrapMachPort(mach_port_t port);
-
-// Unwraps a mach_port_t from a Mojo handle. The caller gets ownership of the
-// Mach port.
-MOJO_CPP_SYSTEM_EXPORT MojoResult UnwrapMachPort(ScopedHandle handle,
- mach_port_t* port);
-#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+MojoResult UnwrapSharedMemoryHandle(ScopedSharedBufferHandle handle,
+ base::SharedMemoryHandle* memory_handle,
+ size_t* size,
+ bool* read_only);
} // namespace mojo
diff --git a/mojo/public/cpp/system/system_export.h b/mojo/public/cpp/system/system_export.h
deleted file mode 100644
index c9bb140..0000000
--- a/mojo/public/cpp/system/system_export.h
+++ /dev/null
@@ -1,34 +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.
-
-#ifndef MOJO_PUBLIC_CPP_SYSTEM_SYSTEM_EXPORT_H_
-#define MOJO_PUBLIC_CPP_SYSTEM_SYSTEM_EXPORT_H_
-
-#if defined(COMPONENT_BUILD)
-
-#if defined(WIN32)
-
-#if defined(MOJO_CPP_SYSTEM_IMPLEMENTATION)
-#define MOJO_CPP_SYSTEM_EXPORT __declspec(dllexport)
-#else
-#define MOJO_CPP_SYSTEM_EXPORT __declspec(dllimport)
-#endif
-
-#else // !defined(WIN32)
-
-#if defined(MOJO_CPP_SYSTEM_IMPLEMENTATION)
-#define MOJO_CPP_SYSTEM_EXPORT __attribute((visibility("default")))
-#else
-#define MOJO_CPP_SYSTEM_EXPORT
-#endif
-
-#endif // defined(WIN32)
-
-#else // !defined(COMPONENT_BUILD)
-
-#define MOJO_CPP_SYSTEM_EXPORT
-
-#endif // defined(COMPONENT_BUILD)
-
-#endif // MOJO_PUBLIC_CPP_SYSTEM_SYSTEM_EXPORT_H_
diff --git a/mojo/public/cpp/system/watcher.cc b/mojo/public/cpp/system/watcher.cc
index 55dcf40..d9319fb 100644
--- a/mojo/public/cpp/system/watcher.cc
+++ b/mojo/public/cpp/system/watcher.cc
@@ -7,17 +7,49 @@
#include "base/bind.h"
#include "base/location.h"
#include "base/macros.h"
-#include "base/trace_event/heap_profiler.h"
+#include "base/message_loop/message_loop.h"
#include "mojo/public/c/system/functions.h"
namespace mojo {
-Watcher::Watcher(const tracked_objects::Location& from_here,
- scoped_refptr<base::SingleThreadTaskRunner> runner)
+class Watcher::MessageLoopObserver
+ : public base::MessageLoop::DestructionObserver {
+ public:
+ explicit MessageLoopObserver(Watcher* watcher) : watcher_(watcher) {
+ base::MessageLoop::current()->AddDestructionObserver(this);
+ }
+
+ ~MessageLoopObserver() override {
+ StopObservingIfNecessary();
+ }
+
+ private:
+ // base::MessageLoop::DestructionObserver:
+ void WillDestroyCurrentMessageLoop() override {
+ StopObservingIfNecessary();
+ if (watcher_->IsWatching()) {
+ // TODO(yzshen): Remove this notification. crbug.com/604762
+ watcher_->OnHandleReady(MOJO_RESULT_ABORTED);
+ }
+ }
+
+ void StopObservingIfNecessary() {
+ if (is_observing_) {
+ is_observing_ = false;
+ base::MessageLoop::current()->RemoveDestructionObserver(this);
+ }
+ }
+
+ bool is_observing_ = true;
+ Watcher* watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageLoopObserver);
+};
+
+Watcher::Watcher(scoped_refptr<base::SingleThreadTaskRunner> runner)
: task_runner_(std::move(runner)),
is_default_task_runner_(task_runner_ ==
base::ThreadTaskRunnerHandle::Get()),
- heap_profiler_tag_(from_here.file_name()),
weak_factory_(this) {
DCHECK(task_runner_->BelongsToCurrentThread());
weak_self_ = weak_factory_.GetWeakPtr();
@@ -40,6 +72,7 @@
DCHECK(!IsWatching());
DCHECK(!callback.is_null());
+ message_loop_observer_.reset(new MessageLoopObserver(this));
callback_ = callback;
handle_ = handle;
MojoResult result = MojoWatch(handle_.value(), signals,
@@ -48,6 +81,7 @@
if (result != MOJO_RESULT_OK) {
handle_.set_value(kInvalidHandleValue);
callback_.Reset();
+ message_loop_observer_.reset();
DCHECK(result == MOJO_RESULT_FAILED_PRECONDITION ||
result == MOJO_RESULT_INVALID_ARGUMENT);
return result;
@@ -65,6 +99,7 @@
MojoResult result =
MojoCancelWatch(handle_.value(), reinterpret_cast<uintptr_t>(this));
+ message_loop_observer_.reset();
// |result| may be MOJO_RESULT_INVALID_ARGUMENT if |handle_| has closed, but
// OnHandleReady has not yet been called.
DCHECK(result == MOJO_RESULT_INVALID_ARGUMENT || result == MOJO_RESULT_OK);
@@ -77,15 +112,14 @@
ReadyCallback callback = callback_;
if (result == MOJO_RESULT_CANCELLED) {
+ message_loop_observer_.reset();
handle_.set_value(kInvalidHandleValue);
callback_.Reset();
}
// NOTE: It's legal for |callback| to delete |this|.
- if (!callback.is_null()) {
- TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event(heap_profiler_tag_);
+ if (!callback.is_null())
callback.Run(result);
- }
}
// static
@@ -99,7 +133,6 @@
// TODO: Maybe we should also expose |signals_state| through the Watcher API.
// Current HandleWatcher users have no need for it, so it's omitted here.
Watcher* watcher = reinterpret_cast<Watcher*>(context);
-
if ((flags & MOJO_WATCH_NOTIFICATION_FLAG_FROM_SYSTEM) &&
watcher->task_runner_->RunsTasksOnCurrentThread() &&
watcher->is_default_task_runner_) {
diff --git a/mojo/public/cpp/system/watcher.h b/mojo/public/cpp/system/watcher.h
index 236788b..82f3e81 100644
--- a/mojo/public/cpp/system/watcher.h
+++ b/mojo/public/cpp/system/watcher.h
@@ -5,6 +5,8 @@
#ifndef MOJO_PUBLIC_CPP_SYSTEM_WATCHER_H_
#define MOJO_PUBLIC_CPP_SYSTEM_WATCHER_H_
+#include <memory>
+
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
@@ -14,14 +16,13 @@
#include "base/threading/thread_task_runner_handle.h"
#include "mojo/public/c/system/types.h"
#include "mojo/public/cpp/system/handle.h"
-#include "mojo/public/cpp/system/system_export.h"
namespace mojo {
// A Watcher watches a single Mojo handle for signal state changes.
//
// NOTE: Watchers may only be used on threads which have a running MessageLoop.
-class MOJO_CPP_SYSTEM_EXPORT Watcher {
+class Watcher {
public:
// A callback to be called any time a watched handle changes state in some
// interesting way. The |result| argument indicates one of the following
@@ -34,11 +35,15 @@
//
// |MOJO_RESULT_CANCELLED|: The handle has been closed and the watch has
// been cancelled implicitly.
+ //
+ // |MOJO_RESULT_ABORTED|: Notifications can no longer be delivered for this
+ // watcher for some unspecified reason, e.g., the watching thread may
+ // be shutting down soon. Note that it is still necessary to explicitly
+ // Cancel() the watch in this case.
using ReadyCallback = base::Callback<void(MojoResult result)>;
- Watcher(const tracked_objects::Location& from_here,
- scoped_refptr<base::SingleThreadTaskRunner> runner =
- base::ThreadTaskRunnerHandle::Get());
+ explicit Watcher(scoped_refptr<base::SingleThreadTaskRunner> runner =
+ base::ThreadTaskRunnerHandle::Get());
// NOTE: This destructor automatically calls |Cancel()| if the Watcher is
// still active.
@@ -75,13 +80,10 @@
Handle handle() const { return handle_; }
ReadyCallback ready_callback() const { return callback_; }
- // Sets the tag used by the heap profiler.
- // |tag| must be a const string literal.
- void set_heap_profiler_tag(const char* heap_profiler_tag) {
- heap_profiler_tag_ = heap_profiler_tag;
- }
-
private:
+ class MessageLoopObserver;
+ friend class MessageLoopObserver;
+
void OnHandleReady(MojoResult result);
static void CallOnHandleReady(uintptr_t context,
@@ -98,6 +100,8 @@
// for the thread.
const bool is_default_task_runner_;
+ std::unique_ptr<MessageLoopObserver> message_loop_observer_;
+
// A persistent weak reference to this Watcher which can be passed to the
// Dispatcher any time this object should be signalled. Safe to access (but
// not to dereference!) from any thread.
@@ -111,10 +115,6 @@
// The callback to call when the handle is signaled.
ReadyCallback callback_;
- // Tag used to ID memory allocations that originated from notifications in
- // this watcher.
- const char* heap_profiler_tag_ = nullptr;
-
base::WeakPtrFactory<Watcher> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(Watcher);
diff --git a/mojo/public/cpp/test_support/BUILD.gn b/mojo/public/cpp/test_support/BUILD.gn
index efa1712..3d33b4a 100644
--- a/mojo/public/cpp/test_support/BUILD.gn
+++ b/mojo/public/cpp/test_support/BUILD.gn
@@ -2,6 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+# GYP version: mojo/public/mojo_public.gyp:mojo_public_test_utils
static_library("test_utils") {
testonly = true
diff --git a/mojo/public/interfaces/BUILD.gn b/mojo/public/interfaces/BUILD.gn
index fb11ec2..654145b 100644
--- a/mojo/public/interfaces/BUILD.gn
+++ b/mojo/public/interfaces/BUILD.gn
@@ -4,6 +4,8 @@
group("interfaces") {
deps = [
+ "application",
"bindings",
+ "network",
]
}
diff --git a/mojo/public/interfaces/bindings/BUILD.gn b/mojo/public/interfaces/bindings/BUILD.gn
index eab75c1..c7421b9 100644
--- a/mojo/public/interfaces/bindings/BUILD.gn
+++ b/mojo/public/interfaces/bindings/BUILD.gn
@@ -5,13 +5,8 @@
import("../../tools/bindings/mojom.gni")
mojom("bindings") {
- visibility = []
sources = [
"interface_control_messages.mojom",
"pipe_control_messages.mojom",
]
-
- export_class_attribute = "MOJO_CPP_BINDINGS_EXPORT"
- export_define = "MOJO_CPP_BINDINGS_IMPLEMENTATION"
- export_header = "mojo/public/cpp/bindings/bindings_export.h"
}
diff --git a/mojo/public/interfaces/bindings/interface_control_messages.mojom b/mojo/public/interfaces/bindings/interface_control_messages.mojom
index 0a19042..2143c06 100644
--- a/mojo/public/interfaces/bindings/interface_control_messages.mojom
+++ b/mojo/public/interfaces/bindings/interface_control_messages.mojom
@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-[JavaPackage="org.chromium.mojo.bindings.interfacecontrol"]
-module mojo.interface_control;
+[JavaPackage="org.chromium.mojo.bindings"]
+module mojo;
-// For each user-defined interface, some control functions are provided by the
-// interface endpoints at both sides.
+// For each user-defined interface, some control functions are provided at the
+// same end of the message pipe as the user-defined interface, providing
+// information about the user-defined interface.
////////////////////////////////////////////////////////////////////////////////
// Run@0xFFFFFFFF(RunInput input) => (RunOutput? output);
@@ -14,54 +15,75 @@
// This control function runs the input command. If the command is not
// supported, |output| is set to null; otherwise |output| stores the result,
// whose type depends on the input.
+//
+// TODO(yzshen): Once union support is ready, switch the following definition
+// to:
+// struct RunMessageParams {
+// RunInput input;
+// };
+// union RunInput {
+// QueryVersion query_version;
+// };
+//
+// struct RunResponseMessageParams {
+// RunOutput? output;
+// };
+// union RunOutput {
+// QueryVersionResult query_version_result;
+// };
const uint32 kRunMessageId = 0xFFFFFFFF;
struct RunMessageParams {
- RunInput input;
-};
-union RunInput {
+ // The reserved fields make the layout compatible with the RunInput union
+ // described above.
+ uint32 reserved0; // Must be set to 16.
+ uint32 reserved1; // Must be set to 0;
QueryVersion query_version;
- FlushForTesting flush_for_testing;
};
struct RunResponseMessageParams {
- RunOutput? output;
-};
-union RunOutput {
+ // The reserved fields make the layout compatible with the RunOutput union
+ // described above.
+ uint32 reserved0; // Must be set to 16.
+ uint32 reserved1; // Must be set to 0.
QueryVersionResult query_version_result;
};
// Queries the max supported version of the user-defined interface.
-// Sent by the interface client side.
struct QueryVersion {
};
struct QueryVersionResult {
uint32 version;
};
-// Sent by either side of the interface.
-struct FlushForTesting {
-};
-
////////////////////////////////////////////////////////////////////////////////
// RunOrClosePipe@0xFFFFFFFE(RunOrClosePipeInput input);
//
// This control function runs the input command. If the operation fails or the
// command is not supported, the message pipe is closed.
+//
+// TODO(yzshen): Once union support is ready, switch the following definition
+// to:
+// struct RunOrClosePipeMessageParams {
+// RunOrClosePipeInput input;
+// };
+// union RunOrClosePipeInput {
+// RequireVersion require_version;
+// };
const uint32 kRunOrClosePipeMessageId = 0xFFFFFFFE;
struct RunOrClosePipeMessageParams {
- RunOrClosePipeInput input;
-};
-union RunOrClosePipeInput {
+ // The reserved fields make the layout compatible with the RunOrClosePipeInput
+ // union described above.
+ uint32 reserved0; // Must be set to 16.
+ uint32 reserved1; // Must be set to 0.
RequireVersion require_version;
};
// If the specified version of the user-defined interface is not supported, the
// function fails and the pipe is closed.
-// Sent by the interface client side.
struct RequireVersion {
uint32 version;
};
diff --git a/mojo/public/interfaces/bindings/pipe_control_messages.mojom b/mojo/public/interfaces/bindings/pipe_control_messages.mojom
index c1453fc..c743ffe 100644
--- a/mojo/public/interfaces/bindings/pipe_control_messages.mojom
+++ b/mojo/public/interfaces/bindings/pipe_control_messages.mojom
@@ -25,21 +25,26 @@
union RunOrClosePipeInput {
PeerAssociatedEndpointClosedEvent peer_associated_endpoint_closed_event;
-};
-
-// A user-defined reason about why the interface is disconnected.
-struct DisconnectReason {
- uint32 custom_reason;
- string description;
+ AssociatedEndpointClosedBeforeSentEvent
+ associated_endpoint_closed_before_sent_event;
};
// An event to notify that an interface endpoint set up at the message sender
// side has been closed.
-//
-// This event is omitted if the endpoint belongs to the master interface and
-// there is no disconnect reason specified.
+//
+// This event is only used for associated interfaces. When a master interface
+// is closed, the message pipe is shutdown directly.
struct PeerAssociatedEndpointClosedEvent {
// The interface ID.
uint32 id;
- DisconnectReason? disconnect_reason;
};
+
+// An event to notify that an interface endpoint that is meant to be set up at
+// the message receiver side has been closed before sent over the message pipe.
+//
+// This event is only used for associated interfaces.
+struct AssociatedEndpointClosedBeforeSentEvent {
+ // The interface ID.
+ uint32 id;
+};
+
diff --git a/mojo/public/interfaces/bindings/tests/BUILD.gn b/mojo/public/interfaces/bindings/tests/BUILD.gn
index 9b10db0..0a48076 100644
--- a/mojo/public/interfaces/bindings/tests/BUILD.gn
+++ b/mojo/public/interfaces/bindings/tests/BUILD.gn
@@ -17,9 +17,7 @@
"sample_service.mojom",
"scoping.mojom",
"serialization_test_structs.mojom",
- "test_bad_messages.mojom",
"test_constants.mojom",
- "test_data_view.mojom",
"test_native_types.mojom",
"test_structs.mojom",
"test_sync_methods.mojom",
@@ -29,59 +27,8 @@
":test_mojom_import",
":test_mojom_import2",
]
-}
-component("test_export_component") {
- testonly = true
- deps = [
- ":test_export",
- ]
-}
-
-if (!is_ios) {
- component("test_export_blink_component") {
- testonly = true
- deps = [
- ":test_export_blink",
- ]
- }
-}
-
-mojom("test_export") {
- testonly = true
- sources = [
- "test_export.mojom",
- ]
- export_class_attribute = "MOJO_TEST_EXPORT"
- export_define = "MOJO_TEST_IMPLEMENTATION=1"
- export_header = "mojo/public/cpp/bindings/tests/mojo_test_export.h"
- if (!is_ios) {
- export_class_attribute_blink = "MOJO_TEST_BLINK_EXPORT"
- export_define_blink = "MOJO_TEST_BLINK_IMPLEMENTATION=1"
- export_header_blink =
- "mojo/public/cpp/bindings/tests/mojo_test_blink_export.h"
- }
- visibility = [ ":test_export_component" ]
- if (!is_ios) {
- visibility_blink = [ ":test_export_blink_component" ]
- }
-}
-
-mojom("test_exported_import") {
- testonly = true
- sources = [
- "test_import.mojom",
- ]
- public_deps = [
- ":test_export",
- ]
-
- overridden_deps = [ ":test_export" ]
- component_deps = [ ":test_export_component" ]
- if (!is_ios) {
- overridden_deps_blink = [ ":test_export" ]
- component_deps_blink = [ ":test_export_blink_component" ]
- }
+ use_new_wrapper_types = true
}
mojom("test_mojom_import") {
@@ -89,6 +36,7 @@
sources = [
"sample_import.mojom",
]
+ use_new_wrapper_types = true
}
mojom("test_mojom_import_wrapper") {
@@ -114,6 +62,7 @@
":test_mojom_import",
":test_mojom_import_wrapper_wrapper",
]
+ use_new_wrapper_types = true
}
mojom("test_struct_traits_interfaces") {
@@ -121,6 +70,7 @@
sources = [
"struct_with_traits.mojom",
]
+ use_new_wrapper_types = true
}
mojom("test_interfaces_experimental") {
@@ -128,6 +78,7 @@
sources = [
"test_unions.mojom",
]
+ use_new_wrapper_types = true
}
mojom("test_associated_interfaces") {
@@ -138,10 +89,7 @@
"test_associated_interfaces.mojom",
"validation_test_associated_interfaces.mojom",
]
-
- public_deps = [
- ":test_interfaces",
- ]
+ use_new_wrapper_types = true
}
mojom("versioning_test_service_interfaces") {
@@ -149,6 +97,7 @@
sources = [
"versioning_test_service.mojom",
]
+ use_new_wrapper_types = true
}
mojom("versioning_test_client_interfaces") {
@@ -156,6 +105,7 @@
sources = [
"versioning_test_client.mojom",
]
+ use_new_wrapper_types = true
}
mojom("test_wtf_types") {
@@ -164,6 +114,7 @@
sources = [
"test_wtf_types.mojom",
]
+ use_new_wrapper_types = true
}
mojom("test_no_sources") {
diff --git a/mojo/public/interfaces/bindings/tests/data/message_data b/mojo/public/interfaces/bindings/tests/data/message_data
new file mode 100644
index 0000000..b288878
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/message_data
@@ -0,0 +1,25 @@
+// File generated by mojo_message_generator.
+0X10
+0X00
+0X00
+0X00
+0X02
+0X00
+0X00
+0X00
+0X15
+0X00
+0X00
+0X00
+0X00
+0X00
+0X00
+0X00
+0X09
+0X08
+0X07
+0X06
+0X00
+0X00
+0X00
+0X00
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_good.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_good.data
index b797fea..efac7ed 100644
--- a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_good.data
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_good.data
@@ -1,26 +1,13 @@
[dist4]message_header // num_bytes
-[u4]2 // version
+[u4]0 // 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]4 // associated interface pointer: interface ID
[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_unexpected_invalid_associated_interface.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_unexpected_invalid_associated_interface.data
index b785ed1..dd16401 100644
--- a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_unexpected_invalid_associated_interface.data
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd0_unexpected_invalid_associated_interface.data
@@ -1,25 +1,14 @@
[dist4]message_header // num_bytes
-[u4]2 // version
+[u4]0 // 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]0xFFFFFFFF // associated interface pointer: Unexpected invalid
- // interface ID index.
+[u4]0xFFFFFFFF // associated interface pointer: unexpected invalid
+ // interface ID
[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]4
-[anchr]interface_id_array
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_good.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_good.data
index efa2162..7ebbe07 100644
--- a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_good.data
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_good.data
@@ -1,26 +1,13 @@
[dist4]message_header // num_bytes
-[u4]2 // version
+[u4]0 // version
[u4]0 // interface ID
[u4]1 // name
[u4]0 // flags
[u4]0 // padding
-[u8]0 // request_id
-[dist8]payload
-[dist8]payload_interface_ids
[anchr]message_header
-[anchr]payload
[dist4]method1_params // num_bytes
[u4]0 // version
-[u4]1 // associated interface request: interface ID index
+[u4]4 // associated interface request
[u4]0 // padding
[anchr]method1_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_mthd1_unexpected_invalid_associated_request.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_unexpected_invalid_associated_request.data
index 5a66aad..d74a306 100644
--- a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_unexpected_invalid_associated_request.data
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd1_unexpected_invalid_associated_request.data
@@ -1,27 +1,14 @@
[dist4]message_header // num_bytes
-[u4]2 // version
+[u4]0 // version
[u4]0 // interface ID
[u4]1 // name
[u4]0 // flags
[u4]0 // padding
-[u8]0 // request_id
-[dist8]payload
-[dist8]payload_interface_ids
[anchr]message_header
-[anchr]payload
[dist4]method1_params // num_bytes
[u4]0 // version
-[u4]0xFFFFFFFF // associated interface request: Unexpected invalid
- // interface ID index.
+[u4]0xFFFFFFFF // associated interface request: unexpected invalid
+ // interface ID
[u4]0 // padding
[anchr]method1_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_mthd2_master_interface_id.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.data
new file mode 100644
index 0000000..511cd0d
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.data
@@ -0,0 +1,15 @@
+[dist4]message_header // num_bytes
+[u4]0 // version
+[u4]0 // interface ID
+[u4]2 // name
+[u4]0 // flags
+[u4]0 // padding
+[anchr]message_header
+
+[dist4]method2_params // num_bytes
+[u4]0 // version
+[u4]0 // associated interface pointer: 0 is the special master
+ // interface ID and should never be used as associated
+ // interface ID
+[u4]1 // associated interface pointer: version
+[anchr]method2_params
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.expected b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.expected
new file mode 100644
index 0000000..420b421
--- /dev/null
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.expected
@@ -0,0 +1 @@
+VALIDATION_ERROR_ILLEGAL_INTERFACE_ID
diff --git a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_good.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_good.data
index 13df01e..da22db1 100644
--- a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_good.data
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_good.data
@@ -1,15 +1,11 @@
[dist4]message_header // num_bytes
-[u4]2 // version
+[u4]0 // 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
@@ -18,18 +14,8 @@
[anchr]param0_ptr
[dist4]associated_interface_array // num_bytes
[u4]2 // num_elements
-[u4]2 // interface ID index
+[u4]4 // interface ID
[u4]14 // version
-[u4]3 // interface ID index
+[u4]5 // interface ID
[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_unexpected_invalid_associated_interface_in_array.data b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_unexpected_invalid_associated_interface_in_array.data
index 2e163be..788cefa 100644
--- a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_unexpected_invalid_associated_interface_in_array.data
+++ b/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd3_unexpected_invalid_associated_interface_in_array.data
@@ -1,15 +1,11 @@
[dist4]message_header // num_bytes
-[u4]2 // version
+[u4]0 // 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
@@ -18,19 +14,8 @@
[anchr]param0_ptr
[dist4]associated_interface_array // num_bytes
[u4]2 // num_elements
-[u4]2 // interface ID index
+[u4]0xFFFFFFFF // unexpected invalid interface ID
[u4]14 // version
-[u4]0xFFFFFFFF // interface ID index: Unexpected invalid
- // value.
+[u4]5 // interface ID
[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/conformance_mthd15_uknown_non_extensible_enum_array_value.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_uknown_non_extensible_enum_array_value.data
index 0a46e0a..1cd3484 100644
--- a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_uknown_non_extensible_enum_array_value.data
+++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd15_uknown_non_extensible_enum_array_value.data
@@ -15,7 +15,7 @@
[anchr]enum_array_0
[dist4]enum_array_0_member // num_bytes
[u4]2 // num_elements
-[u4]1
[u4]0x5678 // Unknown value is not allowed for non-extensible
// enum.
+[u4]1
[anchr]enum_array_0_member
diff --git a/mojo/public/interfaces/bindings/tests/rect.mojom b/mojo/public/interfaces/bindings/tests/rect.mojom
index 4ecc4d9..833c76b 100644
--- a/mojo/public/interfaces/bindings/tests/rect.mojom
+++ b/mojo/public/interfaces/bindings/tests/rect.mojom
@@ -12,20 +12,11 @@
int32 height;
};
-// A copy of Rect that is typemapped differently in the chromium and blink
-// variants.
+// A copy of Rect that can be typemapped. Arrays of Rect are currently used,
+// which do not support typemapping.
struct TypemappedRect {
int32 x;
int32 y;
int32 width;
int32 height;
};
-
-// A copy of Rect that is typemapped to the same custom type in the chromium and
-// blink variants.
-struct SharedTypemappedRect {
- int32 x;
- int32 y;
- int32 width;
- int32 height;
-};
\ No newline at end of file
diff --git a/mojo/public/interfaces/bindings/tests/struct_with_traits.mojom b/mojo/public/interfaces/bindings/tests/struct_with_traits.mojom
index b50409e..b1b7437 100644
--- a/mojo/public/interfaces/bindings/tests/struct_with_traits.mojom
+++ b/mojo/public/interfaces/bindings/tests/struct_with_traits.mojom
@@ -23,7 +23,6 @@
string f_string;
string f_string2;
array<string> f_string_array;
- array<string> f_string_set;
NestedStructWithTraits f_struct;
array<NestedStructWithTraits> f_struct_array;
map<string, NestedStructWithTraits> f_struct_map;
@@ -34,50 +33,28 @@
StructWithTraits f_struct;
};
-// Maps to a pass-by-value trivial struct.
-struct TrivialStructWithTraits {
- int32 value;
-};
-
-// Maps to a move-only struct.
-struct MoveOnlyStructWithTraits {
+struct PassByValueStructWithTraits {
handle f_handle;
};
-// The custom type for MoveOnlyStructWithTraits is not clonable. Test that
+// The custom type for PassByValueStructWithTraits is not clonable. Test that
// this container can compile as long as Clone() is not used.
-struct MoveOnlyStructWithTraitsContainer {
- MoveOnlyStructWithTraits f_struct;
+struct PassByValueStructWithTraitsContainer {
+ PassByValueStructWithTraits f_struct;
};
-struct StructWithTraitsForUniquePtr {
+struct StructWithTraitsForUniquePtrTest {
int32 f_int32;
};
-union UnionWithTraits {
- int32 f_int32;
- NestedStructWithTraits f_struct;
-};
-
interface TraitsTestService {
EchoStructWithTraits(StructWithTraits s) => (StructWithTraits passed);
- EchoTrivialStructWithTraits(TrivialStructWithTraits s) =>
- (TrivialStructWithTraits passed);
-
- EchoMoveOnlyStructWithTraits(MoveOnlyStructWithTraits s) =>
- (MoveOnlyStructWithTraits passed);
-
- EchoNullableMoveOnlyStructWithTraits(MoveOnlyStructWithTraits? s) =>
- (MoveOnlyStructWithTraits? passed);
+ EchoPassByValueStructWithTraits(PassByValueStructWithTraits s) =>
+ (PassByValueStructWithTraits passed);
EchoEnumWithTraits(EnumWithTraits e) => (EnumWithTraits passed);
- EchoStructWithTraitsForUniquePtr(StructWithTraitsForUniquePtr e) => (
- StructWithTraitsForUniquePtr passed);
-
- EchoNullableStructWithTraitsForUniquePtr(StructWithTraitsForUniquePtr? e) => (
- StructWithTraitsForUniquePtr? passed);
-
- EchoUnionWithTraits(UnionWithTraits u) => (UnionWithTraits passed);
+ EchoStructWithTraitsForUniquePtrTest(StructWithTraitsForUniquePtrTest e) => (
+ StructWithTraitsForUniquePtrTest passed);
};
diff --git a/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom b/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom
index adc4e7e..534cfd8 100644
--- a/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom
+++ b/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom
@@ -4,8 +4,6 @@
module mojo.test;
-import "mojo/public/interfaces/bindings/tests/ping_service.mojom";
-
interface FooInterface {};
struct StructContainsAssociated {
@@ -44,11 +42,3 @@
GetSender(associated IntegerSender& sender);
AsyncGetSender() => (associated IntegerSender sender);
};
-
-interface AssociatedPingProvider {
- GetPing(associated PingService& request);
-};
-
-interface AssociatedPingProviderProvider {
- GetPingProvider(associated AssociatedPingProvider& request);
-};
diff --git a/mojo/public/interfaces/bindings/tests/test_constants.mojom b/mojo/public/interfaces/bindings/tests/test_constants.mojom
index 272e603..462d512 100644
--- a/mojo/public/interfaces/bindings/tests/test_constants.mojom
+++ b/mojo/public/interfaces/bindings/tests/test_constants.mojom
@@ -42,16 +42,12 @@
const float kFloatNegativeInfinity = float.NEGATIVE_INFINITY;
const float kFloatNaN = float.NAN;
-const string kStringValue = "test string contents";
-
struct StructWithConstants {
const int8 kInt8Value = 5;
const float kFloatValue = 765.432;
- const string kStringValue = "struct test string contents";
};
interface InterfaceWithConstants {
const uint32 kUint32Value = 20100722;
const double kDoubleValue = 12.34567;
- const string kStringValue = "interface test string contents";
};
diff --git a/mojo/public/interfaces/bindings/tests/test_native_types.mojom b/mojo/public/interfaces/bindings/tests/test_native_types.mojom
index 3df4318..46c6f69 100644
--- a/mojo/public/interfaces/bindings/tests/test_native_types.mojom
+++ b/mojo/public/interfaces/bindings/tests/test_native_types.mojom
@@ -34,5 +34,4 @@
interface RectService {
AddRect(TypemappedRect r);
GetLargestRect() => (TypemappedRect largest);
- PassSharedRect(SharedTypemappedRect r) => (SharedTypemappedRect passed);
};
diff --git a/mojo/public/interfaces/bindings/tests/test_structs.mojom b/mojo/public/interfaces/bindings/tests/test_structs.mojom
index 03a0a20..2709d49 100644
--- a/mojo/public/interfaces/bindings/tests/test_structs.mojom
+++ b/mojo/public/interfaces/bindings/tests/test_structs.mojom
@@ -126,8 +126,6 @@
map<float, float> f9;
map<double, double> f10;
map<string, string> f11;
- // TODO(tibell): JS/Java don't support struct as key.
- // map<Rect, Rect> f12;
};
// Used to verify that various map value types can be encoded and decoded
@@ -360,16 +358,6 @@
bool f_bool;
};
-// A struct where the fields are not sorted by their ordinals.
-struct ReorderedStruct {
- [MinVersion=2]
- int32 a@3 = 3;
- [MinVersion=4]
- int32 b@6 = 6;
- [MinVersion=1]
- int32 c@1 = 1;
-};
-
// Used to verify that interfaces that are struct members can be defined in the
// same file.
@@ -392,23 +380,3 @@
struct ContainsInterfaceRequest {
SomeInterface& request;
};
-
-// Used to verify that boolean fields are correctly serialized/deserialized.
-
-struct SingleBoolStruct {
- bool value;
-};
-
-// Used to verify that structs containing typemapped types can be hashed (if the
-// typemapped type itself is hashable).
-
-struct ContainsHashable {
- TypemappedRect rect;
-};
-
-// Used to test that nested structs can be hashed. The nested struct mustn't be
-// nullable.
-
-struct SimpleNestedStruct {
- ContainsOther nested;
-};
diff --git a/mojo/public/interfaces/bindings/tests/test_wtf_types.mojom b/mojo/public/interfaces/bindings/tests/test_wtf_types.mojom
index 183f184..2fdbea8 100644
--- a/mojo/public/interfaces/bindings/tests/test_wtf_types.mojom
+++ b/mojo/public/interfaces/bindings/tests/test_wtf_types.mojom
@@ -25,26 +25,13 @@
};
struct TestWTFStruct {
- enum NestedEnum {
- E0,
- E1,
- };
string str;
int32 integer;
};
interface TestWTF {
- enum NestedEnum {
- E0,
- E1,
- };
EchoString(string? str) => (string? str);
EchoStringArray(array<string?>? arr) => (array<string?>? arr);
EchoStringMap(map<string, string?>? str_map)
=> (map<string, string?>? str_map);
};
-
-enum TopLevelEnum {
- E0,
- E1,
-};
diff --git a/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom b/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom
index ab69045..c46c0a5 100644
--- a/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom
+++ b/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom
@@ -56,18 +56,6 @@
ENUM_B_2
};
-// A non-extensible enum with no values is valid, but about as useless as
-// you would expect: it will fail validation for all values.
-enum EmptyEnum {};
-
-[Extensible]
-enum ExtensibleEmptyEnum {};
-
-union UnionA {
- StructA struct_a;
- bool b;
-};
-
// This interface is used for testing bounds-checking in the mojom
// binding code. If you add a method please update the files
// ./data/validation/boundscheck_*. If you add a response please update
@@ -96,11 +84,6 @@
Method15(array<EnumA>? param0, array<EnumB>? param1);
Method16(map<EnumA, EnumA>? param0);
Method17(array<InterfaceA> param0);
- Method18(UnionA? param0);
- Method19(Recursive recursive);
- Method20(map<StructB, uint8> param0);
- Method21(ExtensibleEmptyEnum param0);
- Method22(EmptyEnum param0);
};
struct BasicStruct {
@@ -128,8 +111,3 @@
A, B, C, D
};
};
-
-// This is used to test that deeply recursive structures don't blow the stack.
-struct Recursive {
- Recursive? recursive;
-};
diff --git a/mojo/public/java/BUILD.gn b/mojo/public/java/BUILD.gn
index 0780641..e33faaa 100644
--- a/mojo/public/java/BUILD.gn
+++ b/mojo/public/java/BUILD.gn
@@ -4,8 +4,9 @@
import("//build/config/android/rules.gni")
-android_library("system_java") {
+android_library("system") {
java_files = [
+ "system/src/org/chromium/mojo/system/AsyncWaiter.java",
"system/src/org/chromium/mojo/system/Core.java",
"system/src/org/chromium/mojo/system/DataPipe.java",
"system/src/org/chromium/mojo/system/Flags.java",
@@ -19,11 +20,10 @@
"system/src/org/chromium/mojo/system/SharedBufferHandle.java",
"system/src/org/chromium/mojo/system/UntypedHandle.java",
"system/src/org/chromium/mojo/system/RunLoop.java",
- "system/src/org/chromium/mojo/system/Watcher.java",
]
}
-android_library("bindings_java") {
+android_library("bindings") {
java_files = [
"bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java",
"bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java",
@@ -56,7 +56,7 @@
]
deps = [
- ":system_java",
+ ":system",
"//base:base_java",
]
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java
index f77399d..6146316 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java
@@ -4,8 +4,8 @@
package org.chromium.mojo.bindings;
+import org.chromium.mojo.system.AsyncWaiter;
import org.chromium.mojo.system.Handle;
-import org.chromium.mojo.system.Watcher;
/**
* Helper functions.
@@ -189,9 +189,9 @@
/**
* Returns an {@link AsyncWaiter} to use with the given handle, or |null| if none if available.
*/
- static Watcher getWatcherForHandle(Handle handle) {
+ static AsyncWaiter getDefaultAsyncWaiterForHandle(Handle handle) {
if (handle.getCore() != null) {
- return handle.getCore().getWatcher();
+ return handle.getCore().getDefaultAsyncWaiter();
} else {
return null;
}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
index 2aa5ea6..3686bcf 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
@@ -4,13 +4,13 @@
package org.chromium.mojo.bindings;
+import org.chromium.mojo.system.AsyncWaiter;
import org.chromium.mojo.system.Core;
import org.chromium.mojo.system.MessagePipeHandle;
import org.chromium.mojo.system.MessagePipeHandle.ReadMessageResult;
import org.chromium.mojo.system.MojoException;
import org.chromium.mojo.system.MojoResult;
import org.chromium.mojo.system.ResultAnd;
-import org.chromium.mojo.system.Watcher;
import java.nio.ByteBuffer;
@@ -27,7 +27,7 @@
/**
* The callback that is notified when the state of the owned handle changes.
*/
- private final WatcherCallback mWatcherCallback = new WatcherCallback();
+ private final AsyncWaiterCallback mAsyncWaiterCallback = new AsyncWaiterCallback();
/**
* The owned message pipe.
@@ -35,9 +35,9 @@
private final MessagePipeHandle mMessagePipeHandle;
/**
- * A watcher which is notified when a new message is available on the owned message pipe.
+ * A waiter which is notified when a new message is available on the owned message pipe.
*/
- private final Watcher mWatcher;
+ private final AsyncWaiter mAsyncWaiter;
/**
* The {@link MessageReceiver} to which received messages are sent.
@@ -45,6 +45,11 @@
private MessageReceiver mIncomingMessageReceiver;
/**
+ * The Cancellable for the current wait. Is |null| when not currently waiting for new messages.
+ */
+ private AsyncWaiter.Cancellable mCancellable;
+
+ /**
* The error handler to notify of errors.
*/
private ConnectionErrorHandler mErrorHandler;
@@ -54,16 +59,17 @@
* {@link AsyncWaiter} from the {@link Core} implementation of |messagePipeHandle|.
*/
public Connector(MessagePipeHandle messagePipeHandle) {
- this(messagePipeHandle, BindingsHelper.getWatcherForHandle(messagePipeHandle));
+ this(messagePipeHandle, BindingsHelper.getDefaultAsyncWaiterForHandle(messagePipeHandle));
}
/**
* Create a new connector over a |messagePipeHandle| using the given {@link AsyncWaiter} to get
* notified of changes on the handle.
*/
- public Connector(MessagePipeHandle messagePipeHandle, Watcher watcher) {
+ public Connector(MessagePipeHandle messagePipeHandle, AsyncWaiter asyncWaiter) {
+ mCancellable = null;
mMessagePipeHandle = messagePipeHandle;
- mWatcher = watcher;
+ mAsyncWaiter = asyncWaiter;
}
/**
@@ -85,7 +91,8 @@
* Start listening for incoming messages.
*/
public void start() {
- mWatcher.start(mMessagePipeHandle, Core.HandleSignals.READABLE, mWatcherCallback);
+ assert mCancellable == null;
+ registerAsyncWaiterForRead();
}
/**
@@ -133,21 +140,32 @@
}
}
- private class WatcherCallback implements Watcher.Callback {
+ private class AsyncWaiterCallback implements AsyncWaiter.Callback {
+
/**
- * @see org.chromium.mojo.system.Watcher.Callback#onResult(int)
+ * @see org.chromium.mojo.system.AsyncWaiter.Callback#onResult(int)
*/
@Override
public void onResult(int result) {
- Connector.this.onWatcherResult(result);
+ Connector.this.onAsyncWaiterResult(result);
+ }
+
+ /**
+ * @see org.chromium.mojo.system.AsyncWaiter.Callback#onError(MojoException)
+ */
+ @Override
+ public void onError(MojoException exception) {
+ mCancellable = null;
+ Connector.this.onError(exception);
}
}
/**
- * @see org.chromium.mojo.system.Watcher.Callback#onResult(int)
+ * @see org.chromium.mojo.system.AsyncWaiter.Callback#onResult(int)
*/
- private void onWatcherResult(int result) {
+ private void onAsyncWaiterResult(int result) {
+ mCancellable = null;
if (result == MojoResult.OK) {
readOutstandingMessages();
} else {
@@ -157,12 +175,26 @@
private void onError(MojoException exception) {
close();
+ assert mCancellable == null;
if (mErrorHandler != null) {
mErrorHandler.onConnectionError(exception);
}
}
/**
+ * Register to be called back when a new message is available on the owned message pipe.
+ */
+ private void registerAsyncWaiterForRead() {
+ assert mCancellable == null;
+ if (mAsyncWaiter != null) {
+ mCancellable = mAsyncWaiter.asyncWait(mMessagePipeHandle, Core.HandleSignals.READABLE,
+ Core.DEADLINE_INFINITE, mAsyncWaiterCallback);
+ } else {
+ onError(new MojoException(MojoResult.INVALID_ARGUMENT));
+ }
+ }
+
+ /**
* Read all available messages on the owned message pipe.
*/
private void readOutstandingMessages() {
@@ -175,14 +207,18 @@
return;
}
} while (result.getValue());
- if (result.getMojoResult() != MojoResult.SHOULD_WAIT) {
+ if (result.getMojoResult() == MojoResult.SHOULD_WAIT) {
+ registerAsyncWaiterForRead();
+ } else {
onError(new MojoException(result.getMojoResult()));
}
}
private void cancelIfActive() {
- mWatcher.cancel();
- mWatcher.destroy();
+ if (mCancellable != null) {
+ mCancellable.cancel();
+ mCancellable = null;
+ }
}
/**
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java
index 64ff1c0..755fb01 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java
@@ -29,15 +29,11 @@
/**
* Minimal value for the next handle to deserialize.
*/
- private int mMinNextClaimedHandle;
+ private int mMinNextClaimedHandle = 0;
/**
* Minimal value of the start of the next memory to claim.
*/
- private long mMinNextMemory;
- /**
- * The current nesting level when decoding.
- */
- private long mStackDepth;
+ private long mMinNextMemory = 0;
/**
* The maximal memory accessible.
@@ -50,17 +46,11 @@
private final long mNumberOfHandles;
/**
- * The maximum nesting level when decoding.
- */
- private static final int MAX_RECURSION_DEPTH = 100;
-
- /**
* Constructor.
*/
Validator(long maxMemory, int numberOfHandles) {
mMaxMemory = maxMemory;
mNumberOfHandles = numberOfHandles;
- mStackDepth = 0;
}
public void claimHandle(int handle) {
@@ -89,17 +79,6 @@
}
mMinNextMemory = BindingsHelper.align(end);
}
-
- public void increaseStackDepth() {
- ++mStackDepth;
- if (mStackDepth >= MAX_RECURSION_DEPTH) {
- throw new DeserializationException("Recursion depth limit exceeded.");
- }
- }
-
- public void decreaseStackDepth() {
- --mStackDepth;
- }
}
/**
@@ -765,12 +744,4 @@
throw new DeserializationException("Buffer is smaller than expected.");
}
}
-
- public void increaseStackDepth() {
- mValidator.increaseStackDepth();
- }
-
- public void decreaseStackDepth() {
- mValidator.decreaseStackDepth();
- }
}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java
index bb49cbc..c621d9b 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java
@@ -4,6 +4,8 @@
package org.chromium.mojo.bindings;
+import org.chromium.mojo.system.AsyncWaiter;
+import org.chromium.mojo.system.AsyncWaiter.Callback;
import org.chromium.mojo.system.Core;
import org.chromium.mojo.system.MessagePipeHandle;
import org.chromium.mojo.system.MessagePipeHandle.ReadMessageResult;
@@ -11,8 +13,6 @@
import org.chromium.mojo.system.MojoResult;
import org.chromium.mojo.system.Pair;
import org.chromium.mojo.system.ResultAnd;
-import org.chromium.mojo.system.Watcher;
-import org.chromium.mojo.system.Watcher.Callback;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@@ -58,23 +58,32 @@
*/
private final Object mLock;
/**
- * The {@link Watcher} to get notified of new message availability on |mReadHandle|.
+ * The {@link AsyncWaiter} to get notified of new message availability on |mReadHandle|.
*/
- private final Watcher mWatcher;
+ private final AsyncWaiter mWaiter;
/**
* Constructor.
*/
public PipedExecutor(Core core) {
- mWatcher = core.getWatcher();
- assert mWatcher != null;
+ mWaiter = core.getDefaultAsyncWaiter();
+ assert mWaiter != null;
mLock = new Object();
Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe(
new MessagePipeHandle.CreateOptions());
mReadHandle = handles.first;
mWriteHandle = handles.second;
mPendingActions = new ArrayList<Runnable>();
- mWatcher.start(mReadHandle, Core.HandleSignals.READABLE, this);
+ asyncWait();
+ }
+
+ /**
+ * Asynchronously wait for the next command to arrive. This should only be called on the
+ * executor thread.
+ */
+ private void asyncWait() {
+ mWaiter.asyncWait(mReadHandle, Core.HandleSignals.READABLE, Core.DEADLINE_INFINITE,
+ this);
}
/**
@@ -90,6 +99,14 @@
}
/**
+ * @see Callback#onError(MojoException)
+ */
+ @Override
+ public void onError(MojoException exception) {
+ close();
+ }
+
+ /**
* Close the handles. Should only be called on the executor thread.
*/
private void close() {
@@ -97,8 +114,6 @@
mWriteHandle.close();
mPendingActions.clear();
}
- mWatcher.cancel();
- mWatcher.destroy();
mReadHandle.close();
}
@@ -111,6 +126,7 @@
ResultAnd<ReadMessageResult> readMessageResult =
mReadHandle.readMessage(NOTIFY_BUFFER, 0, MessagePipeHandle.ReadFlags.NONE);
if (readMessageResult.getMojoResult() == MojoResult.OK) {
+ asyncWait();
return true;
}
} catch (MojoException e) {
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 e3be8b3..c2bbc8e 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
@@ -6,14 +6,6 @@
import org.chromium.mojo.bindings.Callbacks.Callback1;
import org.chromium.mojo.bindings.Interface.AbstractProxy.HandlerImpl;
-import org.chromium.mojo.bindings.interfacecontrol.QueryVersion;
-import org.chromium.mojo.bindings.interfacecontrol.RequireVersion;
-import org.chromium.mojo.bindings.interfacecontrol.RunInput;
-import org.chromium.mojo.bindings.interfacecontrol.RunMessageParams;
-import org.chromium.mojo.bindings.interfacecontrol.RunOrClosePipeInput;
-import org.chromium.mojo.bindings.interfacecontrol.RunOrClosePipeMessageParams;
-import org.chromium.mojo.bindings.interfacecontrol.RunOutput;
-import org.chromium.mojo.bindings.interfacecontrol.RunResponseMessageParams;
import org.chromium.mojo.system.Core;
import org.chromium.mojo.system.MessagePipeHandle;
import org.chromium.mojo.system.MojoException;
@@ -107,12 +99,12 @@
/**
* The {@link ConnectionErrorHandler} that will be notified of errors.
*/
- private ConnectionErrorHandler mErrorHandler;
+ private ConnectionErrorHandler mErrorHandler = null;
/**
* The currently known version of the interface.
*/
- private int mVersion;
+ private int mVersion = 0;
/**
* Constructor.
@@ -194,18 +186,15 @@
@Override
public void queryVersion(final Callback1<Integer> callback) {
RunMessageParams message = new RunMessageParams();
- message.input = new RunInput();
- message.input.setQueryVersion(new QueryVersion());
+ message.reserved0 = 16;
+ message.reserved1 = 0;
+ message.queryVersion = new QueryVersion();
InterfaceControlMessagesHelper.sendRunMessage(getCore(), mMessageReceiver, message,
new Callback1<RunResponseMessageParams>() {
@Override
public void call(RunResponseMessageParams response) {
- if (response.output != null
- && response.output.which()
- == RunOutput.Tag.QueryVersionResult) {
- mVersion = response.output.getQueryVersionResult().version;
- }
+ mVersion = response.queryVersionResult.version;
callback.call(mVersion);
}
});
@@ -221,9 +210,10 @@
}
mVersion = version;
RunOrClosePipeMessageParams message = new RunOrClosePipeMessageParams();
- message.input = new RunOrClosePipeInput();
- message.input.setRequireVersion(new RequireVersion());
- message.input.getRequireVersion().version = version;
+ message.reserved0 = 16;
+ message.reserved1 = 0;
+ message.requireVersion = new RequireVersion();
+ message.requireVersion.version = version;
InterfaceControlMessagesHelper.sendRunOrClosePipeMessage(
getCore(), mMessageReceiver, message);
}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java
index 51f543d..939fb93 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java
@@ -7,14 +7,6 @@
import org.chromium.mojo.bindings.Callbacks.Callback1;
import org.chromium.mojo.bindings.Interface.Manager;
import org.chromium.mojo.bindings.Interface.Proxy;
-import org.chromium.mojo.bindings.interfacecontrol.InterfaceControlMessagesConstants;
-import org.chromium.mojo.bindings.interfacecontrol.QueryVersionResult;
-import org.chromium.mojo.bindings.interfacecontrol.RunInput;
-import org.chromium.mojo.bindings.interfacecontrol.RunMessageParams;
-import org.chromium.mojo.bindings.interfacecontrol.RunOrClosePipeInput;
-import org.chromium.mojo.bindings.interfacecontrol.RunOrClosePipeMessageParams;
-import org.chromium.mojo.bindings.interfacecontrol.RunOutput;
-import org.chromium.mojo.bindings.interfacecontrol.RunResponseMessageParams;
import org.chromium.mojo.system.Core;
/**
@@ -72,16 +64,11 @@
*/
public static <I extends Interface, P extends Proxy> boolean handleRun(
Core core, Manager<I, P> manager, ServiceMessage message, MessageReceiver responder) {
- Message payload = message.getPayload();
- RunMessageParams query = RunMessageParams.deserialize(payload);
RunResponseMessageParams response = new RunResponseMessageParams();
- response.output = new RunOutput();
- if (query.input.which() == RunInput.Tag.QueryVersion) {
- response.output.setQueryVersionResult(new QueryVersionResult());
- response.output.getQueryVersionResult().version = manager.getVersion();
- } else {
- response.output = null;
- }
+ response.reserved0 = 16;
+ response.reserved1 = 0;
+ response.queryVersionResult = new QueryVersionResult();
+ response.queryVersionResult.version = manager.getVersion();
return responder.accept(response.serializeWithHeader(
core, new MessageHeader(InterfaceControlMessagesConstants.RUN_MESSAGE_ID,
@@ -97,9 +84,6 @@
Manager<I, P> manager, ServiceMessage message) {
Message payload = message.getPayload();
RunOrClosePipeMessageParams query = RunOrClosePipeMessageParams.deserialize(payload);
- if (query.input.which() == RunOrClosePipeInput.Tag.RequireVersion) {
- return query.input.getRequireVersion().version <= manager.getVersion();
- }
- return false;
+ return query.requireVersion.version <= manager.getVersion();
}
}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java
index 996c457..0d270cc 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java
@@ -29,7 +29,7 @@
/**
* This message interpreted as a message for a mojo service with an appropriate header.
*/
- private ServiceMessage mWithHeader;
+ private ServiceMessage mWithHeader = null;
/**
* Constructor.
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java
index a278cc5..16b29b4 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java
@@ -6,9 +6,9 @@
import android.annotation.SuppressLint;
+import org.chromium.mojo.system.AsyncWaiter;
import org.chromium.mojo.system.Core;
import org.chromium.mojo.system.MessagePipeHandle;
-import org.chromium.mojo.system.Watcher;
import java.util.HashMap;
import java.util.Map;
@@ -48,7 +48,7 @@
* {@link MessageReceiver} used to return responses to the caller.
*/
class ResponderThunk implements MessageReceiver {
- private boolean mAcceptWasInvoked;
+ private boolean mAcceptWasInvoked = false;
/**
* @see
@@ -109,23 +109,23 @@
private final Executor mExecutor;
/**
- * Constructor that will use the default {@link Watcher}.
+ * Constructor that will use the default {@link AsyncWaiter}.
*
* @param messagePipeHandle The {@link MessagePipeHandle} to route message for.
*/
public RouterImpl(MessagePipeHandle messagePipeHandle) {
- this(messagePipeHandle, BindingsHelper.getWatcherForHandle(messagePipeHandle));
+ this(messagePipeHandle, BindingsHelper.getDefaultAsyncWaiterForHandle(messagePipeHandle));
}
/**
* Constructor.
*
* @param messagePipeHandle The {@link MessagePipeHandle} to route message for.
- * @param watcher the {@link Watcher} to use to get notification of new messages on the
+ * @param asyncWaiter the {@link AsyncWaiter} to use to get notification of new messages on the
* handle.
*/
- public RouterImpl(MessagePipeHandle messagePipeHandle, Watcher watcher) {
- mConnector = new Connector(messagePipeHandle, watcher);
+ public RouterImpl(MessagePipeHandle messagePipeHandle, AsyncWaiter asyncWaiter) {
+ mConnector = new Connector(messagePipeHandle, asyncWaiter);
mConnector.setIncomingMessageReceiver(new HandleIncomingMessageThunk());
Core core = messagePipeHandle.getCore();
if (core != null) {
@@ -171,20 +171,23 @@
assert messageWithHeader.getHeader().hasFlag(MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG);
// Compute a request id for being able to route the response.
- long requestId = mNextRequestId++;
- // Reserve 0 in case we want it to convey special meaning in the future.
- if (requestId == 0) {
- requestId = mNextRequestId++;
+ // TODO(lhchavez): Remove this hack. See b/28986534 for details.
+ synchronized (mResponders) {
+ long requestId = mNextRequestId++;
+ // Reserve 0 in case we want it to convey special meaning in the future.
+ if (requestId == 0) {
+ requestId = mNextRequestId++;
+ }
+ if (mResponders.containsKey(requestId)) {
+ throw new IllegalStateException("Unable to find a new request identifier.");
+ }
+ messageWithHeader.setRequestId(requestId);
+ if (!mConnector.accept(messageWithHeader)) {
+ return false;
+ }
+ // Only keep the responder is the message has been accepted.
+ mResponders.put(requestId, responder);
}
- if (mResponders.containsKey(requestId)) {
- throw new IllegalStateException("Unable to find a new request identifier.");
- }
- messageWithHeader.setRequestId(requestId);
- if (!mConnector.accept(messageWithHeader)) {
- return false;
- }
- // Only keep the responder is the message has been accepted.
- mResponders.put(requestId, responder);
return true;
}
@@ -227,11 +230,15 @@
return false;
} else if (header.hasFlag(MessageHeader.MESSAGE_IS_RESPONSE_FLAG)) {
long requestId = header.getRequestId();
- MessageReceiver responder = mResponders.get(requestId);
- if (responder == null) {
- return false;
+ MessageReceiver responder;
+ // TODO(lhchavez): Remove this hack. See b/28986534 for details.
+ synchronized (mResponders) {
+ responder = mResponders.get(requestId);
+ if (responder == null) {
+ return false;
+ }
+ mResponders.remove(requestId);
}
- mResponders.remove(requestId);
return responder.accept(message);
} else {
if (mIncomingMessageReceiver != null) {
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java
index 14f4e1e..85cc97c 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java
@@ -6,8 +6,6 @@
import org.chromium.mojo.system.Core;
-import java.nio.ByteBuffer;
-
/**
* Base class for all mojo structs.
*/
@@ -51,23 +49,6 @@
}
/**
- * Similar to the method above, but returns the serialization result as |ByteBuffer|.
- *
- * @throws UnsupportedOperationException if the struct contains interfaces or handles.
- * @throws SerializationException on serialization failure.
- */
- public ByteBuffer serialize() {
- // If the struct contains interfaces which require a non-null |Core| instance, it will throw
- // UnsupportedOperationException.
- Message message = serialize(null);
-
- if (!message.getHandles().isEmpty())
- throw new UnsupportedOperationException("Handles are discarded.");
-
- return message.getData();
- }
-
- /**
* Returns the serialization of the struct prepended with the given header.
*
* @param header the header to prepend to the returned message.
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/AsyncWaiter.java b/mojo/public/java/system/src/org/chromium/mojo/system/AsyncWaiter.java
new file mode 100644
index 0000000..474de4b
--- /dev/null
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/AsyncWaiter.java
@@ -0,0 +1,53 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.mojo.system;
+
+import org.chromium.mojo.system.Core.HandleSignals;
+
+/**
+ * A class which implements the {@link AsyncWaiter} allows asynchronously waiting on a background
+ * thread.
+ */
+public interface AsyncWaiter {
+
+ /**
+ * Allows cancellation of an asyncWait operation.
+ */
+ interface Cancellable {
+ /**
+ * Cancels an asyncWait operation. Has no effect if the operation has already been canceled
+ * or the callback has already been called.
+ * <p>
+ * Must be called from the same thread as {@link AsyncWaiter#asyncWait} was called from.
+ */
+ void cancel();
+ }
+
+ /**
+ * Callback passed to {@link AsyncWaiter#asyncWait}.
+ */
+ public interface Callback {
+ /**
+ * Called when the handle is ready.
+ */
+ public void onResult(int result);
+
+ /**
+ * Called when an error occurred while waiting.
+ */
+ public void onError(MojoException exception);
+ }
+
+ /**
+ * Asynchronously call wait on a background thread. The given {@link Callback} will be notified
+ * of the result of the wait on the same thread as asyncWait was called.
+ *
+ * @return a {@link Cancellable} object that can be used to cancel waiting. The cancellable
+ * should only be used on the current thread, and becomes invalid once the callback has
+ * been notified.
+ */
+ Cancellable asyncWait(Handle handle, HandleSignals signals, long deadline, Callback callback);
+
+}
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Core.java b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
index e5c6d08..ba0e5c6 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
@@ -305,9 +305,9 @@
public UntypedHandle acquireNativeHandle(int handle);
/**
- * Returns an implementation of {@link Watcher}.
+ * Returns a default implementation of {@link AsyncWaiter}.
*/
- public Watcher getWatcher();
+ public AsyncWaiter getDefaultAsyncWaiter();
/**
* Returns a new run loop.
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Watcher.java b/mojo/public/java/system/src/org/chromium/mojo/system/Watcher.java
deleted file mode 100644
index 9c70161..0000000
--- a/mojo/public/java/system/src/org/chromium/mojo/system/Watcher.java
+++ /dev/null
@@ -1,38 +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.
-
-package org.chromium.mojo.system;
-
-import org.chromium.mojo.system.Core.HandleSignals;
-
-/**
- * Watches a handle for signals being satisfied.
- */
-public interface Watcher {
- /**
- * Callback passed to {@link Watcher#start}.
- */
- public interface Callback {
- /**
- * Called when the handle is ready.
- */
- public void onResult(int result);
- }
-
- /**
- * Starts watching a handle.
- */
- int start(Handle handle, HandleSignals signals, Callback callback);
-
- /**
- * Cancels an already-started watch.
- */
- void cancel();
-
- /**
- * Destroys the underlying implementation. Other methods will fail after destroy has been
- * called.
- */
- void destroy();
-}
diff --git a/mojo/public/js/BUILD.gn b/mojo/public/js/BUILD.gn
index 0fae4b4..eda7e04 100644
--- a/mojo/public/js/BUILD.gn
+++ b/mojo/public/js/BUILD.gn
@@ -2,8 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-interfaces_bindings_gen_dir = "$root_gen_dir/mojo/public/interfaces/bindings"
-
source_set("js") {
sources = [
"constants.cc",
@@ -13,43 +11,34 @@
group("bindings") {
data = [
- "$interfaces_bindings_gen_dir/interface_control_messages.mojom.js",
"bindings.js",
"buffer.js",
"codec.js",
+ "connection.js",
"connector.js",
+ "constants.cc",
+ "constants.h",
"core.js",
- "interface_types.js",
- "lib/control_message_handler.js",
- "lib/control_message_proxy.js",
"router.js",
"support.js",
"threading.js",
"unicode.js",
"validator.js",
]
-
- deps = [
- "//mojo/public/interfaces/bindings:bindings__generator",
- ]
}
group("tests") {
testonly = true
data = [
+ "codec_unittests.js",
+ "core_unittests.js",
+ "struct_unittests.js",
+ "test/validation_test_input_parser.js",
+ "union_unittests.js",
+ "validation_unittests.js",
"//mojo/public/interfaces/bindings/tests/data/validation/",
- "tests/codec_unittest.js",
- "tests/connection_unittest.js",
- "tests/core_unittest.js",
- "tests/interface_ptr_unittest.js",
- "tests/sample_service_unittest.js",
- "tests/struct_unittest.js",
- "tests/union_unittest.js",
- "tests/validation_test_input_parser.js",
- "tests/validation_unittest.js",
]
-
public_deps = [
":bindings",
]
diff --git a/mojo/public/js/bindings.js b/mojo/public/js/bindings.js
index f3e40d2..2fdcae3 100644
--- a/mojo/public/js/bindings.js
+++ b/mojo/public/js/bindings.js
@@ -3,283 +3,115 @@
// 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) {
+ "mojo/public/js/core",
+], function(router, core) {
- // ---------------------------------------------------------------------------
+ var Router = router.Router;
- function makeRequest(interfacePtr) {
- var pipe = core.createMessagePipe();
- interfacePtr.ptr.bind(new types.InterfacePtrInfo(pipe.handle0, 0));
- return new types.InterfaceRequest(pipe.handle1);
+ var kProxyProperties = Symbol("proxyProperties");
+ var kStubProperties = Symbol("stubProperties");
+
+ // Public proxy class properties that are managed at runtime by the JS
+ // bindings. See ProxyBindings below.
+ function ProxyProperties(receiver) {
+ this.receiver = receiver;
}
- // ---------------------------------------------------------------------------
-
- // 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);
+ // TODO(hansmuller): remove then after 'Client=' has been removed from Mojom.
+ ProxyProperties.prototype.getLocalDelegate = function() {
+ return this.local && StubBindings(this.local).delegate;
}
- InterfacePtrController.prototype.bind = function(ptrInfoOrHandle) {
- this.reset();
+ // TODO(hansmuller): remove then after 'Client=' has been removed from Mojom.
+ ProxyProperties.prototype.setLocalDelegate = function(impl) {
+ if (this.local)
+ StubBindings(this.local).delegate = impl;
+ else
+ throw new Error("no stub object");
+ }
- if (ptrInfoOrHandle instanceof types.InterfacePtrInfo) {
- this.version = ptrInfoOrHandle.version;
- this.handle_ = ptrInfoOrHandle.handle;
- } else {
- this.handle_ = ptrInfoOrHandle;
- }
- };
+ ProxyProperties.prototype.close = function() {
+ this.connection.close();
+ }
- InterfacePtrController.prototype.isBound = function() {
- return this.router_ !== null || this.handle_ !== null;
- };
+ // Public stub class properties that are managed at runtime by the JS
+ // bindings. See StubBindings below.
+ function StubProperties(delegate) {
+ this.delegate = delegate;
+ }
- // 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;
+ StubProperties.prototype.close = function() {
+ this.connection.close();
+ }
- this.proxy_ = null;
- }
- if (this.handle_) {
- core.close(this.handle_);
- this.handle_ = null;
- }
- };
+ // The base class for generated proxy classes.
+ function ProxyBase(receiver) {
+ this[kProxyProperties] = new ProxyProperties(receiver);
- InterfacePtrController.prototype.setConnectionErrorHandler
- = function(callback) {
- if (!this.isBound())
- throw new Error("Cannot set connection error handler if not bound.");
+ // TODO(hansmuller): Temporary, for Chrome backwards compatibility.
+ if (receiver instanceof Router)
+ this.receiver_ = receiver;
+ }
- this.configureProxyIfNecessary_();
- this.router_.setErrorHandler(callback);
- };
+ // The base class for generated stub classes.
+ function StubBase(delegate) {
+ this[kStubProperties] = new StubProperties(delegate);
+ }
- 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;
- }
+ // TODO(hansmuller): remove everything except the connection property doc
+ // after 'Client=' has been removed from Mojom.
- 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.
+ // Provides access to properties added to a proxy object without risking
+ // Mojo interface name collisions. Unless otherwise specified, the initial
+ // value of all properties is undefined.
//
- // Example:
+ // ProxyBindings(proxy).connection - The Connection object that links the
+ // proxy for a remote Mojo service to an optional local stub for a local
+ // service. The value of ProxyBindings(proxy).connection.remote == proxy.
//
- // // FooImpl implements mojom.Foo.
- // function FooImpl() { ... }
- // FooImpl.prototype.fooMethod1 = function() { ... }
- // FooImpl.prototype.fooMethod2 = function() { ... }
+ // ProxyBindings(proxy).local - The "local" stub object whose delegate
+ // implements the proxy's Mojo client interface.
//
- // 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;
+ // ProxyBindings(proxy).setLocalDelegate(impl) - Sets the implementation
+ // delegate of the proxy's client stub object. This is just shorthand
+ // for |StubBindings(ProxyBindings(proxy).local).delegate = impl|.
+ //
+ // ProxyBindings(proxy).getLocalDelegate() - Returns the implementation
+ // delegate of the proxy's client stub object. This is just shorthand
+ // for |StubBindings(ProxyBindings(proxy).local).delegate|.
- if (requestOrHandle)
- this.bind(requestOrHandle);
+ function ProxyBindings(proxy) {
+ return (proxy instanceof ProxyBase) ? proxy[kProxyProperties] : proxy;
}
- Binding.prototype.isBound = function() {
- return this.router_ !== null;
- };
+ // TODO(hansmuller): remove the remote doc after 'Client=' has been
+ // removed from Mojom.
- 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;
+ // Provides access to properties added to a stub object without risking
+ // Mojo interface name collisions. Unless otherwise specified, the initial
+ // value of all properties is undefined.
+ //
+ // StubBindings(stub).delegate - The optional implementation delegate for
+ // the Mojo interface stub.
+ //
+ // StubBindings(stub).connection - The Connection object that links an
+ // optional proxy for a remote service to this stub. The value of
+ // StubBindings(stub).connection.local == stub.
+ //
+ // StubBindings(stub).remote - A proxy for the the stub's Mojo client
+ // service.
+
+ function StubBindings(stub) {
+ return stub instanceof StubBase ? stub[kStubProperties] : stub;
}
- 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;
-
+ exports.EmptyProxy = ProxyBase;
+ exports.EmptyStub = StubBase;
+ exports.ProxyBase = ProxyBase;
+ exports.ProxyBindings = ProxyBindings;
+ exports.StubBase = StubBase;
+ exports.StubBindings = StubBindings;
return exports;
});
diff --git a/mojo/public/js/codec.js b/mojo/public/js/codec.js
index ff5d31a..4003b51 100644
--- a/mojo/public/js/codec.js
+++ b/mojo/public/js/codec.js
@@ -3,10 +3,9 @@
// 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) {
+ "mojo/public/js/buffer",
+], function(unicode, buffer) {
var kErrorUnsigned = "Passing negative value to unsigned";
var kErrorArray = "Passing non Array for array type";
@@ -304,12 +303,8 @@
};
Encoder.prototype.encodeHandle = function(handle) {
- if (handle) {
- this.handles.push(handle);
- this.writeUint32(this.handles.length - 1);
- } else {
- this.writeUint32(kEncodedInvalidHandleValue);
- }
+ this.handles.push(handle);
+ this.writeUint32(this.handles.length - 1);
};
Encoder.prototype.encodeString = function(val) {
@@ -716,20 +711,6 @@
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;
}
@@ -807,54 +788,33 @@
NullableHandle.encode = Handle.encode;
- function Interface(cls) {
- this.cls = cls;
+ function Interface() {
}
- Interface.prototype.encodedSize = 8;
+ Interface.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.decode = function(decoder) {
+ var handle = decoder.decodeHandle();
+ // Ignore the version field for now.
+ decoder.readUint32();
+
+ return handle;
};
- 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);
+ Interface.encode = function(encoder, val) {
+ encoder.encodeHandle(val);
+ // Set the version field to 0 for now.
+ encoder.writeUint32(0);
};
- function NullableInterface(cls) {
- Interface.call(this, cls);
+ function NullableInterface() {
}
- NullableInterface.prototype = Object.create(Interface.prototype);
+ NullableInterface.encodedSize = Interface.encodedSize;
- function InterfaceRequest() {
- }
+ NullableInterface.decode = Interface.decode;
- 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;
+ NullableInterface.encode = Interface.encode;
function MapOf(keyClass, valueClass) {
this.keyClass = keyClass;
@@ -903,7 +863,6 @@
exports.Float = Float;
exports.Double = Double;
exports.String = String;
- exports.Enum = Enum;
exports.NullableString = NullableString;
exports.PointerTo = PointerTo;
exports.NullablePointerTo = NullablePointerTo;
@@ -914,8 +873,6 @@
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/codec_unittests.js b/mojo/public/js/codec_unittests.js
new file mode 100644
index 0000000..b610d9a
--- /dev/null
+++ b/mojo/public/js/codec_unittests.js
@@ -0,0 +1,296 @@
+// Copyright 2014 The Chromium 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();
+ 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));
+ }
+
+ 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/connection.js b/mojo/public/js/connection.js
new file mode 100644
index 0000000..3f7e839
--- /dev/null
+++ b/mojo/public/js/connection.js
@@ -0,0 +1,176 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define("mojo/public/js/connection", [
+ "mojo/public/js/bindings",
+ "mojo/public/js/connector",
+ "mojo/public/js/core",
+ "mojo/public/js/router",
+], function(bindings, connector, core, router) {
+
+ var Router = router.Router;
+ var EmptyProxy = bindings.EmptyProxy;
+ var EmptyStub = bindings.EmptyStub;
+ var ProxyBindings = bindings.ProxyBindings;
+ var StubBindings = bindings.StubBindings;
+ var TestConnector = connector.TestConnector;
+ var TestRouter = router.TestRouter;
+
+ // TODO(hansmuller): the proxy receiver_ property should be receiver$
+
+ function BaseConnection(localStub, remoteProxy, router) {
+ this.router_ = router;
+ this.local = localStub;
+ this.remote = remoteProxy;
+
+ this.router_.setIncomingReceiver(localStub);
+ this.router_.setErrorHandler(function() {
+ if (StubBindings(this.local) &&
+ StubBindings(this.local).connectionErrorHandler)
+ StubBindings(this.local).connectionErrorHandler();
+ }.bind(this));
+ if (this.remote)
+ this.remote.receiver_ = router;
+
+ // Validate incoming messages: remote responses and local requests.
+ var validateRequest = localStub && localStub.validator;
+ var validateResponse = remoteProxy && remoteProxy.validator;
+ var payloadValidators = [];
+ if (validateRequest)
+ payloadValidators.push(validateRequest);
+ if (validateResponse)
+ payloadValidators.push(validateResponse);
+ this.router_.setPayloadValidators(payloadValidators);
+ }
+
+ BaseConnection.prototype.close = function() {
+ this.router_.close();
+ this.router_ = null;
+ this.local = null;
+ this.remote = null;
+ };
+
+ BaseConnection.prototype.encounteredError = function() {
+ return this.router_.encounteredError();
+ };
+
+ function Connection(
+ handle, localFactory, remoteFactory, routerFactory, connectorFactory) {
+ var routerClass = routerFactory || Router;
+ var router = new routerClass(handle, connectorFactory);
+ var remoteProxy = remoteFactory && new remoteFactory(router);
+ var localStub = localFactory && new localFactory(remoteProxy);
+ BaseConnection.call(this, localStub, remoteProxy, router);
+ }
+
+ Connection.prototype = Object.create(BaseConnection.prototype);
+
+ // The TestConnection subclass is only intended to be used in unit tests.
+ function TestConnection(handle, localFactory, remoteFactory) {
+ Connection.call(this,
+ handle,
+ localFactory,
+ remoteFactory,
+ TestRouter,
+ TestConnector);
+ }
+
+ TestConnection.prototype = Object.create(Connection.prototype);
+
+ // Return a handle for a message pipe that's connected to a proxy
+ // for remoteInterface. Used by generated code for outgoing interface&
+ // (request) parameters: the caller is given the generated proxy via
+ // |proxyCallback(proxy)| and the generated code sends the handle
+ // returned by this function.
+ function bindProxy(proxyCallback, remoteInterface) {
+ var messagePipe = core.createMessagePipe();
+ if (messagePipe.result != core.RESULT_OK)
+ throw new Error("createMessagePipe failed " + messagePipe.result);
+
+ var proxy = new remoteInterface.proxyClass;
+ var router = new Router(messagePipe.handle0);
+ var connection = new BaseConnection(undefined, proxy, router);
+ ProxyBindings(proxy).connection = connection;
+ if (proxyCallback)
+ proxyCallback(proxy);
+
+ return messagePipe.handle1;
+ }
+
+ // Return a handle for a message pipe that's connected to a stub for
+ // localInterface. Used by generated code for outgoing interface
+ // parameters: the caller is given the generated stub via
+ // |stubCallback(stub)| and the generated code sends the handle
+ // returned by this function. The caller is responsible for managing
+ // the lifetime of the stub and for setting it's implementation
+ // delegate with: StubBindings(stub).delegate = myImpl;
+ function bindImpl(stubCallback, localInterface) {
+ var messagePipe = core.createMessagePipe();
+ if (messagePipe.result != core.RESULT_OK)
+ throw new Error("createMessagePipe failed " + messagePipe.result);
+
+ var stub = new localInterface.stubClass;
+ var router = new Router(messagePipe.handle0);
+ var connection = new BaseConnection(stub, undefined, router);
+ StubBindings(stub).connection = connection;
+ if (stubCallback)
+ stubCallback(stub);
+
+ return messagePipe.handle1;
+ }
+
+ // Return a remoteInterface proxy for handle. Used by generated code
+ // for converting incoming interface parameters to proxies.
+ function bindHandleToProxy(handle, remoteInterface) {
+ if (!core.isHandle(handle))
+ throw new Error("Not a handle " + handle);
+
+ var proxy = new remoteInterface.proxyClass;
+ var router = new Router(handle);
+ var connection = new BaseConnection(undefined, proxy, router);
+ ProxyBindings(proxy).connection = connection;
+ return proxy;
+ }
+
+ // Return a localInterface stub for handle. Used by generated code
+ // for converting incoming interface& request parameters to localInterface
+ // stubs. The caller can specify the stub's implementation of localInterface
+ // like this: StubBindings(stub).delegate = myStubImpl.
+ function bindHandleToStub(handle, localInterface) {
+ if (!core.isHandle(handle))
+ throw new Error("Not a handle " + handle);
+
+ var stub = new localInterface.stubClass;
+ var router = new Router(handle);
+ var connection = new BaseConnection(stub, undefined, router);
+ StubBindings(stub).connection = connection;
+ return stub;
+ }
+
+ /**
+ * Creates a messape pipe and links one end of the pipe to the given object.
+ * @param {!Object} obj The object to create a handle for. Must be a subclass
+ * of an auto-generated stub class.
+ * @return {!MojoHandle} The other (not yet connected) end of the message
+ * pipe.
+ */
+ function bindStubDerivedImpl(obj) {
+ var pipe = core.createMessagePipe();
+ var router = new Router(pipe.handle0);
+ var connection = new BaseConnection(obj, undefined, router);
+ obj.connection = connection;
+ return pipe.handle1;
+ }
+
+ var exports = {};
+ exports.Connection = Connection;
+ exports.TestConnection = TestConnection;
+
+ exports.bindProxy = bindProxy;
+ exports.bindImpl = bindImpl;
+ exports.bindHandleToProxy = bindHandleToProxy;
+ exports.bindHandleToStub = bindHandleToStub;
+ exports.bindStubDerivedImpl = bindStubDerivedImpl;
+ return exports;
+});
diff --git a/mojo/public/js/connector.js b/mojo/public/js/connector.js
index ee16be8..674f36b 100644
--- a/mojo/public/js/connector.js
+++ b/mojo/public/js/connector.js
@@ -82,12 +82,6 @@
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_,
@@ -104,12 +98,29 @@
}
var messageBuffer = new buffer.Buffer(read.buffer);
var message = new codec.Message(messageBuffer, read.handles);
- if (this.incomingReceiver_)
- this.incomingReceiver_.accept(message);
+ if (this.incomingReceiver_) {
+ this.incomingReceiver_.accept(message);
+ }
}
};
+ // The TestConnector subclass is only intended to be used in unit tests. It
+ // doesn't automatically listen for input messages. Instead, you need to
+ // call waitForNextMessage to block and wait for the next incoming message.
+ function TestConnector(handle) {
+ Connector.call(this, handle);
+ }
+
+ TestConnector.prototype = Object.create(Connector.prototype);
+
+ TestConnector.prototype.waitForNextMessage = function() {
+ var wait = core.wait(this.handle_, core.HANDLE_SIGNAL_READABLE,
+ core.DEADLINE_INDEFINITE);
+ this.readMore_(wait.result);
+ }
+
var exports = {};
exports.Connector = Connector;
+ exports.TestConnector = TestConnector;
return exports;
});
diff --git a/mojo/public/js/constants.cc b/mojo/public/js/constants.cc
index 58cf274..d29f5cb 100644
--- a/mojo/public/js/constants.cc
+++ b/mojo/public/js/constants.cc
@@ -9,15 +9,10 @@
const char kBindingsModuleName[] = "mojo/public/js/bindings";
const char kBufferModuleName[] = "mojo/public/js/buffer";
const char kCodecModuleName[] = "mojo/public/js/codec";
+const char kConnectionModuleName[] = "mojo/public/js/connection";
const char kConnectorModuleName[] = "mojo/public/js/connector";
-const char kControlMessageHandlerModuleName[] =
- "mojo/public/js/lib/control_message_handler";
-const char kControlMessageProxyModuleName[] =
- "mojo/public/js/lib/control_message_proxy";
-const char kInterfaceControlMessagesMojom[] =
- "mojo/public/interfaces/bindings/interface_control_messages.mojom";
-const char kInterfaceTypesModuleName[] = "mojo/public/js/interface_types";
-const char kRouterModuleName[] = "mojo/public/js/router";
const char kUnicodeModuleName[] = "mojo/public/js/unicode";
+const char kRouterModuleName[] = "mojo/public/js/router";
const char kValidatorModuleName[] = "mojo/public/js/validator";
+
} // namespace mojo
diff --git a/mojo/public/js/constants.h b/mojo/public/js/constants.h
index 9d32d20..de75a90 100644
--- a/mojo/public/js/constants.h
+++ b/mojo/public/js/constants.h
@@ -11,13 +11,10 @@
extern const char kBindingsModuleName[];
extern const char kBufferModuleName[];
extern const char kCodecModuleName[];
+extern const char kConnectionModuleName[];
extern const char kConnectorModuleName[];
-extern const char kControlMessageHandlerModuleName[];
-extern const char kControlMessageProxyModuleName[];
-extern const char kInterfaceControlMessagesMojom[];
-extern const char kInterfaceTypesModuleName[];
-extern const char kRouterModuleName[];
extern const char kUnicodeModuleName[];
+extern const char kRouterModuleName[];
extern const char kValidatorModuleName[];
} // namespace mojo
diff --git a/mojo/public/js/core.js b/mojo/public/js/core.js
index ef480ee..b89a956 100644
--- a/mojo/public/js/core.js
+++ b/mojo/public/js/core.js
@@ -114,27 +114,6 @@
var READ_DATA_FLAG_PEEK;
/**
- * MojoCreateSharedBufferOptionsFlags: Used to specify options to
- * |createSharedBuffer()|.
- * See core.h for more information.
- */
-var CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE;
-
-/**
- * MojoDuplicateBufferHandleOptionsFlags: Used to specify options to
- * |duplicateBufferHandle()|.
- * See core.h for more information.
- */
-var DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE;
-var DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY;
-
-/**
- * MojoMapBufferFlags: Used to specify options to |mapBuffer()|.
- * See core.h for more information.
- */
-var MAP_BUFFER_FLAG_NONE;
-
-/**
* Closes the given |handle|. See MojoClose for more info.
* @param {MojoHandle} Handle to close.
* @return {MojoResult} Result code.
@@ -257,57 +236,3 @@
* @return true or false
*/
function isHandle(value) { [native code] }
-
-/**
- * Creates shared buffer of specified size |num_bytes|.
- * See MojoCreateSharedBuffer for more information including error codes.
- *
- * @param {number} num_bytes Size of the memory to be allocated for shared
- * @param {MojoCreateSharedBufferOptionsFlags} flags Flags.
- * buffer.
- * @return {object} An object of the form {
- * result, // |RESULT_OK| on success, error code otherwise.
- * handle, // An MojoHandle for shared buffer (only on success).
- * }
- */
-function createSharedBuffer(num_bytes, flags) { [native code] }
-
-/**
- * Duplicates the |buffer_handle| to a shared buffer. Duplicated handle can be
- * sent to another process over message pipe. See MojoDuplicateBufferHandle for
- * more information including error codes.
- *
- * @param {MojoHandle} buffer_handle MojoHandle.
- * @param {MojoCreateSharedBufferOptionsFlags} flags Flags.
- * @return {object} An object of the form {
- * result, // |RESULT_OK| on success, error code otherwise.
- * handle, // A duplicated MojoHandle for shared buffer (only on success).
- * }
- */
-function duplicateBufferHandle(buffer_handle, flags) { [native code] }
-
-/**
- * Maps the part (at offset |offset| of length |num_bytes|) of the buffer given
- * by |buffer_handle| into ArrayBuffer memory |buffer|, with options specified
- * by |flags|. See MojoMapBuffer for more information including error codes.
- *
- * @param {MojoHandle} buffer_handle A sharedBufferHandle returned by
- * createSharedBuffer.
- * @param {number} offset Offset.
- * @param {number} num_bytes Size of the memory to be mapped.
- * @param {MojoMapBufferFlags} flags Flags.
- * @return {object} An object of the form {
- * result, // |RESULT_OK| on success, error code otherwise.
- * buffer, // An ArrayBuffer (only on success).
- * }
- */
-function mapBuffer(buffer_handle, offset, num_bytes, flags) { [native code] }
-
-/**
- * Unmaps buffer that was mapped using mapBuffer.
- * See MojoUnmapBuffer for more information including error codes.
- *
- * @param {ArrayBuffer} buffer ArrayBuffer.
- * @return {MojoResult} Result code.
- */
-function unmapBuffer(buffer) { [native code] }
diff --git a/mojo/public/js/core_unittests.js b/mojo/public/js/core_unittests.js
new file mode 100644
index 0000000..12364dc
--- /dev/null
+++ b/mojo/public/js/core_unittests.js
@@ -0,0 +1,198 @@
+// Copyright 2014 The Chromium 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);
+ 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 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();
+ }
+
+});
diff --git a/mojo/public/js/router.js b/mojo/public/js/router.js
index e94c5eb..e3db0a6 100644
--- a/mojo/public/js/router.js
+++ b/mojo/public/js/router.js
@@ -3,20 +3,17 @@
// 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) {
+], function(codec, core, connector, validator) {
var Connector = connector.Connector;
var MessageReader = codec.MessageReader;
var Validator = validator.Validator;
- var ControlMessageHandler = controlMessageHandler.ControlMessageHandler;
- function Router(handle, interface_version, connectorFactory) {
+ function Router(handle, connectorFactory) {
if (!core.isHandle(handle))
throw new Error("Router constructor: Not a handle");
if (connectorFactory === undefined)
@@ -27,12 +24,6 @@
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),
@@ -45,7 +36,6 @@
Router.prototype.close = function() {
this.completers_.clear(); // Drop any responders.
this.connector_.close();
- this.testingController_ = null;
};
Router.prototype.accept = function(message) {
@@ -91,11 +81,6 @@
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);
@@ -110,17 +95,8 @@
};
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_) {
+ if (this.incomingReceiver_) {
this.incomingReceiver_.acceptWithResponder(message, this);
} else {
// If we receive a request expecting a response when the client is not
@@ -131,39 +107,17 @@
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);
- }
+ this.completers_.delete(requestID);
+ completer.resolve(message);
} else {
- if (controlMessageHandler.isControlMessage(message)) {
- if (this.controlMessageHandler_) {
- var ok = this.controlMessageHandler_.accept(message);
- if (ok) return;
- }
- this.close();
- } else if (this.incomingReceiver_) {
+ 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);
- };
+ this.close();
+ }
Router.prototype.handleConnectionError_ = function(result) {
this.completers_.forEach(function(value) {
@@ -174,30 +128,25 @@
this.close();
};
- // The RouterTestingController is used in unit tests. It defeats valid message
- // handling and delgates invalid message handling.
+ // The TestRouter subclass is only intended to be used in unit tests.
+ // It defeats valid message handling and delgates invalid message handling.
- function RouterTestingController(connector) {
- this.connector_ = connector;
- this.invalidMessageHandler_ = null;
+ function TestRouter(handle, connectorFactory) {
+ Router.call(this, handle, connectorFactory);
}
- RouterTestingController.prototype.waitForNextMessage = function() {
- this.connector_.waitForNextMessageForTesting();
+ TestRouter.prototype = Object.create(Router.prototype);
+
+ TestRouter.prototype.handleValidIncomingMessage_ = function() {
};
- RouterTestingController.prototype.setInvalidIncomingMessageHandler =
- function(callback) {
- this.invalidMessageHandler_ = callback;
- };
-
- RouterTestingController.prototype.onInvalidIncomingMessage =
- function(error) {
- if (this.invalidMessageHandler_)
- this.invalidMessageHandler_(error);
- };
+ TestRouter.prototype.handleInvalidIncomingMessage_ =
+ function(message, error) {
+ this.validationErrorHandler(error);
+ };
var exports = {};
exports.Router = Router;
+ exports.TestRouter = TestRouter;
return exports;
});
diff --git a/mojo/public/js/struct_unittests.js b/mojo/public/js/struct_unittests.js
new file mode 100644
index 0000000..691d51b
--- /dev/null
+++ b/mojo/public/js/struct_unittests.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/test/validation_test_input_parser.js b/mojo/public/js/test/validation_test_input_parser.js
new file mode 100644
index 0000000..f5a57f9
--- /dev/null
+++ b/mojo/public/js/test/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/threading.js b/mojo/public/js/threading.js
index 49ab5c9..cfe5037 100644
--- a/mojo/public/js/threading.js
+++ b/mojo/public/js/threading.js
@@ -7,7 +7,7 @@
// Note: This file is for documentation purposes only. The code here is not
// actually executed. The real module is implemented natively in Mojo.
//
-// This module provides a way for a Service implemented in JS
+// This module provides a way for a Mojo application implemented in JS
// to exit by quitting the current message loop. This module is not
// intended to be used by Mojo JS application started by the JS
// content handler.
diff --git a/mojo/public/js/union_unittests.js b/mojo/public/js/union_unittests.js
new file mode 100644
index 0000000..5dcda7d
--- /dev/null
+++ b/mojo/public/js/union_unittests.js
@@ -0,0 +1,184 @@
+// 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({
+ 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/validation_unittests.js b/mojo/public/js/validation_unittests.js
new file mode 100644
index 0000000..817d42c
--- /dev/null
+++ b/mojo/public/js/validation_unittests.js
@@ -0,0 +1,349 @@
+// Copyright 2014 The Chromium 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/buffer",
+ "mojo/public/js/codec",
+ "mojo/public/js/connection",
+ "mojo/public/js/connector",
+ "mojo/public/js/core",
+ "mojo/public/js/test/validation_test_input_parser",
+ "mojo/public/js/router",
+ "mojo/public/js/validator",
+], function(console,
+ file,
+ expect,
+ testInterface,
+ buffer,
+ codec,
+ connection,
+ connector,
+ core,
+ parser,
+ router,
+ 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(yzshen) Skipping struct versioning tests (tests with "mthd11"
+ // in the name) because the feature is not supported in JS yet.
+ // TODO(yzshen) Skipping enum validation tests (tests with "enum" in the
+ // name) because the feature is not supported in JS yet. crbug.com/581390
+ // 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.
+ if (testFiles[i].indexOf("overflow") != -1 ||
+ testFiles[i].indexOf("mthd11") != -1 ||
+ testFiles[i].indexOf("enum") != -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,
+ localFactory,
+ remoteFactory) {
+ var testFiles = getMessageTestFiles(testFilesPattern);
+ expect(testFiles.length).toBeGreaterThan(0);
+
+ var testMessagePipe = core.createMessagePipe();
+ expect(testMessagePipe.result).toBe(core.RESULT_OK);
+ var testConnection = new connection.TestConnection(
+ testMessagePipe.handle1, localFactory, remoteFactory);
+
+ for (var i = 0; i < testFiles.length; i++) {
+ 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);
+
+ var validationError = noError;
+ testConnection.router_.validationErrorHandler = function(err) {
+ validationError = err;
+ }
+
+ testConnection.router_.connector_.waitForNextMessage();
+ checkValidationResult(testFiles[i], validationError);
+ }
+
+ testConnection.close();
+ expect(core.close(testMessagePipe.handle0)).toBe(core.RESULT_OK);
+ }
+
+ function testIntegratedMessageHeaderValidation() {
+ testIntegratedMessageValidation(
+ "integration_msghdr",
+ testInterface.IntegrationTestInterface.stubClass,
+ undefined);
+ testIntegratedMessageValidation(
+ "integration_msghdr",
+ undefined,
+ testInterface.IntegrationTestInterface.proxyClass);
+ }
+
+ function testIntegratedRequestMessageValidation() {
+ testIntegratedMessageValidation(
+ "integration_intf_rqst",
+ testInterface.IntegrationTestInterface.stubClass,
+ undefined);
+ }
+
+ function testIntegratedResponseMessageValidation() {
+ testIntegratedMessageValidation(
+ "integration_intf_resp",
+ undefined,
+ testInterface.IntegrationTestInterface.proxyClass);
+ }
+
+ expect(checkTestMessageParser()).toBeNull();
+ testConformanceMessageValidation();
+ testBoundsCheckMessageValidation();
+ testResponseConformanceMessageValidation();
+ testResponseBoundsCheckMessageValidation();
+ testIntegratedMessageHeaderValidation();
+ testIntegratedResponseMessageValidation();
+ testIntegratedRequestMessageValidation();
+
+ this.result = "PASS";
+});
diff --git a/mojo/public/js/validator.js b/mojo/public/js/validator.js
index fee742d..cbf7521 100644
--- a/mojo/public/js/validator.js
+++ b/mojo/public/js/validator.js
@@ -24,15 +24,10 @@
'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;
}
@@ -42,18 +37,12 @@
}
function isInterfaceClass(cls) {
- return cls instanceof codec.Interface;
- }
-
- function isInterfaceRequestClass(cls) {
- return cls === codec.InterfaceRequest ||
- cls === codec.NullableInterfaceRequest;
+ return cls === codec.Interface || cls === codec.NullableInterface;
}
function isNullable(type) {
return type === codec.NullableString || type === codec.NullableHandle ||
type === codec.NullableInterface ||
- type === codec.NullableInterfaceRequest ||
type instanceof codec.NullableArrayOf ||
type instanceof codec.NullablePointerTo;
}
@@ -87,7 +76,7 @@
return false;
return true;
- };
+ }
Validator.prototype.claimRange = function(start, numBytes) {
if (this.isValidRange(start, numBytes)) {
@@ -95,7 +84,7 @@
return true;
}
return false;
- };
+ }
Validator.prototype.claimHandle = function(index) {
if (index === codec.kEncodedInvalidHandleValue)
@@ -107,13 +96,6 @@
// 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) {
@@ -125,19 +107,15 @@
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) {
+ Validator.prototype.validateStructHeader =
+ function(offset, minNumBytes, minVersion) {
if (!codec.isAligned(offset))
return validationError.MISALIGNED_OBJECT;
@@ -145,44 +123,20 @@
return validationError.ILLEGAL_MEMORY_RANGE;
var numBytes = this.message.buffer.getUint32(offset);
+ var version = this.message.buffer.getUint32(offset + 4);
- if (numBytes < minNumBytes)
+ // Backward compatibility is not yet supported.
+ if (numBytes < minNumBytes || version < minVersion)
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);
+ var err = this.validateStructHeader(0, codec.kMessageHeaderSize, 0);
if (err != validationError.NONE)
return err;
@@ -208,28 +162,7 @@
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
@@ -240,7 +173,7 @@
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);
@@ -263,7 +196,7 @@
return this.validateArray(arrayOffset, elementSize, elementType,
expectedDimensionSizes, currentDimension);
- };
+ }
Validator.prototype.validateStructPointer = function(
offset, structClass, nullable) {
@@ -276,7 +209,7 @@
validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
return structClass.validate(this, structOffset);
- };
+ }
Validator.prototype.validateUnion = function(
offset, unionClass, nullable) {
@@ -287,7 +220,7 @@
}
return unionClass.validate(this, offset);
- };
+ }
Validator.prototype.validateNestedUnion = function(
offset, unionClass, nullable) {
@@ -300,7 +233,7 @@
validationError.NONE : validationError.UNEXPECTED_NULL_UNION;
return this.validateUnion(unionOffset, unionClass, nullable);
- };
+ }
// This method assumes that the array at arrayPointerOffset has
// been validated.
@@ -308,7 +241,7 @@
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) {
@@ -323,7 +256,7 @@
validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
var mapEncodedSize = codec.kStructHeaderSize + codec.kMapStructPayloadSize;
- var err = this.validateStructHeader(structOffset, mapEncodedSize);
+ var err = this.validateStructHeader(structOffset, mapEncodedSize, 0);
if (err !== validationError.NONE)
return err;
@@ -356,12 +289,12 @@
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
@@ -404,9 +337,6 @@
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);
@@ -417,12 +347,9 @@
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
@@ -438,11 +365,11 @@
return err;
}
return validationError.NONE;
- };
+ }
Validator.prototype.validateInterfaceElements =
function(offset, numElements, nullable) {
- var elementSize = codec.Interface.prototype.encodedSize;
+ var elementSize = codec.Interface.encodedSize;
for (var i = 0; i < numElements; i++) {
var elementOffset = offset + i * elementSize;
var err = this.validateInterface(elementOffset, nullable);
@@ -450,19 +377,7 @@
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 =
@@ -478,7 +393,7 @@
return err;
}
return validationError.NONE;
- };
+ }
Validator.prototype.validateStructElements =
function(offset, numElements, structClass, nullable) {
@@ -491,19 +406,7 @@
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;
diff --git a/mojo/public/mojo_application.gni b/mojo/public/mojo_application.gni
new file mode 100644
index 0000000..28c8a8d
--- /dev/null
+++ b/mojo/public/mojo_application.gni
@@ -0,0 +1,270 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/toolchain/toolchain.gni")
+import("//mojo/public/mojo_constants.gni")
+
+if (is_android) {
+ import("//build/config/android/rules.gni")
+ import("//build/config/zip.gni")
+}
+
+# Generate a binary Mojo application in a self-named directory.
+# Application resources are copied to a "resources" directory alongside the app.
+# The parameters of this template are those of a shared library.
+template("mojo_native_application") {
+ base_target_name = target_name
+ if (defined(invoker.output_name)) {
+ base_target_name = invoker.output_name
+ }
+
+ final_target_name = target_name
+
+ mojo_deps = []
+ if (defined(invoker.deps)) {
+ mojo_deps += invoker.deps
+ }
+
+ mojo_data_deps = []
+
+ if (defined(invoker.resources)) {
+ copy_step_name = "${base_target_name}__copy_resources"
+ copy(copy_step_name) {
+ sources = invoker.resources
+ outputs = [
+ "${root_out_dir}/${mojo_application_subdir}/${base_target_name}/resources/{{source_file_part}}",
+ ]
+ if (defined(invoker.testonly)) {
+ testonly = invoker.testonly
+ }
+ deps = mojo_deps
+ }
+ mojo_data_deps += [ ":$copy_step_name" ]
+ }
+
+ output = base_target_name + ".mojo"
+ library_target_name = base_target_name + "_library"
+ library_name = "${shlib_prefix}${library_target_name}${shlib_extension}"
+
+ shared_library(library_target_name) {
+ if (defined(invoker.cflags)) {
+ cflags = invoker.cflags
+ }
+ if (defined(invoker.cflags_c)) {
+ cflags_c = invoker.cflags_c
+ }
+ if (defined(invoker.cflags_cc)) {
+ cflags_cc = invoker.cflags_cc
+ }
+ if (defined(invoker.cflags_objc)) {
+ cflags_objc = invoker.cflags_objc
+ }
+ if (defined(invoker.cflags_objcc)) {
+ cflags_objcc = invoker.cflags_objcc
+ }
+ if (defined(invoker.defines)) {
+ defines = invoker.defines
+ }
+ if (defined(invoker.include_dirs)) {
+ include_dirs = invoker.include_dirs
+ }
+ if (defined(invoker.ldflags)) {
+ ldflags = invoker.ldflags
+ }
+ if (defined(invoker.lib_dirs)) {
+ lib_dirs = invoker.lib_dirs
+ }
+ if (defined(invoker.libs)) {
+ libs = invoker.libs
+ }
+
+ data_deps = []
+ if (!defined(invoker.avoid_runner_cycle) || !invoker.avoid_runner_cycle) {
+ # Give the user an out; as some mojo services are depended on by the
+ # runner.
+ data_deps += [ "//services/shell/standalone" ]
+ }
+ if (defined(invoker.data_deps)) {
+ data_deps += invoker.data_deps
+ }
+ data_deps += mojo_data_deps
+
+ deps = [
+ "//mojo/public/c/system:set_thunks_for_app",
+ "//services/shell/public/cpp:application_support",
+ ]
+
+ deps += mojo_deps
+ if (defined(invoker.public_deps)) {
+ public_deps = invoker.public_deps
+ }
+ if (defined(invoker.all_dependent_configs)) {
+ all_dependent_configs = invoker.all_dependent_configs
+ }
+ if (defined(invoker.public_configs)) {
+ public_configs = invoker.public_configs
+ }
+ if (defined(invoker.check_includes)) {
+ check_includes = invoker.check_includes
+ }
+ if (defined(invoker.configs)) {
+ configs += invoker.configs
+ }
+ if (defined(invoker.data)) {
+ data = invoker.data
+ }
+ if (defined(invoker.inputs)) {
+ inputs = invoker.inputs
+ }
+ if (defined(invoker.public)) {
+ public = invoker.public
+ }
+ if (defined(invoker.sources)) {
+ sources = invoker.sources
+ }
+ if (defined(invoker.testonly)) {
+ testonly = invoker.testonly
+ }
+ }
+
+ copy(final_target_name) {
+ forward_variables_from(invoker,
+ [
+ "testonly",
+ "visibility",
+ ])
+ deps = [
+ ":${library_target_name}",
+ ]
+
+ sources = [
+ "${root_shlib_dir}/${library_name}",
+ ]
+ outputs = [
+ "${root_out_dir}/${mojo_application_subdir}/${base_target_name}/${output}",
+ ]
+ }
+
+ if (is_android) {
+ android_assets("${final_target_name}_assets") {
+ forward_variables_from(invoker, [ "testonly" ])
+ deps = [
+ ":${library_target_name}",
+ ]
+ if (defined(invoker.deps)) {
+ deps += invoker.deps
+ }
+ renaming_sources = [ "${root_shlib_dir}/${library_name}" ]
+ renaming_destinations = [ "${base_target_name}/${output}" ]
+ if (defined(invoker.resources)) {
+ renaming_sources += invoker.resources
+ renaming_destinations += process_file_template(
+ invoker.resources,
+ [ "$base_target_name/resources/{{source_file_part}}" ])
+ }
+ }
+ }
+}
+
+if (is_android) {
+ # Declares an Android Mojo application consisting of an .so file and a
+ # corresponding .dex.jar file.
+ #
+ # Variables:
+ # input_so: the .so file to bundle
+ # input_dex_jar: the .dex.jar file to bundle
+ # deps / public_deps / data_deps (optional):
+ # Dependencies. The targets that generate the .so/jar inputs should be
+ # listed in either deps or public_deps.
+ # output_name (optional): override for the output file name
+ template("mojo_android_application") {
+ assert(defined(invoker.input_so))
+ assert(defined(invoker.input_dex_jar))
+
+ base_target_name = target_name
+ if (defined(invoker.output_name)) {
+ base_target_name = invoker.output_name
+ }
+
+ mojo_data_deps = []
+ if (defined(invoker.resources)) {
+ copy_step_name = "${base_target_name}__copy_resources"
+ copy(copy_step_name) {
+ sources = invoker.resources
+ outputs = [
+ "${root_out_dir}/${mojo_application_subdir}/${base_target_name}/resources/{{source_file_part}}",
+ ]
+ if (defined(invoker.testonly)) {
+ testonly = invoker.testonly
+ }
+ if (defined(invoker.deps)) {
+ deps = invoker.deps
+ }
+ }
+ mojo_data_deps += [ ":$copy_step_name" ]
+ }
+
+ zip_action_name = "${target_name}_zip"
+ zip_action_output = "$target_gen_dir/${target_name}.zip"
+ prepend_action_name = target_name
+ zip(zip_action_name) {
+ visibility = [ ":$prepend_action_name" ]
+ inputs = [
+ invoker.input_so,
+ invoker.input_dex_jar,
+ ]
+ output = zip_action_output
+ forward_variables_from(invoker,
+ [
+ "deps",
+ "public_deps",
+ "data_deps",
+ ])
+ }
+
+ _mojo_output = "${root_out_dir}/${mojo_application_subdir}/${base_target_name}/${base_target_name}.mojo"
+
+ action(target_name) {
+ script = "//mojo/public/tools/prepend.py"
+
+ input = zip_action_output
+ inputs = [
+ input,
+ ]
+
+ outputs = [
+ _mojo_output,
+ ]
+
+ rebase_input = rebase_path(input, root_build_dir)
+ rebase_output = rebase_path(_mojo_output, root_build_dir)
+ args = [
+ "--input=$rebase_input",
+ "--output=$rebase_output",
+ "--line=#!mojo mojo:android_handler",
+ ]
+
+ data_deps = mojo_data_deps
+
+ public_deps = [
+ ":$zip_action_name",
+ ]
+ }
+
+ android_assets("${target_name}_assets") {
+ forward_variables_from(invoker, [ "testonly" ])
+ deps = [
+ ":$prepend_action_name",
+ ]
+ renaming_sources = [ _mojo_output ]
+ renaming_destinations = [ "${base_target_name}/${base_target_name}.mojo" ]
+ if (defined(invoker.resources)) {
+ renaming_sources += invoker.resources
+ renaming_destinations += process_file_template(
+ invoker.resources,
+ [ "$base_target_name/resources/{{source_file_part}}" ])
+ }
+ }
+ }
+}
diff --git a/mojo/public/mojo_application_manifest.gni b/mojo/public/mojo_application_manifest.gni
new file mode 100644
index 0000000..6184417
--- /dev/null
+++ b/mojo/public/mojo_application_manifest.gni
@@ -0,0 +1,139 @@
+# 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.
+
+import("//mojo/public/mojo_constants.gni")
+
+# Used to produce a Mojo Application Manifest for an application.
+#
+# Parameters:
+#
+# source
+# The manifest file template for this application, must be valid JSON with
+# a valid 'url' key matching application_name.
+#
+# base_manifest (optional)
+# A manifest file template to use as a base for |source|. Any properties
+# defined in |source| will overwrite or be merged with properties defined
+# in |base_manifest|.
+#
+# application_name
+# The host portion of the mojo: URL of the application. The script
+# validates that the value of this parameter matches the host name portion
+# of the 'url' property set in the manifest and throws a ValueError if
+# they do not.
+#
+# base_deps (optional)
+# Dependencies required to generate |base_manifest| if applicable.
+#
+# deps (optional)
+# An array of dependent instances of this template. This template enforces
+# that dependencies can only be instances of this template.
+#
+# packaged_applications (optional)
+# An array of application_names of the dependent applications.
+#
+# type (default is mojo)
+# Possible values are 'mojo' and 'exe'. Default is 'mojo'.
+#
+# Outputs:
+#
+# An instantiation of this template produces in
+# $outdir/<application_name>/manifest.json
+# a meta manifest from the source template and the output manifest of all
+# dependent children.
+#
+template("mojo_application_manifest") {
+ assert(defined(invoker.source),
+ "\"source\" must be defined for the $target_name template")
+ assert(defined(invoker.application_name),
+ "\"application_name\" must be defined for the $target_name template")
+ if (defined(invoker.deps)) {
+ assert(defined(invoker.packaged_applications),
+ "\"packaged_applications\" listing the directory containing the " +
+ "manifest.json of dependent applications must be provided.")
+ }
+ if (defined(invoker.packaged_applications)) {
+ assert(defined(invoker.deps),
+ "\"deps\" building the dependent packaged applications must be " +
+ "provided.")
+ }
+ if (defined(invoker.type)) {
+ assert(invoker.type == "mojo" || invoker.type == "exe",
+ "\"type\" must be one of \"mojo\" or \"exe\".")
+ }
+
+ action(target_name) {
+ script = "//mojo/public/tools/manifest/manifest_collator.py"
+
+ type = "mojo"
+ if (defined(invoker.type)) {
+ type = invoker.type
+ }
+
+ application_name = invoker.application_name
+ inputs = [
+ invoker.source,
+ ]
+
+ if (type == "mojo") {
+ output = "$root_out_dir/$mojo_application_subdir/$application_name/manifest.json"
+ } else {
+ output = "$root_out_dir/${application_name}_manifest.json"
+ }
+ outputs = [
+ output,
+ ]
+
+ rebase_parent = rebase_path(invoker.source, root_build_dir)
+ rebase_output = rebase_path(output, root_build_dir)
+
+ args = [
+ "--application-name=$application_name",
+ "--parent=$rebase_parent",
+ "--output=$rebase_output",
+ ]
+
+ if (defined(invoker.base_manifest)) {
+ rebase_base = rebase_path(invoker.base_manifest, root_build_dir)
+ args += [ "--base-manifest=$rebase_base" ]
+ }
+
+ if (defined(invoker.packaged_applications)) {
+ foreach(application_name, invoker.packaged_applications) {
+ input = "$root_out_dir/$mojo_application_subdir/$application_name/manifest.json"
+ inputs += [ input ]
+ args += [ rebase_path(input, root_build_dir) ]
+ }
+ }
+ deps = []
+ data_deps = []
+ if (defined(invoker.deps)) {
+ deps += invoker.deps
+ data_deps += invoker.deps
+ }
+ if (defined(invoker.base_deps)) {
+ deps += invoker.base_deps
+ data_deps += invoker.base_deps
+ }
+ }
+
+ all_deps = []
+ if (defined(invoker.deps)) {
+ all_deps += invoker.deps
+ }
+
+ group("${target_name}__is_mojo_application_manifest") {
+ }
+
+ # Explicitly ensure that all dependencies are mojo_application_manifest
+ # targets themselves.
+ group("${target_name}__check_deps_are_all_mojo_application_manifest") {
+ deps = []
+ foreach(d, all_deps) {
+ name = get_label_info(d, "label_no_toolchain")
+ toolchain = get_label_info(d, "toolchain")
+ deps += [ "${name}__is_mojo_application_manifest(${toolchain})" ]
+ }
+ }
+}
diff --git a/mojo/public/mojo_application_manifest.gypi b/mojo/public/mojo_application_manifest.gypi
new file mode 100644
index 0000000..9b89abb
--- /dev/null
+++ b/mojo/public/mojo_application_manifest.gypi
@@ -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.
+
+{
+ 'variables': {
+ 'variables': {
+ 'application_name%': '<(application_name)',
+ 'application_type%': '<(application_type)',
+ 'base_manifest%': 'none',
+ 'packaged_manifests%': []
+ },
+ 'application_type%': '<(application_type)',
+ 'application_name%': '<(application_name)',
+ 'base_manifest%': '<(base_manifest)',
+ 'manifest_collator_script%':
+ '<(DEPTH)/mojo/public/tools/manifest/manifest_collator.py',
+ 'packaged_manifests%': '<(packaged_manifests)',
+ 'source_manifest%': '<(source_manifest)',
+ 'conditions': [
+ ['application_type=="mojo"', {
+ 'output_manifest%': '<(PRODUCT_DIR)/Mojo Applications/<(application_name)/manifest.json',
+ }, {
+ 'output_manifest%': '<(PRODUCT_DIR)/<(application_name)_manifest.json',
+ }],
+ ['base_manifest!="none"', {
+ 'extra_args%': [
+ '--base-manifest=<(base_manifest)',
+ '<@(packaged_manifests)',
+ ],
+ }, {
+ 'extra_args%': [
+ '<@(packaged_manifests)',
+ ],
+ }]
+ ],
+ },
+ 'actions': [{
+ 'action_name': '<(_target_name)_collation',
+ 'inputs': [
+ '<(manifest_collator_script)',
+ '<(source_manifest)',
+ ],
+ 'outputs': [
+ '<(output_manifest)',
+ ],
+ 'action': [
+ 'python',
+ '<(manifest_collator_script)',
+ '--application-name', '<(application_name)',
+ '--parent=<(source_manifest)',
+ '--output=<(output_manifest)',
+ '<@(extra_args)',
+ ],
+ }],
+}
diff --git a/mojo/public/mojo_constants.gni b/mojo/public/mojo_constants.gni
new file mode 100644
index 0000000..3165a8d
--- /dev/null
+++ b/mojo/public/mojo_constants.gni
@@ -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.
+
+declare_args() {
+ # Mojo application directories are created within this subdirectory.
+ mojo_application_subdir = "Mojo Applications"
+}
diff --git a/mojo/public/tools/bindings/BUILD.gn b/mojo/public/tools/bindings/BUILD.gn
index 153d110..eeea5c5 100644
--- a/mojo/public/tools/bindings/BUILD.gn
+++ b/mojo/public/tools/bindings/BUILD.gn
@@ -16,9 +16,7 @@
"$mojom_generator_root/generators/cpp_templates/interface_request_validator_declaration.tmpl",
"$mojom_generator_root/generators/cpp_templates/interface_response_validator_declaration.tmpl",
"$mojom_generator_root/generators/cpp_templates/interface_stub_declaration.tmpl",
- "$mojom_generator_root/generators/cpp_templates/module-shared-internal.h.tmpl",
- "$mojom_generator_root/generators/cpp_templates/module-shared.cc.tmpl",
- "$mojom_generator_root/generators/cpp_templates/module-shared.h.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/module-internal.h.tmpl",
"$mojom_generator_root/generators/cpp_templates/module.cc.tmpl",
"$mojom_generator_root/generators/cpp_templates/module.h.tmpl",
"$mojom_generator_root/generators/cpp_templates/struct_data_view_declaration.tmpl",
@@ -27,15 +25,11 @@
"$mojom_generator_root/generators/cpp_templates/struct_definition.tmpl",
"$mojom_generator_root/generators/cpp_templates/struct_macros.tmpl",
"$mojom_generator_root/generators/cpp_templates/struct_serialization_declaration.tmpl",
- "$mojom_generator_root/generators/cpp_templates/struct_traits_declaration.tmpl",
- "$mojom_generator_root/generators/cpp_templates/struct_traits_definition.tmpl",
- "$mojom_generator_root/generators/cpp_templates/union_data_view_declaration.tmpl",
- "$mojom_generator_root/generators/cpp_templates/union_data_view_definition.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/struct_serialization_definition.tmpl",
"$mojom_generator_root/generators/cpp_templates/union_declaration.tmpl",
"$mojom_generator_root/generators/cpp_templates/union_definition.tmpl",
"$mojom_generator_root/generators/cpp_templates/union_serialization_declaration.tmpl",
- "$mojom_generator_root/generators/cpp_templates/union_traits_declaration.tmpl",
- "$mojom_generator_root/generators/cpp_templates/union_traits_definition.tmpl",
+ "$mojom_generator_root/generators/cpp_templates/union_serialization_definition.tmpl",
"$mojom_generator_root/generators/cpp_templates/validation_macros.tmpl",
"$mojom_generator_root/generators/cpp_templates/wrapper_class_declaration.tmpl",
"$mojom_generator_root/generators/cpp_templates/wrapper_class_definition.tmpl",
diff --git a/mojo/public/tools/bindings/bindings.gyp b/mojo/public/tools/bindings/bindings.gyp
new file mode 100644
index 0000000..0f00114
--- /dev/null
+++ b/mojo/public/tools/bindings/bindings.gyp
@@ -0,0 +1,83 @@
+# 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.
+
+{
+ 'includes': [
+ '../../../mojom_bindings_generator_variables.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'precompile_mojom_bindings_generator_templates',
+ 'type': 'none',
+ 'actions': [
+ {
+ 'action_name': 'precompile_mojom_bindings_generator_templates',
+ 'inputs': [
+ '<@(mojom_bindings_generator_sources)',
+ 'generators/cpp_templates/enum_macros.tmpl',
+ 'generators/cpp_templates/enum_serialization_declaration.tmpl',
+ 'generators/cpp_templates/interface_declaration.tmpl',
+ 'generators/cpp_templates/interface_definition.tmpl',
+ 'generators/cpp_templates/interface_macros.tmpl',
+ 'generators/cpp_templates/interface_proxy_declaration.tmpl',
+ 'generators/cpp_templates/interface_request_validator_declaration.tmpl',
+ 'generators/cpp_templates/interface_response_validator_declaration.tmpl',
+ 'generators/cpp_templates/interface_stub_declaration.tmpl',
+ 'generators/cpp_templates/module.cc.tmpl',
+ 'generators/cpp_templates/module.h.tmpl',
+ 'generators/cpp_templates/module-internal.h.tmpl',
+ 'generators/cpp_templates/struct_data_view_declaration.tmpl',
+ 'generators/cpp_templates/struct_data_view_definition.tmpl',
+ 'generators/cpp_templates/struct_declaration.tmpl',
+ 'generators/cpp_templates/struct_definition.tmpl',
+ 'generators/cpp_templates/struct_macros.tmpl',
+ 'generators/cpp_templates/struct_serialization_declaration.tmpl',
+ 'generators/cpp_templates/struct_serialization_definition.tmpl',
+ 'generators/cpp_templates/union_declaration.tmpl',
+ 'generators/cpp_templates/union_definition.tmpl',
+ 'generators/cpp_templates/union_serialization_declaration.tmpl',
+ 'generators/cpp_templates/union_serialization_definition.tmpl',
+ 'generators/cpp_templates/validation_macros.tmpl',
+ 'generators/cpp_templates/wrapper_class_declaration.tmpl',
+ 'generators/cpp_templates/wrapper_class_definition.tmpl',
+ 'generators/cpp_templates/wrapper_class_template_definition.tmpl',
+ 'generators/cpp_templates/wrapper_union_class_declaration.tmpl',
+ 'generators/cpp_templates/wrapper_union_class_definition.tmpl',
+ 'generators/cpp_templates/wrapper_union_class_template_definition.tmpl',
+ 'generators/java_templates/constant_definition.tmpl',
+ 'generators/java_templates/constants.java.tmpl',
+ 'generators/java_templates/data_types_definition.tmpl',
+ 'generators/java_templates/enum_definition.tmpl',
+ 'generators/java_templates/enum.java.tmpl',
+ 'generators/java_templates/header.java.tmpl',
+ 'generators/java_templates/interface_definition.tmpl',
+ 'generators/java_templates/interface_internal.java.tmpl',
+ 'generators/java_templates/interface.java.tmpl',
+ 'generators/java_templates/struct.java.tmpl',
+ 'generators/java_templates/union.java.tmpl',
+ 'generators/js_templates/enum_definition.tmpl',
+ 'generators/js_templates/interface_definition.tmpl',
+ 'generators/js_templates/module_definition.tmpl',
+ 'generators/js_templates/module.amd.tmpl',
+ 'generators/js_templates/struct_definition.tmpl',
+ 'generators/js_templates/union_definition.tmpl',
+ 'generators/js_templates/validation_macros.tmpl',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/cpp_templates.zip',
+ '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/java_templates.zip',
+ '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings/js_templates.zip',
+ ],
+ 'action': [
+ 'python', '<@(mojom_bindings_generator)',
+ '--use_bundled_pylibs', 'precompile',
+ '-o', '<(SHARED_INTERMEDIATE_DIR)/mojo/public/tools/bindings',
+ ],
+ }
+ ],
+ 'hard_dependency': 1,
+ },
+ ],
+}
+
diff --git a/mojo/public/tools/bindings/blink_bindings_configuration.gni b/mojo/public/tools/bindings/blink_bindings_configuration.gni
index bb0fc43..ef19cc3 100644
--- a/mojo/public/tools/bindings/blink_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/blink_bindings_configuration.gni
@@ -9,25 +9,21 @@
_typemap_imports = [
"//mojo/public/cpp/bindings/tests/blink_typemaps.gni",
"//third_party/WebKit/Source/platform/mojo/blink_typemaps.gni",
- "//third_party/WebKit/public/blink_typemaps.gni",
- "//third_party/WebKit/public/public_typemaps.gni",
]
_typemaps = []
foreach(typemap_import, _typemap_imports) {
- # Avoid reassignment error by assigning to empty scope first.
- _imported = {
- }
_imported = read_file(typemap_import, "scope")
_typemaps += _imported.typemaps
}
typemaps = []
foreach(typemap, _typemaps) {
- typemaps += [ {
- filename = typemap
- config = read_file(typemap, "scope")
- } ]
+ typemaps += [ read_file(typemap, "scope") ]
}
-blacklist = []
+blacklist = [
+ # TODO(sammc): Remove the following once |for_blink| bindings support WTF
+ # maps with enum keys. See https://crbug.com/583738.
+ "//mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom",
+]
diff --git a/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
index 831157f..44c2ae6 100644
--- a/mojo/public/tools/bindings/chromium_bindings_configuration.gni
+++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni
@@ -3,78 +3,28 @@
# found in the LICENSE file.
_typemap_imports = [
- "//ash/public/interfaces/typemaps.gni",
"//cc/ipc/typemaps.gni",
- "//chrome/browser/media/router/mojo/typemaps.gni",
- "//chrome/common/extensions/typemaps.gni",
- "//chrome/common/importer/typemaps.gni",
- "//chrome/typemaps.gni",
- "//components/arc/common/typemaps.gni",
- "//components/metrics/public/cpp/typemaps.gni",
- "//components/typemaps.gni",
- "//content/common/bluetooth/typemaps.gni",
- "//content/common/indexed_db/typemaps.gni",
- "//content/common/presentation/typemaps.gni",
- "//content/common/typemaps.gni",
- "//content/public/common/typemaps.gni",
"//device/bluetooth/public/interfaces/typemaps.gni",
- "//device/gamepad/public/interfaces/typemaps.gni",
- "//device/generic_sensor/public/interfaces/typemaps.gni",
- "//device/usb/public/interfaces/typemaps.gni",
+ "//components/arc/common/typemaps.gni",
+ "//components/typemaps.gni",
"//gpu/ipc/common/typemaps.gni",
- "//media/capture/mojo/typemaps.gni",
"//media/mojo/interfaces/typemaps.gni",
"//mojo/common/typemaps.gni",
"//mojo/public/cpp/bindings/tests/chromium_typemaps.gni",
- "//net/interfaces/typemaps.gni",
- "//services/preferences/public/cpp/typemaps.gni",
- "//services/resource_coordinator/public/cpp/typemaps.gni",
- "//services/service_manager/public/cpp/typemaps.gni",
- "//services/ui/gpu/interfaces/typemaps.gni",
- "//services/ui/public/interfaces/ime/typemaps.gni",
- "//services/video_capture/public/interfaces/typemaps.gni",
"//skia/public/interfaces/typemaps.gni",
- "//third_party/WebKit/public/public_typemaps.gni",
- "//ui/base/mojo/typemaps.gni",
- "//ui/display/mojo/typemaps.gni",
"//ui/events/devices/mojo/typemaps.gni",
"//ui/events/mojo/typemaps.gni",
"//ui/gfx/typemaps.gni",
- "//ui/message_center/mojo/typemaps.gni",
"//url/mojo/typemaps.gni",
]
-
-_typemap_imports_mac = [ "//content/common/typemaps_mac.gni" ]
-
_typemaps = []
+
foreach(typemap_import, _typemap_imports) {
- # Avoid reassignment error by assigning to empty scope first.
- _imported = {
- }
_imported = read_file(typemap_import, "scope")
_typemaps += _imported.typemaps
}
typemaps = []
foreach(typemap, _typemaps) {
- typemaps += [ {
- filename = typemap
- config = read_file(typemap, "scope")
- } ]
-}
-
-_typemaps_mac = []
-foreach(typemap_import, _typemap_imports_mac) {
- _imported = {
- }
- _imported = read_file(typemap_import, "scope")
- _typemaps_mac += _imported.typemaps
-}
-
-typemaps_mac = []
-foreach(typemap, _typemaps_mac) {
- typemaps_mac += [ {
- filename = typemap
- config = read_file(typemap, "scope")
- } ]
+ typemaps += [ read_file(typemap, "scope") ]
}
diff --git a/mojo/public/tools/bindings/generate_type_mappings.py b/mojo/public/tools/bindings/generate_type_mappings.py
index 824f804..e2fbf31 100755
--- a/mojo/public/tools/bindings/generate_type_mappings.py
+++ b/mojo/public/tools/bindings/generate_type_mappings.py
@@ -97,9 +97,8 @@
mojom_type = match_result.group(1)
native_type = match_result.group(2)
- attributes = []
- if match_result.group(3):
- attributes = match_result.group(3).split(',')
+ # The only attribute supported currently is "move_only".
+ move_only = match_result.group(3) and match_result.group(3) == "move_only"
assert mojom_type not in result, (
"Cannot map multiple native types (%s, %s) to the same mojom type: %s" %
@@ -107,11 +106,7 @@
result[mojom_type] = {
'typename': native_type,
- 'non_copyable_non_movable': 'non_copyable_non_movable' in attributes,
- 'move_only': 'move_only' in attributes,
- 'copyable_pass_by_value': 'copyable_pass_by_value' in attributes,
- 'nullable_is_same_type': 'nullable_is_same_type' in attributes,
- 'hashable': 'hashable' in attributes,
+ 'move_only': move_only,
'public_headers': values['public_headers'],
'traits_headers': values['traits_headers'],
}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl
index f0d503e..0a446a7 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl
@@ -1,55 +1,22 @@
{#---
Macro for enum definition, and the declaration of associated functions.
---#}
-
{%- macro enum_decl(enum) %}
-{%- set enum_name = enum|get_name_for_kind(flatten_nested_kind=True) %}
-enum class {{enum_name}} : int32_t {
-{%- for field in enum.fields %}
-{%- if field.value %}
+enum class {{enum.name}} : int32_t {
+{%- for field in enum.fields %}
+{%- if field.value %}
{{field.name}} = {{field.value|expression_to_text}},
-{%- else %}
+{%- else %}
{{field.name}},
-{%- endif %}
-{%- endfor %}
+{%- endif %}
+{%- endfor %}
};
-
-inline std::ostream& operator<<(std::ostream& os, {{enum_name}} value) {
-{%- if enum.fields %}
- switch(value) {
-{%- for _, values in enum.fields|groupby('numeric_value') %}
- case {{enum_name}}::{{values[0].name}}:
- return os << "{{enum_name}}::
-{%- if values|length > 1 -%}
- {{'{'}}
-{%- endif -%}
- {{values|map(attribute='name')|join(', ')}}
-{%- if values|length > 1 -%}
- {{'}'}}
-{%- endif -%}
- ";
-{%- endfor %}
- default:
- return os << "Unknown {{enum_name}} value: " << static_cast<int32_t>(value);
- }
-{%- else %}
- return os << "Unknown {{enum_name}} value: " << static_cast<int32_t>(value);
-{%- endif %}
-}
-
-{#- Returns true if the given enum value exists in this version of enum. #}
-inline bool IsKnownEnumValue({{enum_name}} value) {
- return {{enum|get_name_for_kind(internal=True,
- flatten_nested_kind=True)}}::IsKnownValue(
- static_cast<int32_t>(value));
-}
{%- endmacro %}
{%- macro enum_data_decl(enum) %}
-{%- set enum_name = enum|get_name_for_kind(flatten_nested_kind=True) %}
-struct {{enum_name}}_Data {
+struct {{enum.name}}_Data {
public:
- static bool constexpr kIsExtensible = {% if enum.extensible %}true{% else %}false{% endif %};
+ static bool const kIsExtensible = {% if enum.extensible %}true{% else %}false{% endif %};
static bool IsKnownValue(int32_t value) {
{%- if enum.fields %}
@@ -75,57 +42,42 @@
};
{%- endmacro %}
-{%- macro enum_hash(enum) %}
-{%- set enum_name = enum|get_qualified_name_for_kind(
- flatten_nested_kind=True) %}
-template <>
-struct hash<{{enum_name}}>
- : public mojo::internal::EnumHashImpl<{{enum_name}}> {};
+{#--- macros for enum-associated functions. Namely:
+ * operator<<(): outputs the given enum value.
+ * IsKnownEnumValue(): returns true if the given enum value exists in this
+ generated version of enum.
+---#}
+
+{%- macro enum_stream_operator(enum) %}
+inline std::ostream& operator<<(std::ostream& os, {{enum|get_name_for_kind}} value) {
+ switch(value) {
+{%- for _, values in enum.fields|groupby('numeric_value') %}
+ case {{enum|get_name_for_kind}}::{{values[0].name}}:
+ return os << "{{enum|get_name_for_kind}}::
+{%- if values|length > 1 -%}
+ {{'{'}}
+{%- endif -%}
+ {{values|map(attribute='name')|join(', ')}}
+{%- if values|length > 1 -%}
+ {{'}'}}
+{%- endif -%}
+ ";
+{%- endfor %}
+ default:
+ return os << "Unknown {{enum|get_name_for_kind}} value: " << static_cast<int32_t>(value);
+ }
+}
{%- endmacro %}
-{%- macro enum_hash_blink(enum) %}
-{%- set enum_name = enum|get_qualified_name_for_kind(
- flatten_nested_kind=True, include_variant=False) %}
-{%- set hash_fn_name = enum|wtf_hash_fn_name_for_enum %}
-{# We need two unused enum values: #}
-{%- set empty_value = -1000000 %}
-{%- set deleted_value = -1000001 %}
-{%- set empty_value_unused = "false" if empty_value in enum|all_enum_values else "true" %}
-{%- set deleted_value_unused = "false" if empty_value in enum|all_enum_values else "true" %}
-namespace WTF {
-struct {{hash_fn_name}} {
- static unsigned hash(const {{enum_name}}& value) {
- typedef base::underlying_type<{{enum_name}}>::type utype;
- return DefaultHash<utype>::Hash().hash(static_cast<utype>(value));
- }
- static bool equal(const {{enum_name}}& left, const {{enum_name}}& right) {
- return left == right;
- }
- static const bool safeToCompareToEmptyOrDeleted = true;
-};
+{%- macro is_known_enum_value(enum) %}
+inline bool IsKnownEnumValue({{enum|get_name_for_kind}} value) {
+ return {{enum|get_qualified_name_for_kind(internal=True)}}::IsKnownValue(
+ static_cast<int32_t>(value));
+}
+{%- endmacro %}
+{%- macro enum_hash(enum) %}
template <>
-struct DefaultHash<{{enum_name}}> {
- using Hash = {{hash_fn_name}};
-};
-
-template <>
-struct HashTraits<{{enum_name}}>
- : public GenericHashTraits<{{enum_name}}> {
- static_assert({{empty_value_unused}},
- "{{empty_value}} is a reserved enum value");
- static_assert({{deleted_value_unused}},
- "{{deleted_value}} is a reserved enum value");
- static const bool hasIsEmptyValueFunction = true;
- static bool isEmptyValue(const {{enum_name}}& value) {
- return value == static_cast<{{enum_name}}>({{empty_value}});
- }
- static void constructDeletedValue({{enum_name}}& slot, bool) {
- slot = static_cast<{{enum_name}}>({{deleted_value}});
- }
- static bool isDeletedValue(const {{enum_name}}& value) {
- return value == static_cast<{{enum_name}}>({{deleted_value}});
- }
-};
-} // namespace WTF
+struct hash<{{enum|get_qualified_name_for_kind}}>
+ : public mojo::internal::EnumHashImpl<{{enum|get_qualified_name_for_kind}}> {};
{%- endmacro %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/enum_serialization_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/enum_serialization_declaration.tmpl
index d7d0e5d..e42128d 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/enum_serialization_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/enum_serialization_declaration.tmpl
@@ -1,5 +1,4 @@
-{%- set mojom_type = enum|get_qualified_name_for_kind(
- flatten_nested_kind=True) %}
+{%- set mojom_type = enum|get_qualified_name_for_kind %}
template <>
struct EnumTraits<{{mojom_type}}, {{mojom_type}}> {
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl
index 7f64974..0249ef3 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl
@@ -1,7 +1,5 @@
{%- import "interface_macros.tmpl" as interface_macros %}
class {{interface.name}}Proxy;
-
-template <typename ImplRefTraits>
class {{interface.name}}Stub;
class {{interface.name}}RequestValidator;
@@ -9,18 +7,15 @@
class {{interface.name}}ResponseValidator;
{%- endif %}
-class {{export_attribute}} {{interface.name}}
- : public {{interface.name}}InterfaceBase {
+class {{interface.name}} {
public:
static const char Name_[];
- static constexpr uint32_t Version_ = {{interface.version}};
- static constexpr bool PassesAssociatedKinds_ = {% if interface|passes_associated_kinds %}true{% else %}false{% endif %};
- static constexpr bool HasSyncMethods_ = {% if interface|has_sync_methods %}true{% else %}false{% endif %};
+ static const uint32_t Version_ = {{interface.version}};
+ static const bool PassesAssociatedKinds_ = {% if interface|passes_associated_kinds %}true{% else %}false{% endif %};
+ static const bool HasSyncMethods_ = {% if interface|has_sync_methods %}true{% else %}false{% endif %};
using Proxy_ = {{interface.name}}Proxy;
-
- template <typename ImplRefTraits>
- using Stub_ = {{interface.name}}Stub<ImplRefTraits>;
+ using Stub_ = {{interface.name}}Stub;
using RequestValidator_ = {{interface.name}}RequestValidator;
{%- if interface|has_callbacks %}
@@ -29,21 +24,23 @@
using ResponseValidator_ = mojo::PassThroughFilter;
{%- endif %}
-{#--- Metadata #}
- enum MethodMinVersions : uint32_t {
-{%- for method in interface.methods %}
- k{{method.name}}MinVersion = {{method.min_version|default(0, true)}},
-{%- endfor %}
- };
-
{#--- Enums #}
+{% from "enum_macros.tmpl" import enum_decl -%}
{%- for enum in interface.enums %}
- using {{enum.name}} = {{enum|get_name_for_kind(flatten_nested_kind=True)}};
+{%- if enum|is_native_only_kind %}
+ using {{enum.name}} = mojo::NativeEnum;
+{%- else %}
+ {{enum_decl(enum)|indent(2)}}
+{%- endif %}
{%- endfor %}
{#--- Constants #}
{%- for constant in interface.constants %}
- static {{constant|format_constant_declaration(nested=True)}};
+{%- if constant.kind|is_integral_kind %}
+ static const {{constant.kind|cpp_pod_type}} {{constant.name}} = {{constant|constant_value}};
+{%- else %}
+ static const {{constant.kind|cpp_pod_type}} {{constant.name}};
+{%- endif %}
{%- endfor %}
{#--- Methods #}
@@ -58,8 +55,8 @@
{%- endif %}
using {{method.name}}Callback = {{interface_macros.declare_callback(method,
- for_blink, use_once_callback)}};
+ for_blink, use_new_wrapper_types)}};
{%- endif %}
- virtual void {{method.name}}({{interface_macros.declare_request_params("", method, use_once_callback)}}) = 0;
+ virtual void {{method.name}}({{interface_macros.declare_request_params("", method)}}) = 0;
{%- endfor %}
};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
index a23b107..4017fc5 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl
@@ -5,22 +5,24 @@
{%- set proxy_name = interface.name ~ "Proxy" %}
{%- set namespace_as_string = "%s"|format(namespace|replace(".","::")) %}
-{%- macro alloc_params(struct, params, message, description) %}
- mojo::internal::SerializationContext serialization_context;
- serialization_context.handles.Swap(({{message}})->mutable_handles());
- serialization_context.associated_endpoint_handles.swap(
- *({{message}})->mutable_associated_endpoint_handles());
+{%- macro alloc_params(struct, params, message, serialization_context,
+ description) %}
+ ({{serialization_context}})->handles.Swap(({{message}})->mutable_handles());
bool success = true;
{%- for param in struct.packed.packed_fields_in_ordinal_order %}
{{param.field.kind|cpp_wrapper_type}} p_{{param.field.name}}{};
{%- endfor %}
- {{struct.name}}DataView input_data_view({{params}}, &serialization_context);
+ {{struct.name}}DataView input_data_view({{params}},
+ {{serialization_context}});
{{struct_macros.deserialize(struct, "input_data_view", "p_%s", "success")}}
if (!success) {
- ReportValidationErrorForMessage(
- {{message}},
- mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED,
+ mojo::internal::ValidationContext validation_context(
+ {{message}}->data(), {{message}}->data_num_bytes(),
+ {{message}}->handles()->size(), {{message}},
"{{description}} deserializer");
+ ReportValidationError(
+ &validation_context,
+ mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED);
return false;
}
{%- endmacro %}
@@ -39,17 +41,18 @@
serialization_context)}}
({{serialization_context}})->handles.Swap(
builder.message()->mutable_handles());
- ({{serialization_context}})->associated_endpoint_handles.swap(
- *builder.message()->mutable_associated_endpoint_handles());
{%- endmacro %}
{#--- Begin #}
const char {{class_name}}::Name_[] = "{{namespace_as_string}}::{{class_name}}";
+const uint32_t {{class_name}}::Version_;
{#--- Constants #}
{%- for constant in interface.constants %}
-{%- if constant.kind|is_string_kind %}
-const char {{interface.name}}::{{constant.name}}[] = {{constant|constant_value}};
+{%- if constant.kind|is_integral_kind %}
+const {{constant.kind|cpp_pod_type}} {{interface.name}}::{{constant.name}};
+{%- else %}
+const {{constant.kind|cpp_pod_type}} {{interface.name}}::{{constant.name}} = {{constant|constant_value}};
{%- endif %}
{%- endfor %}
@@ -71,11 +74,12 @@
: public mojo::MessageReceiver {
public:
{{class_name}}_{{method.name}}_HandleSyncResponse(
+ scoped_refptr<mojo::AssociatedGroupController> group_controller,
bool* result
{%- for param in method.response_parameters -%}
, {{param.kind|cpp_wrapper_type}}* out_{{param.name}}
{%- endfor %})
- : result_(result)
+ : serialization_context_(std::move(group_controller)), result_(result)
{%- for param in method.response_parameters -%}
, out_{{param.name}}_(out_{{param.name}})
{%- endfor %} {
@@ -83,6 +87,7 @@
}
bool Accept(mojo::Message* message) override;
private:
+ mojo::internal::SerializationContext serialization_context_;
bool* result_;
{%- for param in method.response_parameters %}
{{param.kind|cpp_wrapper_type}}* out_{{param.name}}_;
@@ -95,14 +100,13 @@
reinterpret_cast<internal::{{class_name}}_{{method.name}}_ResponseParams_Data*>(
message->mutable_payload());
-{%- set desc = class_name~"::"~method.name~" response" %}
- {{alloc_params(method.response_param_struct, "params", "message", desc)}}
+ {{alloc_params(method.response_param_struct, "params", "message",
+ "&serialization_context_",
+ "{{class_name}}::{{method.name}} response")}}
{%- for param in method.response_parameters %}
*out_{{param.name}}_ = std::move(p_{{param.name}});
{%- endfor %}
- mojo::internal::SyncMessageResponseSetup::SetCurrentSyncResponseMessage(
- message);
*result_ = true;
return true;
}
@@ -112,16 +116,15 @@
: public mojo::MessageReceiver {
public:
{{class_name}}_{{method.name}}_ForwardToCallback(
-{%- if use_once_callback %}
- {{class_name}}::{{method.name}}Callback callback
-{%- else %}
- const {{class_name}}::{{method.name}}Callback& callback
-{%- endif %}
- ) : callback_(std::move(callback)) {
+ const {{class_name}}::{{method.name}}Callback& callback,
+ scoped_refptr<mojo::AssociatedGroupController> group_controller)
+ : callback_(callback),
+ serialization_context_(std::move(group_controller)) {
}
bool Accept(mojo::Message* message) override;
private:
{{class_name}}::{{method.name}}Callback callback_;
+ mojo::internal::SerializationContext serialization_context_;
DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ForwardToCallback);
};
bool {{class_name}}_{{method.name}}_ForwardToCallback::Accept(
@@ -130,19 +133,18 @@
reinterpret_cast<internal::{{class_name}}_{{method.name}}_ResponseParams_Data*>(
message->mutable_payload());
-{%- set desc = class_name~"::"~method.name~" response" %}
- {{alloc_params(method.response_param_struct, "params", "message", desc)}}
- if (!callback_.is_null()) {
- mojo::internal::MessageDispatchContext context(message);
- std::move(callback_).Run({{pass_params(method.response_parameters)}});
- }
+ {{alloc_params(method.response_param_struct, "params", "message",
+ "&serialization_context_",
+ "{{class_name}}_{{method.name}} response")}}
+ if (!callback_.is_null())
+ callback_.Run({{pass_params(method.response_parameters)}});
return true;
}
{%- endif %}
{%- endfor %}
{{proxy_name}}::{{proxy_name}}(mojo::MessageReceiverWithResponder* receiver)
- : receiver_(receiver) {
+ : ControlMessageProxy(receiver) {
}
{#--- Proxy definitions #}
@@ -156,22 +158,19 @@
{%- if method.sync %}
bool {{proxy_name}}::{{method.name}}(
{{interface_macros.declare_sync_method_params("param_", method)}}) {
- mojo::internal::SerializationContext serialization_context;
{{struct_macros.get_serialized_size(params_struct, "param_%s",
- "&serialization_context")}}
+ "&serialization_context_")}}
- mojo::internal::MessageBuilder builder(
- {{message_name}},
- mojo::Message::kFlagIsSync | mojo::Message::kFlagExpectsResponse,
- size, serialization_context.associated_endpoint_count);
+ mojo::internal::RequestMessageBuilder builder({{message_name}}, size,
+ mojo::Message::kFlagIsSync);
{{build_message(params_struct, "param_%s", params_description,
- "&serialization_context")}}
+ "&serialization_context_")}}
bool result = false;
mojo::MessageReceiver* responder =
new {{class_name}}_{{method.name}}_HandleSyncResponse(
- &result
+ serialization_context_.group_controller, &result
{%- for param in method.response_parameters -%}
, param_{{param.name}}
{%- endfor %});
@@ -182,26 +181,23 @@
{%- endif %}
void {{proxy_name}}::{{method.name}}(
- {{interface_macros.declare_request_params("in_", method, use_once_callback)}}) {
- mojo::internal::SerializationContext serialization_context;
+ {{interface_macros.declare_request_params("in_", method)}}) {
{{struct_macros.get_serialized_size(params_struct, "in_%s",
- "&serialization_context")}}
+ "&serialization_context_")}}
{%- if method.response_parameters != None %}
- constexpr uint32_t kFlags = mojo::Message::kFlagExpectsResponse;
+ mojo::internal::RequestMessageBuilder builder({{message_name}}, size);
{%- else %}
- constexpr uint32_t kFlags = 0;
+ mojo::internal::MessageBuilder builder({{message_name}}, size);
{%- endif %}
- mojo::internal::MessageBuilder builder(
- {{message_name}}, kFlags, size,
- serialization_context.associated_endpoint_count);
{{build_message(params_struct, "in_%s", params_description,
- "&serialization_context")}}
+ "&serialization_context_")}}
{%- if method.response_parameters != None %}
mojo::MessageReceiver* responder =
- new {{class_name}}_{{method.name}}_ForwardToCallback(std::move(callback));
+ new {{class_name}}_{{method.name}}_ForwardToCallback(
+ callback, serialization_context_.group_controller);
if (!receiver_->AcceptWithResponder(builder.message(), responder))
delete responder;
{%- else %}
@@ -221,23 +217,42 @@
{%- set response_params_struct = method.response_param_struct %}
{%- set params_description =
"%s.%s response"|format(interface.name, method.name) %}
-class {{class_name}}_{{method.name}}_ProxyToResponder {
+class {{class_name}}_{{method.name}}_ProxyToResponder
+ : public base::RefCountedThreadSafe<
+ {{class_name}}_{{method.name}}_ProxyToResponder> {
public:
static {{class_name}}::{{method.name}}Callback CreateCallback(
uint64_t request_id,
bool is_sync,
- mojo::MessageReceiverWithStatus* responder) {
- std::unique_ptr<{{class_name}}_{{method.name}}_ProxyToResponder> proxy(
- new {{class_name}}_{{method.name}}_ProxyToResponder(
- request_id, is_sync, responder));
+ mojo::MessageReceiverWithStatus* responder,
+ scoped_refptr<mojo::AssociatedGroupController>
+ group_controller) {
+ scoped_refptr<{{class_name}}_{{method.name}}_ProxyToResponder> proxy
+ = new {{class_name}}_{{method.name}}_ProxyToResponder(
+ request_id, is_sync, responder, group_controller);
return base::Bind(&{{class_name}}_{{method.name}}_ProxyToResponder::Run,
- base::Passed(&proxy));
+ proxy);
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<
+ {{class_name}}_{{method.name}}_ProxyToResponder>;
+
+ {{class_name}}_{{method.name}}_ProxyToResponder(
+ uint64_t request_id,
+ bool is_sync,
+ mojo::MessageReceiverWithStatus* responder,
+ scoped_refptr<mojo::AssociatedGroupController> group_controller)
+ : request_id_(request_id),
+ is_sync_(is_sync),
+ responder_(responder),
+ serialization_context_(std::move(group_controller)) {
}
~{{class_name}}_{{method.name}}_ProxyToResponder() {
#if DCHECK_IS_ON()
if (responder_) {
- // Is the Service destroying the callback without running it
+ // Is the Mojo application destroying the callback without running it
// and without first closing the pipe?
responder_->DCheckInvalid("The callback passed to "
"{{class_name}}::{{method.name}}() was never run.");
@@ -248,43 +263,31 @@
delete responder_;
}
- private:
- {{class_name}}_{{method.name}}_ProxyToResponder(
- uint64_t request_id,
- bool is_sync,
- mojo::MessageReceiverWithStatus* responder)
- : request_id_(request_id),
- is_sync_(is_sync),
- responder_(responder) {
- }
-
void Run(
{{interface_macros.declare_responder_params(
- "in_", method.response_parameters, for_blink)}});
+ "in_", method.response_parameters, for_blink,
+ use_new_wrapper_types)}});
uint64_t request_id_;
bool is_sync_;
mojo::MessageReceiverWithStatus* responder_;
+ // TODO(yzshen): maybe I should use a ref to the original one?
+ mojo::internal::SerializationContext serialization_context_;
DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ProxyToResponder);
};
void {{class_name}}_{{method.name}}_ProxyToResponder::Run(
{{interface_macros.declare_responder_params(
- "in_", method.response_parameters, for_blink)}}) {
- mojo::internal::SerializationContext serialization_context;
+ "in_", method.response_parameters, for_blink,
+ use_new_wrapper_types)}}) {
{{struct_macros.get_serialized_size(response_params_struct, "in_%s",
- "&serialization_context")}}
-
- uint32_t flags = (is_sync_ ? mojo::Message::kFlagIsSync : 0) |
- mojo::Message::kFlagIsResponse;
- mojo::internal::MessageBuilder builder(
- {{message_name}}, flags, size,
- serialization_context.associated_endpoint_count);
- builder.message()->set_request_id(request_id_);
-
+ "&serialization_context_")}}
+ mojo::internal::ResponseMessageBuilder builder(
+ {{message_name}}, size, request_id_,
+ is_sync_ ? mojo::Message::kFlagIsSync : 0);
{{build_message(response_params_struct, "in_%s", params_description,
- "&serialization_context")}}
+ "&serialization_context_")}}
bool ok = responder_->Accept(builder.message());
ALLOW_UNUSED_LOCAL(ok);
// TODO(darin): !ok returned here indicates a malformed message, and that may
@@ -296,12 +299,18 @@
{%- endif -%}
{%- endfor %}
-{#--- StubDispatch definition #}
+{{class_name}}Stub::{{class_name}}Stub()
+ : sink_(nullptr),
+ control_message_handler_({{interface.name}}::Version_) {
+}
-// static
-bool {{class_name}}StubDispatch::Accept(
- {{interface.name}}* impl,
- mojo::Message* message) {
+{{class_name}}Stub::~{{interface.name}}Stub() {}
+
+{#--- Stub definition #}
+
+bool {{class_name}}Stub::Accept(mojo::Message* message) {
+ if (mojo::internal::ControlMessageHandler::IsControlMessage(message))
+ return control_message_handler_.Accept(message);
{%- if interface.methods %}
switch (message->header()->name) {
{%- for method in interface.methods %}
@@ -311,14 +320,13 @@
reinterpret_cast<internal::{{class_name}}_{{method.name}}_Params_Data*>(
message->mutable_payload());
-{%- set desc = class_name~"::"~method.name %}
- {{alloc_params(method.param_struct, "params", "message", desc)|
- indent(4)}}
- // A null |impl| means no implementation was bound.
- assert(impl);
+ {{alloc_params(method.param_struct, "params", "message",
+ "&serialization_context_", "{{class_name}}::{{method.name}}")
+ |indent(4)}}
+ // A null |sink_| means no implementation was bound.
+ assert(sink_);
TRACE_EVENT0("mojom", "{{class_name}}::{{method.name}}");
- mojo::internal::MessageDispatchContext context(message);
- impl->{{method.name}}({{pass_params(method.parameters)}});
+ sink_->{{method.name}}({{pass_params(method.parameters)}});
return true;
{%- else %}
break;
@@ -330,11 +338,10 @@
return false;
}
-// static
-bool {{class_name}}StubDispatch::AcceptWithResponder(
- {{interface.name}}* impl,
- mojo::Message* message,
- mojo::MessageReceiverWithStatus* responder) {
+bool {{class_name}}Stub::AcceptWithResponder(
+ mojo::Message* message, mojo::MessageReceiverWithStatus* responder) {
+ if (mojo::internal::ControlMessageHandler::IsControlMessage(message))
+ return control_message_handler_.AcceptWithResponder(message, responder);
{%- if interface.methods %}
switch (message->header()->name) {
{%- for method in interface.methods %}
@@ -344,19 +351,20 @@
reinterpret_cast<internal::{{class_name}}_{{method.name}}_Params_Data*>(
message->mutable_payload());
-{%- set desc = class_name~"::"~method.name %}
- {{alloc_params(method.param_struct, "params", "message", desc)|
- indent(4)}}
+ {{alloc_params(method.param_struct, "params", "message",
+ "&serialization_context_", "{{class_name}}::{{method.name}}")|
+ indent(4)}}
{{class_name}}::{{method.name}}Callback callback =
{{class_name}}_{{method.name}}_ProxyToResponder::CreateCallback(
message->request_id(),
- message->has_flag(mojo::Message::kFlagIsSync), responder);
- // A null |impl| means no implementation was bound.
- assert(impl);
+ message->has_flag(mojo::Message::kFlagIsSync),
+ responder,
+ serialization_context_.group_controller);
+ // A null |sink_| means no implementation was bound.
+ assert(sink_);
TRACE_EVENT0("mojom", "{{class_name}}::{{method.name}}");
- mojo::internal::MessageDispatchContext context(message);
- impl->{{method.name}}(
-{%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}std::move(callback));
+ sink_->{{method.name}}(
+{%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}callback);
return true;
{%- else %}
break;
@@ -370,14 +378,22 @@
{#--- Request validator definitions #}
+{{class_name}}RequestValidator::{{class_name}}RequestValidator(
+ mojo::MessageReceiver* sink) : MessageFilter(sink) {
+}
+
bool {{class_name}}RequestValidator::Accept(mojo::Message* message) {
- if (mojo::internal::ControlMessageHandler::IsControlMessage(message))
- return true;
+ assert(sink_);
mojo::internal::ValidationContext validation_context(
- message->payload(), message->payload_num_bytes(),
- message->handles()->size(), message->payload_num_interface_ids(), message,
- "{{class_name}} RequestValidator");
+ message->data(), message->data_num_bytes(), message->handles()->size(),
+ message, "{{class_name}} RequestValidator");
+
+ if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) {
+ if (!mojo::internal::ValidateControlRequest(message, &validation_context))
+ return false;
+ return sink_->Accept(message);
+ }
switch (message->header()->name) {
{%- for method in interface.methods %}
@@ -398,7 +414,7 @@
message, &validation_context)) {
return false;
}
- return true;
+ return sink_->Accept(message);
}
{%- endfor %}
default:
@@ -414,14 +430,22 @@
{#--- Response validator definitions #}
{% if interface|has_callbacks %}
+{{class_name}}ResponseValidator::{{class_name}}ResponseValidator(
+ mojo::MessageReceiver* sink) : MessageFilter(sink) {
+}
+
bool {{class_name}}ResponseValidator::Accept(mojo::Message* message) {
- if (mojo::internal::ControlMessageHandler::IsControlMessage(message))
- return true;
+ assert(sink_);
mojo::internal::ValidationContext validation_context(
- message->payload(), message->payload_num_bytes(),
- message->handles()->size(), message->payload_num_interface_ids(), message,
- "{{class_name}} ResponseValidator");
+ message->data(), message->data_num_bytes(), message->handles()->size(),
+ message, "{{class_name}} ResponseValidator");
+
+ if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) {
+ if (!mojo::internal::ValidateControlResponse(message, &validation_context))
+ return false;
+ return sink_->Accept(message);
+ }
if (!mojo::internal::ValidateMessageIsResponse(message, &validation_context))
return false;
@@ -433,7 +457,7 @@
message, &validation_context)) {
return false;
}
- return true;
+ return sink_->Accept(message);
}
{%- endfor %}
default:
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl
index 8649273..4bec4c6 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl
@@ -5,35 +5,39 @@
{%- endfor %}
{%- endmacro %}
-{%- macro declare_responder_params(prefix, parameters, for_blink) %}
+{%- macro declare_responder_params(prefix, parameters, for_blink, use_new_wrapper_types) %}
{%- for param in parameters -%}
+{%- if (not param.kind|is_string_kind) or for_blink or
+ use_new_wrapper_types -%}
{{param.kind|cpp_wrapper_param_type}} {{prefix}}{{param.name}}
+{%- else %}
+mojo::String {{prefix}}{{param.name}}
+{%- endif %}
{%- if not loop.last %}, {% endif %}
{%- endfor %}
{%- endmacro %}
-{%- macro declare_callback(method, for_blink, use_once_callback) -%}
-{%- if use_once_callback -%}
-base::OnceCallback<void(
-{%- else -%}
+{%- macro declare_callback(method, for_blink, use_new_wrapper_types) -%}
base::Callback<void(
-{%- endif -%}
{%- for param in method.response_parameters -%}
+{#- TODO(yzshen): For historical reasons, we use mojo::String here (instead of
+ const mojo::String&) inconsistently. Preserve the behavior temporarily. #}
+{%- if (not param.kind|is_string_kind) or for_blink or
+ use_new_wrapper_types -%}
{{param.kind|cpp_wrapper_param_type}}
+{%- else -%}
+mojo::String
+{%- endif %}
{%- if not loop.last %}, {% endif %}
{%- endfor -%}
)>
{%- endmacro -%}
-{%- macro declare_request_params(prefix, method, use_once_callback) -%}
+{%- macro declare_request_params(prefix, method) -%}
{{declare_params(prefix, method.parameters)}}
{%- if method.response_parameters != None -%}
-{%- if method.parameters %}, {% endif -%}
-{%- if use_once_callback -%}
-{{method.name}}Callback callback
-{%- else -%}
+{%- if method.parameters %}, {% endif -%}
const {{method.name}}Callback& callback
-{%- endif -%}
{%- endif -%}
{%- endmacro -%}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl
index 0a158ec..477116b 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl
@@ -1,6 +1,7 @@
{%- import "interface_macros.tmpl" as interface_macros %}
-class {{export_attribute}} {{interface.name}}Proxy
- : public {{interface.name}} {
+class {{interface.name}}Proxy
+ : public {{interface.name}},
+ public mojo::internal::ControlMessageProxy {
public:
explicit {{interface.name}}Proxy(mojo::MessageReceiverWithResponder* receiver);
@@ -8,9 +9,13 @@
{%- if method.sync %}
bool {{method.name}}({{interface_macros.declare_sync_method_params("", method)}}) override;
{%- endif %}
- void {{method.name}}({{interface_macros.declare_request_params("", method, use_once_callback)}}) override;
+ void {{method.name}}({{interface_macros.declare_request_params("", method)}}) override;
{%- endfor %}
+ mojo::internal::SerializationContext* serialization_context() {
+ return &serialization_context_;
+ }
+
private:
- mojo::MessageReceiverWithResponder* receiver_;
+ mojo::internal::SerializationContext serialization_context_;
};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl
index a00d148..29917ea 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl
@@ -1,4 +1,6 @@
-class {{export_attribute}} {{interface.name}}RequestValidator : public NON_EXPORTED_BASE(mojo::MessageReceiver) {
+class {{interface.name}}RequestValidator : public mojo::MessageFilter {
public:
+ explicit {{interface.name}}RequestValidator(mojo::MessageReceiver* sink = nullptr);
+
bool Accept(mojo::Message* message) override;
};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl
index e2caa02..5893bfd 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl
@@ -1,4 +1,6 @@
-class {{export_attribute}} {{interface.name}}ResponseValidator : public NON_EXPORTED_BASE(mojo::MessageReceiver) {
+class {{interface.name}}ResponseValidator : public mojo::MessageFilter {
public:
+ explicit {{interface.name}}ResponseValidator(mojo::MessageReceiver* sink = nullptr);
+
bool Accept(mojo::Message* message) override;
};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl
index 9f01348..30b5de7 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl
@@ -1,40 +1,19 @@
-class {{export_attribute}} {{interface.name}}StubDispatch {
+class {{interface.name}}Stub : public mojo::MessageReceiverWithResponderStatus {
public:
- static bool Accept({{interface.name}}* impl, mojo::Message* message);
- static bool AcceptWithResponder({{interface.name}}* impl,
- mojo::Message* message,
- mojo::MessageReceiverWithStatus* responder);
-};
-
-template <typename ImplRefTraits =
- mojo::RawPtrImplRefTraits<{{interface.name}}>>
-class {{interface.name}}Stub
- : public NON_EXPORTED_BASE(mojo::MessageReceiverWithResponderStatus) {
- public:
- using ImplPointerType = typename ImplRefTraits::PointerType;
-
- {{interface.name}}Stub() {}
- ~{{interface.name}}Stub() override {}
-
- void set_sink(ImplPointerType sink) { sink_ = std::move(sink); }
- ImplPointerType& sink() { return sink_; }
-
- bool Accept(mojo::Message* message) override {
- if (ImplRefTraits::IsNull(sink_))
- return false;
- return {{interface.name}}StubDispatch::Accept(
- ImplRefTraits::GetRawPointer(&sink_), message);
+ {{interface.name}}Stub();
+ ~{{interface.name}}Stub() override;
+ void set_sink({{interface.name}}* sink) { sink_ = sink; }
+ {{interface.name}}* sink() { return sink_; }
+ mojo::internal::SerializationContext* serialization_context() {
+ return &serialization_context_;
}
- bool AcceptWithResponder(
- mojo::Message* message,
- mojo::MessageReceiverWithStatus* responder) override {
- if (ImplRefTraits::IsNull(sink_))
- return false;
- return {{interface.name}}StubDispatch::AcceptWithResponder(
- ImplRefTraits::GetRawPointer(&sink_), message, responder);
- }
+ bool Accept(mojo::Message* message) override;
+ bool AcceptWithResponder(mojo::Message* message,
+ mojo::MessageReceiverWithStatus* responder) override;
private:
- ImplPointerType sink_;
+ {{interface.name}}* sink_;
+ mojo::internal::SerializationContext serialization_context_;
+ mojo::internal::ControlMessageHandler control_message_handler_;
};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl
new file mode 100644
index 0000000..5256e75
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl
@@ -0,0 +1,126 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+{%- if variant -%}
+{%- set variant_path = "%s-%s"|format(module.path, variant) -%}
+{%- else -%}
+{%- set variant_path = module.path -%}
+{%- endif -%}
+
+{%- set header_guard = "%s_INTERNAL_H_"|format(
+ variant_path|upper|replace("/","_")|replace(".","_")|
+ replace("-", "_")) %}
+
+#ifndef {{header_guard}}
+#define {{header_guard}}
+
+#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
+#include "mojo/public/cpp/bindings/lib/buffer.h"
+#include "mojo/public/cpp/bindings/lib/serialization.h"
+#include "mojo/public/cpp/bindings/lib/union_accessor.h"
+#include "mojo/public/cpp/bindings/struct_ptr.h"
+
+{%- for import in imports %}
+{%- if variant %}
+#include "{{"%s-%s-internal.h"|format(import.module.path, variant)}}"
+{%- else %}
+#include "{{import.module.path}}-internal.h"
+{%- endif %}
+{%- endfor %}
+
+namespace mojo {
+namespace internal {
+class ValidationContext;
+}
+}
+
+{%- for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%- endfor %}
+{%- if variant %}
+namespace {{variant}} {
+{%- endif %}
+
+{#--- Wrapper forward declarations #}
+{% for struct in structs %}
+{%- if struct|is_native_only_kind %}
+using {{struct.name}} = mojo::NativeStruct;
+{%- else %}
+class {{struct.name}};
+{%- endif %}
+{%- endfor %}
+
+{#--- Wrapper forward declarations for unions #}
+{% for union in unions %}
+class {{union.name}};
+{%- endfor %}
+
+namespace internal {
+
+{#--- Internal forward declarations #}
+{% for struct in structs %}
+{%- if struct|is_native_only_kind %}
+using {{struct.name}}_Data = mojo::internal::NativeStruct_Data;
+{%- else %}
+class {{struct.name}}_Data;
+{%- endif %}
+{%- endfor %}
+
+{% for union in unions %}
+class {{union.name}}_Data;
+{%- endfor %}
+
+{#--- Enums #}
+{% from "enum_macros.tmpl" import enum_data_decl -%}
+{%- for enum in enums %}
+{%- if enum|is_native_only_kind %}
+ using {{enum.name}}_Data = mojo::internal::NativeEnum_Data;
+{%- else %}
+ {{enum_data_decl(enum)}}
+{%- endif %}
+{%- endfor %}
+
+#pragma pack(push, 1)
+
+{#--- Unions must be declared first because they can be members of structs #}
+{#--- Union class declarations #}
+{% for union in unions %}
+{% include "union_declaration.tmpl" %}
+{%- endfor %}
+
+{#--- Struct class declarations #}
+{% for struct in structs %}
+{%- if not struct|is_native_only_kind %}
+{% include "struct_declaration.tmpl" %}
+{%- endif %}
+{%- endfor %}
+
+{#--- Interface class declarations. They are needed only when they contain
+ enums. #}
+{%- for interface in interfaces %}
+{%- if interface.enums %}
+class {{interface.name}}_Data {
+ public:
+{%- for enum in interface.enums %}
+{%- if enum|is_native_only_kind %}
+ using {{enum.name}}_Data = mojo::internal::NativeEnum_Data;
+{%- else %}
+ {{enum_data_decl(enum)|indent(2)}}
+{%- endif %}
+{%- endfor %}
+};
+{%- endif %}
+{%- endfor %}
+
+#pragma pack(pop)
+
+} // namespace internal
+{%- if variant %}
+} // namespace {{variant}}
+{%- endif %}
+{%- for namespace in namespaces_as_array|reverse %}
+} // namespace {{namespace}}
+{%- endfor %}
+
+#endif // {{header_guard}}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl
deleted file mode 100644
index 964b254..0000000
--- a/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl
+++ /dev/null
@@ -1,96 +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.
-
-{%- set header_guard = "%s_SHARED_INTERNAL_H_"|format(
- module.path|upper|replace("/","_")|replace(".","_")|
- replace("-", "_")) %}
-
-#ifndef {{header_guard}}
-#define {{header_guard}}
-
-#include <stdint.h>
-
-#include "mojo/public/cpp/bindings/lib/array_internal.h"
-#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
-#include "mojo/public/cpp/bindings/lib/map_data_internal.h"
-#include "mojo/public/cpp/bindings/lib/native_enum_data.h"
-#include "mojo/public/cpp/bindings/lib/native_struct_data.h"
-#include "mojo/public/cpp/bindings/lib/buffer.h"
-
-{%- for import in imports %}
-#include "{{import.module.path}}-shared-internal.h"
-{%- endfor %}
-
-namespace mojo {
-namespace internal {
-class ValidationContext;
-}
-}
-
-{%- for namespace in namespaces_as_array %}
-namespace {{namespace}} {
-{%- endfor %}
-namespace internal {
-
-{#--- Internal forward declarations #}
-{%- for struct in structs %}
-{%- if struct|is_native_only_kind %}
-using {{struct.name}}_Data = mojo::internal::NativeStruct_Data;
-{%- else %}
-class {{struct.name}}_Data;
-{%- endif %}
-{%- endfor %}
-
-{%- for union in unions %}
-class {{union.name}}_Data;
-{%- endfor %}
-
-{#--- Enums #}
-{%- from "enum_macros.tmpl" import enum_data_decl -%}
-{%- for enum in all_enums %}
-{%- if enum|is_native_only_kind %}
-using {{enum|get_name_for_kind(flatten_nested_kind=True)}}_Data =
- mojo::internal::NativeEnum_Data;
-{%- else %}
-{{enum_data_decl(enum)}}
-{%- endif %}
-{%- endfor %}
-
-#pragma pack(push, 1)
-
-{#--- Unions must be declared first because they can be members of structs #}
-{#--- Union class declarations #}
-{%- for union in unions %}
-{% include "union_declaration.tmpl" %}
-{%- endfor %}
-
-{#--- Struct class declarations #}
-{%- for struct in structs %}
-{%- if not struct|is_native_only_kind %}
-{% include "struct_declaration.tmpl" %}
-{%- endif %}
-{%- endfor %}
-
-{#--- Interface parameter definitions #}
-{%- for interface in interfaces %}
-{%- for method in interface.methods %}
-{%- set method_name = "k%s_%s_Name"|format(interface.name, method.name) %}
-constexpr uint32_t {{method_name}} = {{method.ordinal}};
-{%- set struct = method.param_struct %}
-{% include "struct_declaration.tmpl" %}
-{%- if method.response_parameters != None %}
-{%- set struct = method.response_param_struct %}
-{% include "struct_declaration.tmpl" %}
-{%- endif %}
-{%- endfor %}
-{%- endfor %}
-
-#pragma pack(pop)
-
-} // namespace internal
-{%- for namespace in namespaces_as_array|reverse %}
-} // namespace {{namespace}}
-{%- endfor %}
-
-#endif // {{header_guard}}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-shared.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-shared.cc.tmpl
deleted file mode 100644
index 645bb69..0000000
--- a/mojo/public/tools/bindings/generators/cpp_templates/module-shared.cc.tmpl
+++ /dev/null
@@ -1,64 +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.
-
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable:4065)
-#endif
-
-#include "{{module.path}}-shared.h"
-
-#include <utility>
-
-#include "base/logging.h"
-#include "mojo/public/cpp/bindings/lib/validate_params.h"
-#include "mojo/public/cpp/bindings/lib/validation_context.h"
-#include "mojo/public/cpp/bindings/lib/validation_errors.h"
-#include "mojo/public/cpp/bindings/lib/validation_util.h"
-
-{%- for header in extra_traits_headers %}
-#include "{{header}}"
-{%- endfor %}
-
-{%- for namespace in namespaces_as_array %}
-namespace {{namespace}} {
-{%- endfor %}
-
-namespace internal {
-
-{#--- Union definitions #}
-{%- for union in unions %}
-{% include "union_definition.tmpl" %}
-{%- endfor %}
-
-{#--- Struct definitions #}
-{%- for struct in structs %}
-{%- if not struct|is_native_only_kind %}
-{% include "struct_definition.tmpl" %}
-{%- endif %}
-{%- endfor %}
-
-{#--- Interface parameter definitions #}
-{%- for interface in interfaces %}
-{%- for method in interface.methods %}
-{%- set method_name = "k%s_%s_Name"|format(interface.name, method.name) %}
-{%- set struct = method.param_struct %}
-{% include "struct_definition.tmpl" %}
-{%- if method.response_parameters != None %}
-{%- set struct = method.response_param_struct %}
-{% include "struct_definition.tmpl" %}
-{%- endif %}
-{%- endfor %}
-{%- endfor %}
-
-} // namespace internal
-
-{%- for namespace in namespaces_as_array|reverse %}
-} // namespace {{namespace}}
-{%- endfor %}
-
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl
deleted file mode 100644
index dd13466..0000000
--- a/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl
+++ /dev/null
@@ -1,212 +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.
-
-{%- set header_guard = "%s_SHARED_H_"|format(
- module.path|upper|replace("/","_")|replace(".","_")|
- replace("-", "_")) %}
-
-{%- macro mojom_type_traits(kind) %}
-template <>
-struct MojomTypeTraits<{{kind|get_qualified_name_for_kind}}DataView> {
- using Data = {{kind|get_qualified_name_for_kind(internal=True)}};
-{%- if kind|is_union_kind %}
- using DataAsArrayElement = Data;
- static constexpr MojomTypeCategory category = MojomTypeCategory::UNION;
-{%- else %}
- using DataAsArrayElement = Pointer<Data>;
- static constexpr MojomTypeCategory category = MojomTypeCategory::STRUCT;
-{%- endif %}
-};
-{%- endmacro %}
-
-{%- macro namespace_begin() %}
-{%- for namespace in namespaces_as_array %}
-namespace {{namespace}} {
-{%- endfor %}
-{%- endmacro %}
-
-{%- macro namespace_end() %}
-{%- for namespace in namespaces_as_array|reverse %}
-} // namespace {{namespace}}
-{%- endfor %}
-{%- endmacro %}
-
-#ifndef {{header_guard}}
-#define {{header_guard}}
-
-#include <stdint.h>
-
-#include <functional>
-#include <ostream>
-#include <type_traits>
-#include <utility>
-
-#include "base/compiler_specific.h"
-#include "mojo/public/cpp/bindings/array_data_view.h"
-#include "mojo/public/cpp/bindings/enum_traits.h"
-#include "mojo/public/cpp/bindings/interface_data_view.h"
-#include "mojo/public/cpp/bindings/lib/bindings_internal.h"
-#include "mojo/public/cpp/bindings/lib/serialization.h"
-#include "mojo/public/cpp/bindings/map_data_view.h"
-#include "mojo/public/cpp/bindings/native_enum.h"
-#include "mojo/public/cpp/bindings/native_struct_data_view.h"
-#include "mojo/public/cpp/bindings/string_data_view.h"
-#include "{{module.path}}-shared-internal.h"
-{%- for import in imports %}
-#include "{{import.module.path}}-shared.h"
-{%- endfor %}
-
-{{namespace_begin()}}
-
-{#--- Struct Forward Declarations -#}
-{%- for struct in structs %}
-{%- if struct|is_native_only_kind %}
-using {{struct.name}}DataView = mojo::NativeStructDataView;
-{%- else %}
-class {{struct.name}}DataView;
-{%- endif %}
-{% endfor %}
-
-{#--- Union Forward Declarations -#}
-{%- for union in unions %}
-class {{union.name}}DataView;
-{%- endfor %}
-
-{{namespace_end()}}
-
-namespace mojo {
-namespace internal {
-
-{%- for struct in structs %}
-{%- if not struct|is_native_only_kind %}
-{{mojom_type_traits(struct)}}
-{%- endif %}
-{%- endfor %}
-
-{%- for union in unions %}
-{{mojom_type_traits(union)}}
-{%- endfor %}
-
-} // namespace internal
-} // namespace mojo
-
-{{namespace_begin()}}
-
-{#--- Enums #}
-{%- from "enum_macros.tmpl" import enum_decl%}
-{%- for enum in all_enums %}
-{%- if enum|is_native_only_kind %}
-using {{enum|get_name_for_kind(flatten_nested_kind=True)}} = mojo::NativeEnum;
-{%- else %}
-{{enum_decl(enum)}}
-{%- endif %}
-{%- endfor %}
-
-{#--- Interfaces #}
-{%- if interfaces %}
-// Interface base classes. They are used for type safety check.
-{%- endif %}
-{%- for interface in interfaces %}
-class {{interface.name}}InterfaceBase {};
-
-using {{interface.name}}PtrDataView =
- mojo::InterfacePtrDataView<{{interface.name}}InterfaceBase>;
-using {{interface.name}}RequestDataView =
- mojo::InterfaceRequestDataView<{{interface.name}}InterfaceBase>;
-using {{interface.name}}AssociatedPtrInfoDataView =
- mojo::AssociatedInterfacePtrInfoDataView<{{interface.name}}InterfaceBase>;
-using {{interface.name}}AssociatedRequestDataView =
- mojo::AssociatedInterfaceRequestDataView<{{interface.name}}InterfaceBase>;
-
-{%- endfor %}
-
-{#--- Structs #}
-{%- for struct in structs %}
-{%- if not struct|is_native_only_kind %}
-{% include "struct_data_view_declaration.tmpl" %}
-{%- endif %}
-{%- endfor %}
-
-{#--- Interface parameter definitions #}
-{%- for interface in interfaces %}
-{%- for method in interface.methods %}
-{%- set struct = method.param_struct %}
-{% include "struct_data_view_declaration.tmpl" %}
-{%- if method.response_parameters != None %}
-{%- set struct = method.response_param_struct %}
-{% include "struct_data_view_declaration.tmpl" %}
-{%- endif %}
-{%- endfor %}
-{%- endfor %}
-
-{#--- Unions #}
-{%- for union in unions %}
-{% include "union_data_view_declaration.tmpl" %}
-{%- endfor %}
-
-{{namespace_end()}}
-
-namespace std {
-
-{%- from "enum_macros.tmpl" import enum_hash %}
-{%- for enum in all_enums %}
-{%- if not enum|is_native_only_kind %}
-{{enum_hash(enum)}}
-{%- endif %}
-{%- endfor %}
-
-} // namespace std
-
-namespace mojo {
-
-{#--- Enum Serialization Helpers -#}
-{%- for enum in all_enums %}
-{%- if not enum|is_native_only_kind %}
-{% include "enum_serialization_declaration.tmpl" %}
-{%- endif %}
-{%- endfor %}
-
-{#--- Struct Serialization Helpers -#}
-{% for struct in structs %}
-{%- if not struct|is_native_only_kind %}
-{% include "struct_serialization_declaration.tmpl" %}
-{%- endif %}
-{%- endfor %}
-
-{#--- Union Serialization Helpers -#}
-{% if unions %}
-{%- for union in unions %}
-{% include "union_serialization_declaration.tmpl" %}
-{%- endfor %}
-{%- endif %}
-
-} // namespace mojo
-
-{{namespace_begin()}}
-
-{%- for struct in structs %}
-{%- if not struct|is_native_only_kind %}
-{% include "struct_data_view_definition.tmpl" %}
-{%- endif %}
-{%- endfor %}
-
-{%- for interface in interfaces %}
-{%- for method in interface.methods %}
-{%- set struct = method.param_struct %}
-{% include "struct_data_view_definition.tmpl" %}
-{%- if method.response_parameters != None %}
-{%- set struct = method.response_param_struct %}
-{% include "struct_data_view_definition.tmpl" %}
-{%- endif %}
-{%- endfor %}
-{%- endfor %}
-
-{%- for union in unions %}
-{% include "union_data_view_definition.tmpl" %}
-{%- endfor %}
-
-{{namespace_end()}}
-
-#endif // {{header_guard}}
-
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
index 2c66a85..efb9db6 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
@@ -26,11 +26,13 @@
#include "base/logging.h"
#include "base/trace_event/trace_event.h"
+#include "mojo/public/cpp/bindings/lib/map_data_internal.h"
#include "mojo/public/cpp/bindings/lib/message_builder.h"
#include "mojo/public/cpp/bindings/lib/serialization_util.h"
#include "mojo/public/cpp/bindings/lib/validate_params.h"
#include "mojo/public/cpp/bindings/lib/validation_context.h"
#include "mojo/public/cpp/bindings/lib/validation_errors.h"
+#include "mojo/public/cpp/bindings/lib/validation_util.h"
#include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h"
{%- if for_blink %}
@@ -50,16 +52,75 @@
{#--- Constants #}
{%- for constant in module.constants %}
-{%- if constant.kind|is_string_kind %}
-const char {{constant.name}}[] = {{constant|constant_value}};
+{%- if not constant.kind|is_integral_kind %}
+const {{constant.kind|cpp_pod_type}} {{constant.name}} = {{constant|constant_value}};
{%- endif %}
{%- endfor %}
+namespace internal {
+namespace {
+
+#pragma pack(push, 1)
+
+{#--- Interface parameter definitions #}
+{%- for interface in interfaces %}
+{%- for method in interface.methods %}
+{%- set method_name = "k%s_%s_Name"|format(interface.name, method.name) %}
+const uint32_t {{method_name}} = {{method.ordinal}};
+{% set struct = method.param_struct %}
+{% include "struct_declaration.tmpl" %}
+{%- include "struct_definition.tmpl" %}
+{%- if method.response_parameters != None %}
+{%- set struct = method.response_param_struct %}
+{% include "struct_declaration.tmpl" %}
+{%- include "struct_definition.tmpl" %}
+{%- endif %}
+{%- endfor %}
+{%- endfor %}
+
+#pragma pack(pop)
+
+} // namespace
+
+{#--- Struct definitions #}
+{% for struct in structs %}
+{%- if not struct|is_native_only_kind %}
+{%- include "struct_definition.tmpl" %}
+{%- endif %}
+{%- endfor %}
+
+{#--- Union definitions #}
+{% for union in unions %}
+{%- include "union_definition.tmpl" %}
+{%- endfor %}
+
+} // namespace internal
+
+namespace {
+
+{#--- Interface parameter data view definitions #}
+{%- for interface in interfaces %}
+{%- for method in interface.methods %}
+{% set struct = method.param_struct %}
+{% include "struct_data_view_declaration.tmpl" %}
+{% include "struct_data_view_definition.tmpl" %}
+{%- if method.response_parameters != None %}
+{%- set struct = method.response_param_struct %}
+{% include "struct_data_view_declaration.tmpl" %}
+{% include "struct_data_view_definition.tmpl" %}
+{%- endif %}
+{%- endfor %}
+{%- endfor %}
+
+} // namespace
+
{#--- Struct Constants #}
{%- for struct in structs %}
{%- for constant in struct.constants %}
-{%- if constant.kind|is_string_kind %}
-const char {{struct.name}}::{{constant.name}}[] = {{constant|constant_value}};
+{%- if constant.kind|is_integral_kind %}
+const {{constant.kind|cpp_pod_type}} {{struct.name}}::{{constant.name}};
+{%- else %}
+const {{constant.kind|cpp_pod_type}} {{struct.name}}::{{constant.name}} = {{constant|constant_value}};
{%- endif %}
{%- endfor %}
{%- endfor %}
@@ -68,6 +129,7 @@
{%- for struct in structs %}
{%- if not struct|is_native_only_kind %}
{%- include "wrapper_class_definition.tmpl" %}
+{%- include "struct_data_view_definition.tmpl" %}
{%- endif %}
{%- endfor %}
@@ -93,17 +155,18 @@
{#--- Struct Serialization Helpers -#}
{% for struct in structs %}
{%- if not struct|is_native_only_kind %}
-{% include "struct_traits_definition.tmpl" %}
+{% include "struct_serialization_definition.tmpl" %}
{%- endif %}
{%- endfor %}
{#--- Union Serialization Helpers #}
{%- for union in unions %}
-{%- include "union_traits_definition.tmpl" %}
+{%- include "union_serialization_definition.tmpl" %}
{%- endfor %}
} // namespace mojo
+
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(_MSC_VER)
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
index acdad5e..c4f3e07 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
@@ -12,6 +12,11 @@
variant_path|upper|replace("/","_")|replace(".","_")|
replace("-", "_")) %}
+{%- from "enum_macros.tmpl" import enum_decl %}
+{%- from "enum_macros.tmpl" import enum_stream_operator %}
+{%- from "enum_macros.tmpl" import is_known_enum_value %}
+{%- from "enum_macros.tmpl" import enum_hash %}
+
{%- macro namespace_begin() %}
{%- for namespace in namespaces_as_array %}
namespace {{namespace}} {
@@ -35,30 +40,30 @@
#include <stdint.h>
-#include <limits>
+#include <functional>
+#include <ostream>
#include <type_traits>
#include <utility>
#include "base/callback.h"
#include "base/optional.h"
+#include "base/strings/string_piece.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/clone_traits.h"
#include "mojo/public/cpp/bindings/interface_ptr.h"
#include "mojo/public/cpp/bindings/interface_request.h"
-#include "mojo/public/cpp/bindings/lib/equals_traits.h"
#include "mojo/public/cpp/bindings/lib/control_message_handler.h"
#include "mojo/public/cpp/bindings/lib/control_message_proxy.h"
#include "mojo/public/cpp/bindings/lib/serialization.h"
-#include "mojo/public/cpp/bindings/lib/union_accessor.h"
+#include "mojo/public/cpp/bindings/map.h"
+#include "mojo/public/cpp/bindings/message_filter.h"
+#include "mojo/public/cpp/bindings/native_enum.h"
#include "mojo/public/cpp/bindings/native_struct.h"
-#include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h"
+#include "mojo/public/cpp/bindings/no_interface.h"
#include "mojo/public/cpp/bindings/struct_ptr.h"
#include "mojo/public/cpp/bindings/struct_traits.h"
-#include "mojo/public/cpp/bindings/thread_safe_interface_ptr.h"
-#include "mojo/public/cpp/bindings/union_traits.h"
-#include "{{module.path}}-shared.h"
+#include "{{variant_path}}-internal.h"
{%- for import in imports %}
{%- if variant %}
#include "{{"%s-%s.h"|format(import.module.path, variant)}}"
@@ -67,13 +72,11 @@
{%- endif %}
{%- endfor %}
{%- if not for_blink %}
-#include <string>
-#include <vector>
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/string.h"
{%- else %}
-{# hash_util.h includes template specializations that should be present for
- every use of {Inlined}StructPtr. #}
-#include "mojo/public/cpp/bindings/lib/wtf_hash_util.h"
-#include "third_party/WebKit/Source/wtf/HashFunctions.h"
+#include "mojo/public/cpp/bindings/wtf_array.h"
+#include "mojo/public/cpp/bindings/wtf_map.h"
#include "third_party/WebKit/Source/wtf/Optional.h"
#include "third_party/WebKit/Source/wtf/text/WTFString.h"
{%- endif %}
@@ -82,32 +85,42 @@
#include "{{header}}"
{%- endfor %}
-{%- if export_header %}
-#include "{{export_header}}"
-{%- endif %}
-
-{#--- WTF enum hashing #}
-{%- from "enum_macros.tmpl" import enum_hash_blink%}
-{%- if for_blink %}
-{%- for enum in all_enums %}
-{%- if not enum|is_native_only_kind %}
-{{enum_hash_blink(enum)}}
+{#--- Enums #}
+{%- if enums %}
+{{namespace_begin()}}
+{%- for enum in enums %}
+{%- if enum|is_native_only_kind %}
+using {{enum.name}} = mojo::NativeEnum;
+{%- else %}
+{{enum_decl(enum)}}
+{{enum_stream_operator(enum)}}
+{{is_known_enum_value(enum)}}
{%- endif %}
{%- endfor %}
+{{namespace_end()}}
+
+namespace std {
+
+{%- for enum in enums %}
+{%- if not enum|is_native_only_kind %}
+{{enum_hash(enum)}}
+{%- endif %}
+{%- endfor %}
+
+} // namespace std
{%- endif %}
{{namespace_begin()}}
-{#--- Enums #}
-{%- if variant %}
-{%- for enum in enums %}
-using {{enum.name}} = {{enum.name}}; // Alias for definition in the parent namespace.
-{%- endfor %}
-{%- endif %}
-
{#--- Constants #}
{%- for constant in module.constants %}
-{{constant|format_constant_declaration}};
+{#- To be consistent with constants defined inside interfaces, only make
+ integral types compile-time constants. #}
+{%- if constant.kind|is_integral_kind %}
+const {{constant.kind|cpp_pod_type}} {{constant.name}} = {{constant|constant_value}};
+{%- else %}
+extern const {{constant.kind|cpp_pod_type}} {{constant.name}};
+{%- endif %}
{%- endfor %}
{#--- Interface Forward Declarations -#}
@@ -115,13 +128,9 @@
class {{interface.name}};
using {{interface.name}}Ptr = mojo::InterfacePtr<{{interface.name}}>;
using {{interface.name}}PtrInfo = mojo::InterfacePtrInfo<{{interface.name}}>;
-using ThreadSafe{{interface.name}}Ptr =
- mojo::ThreadSafeInterfacePtr<{{interface.name}}>;
using {{interface.name}}Request = mojo::InterfaceRequest<{{interface.name}}>;
using {{interface.name}}AssociatedPtr =
mojo::AssociatedInterfacePtr<{{interface.name}}>;
-using ThreadSafe{{interface.name}}AssociatedPtr =
- mojo::ThreadSafeAssociatedInterfacePtr<{{interface.name}}>;
using {{interface.name}}AssociatedPtrInfo =
mojo::AssociatedInterfacePtrInfo<{{interface.name}}>;
using {{interface.name}}AssociatedRequest =
@@ -135,6 +144,7 @@
using {{struct.name}}Ptr = mojo::NativeStructPtr;
{%- else %}
class {{struct.name}};
+class {{struct.name}}DataView;
{%- if struct|should_inline %}
using {{struct.name}}Ptr = mojo::InlinedStructPtr<{{struct.name}}>;
{%- else %}
@@ -156,6 +166,27 @@
{#--- Interfaces -#}
{% for interface in interfaces %}
{% include "interface_declaration.tmpl" %}
+
+{%- if interface.enums %}
+{{namespace_end()}}
+namespace std {
+
+{%- for enum in interface.enums %}
+{%- if not enum|is_native_only_kind %}
+{{enum_hash(enum)}}
+{%- endif %}
+{%- endfor %}
+
+} // namespace std
+{{namespace_begin()}}
+{%- endif %}
+
+{%- for enum in interface.enums %}
+{%- if not enum|is_native_only_kind %}
+{{enum_stream_operator(enum)}}
+{{is_known_enum_value(enum)}}
+{%- endif %}
+{%- endfor %}
{%- endfor %}
{#--- Interface Proxies -#}
@@ -185,6 +216,7 @@
{% for struct in structs %}
{% if struct|should_inline and not struct|is_native_only_kind %}
{% include "wrapper_class_declaration.tmpl" %}
+{% include "struct_data_view_declaration.tmpl" %}
{% endif %}
{%- endfor %}
@@ -199,6 +231,7 @@
{% for struct in structs %}
{% if not struct|should_inline and not struct|is_native_only_kind %}
{% include "wrapper_class_declaration.tmpl" %}
+{% include "struct_data_view_declaration.tmpl" %}
{% endif %}
{%- endfor %}
@@ -210,23 +243,67 @@
{%- if not struct|is_native_only_kind %}
{% include "wrapper_class_template_definition.tmpl" %}
{%- endif %}
+
+{%- if struct.enums %}
+{{namespace_end()}}
+namespace std {
+
+{%- for enum in struct.enums %}
+{%- if not enum|is_native_only_kind %}
+{{enum_hash(enum)}}
+{%- endif %}
+{%- endfor %}
+
+} // namespace std
+{{namespace_begin()}}
+{%- endif %}
+
+{%- for enum in struct.enums %}
+{%- if not enum|is_native_only_kind %}
+{{enum_stream_operator(enum)}}
+{{is_known_enum_value(enum)}}
+{%- endif %}
+{%- endfor %}
{%- endfor %}
{{namespace_end()}}
namespace mojo {
+{#--- Enum Serialization Helpers -#}
+{%- for enum in enums %}
+{%- if not enum|is_native_only_kind %}
+{% include "enum_serialization_declaration.tmpl" %}
+{%- endif %}
+{%- endfor %}
+
+{%- for struct in structs %}
+{%- for enum in struct.enums %}
+{%- if not enum|is_native_only_kind %}
+{% include "enum_serialization_declaration.tmpl" %}
+{%- endif %}
+{%- endfor %}
+{%- endfor %}
+
+{%- for interface in interfaces %}
+{%- for enum in interface.enums %}
+{%- if not enum|is_native_only_kind %}
+{% include "enum_serialization_declaration.tmpl" %}
+{%- endif %}
+{%- endfor %}
+{%- endfor %}
+
{#--- Struct Serialization Helpers -#}
{% for struct in structs %}
{%- if not struct|is_native_only_kind %}
-{% include "struct_traits_declaration.tmpl" %}
+{% include "struct_serialization_declaration.tmpl" %}
{%- endif %}
{%- endfor %}
{#--- Union Serialization Helpers -#}
{% if unions %}
{%- for union in unions %}
-{% include "union_traits_declaration.tmpl" %}
+{% include "union_serialization_declaration.tmpl" %}
{%- endfor %}
{%- endif %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_declaration.tmpl
index 96e0d61..7c34939 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_declaration.tmpl
@@ -1,56 +1,29 @@
class {{struct.name}}DataView {
public:
- {{struct.name}}DataView() {}
-
{{struct.name}}DataView(
internal::{{struct.name}}_Data* data,
- mojo::internal::SerializationContext* context)
-{%- if struct|requires_context_for_data_view %}
- : data_(data), context_(context) {}
-{%- else %}
- : data_(data) {}
-{%- endif %}
-
- bool is_null() const { return !data_; }
+ mojo::internal::SerializationContext* context);
{%- for pf in struct.packed.packed_fields_in_ordinal_order %}
-{%- set kind = pf.field.kind %}
-{%- set name = pf.field.name %}
-{%- if kind|is_union_kind %}
- inline void Get{{name|under_to_camel}}DataView(
- {{kind|cpp_data_view_type}}* output);
-
+{%- set kind = pf.field.kind -%}
+{%- set name = pf.field.name -%}
+{%- if kind|is_struct_kind or kind|is_array_kind or kind|is_string_kind
+ or kind|is_map_kind %}
template <typename UserType>
- WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) {
-{%- if pf.min_version != 0 %}
- auto* pointer = data_->header_.version >= {{pf.min_version}}
- ? &data_->{{name}} : nullptr;
-{%- else %}
- auto* pointer = &data_->{{name}};
-{%- endif %}
+ bool Read{{name|under_to_camel}}(UserType* value) {
+{%- if pf.min_version != 0 %}
+ auto pointer = data_->header_.version >= {{pf.min_version}}
+ ? data_->{{name}}.Get() : nullptr;
+{%- else %}
+ auto pointer = data_->{{name}}.Get();
+{%- endif %}
return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
- pointer, output, context_);
- }
-
-{%- elif kind|is_object_kind %}
- inline void Get{{name|under_to_camel}}DataView(
- {{kind|cpp_data_view_type}}* output);
-
- template <typename UserType>
- WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) {
-{%- if pf.min_version != 0 %}
- auto* pointer = data_->header_.version >= {{pf.min_version}}
- ? data_->{{name}}.Get() : nullptr;
-{%- else %}
- auto* pointer = data_->{{name}}.Get();
-{%- endif %}
- return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
- pointer, output, context_);
+ pointer, value, context_);
}
{%- elif kind|is_enum_kind %}
template <typename UserType>
- WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) const {
+ bool Read{{name|under_to_camel}}(UserType* value) const {
{%- if pf.min_version != 0 %}
auto data_value = data_->header_.version >= {{pf.min_version}}
? data_->{{name}} : 0;
@@ -58,61 +31,23 @@
auto data_value = data_->{{name}};
{%- endif %}
return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
- data_value, output);
+ data_value, value);
}
- {{kind|cpp_data_view_type}} {{name}}() const {
-{%- if pf.min_version != 0 %}
- if (data_->header_.version < {{pf.min_version}})
- return {{kind|get_qualified_name_for_kind}}{};
-{%- endif %}
- return static_cast<{{kind|cpp_data_view_type}}>(data_->{{name}});
- }
+ {{kind|get_qualified_name_for_kind}} {{name}}() const;
-{%- elif kind|is_any_handle_kind %}
- {{kind|cpp_data_view_type}} Take{{name|under_to_camel}}() {
- {{kind|cpp_data_view_type}} result;
-{%- if pf.min_version != 0 %}
- if (data_->header_.version < {{pf.min_version}})
- return result;
-{%- endif %}
- bool ret =
- mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
- &data_->{{name}}, &result, context_);
- DCHECK(ret);
- return result;
- }
+{%- elif kind|is_union_kind %}
+ bool Read{{name|under_to_camel}}({{kind|cpp_wrapper_type}}* value);
-{%- elif kind|is_any_interface_kind %}
- template <typename UserType>
- UserType Take{{name|under_to_camel}}() {
- UserType result;
-{%- if pf.min_version != 0 %}
- if (data_->header_.version < {{pf.min_version}})
- return result;
-{%- endif %}
- bool ret =
- mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
- &data_->{{name}}, &result, context_);
- DCHECK(ret);
- return result;
- }
+{%- elif kind|is_any_handle_or_interface_kind %}
+ {{kind|cpp_wrapper_type}} Take{{name|under_to_camel}}();
{%- else %}
- {{kind|cpp_data_view_type}} {{name}}() const {
-{%- if pf.min_version != 0 %}
- if (data_->header_.version < {{pf.min_version}})
- return {{kind|cpp_data_view_type}}{};
-{%- endif %}
- return data_->{{name}};
- }
-
+ {{kind|cpp_wrapper_type}} {{name}}() const;
{%- endif %}
{%- endfor %}
private:
- internal::{{struct.name}}_Data* data_ = nullptr;
-{%- if struct|requires_context_for_data_view %}
- mojo::internal::SerializationContext* context_ = nullptr;
-{%- endif %}
+ internal::{{struct.name}}_Data* data_;
+ mojo::internal::SerializationContext* context_;
};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_definition.tmpl
index 95311dc..2be92b3 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_data_view_definition.tmpl
@@ -1,29 +1,59 @@
-{%- for pf in struct.packed.packed_fields_in_ordinal_order %}
-{%- set kind = pf.field.kind %}
-{%- set name = pf.field.name %}
-
-{%- if kind|is_union_kind %}
-inline void {{struct.name}}DataView::Get{{name|under_to_camel}}DataView(
- {{kind|cpp_data_view_type}}* output) {
-{%- if pf.min_version != 0 %}
- auto pointer = data_->header_.version >= {{pf.min_version}}
- ? &data_->{{name}} : nullptr;
-{%- else %}
- auto pointer = &data_->{{name}};
-{%- endif %}
- *output = {{kind|cpp_data_view_type}}(pointer, context_);
+{{struct.name}}DataView::{{struct.name}}DataView(
+ internal::{{struct.name}}_Data* data,
+ mojo::internal::SerializationContext* context)
+ : data_(data), context_(context) {
+ DCHECK(data_);
}
-{%- elif kind|is_object_kind %}
-inline void {{struct.name}}DataView::Get{{name|under_to_camel}}DataView(
- {{kind|cpp_data_view_type}}* output) {
+{%- for pf in struct.packed.packed_fields_in_ordinal_order %}
+{%- set kind = pf.field.kind -%}
+{%- set name = pf.field.name -%}
+{%- if kind|is_struct_kind or kind|is_array_kind or kind|is_string_kind or
+ kind|is_map_kind %}
+{#- Does nothing. They are already defined in the class declaration. #}
+
+{%- elif kind|is_enum_kind %}
+{{kind|get_qualified_name_for_kind}} {{struct.name}}DataView::{{name}}() const {
{%- if pf.min_version != 0 %}
- auto pointer = data_->header_.version >= {{pf.min_version}}
- ? data_->{{name}}.Get() : nullptr;
-{%- else %}
- auto pointer = data_->{{name}}.Get();
+ if (data_->header_.version < {{pf.min_version}})
+ return {{kind|get_qualified_name_for_kind}}{};
{%- endif %}
- *output = {{kind|cpp_data_view_type}}(pointer, context_);
+ return static_cast<{{kind|get_qualified_name_for_kind}}>(data_->{{name}});
+}
+
+{%- elif kind|is_union_kind %}
+bool {{struct.name}}DataView::Read{{name|under_to_camel}}(
+ {{kind|cpp_wrapper_type}}* value) {
+{%- if pf.min_version != 0 %}
+ auto pointer = data_->header_.version >= {{pf.min_version}}
+ ? &data_->{{name}} : nullptr;
+{%- else %}
+ auto pointer = &data_->{{name}};
+{%- endif %}
+ return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
+ pointer, value, context_);
+}
+
+{%- elif kind|is_any_handle_or_interface_kind %}
+{{kind|cpp_wrapper_type}} {{struct.name}}DataView::Take{{name|under_to_camel}}() {
+ {{kind|cpp_wrapper_type}} result;
+{%- if pf.min_version != 0 %}
+ if (data_->header_.version < {{pf.min_version}})
+ return result;
+{%- endif %}
+ bool ret = mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
+ &data_->{{name}}, &result, context_);
+ DCHECK(ret);
+ return result;
+}
+
+{%- else %}
+{{kind|cpp_wrapper_type}} {{struct.name}}DataView::{{name}}() const {
+{%- if pf.min_version != 0 %}
+ if (data_->header_.version < {{pf.min_version}})
+ return {{kind|cpp_wrapper_type}}{};
+{%- endif %}
+ return data_->{{name}};
}
{%- endif %}
{%- endfor %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl
index 156f774..dd87be7 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl
@@ -2,13 +2,21 @@
class {{class_name}} {
public:
- static {{class_name}}* New(mojo::internal::Buffer* buf) {
- return new (buf->Allocate(sizeof({{class_name}}))) {{class_name}}();
- }
+ static {{class_name}}* New(mojo::internal::Buffer* buf);
static bool Validate(const void* data,
mojo::internal::ValidationContext* validation_context);
+{% from "enum_macros.tmpl" import enum_data_decl -%}
+{#--- Enums #}
+{%- for enum in struct.enums -%}
+{%- if enum|is_native_only_kind %}
+ using {{enum.name}}_Data = mojo::internal::NativeEnum_Data;
+{%- else %}
+ {{enum_data_decl(enum)|indent(2)}}
+{%- endif %}
+{%- endfor %}
+
mojo::internal::StructHeader header_;
{%- for packed_field in struct.packed.packed_fields %}
{%- set name = packed_field.field.name %}
@@ -38,8 +46,7 @@
{%- endif %}
private:
- {{class_name}}() : header_({sizeof(*this), {{struct.versions[-1].version}}}) {
- }
+ {{class_name}}();
~{{class_name}}() = delete;
};
static_assert(sizeof({{class_name}}) == {{struct.versions[-1].num_bytes}},
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl
index 60dca40..a2e5708 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl
@@ -2,6 +2,11 @@
{%- set class_name = struct.name ~ "_Data" %}
// static
+{{class_name}}* {{class_name}}::New(mojo::internal::Buffer* buf) {
+ return new (buf->Allocate(sizeof({{class_name}}))) {{class_name}}();
+}
+
+// static
bool {{class_name}}::Validate(
const void* data,
mojo::internal::ValidationContext* validation_context) {
@@ -15,7 +20,7 @@
// the message comes from an older version.
const {{class_name}}* object = static_cast<const {{class_name}}*>(data);
- static constexpr struct {
+ static const struct {
uint32_t version;
uint32_t num_bytes;
} kVersionSizes[] = {
@@ -68,3 +73,7 @@
return true;
}
+{{class_name}}::{{class_name}}() {
+ header_.num_bytes = sizeof(*this);
+ header_.version = {{struct.versions[-1].version}};
+}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl
index bb5fb9c..c59f317 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl
@@ -19,8 +19,7 @@
{%- macro get_serialized_size(struct, input_field_pattern, context,
input_may_be_temp=False) -%}
size_t size = sizeof({{struct|get_qualified_name_for_kind(internal=True)}});
-{%- for pf in struct.packed.packed_fields_in_ordinal_order
- if pf.field.kind|is_object_kind or pf.field.kind|is_associated_kind %}
+{%- for pf in struct.packed.packed_fields_in_ordinal_order if pf.field.kind|is_object_kind %}
{%- set name = pf.field.name -%}
{%- set kind = pf.field.kind -%}
{%- set original_input_field = input_field_pattern|format(name) %}
@@ -149,11 +148,8 @@
{%- if kind|is_object_kind or kind|is_enum_kind %}
if (!{{input}}.Read{{name|under_to_camel}}(&{{output_field}}))
{{success}} = false;
-{%- elif kind|is_any_handle_kind %}
+{%- elif kind|is_any_handle_or_interface_kind %}
{{output_field}} = {{input}}.Take{{name|under_to_camel}}();
-{%- elif kind|is_any_interface_kind %}
- {{output_field}} =
- {{input}}.Take{{name|under_to_camel}}<decltype({{output_field}})>();
{%- else %}
{{output_field}} = {{input}}.{{name}}();
{%- endif %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl
index 835178b..aede1a7 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl
@@ -1,13 +1,37 @@
{%- import "struct_macros.tmpl" as struct_macros %}
-{%- set data_view = struct|get_qualified_name_for_kind ~ "DataView" %}
+{%- set mojom_type = struct|get_qualified_name_for_kind %}
{%- set data_type = struct|get_qualified_name_for_kind(internal=True) %}
+template <>
+struct StructTraits<{{mojom_type}}, {{mojom_type}}Ptr> {
+ static bool IsNull(const {{mojom_type}}Ptr& input) { return !input; }
+ static void SetToNull({{mojom_type}}Ptr* output) { output->reset(); }
+
+{%- for field in struct.fields %}
+{%- set return_ref = field.kind|is_object_kind or
+ field.kind|is_any_handle_or_interface_kind %}
+{%- if return_ref %}
+ static decltype({{mojom_type}}::{{field.name}})& {{field.name}}(
+ {{mojom_type}}Ptr& input) {
+ return input->{{field.name}};
+ }
+{%- else %}
+ static decltype({{mojom_type}}::{{field.name}}) {{field.name}}(
+ const {{mojom_type}}Ptr& input) {
+ return input->{{field.name}};
+ }
+{%- endif %}
+{%- endfor %}
+
+ static bool Read({{mojom_type}}DataView input, {{mojom_type}}Ptr* output);
+};
+
namespace internal {
template <typename MaybeConstUserType>
-struct Serializer<{{data_view}}, MaybeConstUserType> {
+struct Serializer<{{mojom_type}}Ptr, MaybeConstUserType> {
using UserType = typename std::remove_const<MaybeConstUserType>::type;
- using Traits = StructTraits<{{data_view}}, UserType>;
+ using Traits = StructTraits<{{mojom_type}}, UserType>;
static size_t PrepareToSerialize(MaybeConstUserType& input,
SerializationContext* context) {
@@ -37,7 +61,7 @@
{{struct_macros.serialize(
struct, struct.name ~ " struct",
"CallWithContext(Traits::%s, input, custom_context)", "result",
- "buffer", "context", True)|indent(2)}}
+ "buffer", "context", True)|indent(4)}}
*output = result;
CustomContextHelper<Traits>::TearDown(input, custom_context);
@@ -49,7 +73,7 @@
if (!input)
return CallSetToNullIfExists<Traits>(output);
- {{data_view}} data_view(input, context);
+ {{mojom_type}}DataView data_view(input, context);
return Traits::Read(data_view, output);
}
};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl
similarity index 78%
rename from mojo/public/tools/bindings/generators/cpp_templates/struct_traits_definition.tmpl
rename to mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl
index f84337f..7421abc 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl
@@ -2,8 +2,8 @@
{%- set mojom_type = struct|get_qualified_name_for_kind %}
// static
-bool StructTraits<{{mojom_type}}::DataView, {{mojom_type}}Ptr>::Read(
- {{mojom_type}}::DataView input,
+bool StructTraits<{{mojom_type}}, {{mojom_type}}Ptr>::Read(
+ {{mojom_type}}DataView input,
{{mojom_type}}Ptr* output) {
bool success = true;
{{mojom_type}}Ptr result({{mojom_type}}::New());
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_declaration.tmpl
deleted file mode 100644
index 1b7cf89..0000000
--- a/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_declaration.tmpl
+++ /dev/null
@@ -1,32 +0,0 @@
-{%- set mojom_type = struct|get_qualified_name_for_kind %}
-
-template <>
-struct {{export_attribute}} StructTraits<{{mojom_type}}::DataView,
- {{mojom_type}}Ptr> {
- static bool IsNull(const {{mojom_type}}Ptr& input) { return !input; }
- static void SetToNull({{mojom_type}}Ptr* output) { output->reset(); }
-
-{%- for field in struct.fields %}
-{%- set return_ref = field.kind|is_object_kind or
- field.kind|is_any_handle_or_interface_kind %}
-{# We want the field accessor to be const whenever possible to allow
- structs to be used as map keys.
- TODO(tibell): Make this check more precise to deal with e.g.
- custom types which don't contain handles but require non-const
- reference for serialization. #}
-{%- set maybe_const = "" if field.kind|contains_handles_or_interfaces else "const" %}
-{%- if return_ref %}
- static {{maybe_const}} decltype({{mojom_type}}::{{field.name}})& {{field.name}}(
- {{maybe_const}} {{mojom_type}}Ptr& input) {
- return input->{{field.name}};
- }
-{%- else %}
- static decltype({{mojom_type}}::{{field.name}}) {{field.name}}(
- const {{mojom_type}}Ptr& input) {
- return input->{{field.name}};
- }
-{%- endif %}
-{%- endfor %}
-
- static bool Read({{mojom_type}}::DataView input, {{mojom_type}}Ptr* output);
-};
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
deleted file mode 100644
index 2a9e02d..0000000
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl
+++ /dev/null
@@ -1,91 +0,0 @@
-class {{union.name}}DataView {
- public:
- using Tag = internal::{{union.name}}_Data::{{union.name}}_Tag;
-
- {{union.name}}DataView() {}
-
- {{union.name}}DataView(
- internal::{{union.name}}_Data* data,
- mojo::internal::SerializationContext* context)
-{%- if union|requires_context_for_data_view %}
- : data_(data), context_(context) {}
-{%- else %}
- : data_(data) {}
-{%- endif %}
-
- bool is_null() const {
- // For inlined unions, |data_| is always non-null. In that case we need to
- // check |data_->is_null()|.
- return !data_ || data_->is_null();
- }
-
- Tag tag() const { return data_->tag; }
-
-{%- for field in union.fields %}
-{%- set kind = field.kind %}
-{%- set name = field.name %}
- bool is_{{name}}() const { return data_->tag == Tag::{{name|upper}}; }
-
-{%- if kind|is_object_kind %}
- inline void Get{{name|under_to_camel}}DataView(
- {{kind|cpp_data_view_type}}* output);
-
- template <typename UserType>
- WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) {
- DCHECK(is_{{name}}());
- return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
- data_->data.f_{{name}}.Get(), output, context_);
- }
-
-{%- elif kind|is_enum_kind %}
- template <typename UserType>
- WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) const {
- DCHECK(is_{{name}}());
- return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
- data_->data.f_{{name}}, output);
- }
-
- {{kind|cpp_data_view_type}} {{name}}() const {
- DCHECK(is_{{name}}());
- return static_cast<{{kind|cpp_data_view_type}}>(
- data_->data.f_{{name}});
- }
-
-{%- elif kind|is_any_handle_kind %}
- {{kind|cpp_data_view_type}} Take{{name|under_to_camel}}() {
- DCHECK(is_{{name}}());
- {{kind|cpp_data_view_type}} result;
- bool ret =
- mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
- &data_->data.f_{{name}}, &result, context_);
- DCHECK(ret);
- return result;
- }
-
-{%- elif kind|is_any_interface_kind %}
- template <typename UserType>
- UserType Take{{name|under_to_camel}}() {
- DCHECK(is_{{name}}());
- UserType result;
- bool ret =
- mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>(
- &data_->data.f_{{name}}, &result, context_);
- DCHECK(ret);
- return result;
- }
-
-{%- else %}
- {{kind|cpp_data_view_type}} {{name}}() const {
- DCHECK(is_{{name}}());
- return data_->data.f_{{name}};
- }
-
-{%- endif %}
-{%- endfor %}
-
- private:
- internal::{{union.name}}_Data* data_ = nullptr;
-{%- if union|requires_context_for_data_view %}
- mojo::internal::SerializationContext* context_ = nullptr;
-{%- endif %}
-};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_definition.tmpl
deleted file mode 100644
index 6da9280..0000000
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_definition.tmpl
+++ /dev/null
@@ -1,12 +0,0 @@
-{%- for field in union.fields %}
-{%- set kind = field.kind %}
-{%- set name = field.name %}
-
-{%- if kind|is_object_kind %}
-inline void {{union.name}}DataView::Get{{name|under_to_camel}}DataView(
- {{kind|cpp_data_view_type}}* output) {
- DCHECK(is_{{name}}());
- *output = {{kind|cpp_data_view_type}}(data_->data.f_{{name}}.Get(), context_);
-}
-{%- endif %}
-{%- endfor %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl
index 005ba76..be2db4c 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl
@@ -6,28 +6,21 @@
public:
// Used to identify Mojom Union Data Classes.
typedef void MojomUnionDataType;
-
- {{class_name}}() {}
- // Do nothing in the destructor since it won't be called when it is a
- // non-inlined union.
+ static {{class_name}}* New(mojo::internal::Buffer* buf);
+ {{class_name}}();
+ // Do nothing in the destructor since it won't be called.
~{{class_name}}() {}
- static {{class_name}}* New(mojo::internal::Buffer* buf) {
- return new (buf->Allocate(sizeof({{class_name}}))) {{class_name}}();
- }
-
static bool Validate(const void* data,
mojo::internal::ValidationContext* validation_context,
bool inlined);
- bool is_null() const { return size == 0; }
-
- void set_null() {
- size = 0U;
- tag = static_cast<{{enum_name}}>(0);
- data.unknown = 0U;
+ bool is_null() const {
+ return size == 0;
}
+ void set_null();
+
enum class {{enum_name}} : uint32_t {
{% for field in union.fields %}
{{field.name|upper}},
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl
index af5ea9f..fc6eddf 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl
@@ -3,31 +3,24 @@
{%- set enum_name = union.name ~ "_Tag" -%}
// static
+{{class_name}}* {{class_name}}::New(mojo::internal::Buffer* buf) {
+ return new (buf->Allocate(sizeof({{class_name}}))) {{class_name}}();
+}
+
+// static
bool {{class_name}}::Validate(
const void* data,
mojo::internal::ValidationContext* validation_context,
bool inlined) {
- if (!data) {
- DCHECK(!inlined);
+ if (!data)
return true;
- }
- // If it is inlined, the alignment is already enforced by its enclosing
- // object. We don't have to validate that.
- DCHECK(!inlined || mojo::internal::IsAligned(data));
-
- if (!inlined &&
- !mojo::internal::ValidateNonInlinedUnionHeaderAndClaimMemory(
- data, validation_context)) {
+ if (!ValidateUnionHeaderAndClaimMemory(data, inlined, validation_context))
return false;
- }
const {{class_name}}* object = static_cast<const {{class_name}}*>(data);
ALLOW_UNUSED_LOCAL(object);
- if (inlined && object->is_null())
- return true;
-
switch (object->tag) {
{% for field in union.fields %}
case {{enum_name}}::{{field.name|upper}}: {
@@ -45,3 +38,13 @@
}
}
}
+
+void {{class_name}}::set_null() {
+ size = 0U;
+ tag = static_cast<{{enum_name}}>(0);
+ data.unknown = 0U;
+}
+
+{{class_name}}::{{class_name}}() {
+}
+
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl
index b589ae9..bab9045 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl
@@ -1,141 +1,37 @@
-{%- set data_view = union|get_qualified_name_for_kind ~ "DataView" %}
+{%- set mojom_type = union|get_qualified_name_for_kind %}
{%- set data_type = union|get_qualified_name_for_kind(internal=True) %}
namespace internal {
-template <typename MaybeConstUserType>
-struct Serializer<{{data_view}}, MaybeConstUserType> {
- using UserType = typename std::remove_const<MaybeConstUserType>::type;
- using Traits = UnionTraits<{{data_view}}, UserType>;
+template <typename MojomType>
+struct UnionSerializerImpl;
- static size_t PrepareToSerialize(MaybeConstUserType& input,
+template <>
+struct UnionSerializerImpl<{{mojom_type}}Ptr> {
+ static size_t PrepareToSerialize({{mojom_type}}Ptr& input,
bool inlined,
- SerializationContext* context) {
- size_t size = inlined ? 0 : sizeof({{data_type}});
+ SerializationContext* context);
- if (CallIsNullIfExists<Traits>(input))
- return size;
-
- void* custom_context = CustomContextHelper<Traits>::SetUp(input, context);
- ALLOW_UNUSED_LOCAL(custom_context);
-
- switch (CallWithContext(Traits::GetTag, input, custom_context)) {
-{%- for field in union.fields %}
-{%- set name = field.name %}
- case {{data_view}}::Tag::{{name|upper}}: {
-{%- if field.kind|is_object_kind or field.kind|is_associated_kind %}
-{%- set kind = field.kind %}
-{%- set serializer_type = kind|unmapped_type_for_serializer %}
- decltype(CallWithContext(Traits::{{name}}, input, custom_context))
- in_{{name}} = CallWithContext(Traits::{{name}}, input,
- custom_context);
-{%- if kind|is_union_kind %}
- size += mojo::internal::PrepareToSerialize<{{serializer_type}}>(
- in_{{name}}, false, context);
-{%- else %}
- size += mojo::internal::PrepareToSerialize<{{serializer_type}}>(
- in_{{name}}, context);
-{%- endif %}
-{%- endif %}
- break;
- }
-{%- endfor %}
- }
- return size;
- }
-
- static void Serialize(MaybeConstUserType& input,
+ static void Serialize({{mojom_type}}Ptr& input,
Buffer* buffer,
{{data_type}}** output,
bool inlined,
- SerializationContext* context) {
- if (CallIsNullIfExists<Traits>(input)) {
- if (inlined)
- (*output)->set_null();
- else
- *output = nullptr;
- return;
- }
-
- void* custom_context = CustomContextHelper<Traits>::GetNext(context);
-
- if (!inlined)
- *output = {{data_type}}::New(buffer);
-
- {{data_type}}* result = *output;
- ALLOW_UNUSED_LOCAL(result);
- // TODO(azani): Handle unknown and objects.
- // Set the not-null flag.
- result->size = kUnionDataSize;
- result->tag = CallWithContext(Traits::GetTag, input, custom_context);
- switch (result->tag) {
-{%- for field in union.fields %}
-{%- set name = field.name %}
-{%- set kind = field.kind %}
-{%- set serializer_type = kind|unmapped_type_for_serializer %}
- case {{data_view}}::Tag::{{field.name|upper}}: {
- decltype(CallWithContext(Traits::{{name}}, input, custom_context))
- in_{{name}} = CallWithContext(Traits::{{name}}, input,
- custom_context);
-{%- if kind|is_object_kind %}
- typename decltype(result->data.f_{{name}})::BaseType* ptr;
-{%- if kind|is_union_kind %}
- mojo::internal::Serialize<{{serializer_type}}>(
- in_{{name}}, buffer, &ptr, false, context);
-{%- elif kind|is_array_kind or kind|is_map_kind %}
- const ContainerValidateParams {{name}}_validate_params(
- {{kind|get_container_validate_params_ctor_args|indent(16)}});
- mojo::internal::Serialize<{{serializer_type}}>(
- in_{{name}}, buffer, &ptr, &{{name}}_validate_params, context);
-{%- else %}
- mojo::internal::Serialize<{{serializer_type}}>(
- in_{{name}}, buffer, &ptr, context);
-{%- endif %}
- result->data.f_{{name}}.Set(ptr);
-{%- if not kind|is_nullable_kind %}
- MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
- !ptr, mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
- "null {{name}} in {{union.name}} union");
-{%- endif %}
-
-{%- elif kind|is_any_handle_or_interface_kind %}
- mojo::internal::Serialize<{{serializer_type}}>(
- in_{{name}}, &result->data.f_{{name}}, context);
-{%- if not kind|is_nullable_kind %}
- MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
- !mojo::internal::IsHandleOrInterfaceValid(result->data.f_{{name}}),
-{%- if kind|is_associated_kind %}
- mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID,
-{%- else %}
- mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
-{%- endif %}
- "invalid {{name}} in {{union.name}} union");
-{%- endif %}
-
-{%- elif kind|is_enum_kind %}
- mojo::internal::Serialize<{{serializer_type}}>(
- in_{{name}}, &result->data.f_{{name}});
-
-{%- else %}
- result->data.f_{{name}} = in_{{name}};
-{%- endif %}
- break;
- }
-{%- endfor %}
- }
-
- CustomContextHelper<Traits>::TearDown(input, custom_context);
- }
+ SerializationContext* context);
static bool Deserialize({{data_type}}* input,
- UserType* output,
- SerializationContext* context) {
- if (!input || input->is_null())
- return CallSetToNullIfExists<Traits>(output);
+ {{mojom_type}}Ptr* output,
+ SerializationContext* context);
+};
- {{data_view}} data_view(input, context);
- return Traits::Read(data_view, output);
- }
+template <typename MaybeConstUserType>
+struct Serializer<{{mojom_type}}Ptr, MaybeConstUserType>
+ : public UnionSerializerImpl<{{mojom_type}}Ptr> {
+ using UserType = typename std::remove_const<MaybeConstUserType>::type;
+
+ static_assert(std::is_same<MaybeConstUserType, UserType>::value,
+ "Only support serialization of non-const Unions.");
+ static_assert(std::is_same<UserType, {{mojom_type}}Ptr>::value,
+ "Custom mapping of mojom union is not supported.");
};
} // namespace internal
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl
new file mode 100644
index 0000000..5a11ff7
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl
@@ -0,0 +1,164 @@
+{%- set mojom_type = union|get_qualified_name_for_kind %}
+{%- set data_type = union|get_qualified_name_for_kind(internal=True) %}
+
+namespace internal {
+
+// static
+size_t UnionSerializerImpl<{{mojom_type}}Ptr>::PrepareToSerialize(
+ {{mojom_type}}Ptr& input,
+ bool inlined,
+ SerializationContext* context) {
+ size_t size = inlined ? 0 : sizeof({{data_type}});
+
+ if (!input)
+ return size;
+
+ UnionAccessor<{{mojom_type}}> input_acc(input.get());
+ switch (input->which()) {
+{% for field in union.fields %}
+{% if field.kind|is_object_kind %}
+{%- set serializer_type = field.kind|unmapped_type_for_serializer %}
+ case {{mojom_type}}::Tag::{{field.name|upper}}:
+{% if field.kind|is_union_kind %}
+ size += mojo::internal::PrepareToSerialize<{{serializer_type}}>(
+ *(input_acc.data()->{{field.name}}), false, context);
+{% else %}
+ size += mojo::internal::PrepareToSerialize<{{serializer_type}}>(
+ *(input_acc.data()->{{field.name}}), context);
+{% endif %}
+ break;
+{%- endif %}
+{%- endfor %}
+ default:
+ break;
+ }
+ return size;
+}
+
+// static
+void UnionSerializerImpl<{{mojom_type}}Ptr>::Serialize(
+ {{mojom_type}}Ptr& input,
+ Buffer* buf,
+ {{data_type}}** output,
+ bool inlined,
+ SerializationContext* context) {
+ {{data_type}}* result = *output;
+ if (input) {
+ if (!inlined)
+ result = {{data_type}}::New(buf);
+ UnionAccessor<{{mojom_type}}> input_acc(input.get());
+ // TODO(azani): Handle unknown and objects.
+ // Set the not-null flag.
+ result->size = 16;
+ result->tag = input->which();
+ switch (input->which()) {
+{%- for field in union.fields %}
+ case {{mojom_type}}::Tag::{{field.name|upper}}: {
+{%- set serializer_type = field.kind|unmapped_type_for_serializer %}
+{%- if field.kind|is_object_kind %}
+ typename decltype(result->data.f_{{field.name}})::BaseType* ptr;
+{%- if field.kind|is_union_kind %}
+ mojo::internal::Serialize<{{serializer_type}}>(
+ *(input_acc.data()->{{field.name}}), buf, &ptr, false, context);
+{%- elif field.kind|is_array_kind or field.kind|is_map_kind %}
+ const ContainerValidateParams {{field.name}}_validate_params(
+ {{field.kind|get_container_validate_params_ctor_args|indent(16)}});
+ mojo::internal::Serialize<{{serializer_type}}>(
+ *(input_acc.data()->{{field.name}}), buf, &ptr,
+ &{{field.name}}_validate_params, context);
+{%- else %}
+ mojo::internal::Serialize<{{serializer_type}}>(
+ *(input_acc.data()->{{field.name}}), buf, &ptr, context);
+{%- endif %}
+ result->data.f_{{field.name}}.Set(ptr);
+{%- if not field.kind|is_nullable_kind %}
+ MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+ !ptr, mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER,
+ "null {{field.name}} in {{union.name}} union");
+{%- endif %}
+
+{%- elif field.kind|is_any_handle_or_interface_kind %}
+ mojo::internal::Serialize<{{serializer_type}}>(
+ *input_acc.data()->{{field.name}}, &result->data.f_{{field.name}},
+ context);
+{%- if not field.kind|is_nullable_kind %}
+ MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING(
+ !mojo::internal::IsHandleOrInterfaceValid(result->data.f_{{field.name}}),
+{%- if field.kind|is_associated_kind %}
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_INTERFACE_ID,
+{%- else %}
+ mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE,
+{%- endif %}
+ "invalid {{field.name}} in {{union.name}} union");
+{%- endif %}
+
+{%- elif field.kind|is_enum_kind %}
+ mojo::internal::Serialize<{{serializer_type}}>(
+ input_acc.data()->{{field.name}}, &result->data.f_{{field.name}});
+
+{%- else %}
+ result->data.f_{{field.name}} = input_acc.data()->{{field.name}};
+{%- endif %}
+ break;
+ }
+{%- endfor %}
+ }
+ } else if (inlined) {
+ result->set_null();
+ } else {
+ result = nullptr;
+ }
+ *output = result;
+}
+
+// static
+bool UnionSerializerImpl<{{mojom_type}}Ptr>::Deserialize(
+ {{data_type}}* input,
+ {{mojom_type}}Ptr* output,
+ SerializationContext* context) {
+ bool success = true;
+ if (input && !input->is_null()) {
+ {{mojom_type}}Ptr result({{mojom_type}}::New());
+ UnionAccessor<{{mojom_type}}> result_acc(result.get());
+ switch (input->tag) {
+{%- for field in union.fields %}
+ case {{mojom_type}}::Tag::{{field.name|upper}}: {
+{%- set serializer_type = field.kind|unmapped_type_for_serializer %}
+{%- if field.kind|is_object_kind %}
+ result_acc.SwitchActive({{mojom_type}}::Tag::{{field.name|upper}});
+ if (!mojo::internal::Deserialize<{{serializer_type}}>(
+ input->data.f_{{field.name}}.Get(),
+ result_acc.data()->{{field.name}}, context))
+ success = false;
+
+{%- elif field.kind|is_any_handle_or_interface_kind %}
+ typename std::remove_reference<
+ decltype(result->get_{{field.name}}())>::type result_{{field.name}};
+ bool ret = mojo::internal::Deserialize<{{serializer_type}}>(
+ &input->data.f_{{field.name}}, &result_{{field.name}}, context);
+ DCHECK(ret);
+ result->set_{{field.name}}(std::move(result_{{field.name}}));
+
+{%- elif field.kind|is_enum_kind %}
+ decltype(result->get_{{field.name}}()) result_{{field.name}};
+ if (!mojo::internal::Deserialize<{{serializer_type}}>(
+ input->data.f_{{field.name}}, &result_{{field.name}}))
+ success = false;
+ else
+ result->set_{{field.name}}(result_{{field.name}});
+
+{%- else %}
+ result->set_{{field.name}}(input->data.f_{{field.name}});
+{%- endif %}
+ break;
+ }
+{%- endfor %}
+ }
+ *output = std::move(result);
+ } else {
+ output->reset();
+ }
+ return success;
+}
+
+} // namespace internal
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_traits_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_traits_declaration.tmpl
deleted file mode 100644
index 4933e57..0000000
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_traits_declaration.tmpl
+++ /dev/null
@@ -1,24 +0,0 @@
-{%- set mojom_type = union|get_qualified_name_for_kind %}
-
-template <>
-struct {{export_attribute}} UnionTraits<{{mojom_type}}::DataView,
- {{mojom_type}}Ptr> {
- static bool IsNull(const {{mojom_type}}Ptr& input) { return !input; }
- static void SetToNull({{mojom_type}}Ptr* output) { output->reset(); }
-
- static {{mojom_type}}::Tag GetTag(const {{mojom_type}}Ptr& input) {
- return input->which();
- }
-
-{%- for field in union.fields %}
-{%- set maybe_const_in = "" if field.kind|contains_handles_or_interfaces else "const" %}
-{%- set maybe_const_out = "" if field.kind|contains_handles_or_interfaces or not field.kind|is_reference_kind else "const" %}
-{# We want the field accessor to be const whenever possible to allow
- structs to be used as map keys. #}
- static {{maybe_const_out}} {{field.kind|cpp_union_trait_getter_return_type}} {{field.name}}({{maybe_const_in}} {{mojom_type}}Ptr& input) {
- return input->get_{{field.name}}();
- }
-{%- endfor %}
-
- static bool Read({{mojom_type}}::DataView input, {{mojom_type}}Ptr* output);
-};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/union_traits_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_traits_definition.tmpl
deleted file mode 100644
index cde3f95..0000000
--- a/mojo/public/tools/bindings/generators/cpp_templates/union_traits_definition.tmpl
+++ /dev/null
@@ -1,47 +0,0 @@
-{%- set mojom_type = union|get_qualified_name_for_kind %}
-
-// static
-bool UnionTraits<{{mojom_type}}::DataView, {{mojom_type}}Ptr>::Read(
- {{mojom_type}}::DataView input,
- {{mojom_type}}Ptr* output) {
- *output = {{mojom_type}}::New();
- {{mojom_type}}Ptr& result = *output;
-
- internal::UnionAccessor<{{mojom_type}}> result_acc(result.get());
- switch (input.tag()) {
-{%- for field in union.fields %}
- case {{mojom_type}}::Tag::{{field.name|upper}}: {
-{%- set name = field.name %}
-{%- set kind = field.kind %}
-{%- set serializer_type = kind|unmapped_type_for_serializer %}
-{%- if kind|is_object_kind %}
- result_acc.SwitchActive({{mojom_type}}::Tag::{{name|upper}});
- if (!input.Read{{name|under_to_camel}}(result_acc.data()->{{name}}))
- return false;
-
-{%- elif kind|is_any_handle_kind %}
- auto result_{{name}} = input.Take{{name|under_to_camel}}();
- result->set_{{name}}(std::move(result_{{name}}));
-
-{%- elif kind|is_any_interface_kind %}
- auto result_{{name}} =
- input.Take{{name|under_to_camel}}<typename std::remove_reference<decltype(result->get_{{name}}())>::type>();
- result->set_{{name}}(std::move(result_{{name}}));
-
-{%- elif kind|is_enum_kind %}
- decltype(result->get_{{name}}()) result_{{name}};
- if (!input.Read{{name|under_to_camel}}(&result_{{name}}))
- return false;
- result->set_{{name}}(result_{{name}});
-
-{%- else %}
- result->set_{{name}}(input.{{name}}());
-{%- endif %}
- break;
- }
-{%- endfor %}
- default:
- return false;
- }
- return true;
-}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl
index a50a585..367be6d 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/validation_macros.tmpl
@@ -66,7 +66,7 @@
{#- Validates the specified field, which is supposed to be an enum.
This macro is expanded by the Validate() method. #}
{%- macro validate_enum(field, field_expr) %}
- if (!{{field.kind|get_qualified_name_for_kind(internal=True,flatten_nested_kind=True)}}
+ if (!{{field.kind|get_qualified_name_for_kind(internal=True)}}
::Validate({{field_expr}}, validation_context))
return false;
{%- endmacro %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl
index 9e6e46f..98ae23c 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_declaration.tmpl
@@ -1,24 +1,29 @@
-class {{export_attribute}} {{struct.name}} {
+{% from "enum_macros.tmpl" import enum_decl -%}
+
+class {{struct.name}} {
public:
using DataView = {{struct.name}}DataView;
using Data_ = internal::{{struct.name}}_Data;
{#--- Enums #}
{%- for enum in struct.enums -%}
- using {{enum.name}} = {{enum|get_name_for_kind(flatten_nested_kind=True)}};
+{%- if enum|is_native_only_kind %}
+ using {{enum.name}} = mojo::NativeEnum;
+{%- else %}
+ {{enum_decl(enum)|indent(2)}}
+{%- endif %}
{%- endfor %}
{#--- Constants #}
{%- for constant in struct.constants %}
- static {{constant|format_constant_declaration(nested=True)}};
+{%- if constant.kind|is_integral_kind %}
+ static const {{constant.kind|cpp_pod_type}} {{constant.name}} = {{constant|constant_value}};
+{%- else %}
+ static const {{constant.kind|cpp_pod_type}} {{constant.name}};
+{%- endif %}
{%- endfor %}
- template <typename... Args>
- static {{struct.name}}Ptr New(Args&&... args) {
- return {{struct.name}}Ptr(
- base::in_place,
- std::forward<Args>(args)...);
- }
+ static {{struct.name}}Ptr New();
template <typename U>
static {{struct.name}}Ptr From(const U& u) {
@@ -30,15 +35,7 @@
return mojo::TypeConverter<U, {{struct.name}}>::Convert(*this);
}
-{% for constructor in struct|struct_constructors %}
- {% if constructor.params|length == 1 %}explicit {% endif %}{{struct.name}}(
-{%- for field in constructor.params %}
-{%- set type = field.kind|cpp_wrapper_param_type %}
-{%- set name = field.name %}
- {{type}} {{name}}
-{%- if not loop.last -%},{%- endif %}
-{%- endfor %});
-{% endfor %}
+ {{struct.name}}();
~{{struct.name}}();
// Clone() is a template so it is only instantiated if it is used. Thus, the
@@ -55,24 +52,20 @@
T, {{struct.name}}>::value>::type* = nullptr>
bool Equals(const T& other) const;
-{%- if struct|is_hashable %}
- size_t Hash(size_t seed) const;
-{%- endif %}
-
-{%- set serialization_result_type = "WTF::Vector<uint8_t>"
- if for_blink else "std::vector<uint8_t>" %}
+{%- set serialization_result_type = "mojo::WTFArray<uint8_t>"
+ if for_blink else "mojo::Array<uint8_t>" %}
template <typename UserType>
static {{serialization_result_type}} Serialize(UserType* input) {
return mojo::internal::StructSerializeImpl<
- {{struct.name}}::DataView, {{serialization_result_type}}>(input);
+ {{struct.name}}Ptr, {{serialization_result_type}}>(input);
}
template <typename UserType>
static bool Deserialize(const {{serialization_result_type}}& input,
UserType* output) {
return mojo::internal::StructDeserializeImpl<
- {{struct.name}}::DataView, {{serialization_result_type}}>(
+ {{struct.name}}Ptr, {{serialization_result_type}}>(
input, output);
}
@@ -82,10 +75,5 @@
{%- set name = field.name %}
{{type}} {{name}};
{%- endfor %}
-
-{%- if struct|contains_move_only_members %}
- private:
- DISALLOW_COPY_AND_ASSIGN({{struct.name}});
-{%- endif %}
};
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl
index e75543f..0bb1cda 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl
@@ -1,33 +1,15 @@
-{% for constructor in struct|struct_constructors %}
-{{struct.name}}::{{struct.name}}(
-{%- for field in constructor.params %}
-{%- set type = field.kind|cpp_wrapper_param_type %}
-{%- set name = field.name %}
- {{type}} {{name}}_in
-{%- if not loop.last -%},{%- endif %}
-{%- endfor %})
-{%- for field, is_parameter in constructor.fields %}
-{%- set name = field.name %}
- {% if loop.first %}:{% else %} {% endif %} {{name}}(
-{%- if is_parameter -%}
-std::move({{name}}_in)
-{%- else -%}
-{{ field|default_value }}
-{%- endif -%}
-){% if not loop.last %},{% endif %}
-{%- endfor %} {}
-{% endfor %}
-{{struct.name}}::~{{struct.name}}() = default;
-
-{%- if struct|is_hashable %}
-size_t {{struct.name}}::Hash(size_t seed) const {
-{%- for field in struct.fields %}
-{%- if for_blink %}
- seed = mojo::internal::WTFHash(seed, this->{{field.name}});
-{%- else %}
- seed = mojo::internal::Hash(seed, this->{{field.name}});
-{%- endif %}
-{%- endfor %}
- return seed;
+// static
+{{struct.name}}Ptr {{struct.name}}::New() {
+ {{struct.name}}Ptr rv;
+ mojo::internal::StructHelper<{{struct.name}}>::Initialize(&rv);
+ return rv;
}
-{%- endif %}
+
+{{struct.name}}::{{struct.name}}()
+{%- for field in struct.fields %}
+ {% if loop.first %}:{% else %} {% endif %} {{field.name}}({{field|default_value}}){% if not loop.last %},{% endif %}
+{%- endfor %} {
+}
+
+{{struct.name}}::~{{struct.name}}() {
+}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl
index feb8615..f4aa314 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_template_definition.tmpl
@@ -1,11 +1,12 @@
template <typename StructPtrType>
{{struct.name}}Ptr {{struct.name}}::Clone() const {
- return New(
+ // Use StructPtrType to prevent the compiler from trying to compile this
+ // without being asked.
+ StructPtrType rv(New());
{%- for field in struct.fields %}
- mojo::Clone({{field.name}})
-{%- if not loop.last -%},{%- endif %}
+ rv->{{field.name}} = mojo::internal::Clone({{field.name}});
{%- endfor %}
- );
+ return rv;
}
template <typename T,
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl
index 8b7cf9e..f62bc72 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_declaration.tmpl
@@ -1,6 +1,5 @@
-class {{export_attribute}} {{union.name}} {
+class {{union.name}} {
public:
- using DataView = {{union.name}}DataView;
using Data_ = internal::{{union.name}}_Data;
using Tag = Data_::{{union.name}}_Tag;
@@ -33,27 +32,13 @@
T, {{union.name}}>::value>::type* = nullptr>
bool Equals(const T& other) const;
-{%- if union|is_hashable %}
- size_t Hash(size_t seed) const;
-{%- endif %}
-
Tag which() const {
return tag_;
}
{% for field in union.fields %}
- bool is_{{field.name}}() const { return tag_ == Tag::{{field.name|upper}}; }
-
- {{field.kind|cpp_union_getter_return_type}} get_{{field.name}}() const {
- DCHECK(tag_ == Tag::{{field.name|upper}});
-{%- if field.kind|is_object_kind or
- field.kind|is_any_handle_or_interface_kind %}
- return *(data_.{{field.name}});
-{%- else %}
- return data_.{{field.name}};
-{%- endif %}
- }
-
+ bool is_{{field.name}}() const;
+ {{field.kind|cpp_union_getter_return_type}} get_{{field.name}}() const;
void set_{{field.name}}({{field.kind|cpp_wrapper_param_type}} {{field.name}});
{%- endfor %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl
index b9e416a..85cc4e6 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_definition.tmpl
@@ -1,6 +1,8 @@
// static
{{union.name}}Ptr {{union.name}}::New() {
- return {{union.name}}Ptr(base::in_place);
+ {{union.name}}Ptr rv;
+ mojo::internal::StructHelper<{{union.name}}>::Initialize(&rv);
+ return rv;
}
{{union.name}}::{{union.name}}() {
@@ -14,6 +16,20 @@
}
{% for field in union.fields %}
+bool {{union.name}}::is_{{field.name}}() const {
+ return tag_ == Tag::{{field.name|upper}};
+}
+
+{{field.kind|cpp_union_getter_return_type}} {{union.name}}::get_{{field.name}}() const {
+ DCHECK(tag_ == Tag::{{field.name|upper}});
+{% if field.kind|is_object_kind or
+ field.kind|is_any_handle_or_interface_kind %}
+ return *(data_.{{field.name}});
+{%- else %}
+ return data_.{{field.name}};
+{%- endif %}
+}
+
void {{union.name}}::set_{{field.name}}({{field.kind|cpp_wrapper_param_type}} {{field.name}}) {
SwitchActive(Tag::{{field.name|upper}});
{% if field.kind|is_string_kind %}
@@ -63,23 +79,3 @@
{%- endfor %}
}
}
-
-{%- if union|is_hashable %}
-size_t {{union.name}}::Hash(size_t seed) const {
- seed = mojo::internal::HashCombine(seed, static_cast<uint32_t>(tag_));
- switch (tag_) {
-{% for field in union.fields %}
- case Tag::{{field.name|upper}}:
-{%- if for_blink %}
- return mojo::internal::WTFHash(seed, data_.{{field.name}});
-{%- else %}
- return mojo::internal::Hash(seed, data_.{{field.name}});
-{%- endif %}
-{%- endfor %}
- default:
- NOTREACHED();
- return seed;
- }
-}
-
-{%- endif %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl
index 4c4851f..d3371d9 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl
@@ -8,9 +8,9 @@
case Tag::{{field.name|upper}}:
{%- if field.kind|is_object_kind or
field.kind|is_any_handle_or_interface_kind %}
- rv->set_{{field.name}}(mojo::Clone(*data_.{{field.name}}));
+ rv->set_{{field.name}}(mojo::internal::Clone(*data_.{{field.name}}));
{%- else %}
- rv->set_{{field.name}}(mojo::Clone(data_.{{field.name}}));
+ rv->set_{{field.name}}(mojo::internal::Clone(data_.{{field.name}}));
{%- endif %}
break;
{%- endfor %}
diff --git a/mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl b/mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl
index 4c0823c..5cf9a68 100644
--- a/mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl
@@ -1,19 +1,19 @@
-{%- from "constant_definition.tmpl" import constant_def %}
-{%- from "enum_definition.tmpl" import enum_def %}
+{% from "constant_definition.tmpl" import constant_def %}
+{% from "enum_definition.tmpl" import enum_def %}
{%- macro equality(kind, v1, v2, ne=False) -%}
{%- if kind|is_reference_kind -%}
{%- if kind|is_array_kind -%}
{%- if kind.kind|is_reference_kind -%}
-{%- if ne %}!{%- endif %}java.util.Arrays.deepEquals({{v1}}, {{v2}})
+{% if ne %}!{% endif %}java.util.Arrays.deepEquals({{v1}}, {{v2}})
{%- else -%}
-{%- if ne %}!{%- endif %}java.util.Arrays.equals({{v1}}, {{v2}})
+{% if ne %}!{% endif %}java.util.Arrays.equals({{v1}}, {{v2}})
{%- endif -%}
{%- else -%}
-{%- if ne %}!{%- endif %}org.chromium.mojo.bindings.BindingsHelper.equals({{v1}}, {{v2}})
+{% if ne %}!{% endif %}org.chromium.mojo.bindings.BindingsHelper.equals({{v1}}, {{v2}})
{%- endif -%}
{%- else -%}
-{{v1}} {%- if ne %}!={%- else %}=={%- endif %} {{v2}}
+{{v1}} {% if ne %}!={% else %}=={% endif %} {{v2}}
{%- endif -%}
{%- endmacro -%}
@@ -37,27 +37,27 @@
{%- endif -%}
{%- endmacro -%}
-{%- macro encode(variable, kind, offset, bit, level=0, check_for_null=True) %}
-{%- if kind|is_pointer_array_kind or kind|is_union_array_kind %}
-{%- set sub_kind = kind.kind %}
-{%- if check_for_null %}
+{% macro encode(variable, kind, offset, bit, level=0, check_for_null=True) %}
+{% if kind|is_pointer_array_kind or kind|is_union_array_kind %}
+{% set sub_kind = kind.kind %}
+{% if check_for_null %}
if ({{variable}} == null) {
encoder{{level}}.encodeNullPointer({{offset}}, {{kind|is_nullable_kind|java_true_false}});
} else {
-{%- else %}
+{% else %}
{
-{%- endif %}
-{%- if kind|is_pointer_array_kind %}
-{%- set encodePointer = 'encodePointerArray' %}
-{%- else %}
-{%- set encodePointer = 'encodeUnionArray' %}
-{%- endif %}
+{% endif %}
+{% if kind|is_pointer_array_kind %}
+{% set encodePointer = 'encodePointerArray' %}
+{% else %}
+{% set encodePointer = 'encodeUnionArray' %}
+{% endif %}
org.chromium.mojo.bindings.Encoder encoder{{level + 1}} = encoder{{level}}.{{encodePointer}}({{variable}}.length, {{offset}}, {{kind|array_expected_length}});
for (int i{{level}} = 0; i{{level}} < {{variable}}.length; ++i{{level}}) {
{{encode(variable~'[i'~level~']', sub_kind, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE + ' ~ array_element_size(sub_kind) ~ ' * i'~level, 0, level+1)|indent(8)}}
}
}
-{%- elif kind|is_map_kind %}
+{% elif kind|is_map_kind %}
if ({{variable}} == null) {
encoder{{level}}.encodeNullPointer({{offset}}, {{kind|is_nullable_kind|java_true_false}});
} else {
@@ -74,28 +74,28 @@
{{encode('keys'~level, kind.key_kind|array, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE', 0, level+1, False)|indent(4)}}
{{encode('values'~level, kind.value_kind|array, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE + org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE', 0, level+1, False)|indent(4)}}
}
-{%- else %}
+{% else %}
encoder{{level}}.{{kind|encode_method(variable, offset, bit)}};
-{%- endif %}
-{%- endmacro %}
+{% endif %}
+{% endmacro %}
-{%- macro decode(variable, kind, offset, bit, level=0) %}
-{%- if kind|is_struct_kind or
+{% macro decode(variable, kind, offset, bit, level=0) %}
+{% if kind|is_struct_kind or
kind|is_pointer_array_kind or
kind|is_union_array_kind or
kind|is_map_kind %}
org.chromium.mojo.bindings.Decoder decoder{{level+1}} = decoder{{level}}.readPointer({{offset}}, {{kind|is_nullable_kind|java_true_false}});
-{%- if kind|is_struct_kind %}
+{% if kind|is_struct_kind %}
{{variable}} = {{kind|java_type}}.decode(decoder{{level+1}});
-{%- else %}{# kind|is_pointer_array_kind or is_map_kind #}
-{%- if kind|is_nullable_kind %}
+{% else %}{# kind|is_pointer_array_kind or is_map_kind #}
+{% if kind|is_nullable_kind %}
if (decoder{{level+1}} == null) {
{{variable}} = null;
} else {
-{%- else %}
+{% else %}
{
-{%- endif %}
-{%- if kind|is_map_kind %}
+{% endif %}
+{% if kind|is_map_kind %}
decoder{{level+1}}.readDataHeaderForMap();
{{kind.key_kind|java_type}}[] keys{{level}};
{{kind.value_kind|java_type}}[] values{{level}};
@@ -109,69 +109,69 @@
for (int index{{level}} = 0; index{{level}} < keys{{level}}.length; ++index{{level}}) {
{{variable}}.put(keys{{level}}[index{{level}}], values{{level}}[index{{level}}]);
}
-{%- else %}
+{% else %}
org.chromium.mojo.bindings.DataHeader si{{level+1}} = decoder{{level+1}}.readDataHeaderForPointerArray({{kind|array_expected_length}});
{{variable}} = {{kind|new_array('si'~(level+1)~'.elementsOrVersion')}};
for (int i{{level+1}} = 0; i{{level+1}} < si{{level+1}}.elementsOrVersion; ++i{{level+1}}) {
{{decode(variable~'[i'~(level+1)~']', kind.kind, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE + ' ~ array_element_size(kind.kind) ~' * i'~(level+1), 0, level+1)|indent(8)}}
}
-{%- endif %}
+{% endif %}
}
-{%- endif %}
-{%- elif kind|is_union_kind %}
+{% endif %}
+{% elif kind|is_union_kind %}
{{variable}} = {{kind|java_type}}.decode(decoder{{level}}, {{offset}});
-{%- else %}
+{% else %}
{{variable}} = decoder{{level}}.{{kind|decode_method(offset, bit)}};
-{%- if kind|is_array_kind and kind.kind|is_enum_kind %}
-{%- if kind|is_nullable_kind %}
+{% if kind|is_array_kind and kind.kind|is_enum_kind %}
+{% if kind|is_nullable_kind %}
if ({{variable}} != null) {
-{%- else %}
+{% else %}
{
-{%- endif %}
+{% endif %}
for (int i{{level}} = 0; i{{level}} < {{variable}}.length; ++i{{level}}) {
{{kind.kind|java_class_for_enum}}.validate({{variable}}[i{{level}}]);
}
}
-{%- elif kind|is_enum_kind %}
+{% elif kind|is_enum_kind %}
{{kind|java_class_for_enum}}.validate({{variable}});
-{%- endif %}
-{%- endif %}
-{%- endmacro %}
+{% endif %}
+{% endif %}
+{% endmacro %}
-{%- macro struct_def(struct, inner_class=False) %}
+{% macro struct_def(struct, inner_class=False) %}
{{'static' if inner_class else 'public'}} final class {{struct|name}} extends org.chromium.mojo.bindings.Struct {
private static final int STRUCT_SIZE = {{struct.versions[-1].num_bytes}};
private static final org.chromium.mojo.bindings.DataHeader[] VERSION_ARRAY = new org.chromium.mojo.bindings.DataHeader[] {
{%- for version in struct.versions -%}
- new org.chromium.mojo.bindings.DataHeader({{version.num_bytes}}, {{version.version}}){%- if not loop.last %}, {%- endif -%}
+ new org.chromium.mojo.bindings.DataHeader({{version.num_bytes}}, {{version.version}}){% if not loop.last %}, {% endif -%}
{%- endfor -%}
};
private static final org.chromium.mojo.bindings.DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[{{struct.versions|length - 1}}];
-{%- for constant in struct.constants %}
+{% for constant in struct.constants %}
{{constant_def(constant)|indent(4)}}
-{%- endfor %}
-{%- for enum in struct.enums %}
+{% endfor %}
+{% for enum in struct.enums %}
{{enum_def(enum, false)|indent(4)}}
-{%- endfor %}
-{%- if struct.fields %}
+{% endfor %}
+{% if struct.fields %}
-{%- for field in struct.fields %}
+{% for field in struct.fields %}
public {{field.kind|java_type}} {{field|name}};
-{%- endfor %}
-{%- endif %}
+{% endfor %}
+{% endif %}
private {{struct|name}}(int version) {
super(STRUCT_SIZE, version);
-{%- for field in struct.fields %}
-{%- if field.default %}
+{% for field in struct.fields %}
+{% if field.default %}
{{field|name}} = {{field|default_value}};
-{%- elif field.kind|is_any_handle_kind %}
+{% elif field.kind|is_any_handle_kind and not field.kind|is_interface_request_kind %}
{{field|name}} = org.chromium.mojo.system.InvalidHandle.INSTANCE;
-{%- endif %}
-{%- endfor %}
+{% endif %}
+{% endfor %}
}
public {{struct|name}}() {
@@ -182,55 +182,36 @@
return decode(new org.chromium.mojo.bindings.Decoder(message));
}
- /**
- * Similar to the method above, but deserializes from a |ByteBuffer| instance.
- *
- * @throws org.chromium.mojo.bindings.DeserializationException on deserialization failure.
- */
- public static {{struct|name}} deserialize(java.nio.ByteBuffer data) {
- if (data == null)
- return null;
-
- return deserialize(new org.chromium.mojo.bindings.Message(
- data, new java.util.ArrayList<org.chromium.mojo.system.Handle>()));
- }
-
@SuppressWarnings("unchecked")
public static {{struct|name}} decode(org.chromium.mojo.bindings.Decoder decoder0) {
if (decoder0 == null) {
return null;
}
- decoder0.increaseStackDepth();
- {{struct|name}} result;
- try {
- org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY);
- result = new {{struct|name}}(mainDataHeader.elementsOrVersion);
-{%- for byte in struct.bytes %}
-{%- for packed_field in byte.packed_fields %}
- if (mainDataHeader.elementsOrVersion >= {{packed_field.min_version}}) {
- {{decode('result.' ~ packed_field.field|name, packed_field.field.kind, 8+packed_field.offset, packed_field.bit)|indent(16)}}
- }
-{%- endfor %}
-{%- endfor %}
- } finally {
- decoder0.decreaseStackDepth();
+ org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY);
+ {{struct|name}} result = new {{struct|name}}(mainDataHeader.elementsOrVersion);
+{% for byte in struct.bytes %}
+{% for packed_field in byte.packed_fields %}
+ if (mainDataHeader.elementsOrVersion >= {{packed_field.min_version}}) {
+ {{decode('result.' ~ packed_field.field|name, packed_field.field.kind, 8+packed_field.offset, packed_field.bit)|indent(12)}}
}
+{% endfor %}
+{% endfor %}
return result;
}
@SuppressWarnings("unchecked")
@Override
protected final void encode(org.chromium.mojo.bindings.Encoder encoder) {
-{%- if not struct.bytes %}
+{% if not struct.bytes %}
encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO);
-{%- else %}
+{% else %}
org.chromium.mojo.bindings.Encoder encoder0 = encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO);
-{%- endif %}
-{%- for byte in struct.bytes %}
-{%- for packed_field in byte.packed_fields %}
+{% endif %}
+{% for byte in struct.bytes %}
+{% for packed_field in byte.packed_fields %}
{{encode(packed_field.field|name, packed_field.field.kind, 8+packed_field.offset, packed_field.bit)|indent(8)}}
-{%- endfor %}
-{%- endfor %}
+{% endfor %}
+{% endfor %}
}
/**
@@ -244,13 +225,13 @@
return false;
if (getClass() != object.getClass())
return false;
-{%- if struct.fields|length %}
+{% if struct.fields|length %}
{{struct|name}} other = ({{struct|name}}) object;
-{%- for field in struct.fields %}
+{% for field in struct.fields %}
if ({{equality(field.kind, 'this.'~field|name, 'other.'~field|name, True)}})
return false;
-{%- endfor %}
-{%- endif %}
+{% endfor %}
+{% endif %}
return true;
}
@@ -261,28 +242,28 @@
public int hashCode() {
final int prime = 31;
int result = prime + getClass().hashCode();
-{%- for field in struct.fields %}
+{% for field in struct.fields %}
result = prime * result + {{hash(field.kind, field|name)}};
-{%- endfor %}
+{% endfor %}
return result;
}
}
-{%- endmacro %}
+{% endmacro %}
-{%- macro union_def(union) %}
+{% macro union_def(union) %}
public final class {{union|name}} extends org.chromium.mojo.bindings.Union {
public static final class Tag {
-{%- for field in union.fields %}
+{% for field in union.fields %}
public static final int {{field|ucc}} = {{loop.index0}};
-{%- endfor %}
+{% endfor %}
};
private int mTag_ = -1;
-{%- for field in union.fields %}
+{% for field in union.fields %}
private {{field.kind|java_type}} m{{field|ucc}};
-{%- endfor %}
+{% endfor %}
public int which() {
return mTag_;
@@ -291,7 +272,7 @@
public boolean isUnknown() {
return mTag_ == -1;
}
-{%- for field in union.fields %}
+{% for field in union.fields %}
// TODO(rockot): Fix the findbugs error and remove this suppression.
// See http://crbug.com/570386.
@@ -308,7 +289,7 @@
assert mTag_ == Tag.{{field|ucc}};
return m{{field|ucc}};
}
-{%- endfor %}
+{% endfor %}
@Override
@@ -316,20 +297,20 @@
encoder0.encode(org.chromium.mojo.bindings.BindingsHelper.UNION_SIZE, offset);
encoder0.encode(mTag_, offset + 4);
switch (mTag_) {
-{%- for field in union.fields %}
+{% for field in union.fields %}
case Tag.{{field|ucc}}: {
-{%- if field.kind|is_union_kind %}
+{% if field.kind|is_union_kind %}
if (m{{field|ucc}} == null) {
encoder0.encodeNullPointer(offset + 8, {{field.kind|is_nullable_kind|java_true_false}});
} else {
m{{field|ucc}}.encode(encoder0.encoderForUnionPointer(offset + 8), 0);
}
-{%- else %}
+{% else %}
{{encode('m' ~ field|ucc, field.kind, 'offset + 8', 0)|indent(16)}}
-{%- endif %}
+{% endif %}
break;
}
-{%- endfor %}
+{% endfor %}
default: {
break;
}
@@ -347,20 +328,20 @@
}
{{union|name}} result = new {{union|name}}();
switch (dataHeader.elementsOrVersion) {
-{%- for field in union.fields %}
+{% for field in union.fields %}
case Tag.{{field|ucc}}: {
-{%- if field.kind|is_union_kind %}
+{% if field.kind|is_union_kind %}
org.chromium.mojo.bindings.Decoder decoder1 = decoder0.readPointer(offset + org.chromium.mojo.bindings.DataHeader.HEADER_SIZE, {{field.kind|is_nullable_kind|java_true_false}});
if (decoder1 != null) {
result.m{{field|ucc}} = {{field.kind|name}}.decode(decoder1, 0);
}
-{%- else %}
+{% else %}
{{decode('result.m'~field|ucc, field.kind, 'offset + org.chromium.mojo.bindings.DataHeader.HEADER_SIZE', 0)|indent(16)}}
-{%- endif %}
+{% endif %}
result.mTag_ = Tag.{{field|ucc}};
break;
}
-{%- endfor %}
+{% endfor %}
default: {
break;
}
@@ -383,10 +364,10 @@
if (mTag_ != other.mTag_)
return false;
switch (mTag_) {
-{%- for field in union.fields %}
+{% for field in union.fields %}
case Tag.{{field|ucc}}:
return {{equality(field.kind, 'm'~field|ucc, 'other.m'~field|ucc)}};
-{%- endfor %}
+{% endfor %}
default:
break;
}
@@ -402,12 +383,12 @@
int result = prime + getClass().hashCode();
result = prime * result + org.chromium.mojo.bindings.BindingsHelper.hashCode(mTag_);
switch (mTag_) {
-{%- for field in union.fields %}
+{% for field in union.fields %}
case Tag.{{field|ucc}}: {
result = prime * result + {{hash(field.kind, 'm'~field|ucc)}};
break;
}
-{%- endfor %}
+{% endfor %}
default: {
break;
}
@@ -415,4 +396,4 @@
return result;
}
}
-{%- endmacro %}
+{% endmacro %}
diff --git a/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl
index c7dcbbc..3928e0c 100644
--- a/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/java_templates/interface_definition.tmpl
@@ -91,11 +91,11 @@
}
switch(header.getType()) {
{% if with_response %}
- case org.chromium.mojo.bindings.interfacecontrol.InterfaceControlMessagesConstants.RUN_MESSAGE_ID:
+ case org.chromium.mojo.bindings.InterfaceControlMessagesConstants.RUN_MESSAGE_ID:
return org.chromium.mojo.bindings.InterfaceControlMessagesHelper.handleRun(
getCore(), {{interface|name}}_Internal.MANAGER, messageWithHeader, receiver);
{% else %}
- case org.chromium.mojo.bindings.interfacecontrol.InterfaceControlMessagesConstants.RUN_OR_CLOSE_PIPE_MESSAGE_ID:
+ case org.chromium.mojo.bindings.InterfaceControlMessagesConstants.RUN_OR_CLOSE_PIPE_MESSAGE_ID:
return org.chromium.mojo.bindings.InterfaceControlMessagesHelper.handleRunOrClosePipe(
{{interface|name}}_Internal.MANAGER, messageWithHeader);
{% endif %}
@@ -113,7 +113,12 @@
{% else %}
{{request_struct|name}}.deserialize(messageWithHeader.getPayload());
{% endif %}
- getImpl().{{method|name}}({{run_callback('data', method.parameters)}}{% if with_response %}{% if method.parameters %}, {% endif %}new {{response_struct|name}}ProxyToResponder(getCore(), receiver, header.getRequestId()){% endif %});
+ try {
+ getImpl().{{method|name}}({{run_callback('data', method.parameters)}}{% if with_response %}{% if method.parameters %}, {% endif %}new {{response_struct|name}}ProxyToResponder(getCore(), receiver, header.getRequestId()){% endif %});
+ } catch (RuntimeException e) {
+ // TODO(lhchavez): Remove this hack. See b/28814913 for details.
+ android.util.Log.wtf("{{namespace}}.{{interface.name}}", "Uncaught runtime exception", e);
+ }
return true;
}
{% endif %}
diff --git a/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl
index 019b1b6..4ae0a9b 100644
--- a/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl
@@ -1,33 +1,13 @@
{%- macro enum_def(enum_name, enum) -%}
{{enum_name}} = {};
-{%- set prev_enum = 0 %}
-{%- for field in enum.fields %}
-{%- if field.value %}
+{%- set prev_enum = 0 %}
+{%- for field in enum.fields %}
+{%- if field.value %}
{{enum_name}}.{{field.name}} = {{field.value|expression_to_text}};
-{%- elif loop.first %}
+{%- elif loop.first %}
{{enum_name}}.{{field.name}} = 0;
-{%- else %}
+{%- else %}
{{enum_name}}.{{field.name}} = {{enum_name}}.{{enum.fields[loop.index0 - 1].name}} + 1;
-{%- endif %}
-{%- endfor %}
-
- {{enum_name}}.isKnownEnumValue = function(value) {
-{%- if enum.fields %}
- switch (value) {
-{%- for enum_field in enum.fields|groupby('numeric_value') %}
- case {{enum_field[0]}}:
-{%- endfor %}
- return true;
- }
{%- endif %}
- return false;
- };
-
- {{enum_name}}.validate = function(enumValue) {
- var isExtensible = {% if enum.extensible %}true{% else %}false{% endif %};
- if (isExtensible || this.isKnownEnumValue(enumValue))
- return validator.validationError.NONE;
-
- return validator.validationError.UNKNOWN_ENUM_VALUE;
- };
+{%- endfor %}
{%- endmacro %}
diff --git a/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
index 54e2d4e..1b5cafa 100644
--- a/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl
@@ -2,21 +2,12 @@
var k{{interface.name}}_{{method.name}}_Name = {{method.ordinal}};
{%- endfor %}
- function {{interface.name}}Ptr(handleOrPtrInfo) {
- this.ptr = new bindings.InterfacePtrController({{interface.name}},
- handleOrPtrInfo);
- }
-
function {{interface.name}}Proxy(receiver) {
- this.receiver_ = receiver;
+ bindings.ProxyBase.call(this, receiver);
}
+ {{interface.name}}Proxy.prototype = Object.create(bindings.ProxyBase.prototype);
{%- for method in interface.methods %}
- {{interface.name}}Ptr.prototype.{{method.name|stylize_method}} = function() {
- return {{interface.name}}Proxy.prototype.{{method.name|stylize_method}}
- .apply(this.ptr.getProxy(), arguments);
- };
-
{{interface.name}}Proxy.prototype.{{method.name|stylize_method}} = function(
{%- for parameter in method.parameters -%}
{{parameter.name}}{% if not loop.last %}, {% endif %}
@@ -24,7 +15,7 @@
) {
var params = new {{interface.name}}_{{method.name}}_Params();
{%- for parameter in method.parameters %}
- params.{{parameter.name}} = {{parameter.name}};
+ params.{{parameter.name}} = {{parameter|js_proxy_method_parameter_value}};
{%- endfor %}
{%- if method.response_parameters == None %}
@@ -56,13 +47,15 @@
{%- endfor %}
function {{interface.name}}Stub(delegate) {
- this.delegate_ = delegate;
+ bindings.StubBase.call(this, delegate);
}
+ {{interface.name}}Stub.prototype = Object.create(bindings.StubBase.prototype);
{%- for method in interface.methods %}
{%- set js_method_name = method.name|stylize_method %}
+{%- set delegate_expr = "bindings.StubBindings(this).delegate" %}
{{interface.name}}Stub.prototype.{{js_method_name}} = function({{method.parameters|map(attribute='name')|join(', ')}}) {
- return this.delegate_ && this.delegate_.{{js_method_name}} && this.delegate_.{{js_method_name}}({{method.parameters|map(attribute='name')|join(', ')}});
+ return {{delegate_expr}} && {{delegate_expr}}.{{js_method_name}} && {{delegate_expr}}.{{js_method_name}}({{method.parameters|map('js_stub_method_parameter_value')|join(', ')}});
}
{%- endfor %}
@@ -169,8 +162,6 @@
var {{interface.name}} = {
name: '{{namespace|replace(".","::")}}::{{interface.name}}',
- kVersion: {{interface.version}},
- ptrClass: {{interface.name}}Ptr,
proxyClass: {{interface.name}}Proxy,
stubClass: {{interface.name}}Stub,
validateRequest: validate{{interface.name}}Request,
diff --git a/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl b/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl
index 3ce4ab6..6d7a1a2 100644
--- a/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl
@@ -3,19 +3,15 @@
// found in the LICENSE file.
define("{{module.path}}", [
-{%- if module.path != "mojo/public/interfaces/bindings/interface_control_messages.mojom" %}
"mojo/public/js/bindings",
-{%- endif %}
"mojo/public/js/codec",
+ "mojo/public/js/connection",
"mojo/public/js/core",
"mojo/public/js/validator",
{%- for import in imports %}
"{{import.module.path}}",
{%- endfor %}
-], function(
-{%- if module.path != "mojo/public/interfaces/bindings/interface_control_messages.mojom" -%}
-bindings, {% endif -%}
-codec, core, validator
+], function(bindings, codec, connection, core, validator
{%- for import in imports -%}
, {{import.unique_name}}
{%- endfor -%}
diff --git a/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl
index ddfef72..86ea2ce 100644
--- a/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl
@@ -40,7 +40,6 @@
{%- endfor %}
{%- for interface in interfaces %}
exports.{{interface.name}} = {{interface.name}};
- exports.{{interface.name}}Ptr = {{interface.name}}Ptr;
{#--- Interface Client #}
{%- if interface.client in interfaces|map(attribute='name') %}
exports.{{interface.name}}.client = {{interface.client}};
diff --git a/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
index e823e46..ca80d67 100644
--- a/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl
@@ -34,40 +34,16 @@
{{struct.name}}.validate = function(messageValidator, offset) {
var err;
- err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize);
+ err = messageValidator.validateStructHeader(offset, {{struct.name}}.encodedSize, {{struct.versions[-1].version}});
if (err !== validator.validationError.NONE)
return err;
- var kVersionSizes = [
-{%- for version in struct.versions %}
- {version: {{version.version}}, numBytes: {{version.num_bytes}}}{% if not loop.last %},
- {%- endif -%}
-{% endfor %}
- ];
- err = messageValidator.validateStructVersion(offset, kVersionSizes);
- if (err !== validator.validationError.NONE)
- return err;
-
-{#- Before validating fields introduced at a certain version, we need to add
- a version check, which makes sure we skip further validation if |object|
- is from an earlier version. |last_checked_version| records the last
- version that we have added such version check. #}
{%- from "validation_macros.tmpl" import validate_struct_field %}
-{%- set last_checked_version = 0 %}
-{%- for packed_field in struct.packed.packed_fields_in_ordinal_order %}
+{%- for packed_field in struct.packed.packed_fields %}
{%- set offset = packed_field|field_offset %}
{%- set field = packed_field.field %}
{%- set name = struct.name ~ '.' ~ field.name %}
-{% if field|is_object_field or field|is_any_handle_or_interface_field or
- field|is_enum_field %}
-{% if packed_field.min_version > last_checked_version %}
-{% set last_checked_version = packed_field.min_version %}
- // version check {{name}}
- if (!messageValidator.isFieldInStructVersion(offset, {{packed_field.min_version}}))
- return validator.validationError.NONE;
-{%- endif -%}
{{validate_struct_field(field, offset, name)|indent(4)}}
-{%- endif %}
{%- endfor %}
return validator.validationError.NONE;
@@ -83,8 +59,7 @@
var numberOfBytes = decoder.readUint32();
var version = decoder.readUint32();
{%- for byte in struct.bytes %}
-{%- if byte.packed_fields|length >= 1 and
- byte.packed_fields[0].field|is_bool_field %}
+{%- if byte.packed_fields|length > 1 %}
packed = decoder.readUint8();
{%- for packed_field in byte.packed_fields %}
val.{{packed_field.field.name}} = (packed >> {{packed_field.bit}}) & 1 ? true : false;
@@ -107,8 +82,7 @@
encoder.writeUint32({{struct.versions[-1].version}});
{%- for byte in struct.bytes %}
-{%- if byte.packed_fields|length >= 1 and
- byte.packed_fields[0].field|is_bool_field %}
+{%- if byte.packed_fields|length > 1 %}
packed = 0;
{%- for packed_field in byte.packed_fields %}
packed |= (val.{{packed_field.field.name}} & 1) << {{packed_field.bit}}
diff --git a/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl
index 4823feb..3c903bc 100644
--- a/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl
@@ -89,11 +89,7 @@
switch (val.$tag) {
{%- for field in union.fields %}
case {{union.name}}.Tags.{{field.name}}:
-{%- if field|is_bool_field %}
- encoder.writeUint8(val.{{field.name}} ? 1 : 0);
-{%- else %}
encoder.{{field.kind|union_encode_snippet}}val.{{field.name}});
-{%- endif %}
break;
{%- endfor %}
}
@@ -115,11 +111,7 @@
switch (tag) {
{%- for field in union.fields %}
case {{union.name}}.Tags.{{field.name}}:
-{%- if field|is_bool_field %}
- result.{{field.name}} = decoder.readUint8() ? true : false;
-{%- else %}
result.{{field.name}} = decoder.{{field.kind|union_decode_snippet}};
-{%- endif %}
break;
{%- endfor %}
}
diff --git a/mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl b/mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl
index d4e15a7..7a39749 100644
--- a/mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl
+++ b/mojo/public/tools/bindings/generators/js_templates/validation_macros.tmpl
@@ -6,7 +6,7 @@
{%- macro _validate_field(field, offset, name) %}
{%- if field|is_string_pointer_field %}
// validate {{name}}
-err = messageValidator.validateStringPointer({{offset}}, {{field|validate_nullable_params}})
+err = messageValidator.validateStringPointer({{offset}}, {{field|validate_string_params}})
{{_check_err()}}
{%- elif field|is_array_pointer_field %}
// validate {{name}}
@@ -22,19 +22,11 @@
{{_check_err()}}
{%- elif field|is_interface_field %}
// validate {{name}}
-err = messageValidator.validateInterface({{offset}}, {{field|validate_nullable_params}});
-{{_check_err()}}
-{%- elif field|is_interface_request_field %}
-// validate {{name}}
-err = messageValidator.validateInterfaceRequest({{offset}}, {{field|validate_nullable_params}})
+err = messageValidator.validateInterface({{offset}}, {{field|validate_interface_params}});
{{_check_err()}}
{%- elif field|is_handle_field %}
// validate {{name}}
-err = messageValidator.validateHandle({{offset}}, {{field|validate_nullable_params}})
-{{_check_err()}}
-{%- elif field|is_enum_field %}
-// validate {{name}}
-err = messageValidator.validateEnum({{offset}}, {{field|validate_enum_params}});
+err = messageValidator.validateHandle({{offset}}, {{field|validate_handle_params}})
{{_check_err()}}
{%- endif %}
{%- endmacro %}
diff --git a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
index 38d222b..38b219c 100644
--- a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
@@ -36,10 +36,10 @@
# generator library code so that filters can use the generator as context.
_current_typemap = {}
_for_blink = False
+_use_new_wrapper_types = False
# TODO(rockot, yzshen): The variant handling is kind of a hack currently. Make
# it right.
_variant = None
-_export_attribute = None
class _NameFormatter(object):
@@ -50,40 +50,22 @@
self._variant = variant
def Format(self, separator, prefixed=False, internal=False,
- include_variant=False, add_same_module_namespaces=False,
- flatten_nested_kind=False):
- """Formats the name according to the given configuration.
-
- Args:
- separator: Separator between different parts of the name.
- prefixed: Whether a leading separator should be added.
- internal: Returns the name in the "internal" namespace.
- include_variant: Whether to include variant as namespace. If |internal| is
- True, then this flag is ignored and variant is not included.
- add_same_module_namespaces: Includes all namespaces even if the token is
- from the same module as the current mojom file.
- flatten_nested_kind: It is allowed to define enums inside structs and
- interfaces. If this flag is set to True, this method concatenates the
- parent kind and the nested kind with '_', instead of treating the
- parent kind as a scope."""
-
+ include_variant=False, add_same_module_namespaces=False):
parts = []
if self._ShouldIncludeNamespace(add_same_module_namespaces):
if prefixed:
parts.append("")
parts.extend(self._GetNamespace())
- if include_variant and self._variant and not internal:
+ if include_variant and self._variant:
parts.append(self._variant)
- parts.extend(self._GetName(internal, flatten_nested_kind))
+ parts.extend(self._GetName(internal))
return separator.join(parts)
- def FormatForCpp(self, add_same_module_namespaces=False, internal=False,
- flatten_nested_kind=False):
+ def FormatForCpp(self, add_same_module_namespaces=False, internal=False):
return self.Format(
"::", prefixed=True,
add_same_module_namespaces=add_same_module_namespaces,
- internal=internal, include_variant=True,
- flatten_nested_kind=flatten_nested_kind)
+ internal=internal, include_variant=True)
def FormatForMojom(self):
return self.Format(".", add_same_module_namespaces=True)
@@ -92,32 +74,24 @@
if not internal:
return token.name
if (mojom.IsStructKind(token) or mojom.IsUnionKind(token) or
- mojom.IsEnumKind(token)):
+ mojom.IsInterfaceKind(token) or mojom.IsEnumKind(token)):
return token.name + "_Data"
return token.name
- def _GetName(self, internal, flatten_nested_kind):
- if isinstance(self._token, mojom.EnumValue):
- name_parts = _NameFormatter(self._token.enum, self._variant)._GetName(
- internal, flatten_nested_kind)
- name_parts.append(self._token.name)
- return name_parts
-
- name_parts = []
+ def _GetName(self, internal):
+ name = []
if internal:
- name_parts.append("internal")
-
- if (flatten_nested_kind and mojom.IsEnumKind(self._token) and
- self._token.parent_kind):
- name = "%s_%s" % (self._token.parent_kind.name,
- self._MapKindName(self._token, internal))
- name_parts.append(name)
- return name_parts
-
+ name.append("internal")
if self._token.parent_kind:
- name_parts.append(self._MapKindName(self._token.parent_kind, internal))
- name_parts.append(self._MapKindName(self._token, internal))
- return name_parts
+ name.append(self._MapKindName(self._token.parent_kind, internal))
+ # Both variable and enum constants are constructed like:
+ # Namespace::Struct::CONSTANT_NAME
+ # For enums, CONSTANT_NAME is EnumName::ENUM_VALUE.
+ if isinstance(self._token, mojom.EnumValue):
+ name.extend([self._token.enum.name, self._token.name])
+ else:
+ name.append(self._MapKindName(self._token, internal))
+ return name
def _ShouldIncludeNamespace(self, add_same_module_namespaces):
return add_same_module_namespaces or self._token.imported_from
@@ -142,30 +116,22 @@
if not IsTypemappedKind(field.kind):
return "%s::New()" % GetNameForKind(field.kind)
return ExpressionToText(field.default, kind=field.kind)
+ if not _use_new_wrapper_types:
+ if mojom.IsArrayKind(field.kind) or mojom.IsMapKind(field.kind):
+ return "nullptr";
+ if mojom.IsStringKind(field.kind):
+ return "" if _for_blink else "nullptr"
return ""
def NamespaceToArray(namespace):
return namespace.split(".") if namespace else []
-def GetNameForKind(kind, internal=False, flatten_nested_kind=False,
- add_same_module_namespaces=False):
+def GetNameForKind(kind, internal=False):
+ return _NameFormatter(kind, _variant).FormatForCpp(internal=internal)
+
+def GetQualifiedNameForKind(kind, internal=False):
return _NameFormatter(kind, _variant).FormatForCpp(
- internal=internal, flatten_nested_kind=flatten_nested_kind,
- add_same_module_namespaces=add_same_module_namespaces)
-
-def GetQualifiedNameForKind(kind, internal=False, flatten_nested_kind=False,
- include_variant=True):
- return _NameFormatter(
- kind, _variant if include_variant else None).FormatForCpp(
- internal=internal, add_same_module_namespaces=True,
- flatten_nested_kind=flatten_nested_kind)
-
-
-def GetWtfHashFnNameForEnum(enum):
- return _NameFormatter(
- enum, None).Format("_", internal=True, add_same_module_namespaces=True,
- flatten_nested_kind=True) + "HashFn"
-
+ internal=internal, add_same_module_namespaces=True)
def GetFullMojomNameForKind(kind):
return _NameFormatter(kind, _variant).FormatForMojom()
@@ -178,128 +144,60 @@
return (mojom.IsStructKind(kind) or mojom.IsEnumKind(kind)) and \
kind.native_only
-
-def IsHashableKind(kind):
- """Check if the kind can be hashed.
-
- Args:
- kind: {Kind} The kind to check.
-
- Returns:
- {bool} True if a value of this kind can be hashed.
- """
- checked = set()
- def Check(kind):
- if kind.spec in checked:
- return True
- checked.add(kind.spec)
- if mojom.IsNullableKind(kind):
- return False
- elif mojom.IsStructKind(kind):
- if (IsTypemappedKind(kind) and
- not _current_typemap[GetFullMojomNameForKind(kind)]["hashable"]):
- return False
- return all(Check(field.kind) for field in kind.fields)
- elif mojom.IsEnumKind(kind):
- return not IsTypemappedKind(kind) or _current_typemap[
- GetFullMojomNameForKind(kind)]["hashable"]
- elif mojom.IsUnionKind(kind):
- return all(Check(field.kind) for field in kind.fields)
- elif mojom.IsAnyHandleKind(kind):
- return False
- elif mojom.IsAnyInterfaceKind(kind):
- return False
- # TODO(tibell): Arrays and maps could be made hashable. We just don't have a
- # use case yet.
- elif mojom.IsArrayKind(kind):
- return False
- elif mojom.IsMapKind(kind):
- return False
- else:
- return True
- return Check(kind)
-
-
-def AllEnumValues(enum):
- """Return all enum values associated with an enum.
-
- Args:
- enum: {mojom.Enum} The enum type.
-
- Returns:
- {Set[int]} The values.
- """
- return set(field.numeric_value for field in enum.fields)
-
-
def GetNativeTypeName(typemapped_kind):
return _current_typemap[GetFullMojomNameForKind(typemapped_kind)]["typename"]
def GetCppPodType(kind):
+ if mojom.IsStringKind(kind):
+ return "char*"
return _kind_to_cpp_type[kind]
-def FormatConstantDeclaration(constant, nested=False):
- if mojom.IsStringKind(constant.kind):
- if nested:
- return "const char %s[]" % constant.name
- return "%sextern const char %s[]" % \
- ((_export_attribute + " ") if _export_attribute else "", constant.name)
- return "constexpr %s %s = %s" % (GetCppPodType(constant.kind), constant.name,
- ConstantValue(constant))
-
-def GetCppWrapperType(kind, add_same_module_namespaces=False):
- def _AddOptional(type_name):
- pattern = "WTF::Optional<%s>" if _for_blink else "base::Optional<%s>"
- return pattern % type_name
-
+def GetCppWrapperType(kind):
if IsTypemappedKind(kind):
- type_name = GetNativeTypeName(kind)
- if (mojom.IsNullableKind(kind) and
- not _current_typemap[GetFullMojomNameForKind(kind)][
- "nullable_is_same_type"]):
- type_name = _AddOptional(type_name)
- return type_name
+ return GetNativeTypeName(kind)
if mojom.IsEnumKind(kind):
- return GetNameForKind(
- kind, add_same_module_namespaces=add_same_module_namespaces)
+ return GetNameForKind(kind)
if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
- return "%sPtr" % GetNameForKind(
- kind, add_same_module_namespaces=add_same_module_namespaces)
+ return "%sPtr" % GetNameForKind(kind)
if mojom.IsArrayKind(kind):
- pattern = "WTF::Vector<%s>" if _for_blink else "std::vector<%s>"
- if mojom.IsNullableKind(kind):
- pattern = _AddOptional(pattern)
- return pattern % GetCppWrapperType(
- kind.kind, add_same_module_namespaces=add_same_module_namespaces)
+ pattern = None
+ if _use_new_wrapper_types:
+ if mojom.IsNullableKind(kind):
+ pattern = ("WTF::Optional<WTF::Vector<%s>>" if _for_blink else
+ "base::Optional<std::vector<%s>>")
+ else:
+ pattern = "WTF::Vector<%s>" if _for_blink else "std::vector<%s>"
+ else:
+ pattern = "mojo::WTFArray<%s>" if _for_blink else "mojo::Array<%s>"
+ return pattern % GetCppWrapperType(kind.kind)
if mojom.IsMapKind(kind):
- pattern = ("WTF::HashMap<%s, %s>" if _for_blink else
- "std::unordered_map<%s, %s>")
- if mojom.IsNullableKind(kind):
- pattern = _AddOptional(pattern)
- return pattern % (
- GetCppWrapperType(
- kind.key_kind,
- add_same_module_namespaces=add_same_module_namespaces),
- GetCppWrapperType(
- kind.value_kind,
- add_same_module_namespaces=add_same_module_namespaces))
+ pattern = None
+ if _use_new_wrapper_types:
+ if mojom.IsNullableKind(kind):
+ pattern = ("WTF::Optional<WTF::HashMap<%s, %s>>" if _for_blink else
+ "base::Optional<std::unordered_map<%s, %s>>")
+ else:
+ pattern = ("WTF::HashMap<%s, %s>" if _for_blink else
+ "std::unordered_map<%s, %s>")
+ else:
+ pattern = "mojo::WTFMap<%s, %s>" if _for_blink else "mojo::Map<%s, %s>"
+ return pattern % (GetCppWrapperType(kind.key_kind),
+ GetCppWrapperType(kind.value_kind))
if mojom.IsInterfaceKind(kind):
- return "%sPtr" % GetNameForKind(
- kind, add_same_module_namespaces=add_same_module_namespaces)
+ return "%sPtr" % GetNameForKind(kind)
if mojom.IsInterfaceRequestKind(kind):
- return "%sRequest" % GetNameForKind(
- kind.kind, add_same_module_namespaces=add_same_module_namespaces)
+ return "%sRequest" % GetNameForKind(kind.kind)
if mojom.IsAssociatedInterfaceKind(kind):
- return "%sAssociatedPtrInfo" % GetNameForKind(
- kind.kind, add_same_module_namespaces=add_same_module_namespaces)
+ return "%sAssociatedPtrInfo" % GetNameForKind(kind.kind)
if mojom.IsAssociatedInterfaceRequestKind(kind):
- return "%sAssociatedRequest" % GetNameForKind(
- kind.kind, add_same_module_namespaces=add_same_module_namespaces)
+ return "%sAssociatedRequest" % GetNameForKind(kind.kind)
if mojom.IsStringKind(kind):
if _for_blink:
return "WTF::String"
- type_name = "std::string"
- return _AddOptional(type_name) if mojom.IsNullableKind(kind) else type_name
+ if not _use_new_wrapper_types:
+ return "mojo::String"
+ return ("base::Optional<std::string>" if mojom.IsNullableKind(kind) else
+ "std::string")
if mojom.IsGenericHandleKind(kind):
return "mojo::ScopedHandle"
if mojom.IsDataPipeConsumerKind(kind):
@@ -322,22 +220,15 @@
if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
return True
if mojom.IsArrayKind(kind):
- return IsMoveOnlyKind(kind.kind)
+ return IsMoveOnlyKind(kind.kind) if _use_new_wrapper_types else True
if mojom.IsMapKind(kind):
- return IsMoveOnlyKind(kind.value_kind)
+ return IsMoveOnlyKind(kind.value_kind) if _use_new_wrapper_types else True
if mojom.IsAnyHandleOrInterfaceKind(kind):
return True
return False
-def IsCopyablePassByValue(kind):
- if not IsTypemappedKind(kind):
- return False
- return _current_typemap[GetFullMojomNameForKind(kind)][
- "copyable_pass_by_value"]
-
def ShouldPassParamByValue(kind):
- return ((not mojom.IsReferenceKind(kind)) or IsMoveOnlyKind(kind) or
- IsCopyablePassByValue(kind))
+ return (not mojom.IsReferenceKind(kind)) or IsMoveOnlyKind(kind)
def GetCppWrapperParamType(kind):
cpp_wrapper_type = GetCppWrapperType(kind)
@@ -363,7 +254,7 @@
if mojom.IsAssociatedInterfaceKind(kind):
return "mojo::internal::AssociatedInterface_Data"
if mojom.IsAssociatedInterfaceRequestKind(kind):
- return "mojo::internal::AssociatedEndpointHandle_Data"
+ return "mojo::internal::AssociatedInterfaceRequest_Data"
if mojom.IsEnumKind(kind):
return "int32_t"
if mojom.IsStringKind(kind):
@@ -382,47 +273,27 @@
return "%s&" % GetCppWrapperType(kind)
return GetCppWrapperType(kind)
-def GetUnionTraitGetterReturnType(kind):
- """Get field type used in UnionTraits template specialization.
-
- The type may be qualified as UnionTraits specializations live outside the
- namespace where e.g. structs are defined.
-
- Args:
- kind: {Kind} The type of the field.
-
- Returns:
- {str} The C++ type to use for the field.
- """
- if mojom.IsReferenceKind(kind):
- return "%s&" % GetCppWrapperType(kind, add_same_module_namespaces=True)
- return GetCppWrapperType(kind, add_same_module_namespaces=True)
-
-def GetCppDataViewType(kind, qualified=False):
- def _GetName(input_kind):
- return _NameFormatter(input_kind, None).FormatForCpp(
- add_same_module_namespaces=qualified, flatten_nested_kind=True)
-
+def GetUnmappedTypeForSerializer(kind):
if mojom.IsEnumKind(kind):
- return _GetName(kind)
+ return GetQualifiedNameForKind(kind)
if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
- return "%sDataView" % _GetName(kind)
+ return "%sPtr" % GetQualifiedNameForKind(kind)
if mojom.IsArrayKind(kind):
- return "mojo::ArrayDataView<%s>" % GetCppDataViewType(kind.kind, qualified)
+ return "mojo::Array<%s>" % GetUnmappedTypeForSerializer(kind.kind)
if mojom.IsMapKind(kind):
- return ("mojo::MapDataView<%s, %s>" % (
- GetCppDataViewType(kind.key_kind, qualified),
- GetCppDataViewType(kind.value_kind, qualified)))
- if mojom.IsStringKind(kind):
- return "mojo::StringDataView"
+ return "mojo::Map<%s, %s>" % (
+ GetUnmappedTypeForSerializer(kind.key_kind),
+ GetUnmappedTypeForSerializer(kind.value_kind))
if mojom.IsInterfaceKind(kind):
- return "%sPtrDataView" % _GetName(kind)
+ return "%sPtr" % GetQualifiedNameForKind(kind)
if mojom.IsInterfaceRequestKind(kind):
- return "%sRequestDataView" % _GetName(kind.kind)
+ return "%sRequest" % GetQualifiedNameForKind(kind.kind)
if mojom.IsAssociatedInterfaceKind(kind):
- return "%sAssociatedPtrInfoDataView" % _GetName(kind.kind)
+ return "%sAssociatedPtrInfo" % GetQualifiedNameForKind(kind.kind)
if mojom.IsAssociatedInterfaceRequestKind(kind):
- return "%sAssociatedRequestDataView" % _GetName(kind.kind)
+ return "%sAssociatedRequest" % GetQualifiedNameForKind(kind.kind)
+ if mojom.IsStringKind(kind):
+ return "mojo::String"
if mojom.IsGenericHandleKind(kind):
return "mojo::ScopedHandle"
if mojom.IsDataPipeConsumerKind(kind):
@@ -435,26 +306,18 @@
return "mojo::ScopedSharedBufferHandle"
return _kind_to_cpp_type[kind]
-def GetUnmappedTypeForSerializer(kind):
- return GetCppDataViewType(kind, qualified=True)
-
def TranslateConstants(token, kind):
if isinstance(token, mojom.NamedValue):
- return GetNameForKind(token, flatten_nested_kind=True)
+ return _NameFormatter(token, _variant).FormatForCpp()
if isinstance(token, mojom.BuiltinValue):
- if token.value == "double.INFINITY":
- return "std::numeric_limits<double>::infinity()"
- if token.value == "float.INFINITY":
- return "std::numeric_limits<float>::infinity()"
- if token.value == "double.NEGATIVE_INFINITY":
- return "-std::numeric_limits<double>::infinity()"
- if token.value == "float.NEGATIVE_INFINITY":
- return "-std::numeric_limits<float>::infinity()"
- if token.value == "double.NAN":
- return "std::numeric_limits<double>::quiet_NaN()"
- if token.value == "float.NAN":
- return "std::numeric_limits<float>::quiet_NaN()"
+ if token.value == "double.INFINITY" or token.value == "float.INFINITY":
+ return "INFINITY";
+ if token.value == "double.NEGATIVE_INFINITY" or \
+ token.value == "float.NEGATIVE_INFINITY":
+ return "-INFINITY";
+ if token.value == "double.NAN" or token.value == "float.NAN":
+ return "NAN";
if (kind is not None and mojom.IsFloatKind(kind)):
return token if token.isdigit() else token + "f";
@@ -479,12 +342,6 @@
def ExpressionToText(value, kind=None):
return TranslateConstants(value, kind)
-def RequiresContextForDataView(kind):
- for field in kind.fields:
- if mojom.IsReferenceKind(field.kind):
- return True
- return False
-
def ShouldInlineStruct(struct):
# TODO(darin): Base this on the size of the wrapper class.
if len(struct.fields) > 4:
@@ -494,69 +351,11 @@
return False
return True
-def ContainsMoveOnlyMembers(struct):
- for field in struct.fields:
- if IsMoveOnlyKind(field.kind):
- return True
- return False
-
def ShouldInlineUnion(union):
return not any(
mojom.IsReferenceKind(field.kind) and not mojom.IsStringKind(field.kind)
for field in union.fields)
-
-class StructConstructor(object):
- """Represents a constructor for a generated struct.
-
- Fields:
- fields: {[Field]} All struct fields in order.
- params: {[Field]} The fields that are passed as params.
- """
-
- def __init__(self, fields, params):
- self._fields = fields
- self._params = set(params)
-
- @property
- def params(self):
- return [field for field in self._fields if field in self._params]
-
- @property
- def fields(self):
- for field in self._fields:
- yield (field, field in self._params)
-
-
-def GetStructConstructors(struct):
- """Returns a list of constructors for a struct.
-
- Params:
- struct: {Struct} The struct to return constructors for.
-
- Returns:
- {[StructConstructor]} A list of StructConstructors that should be generated
- for |struct|.
- """
- if not mojom.IsStructKind(struct):
- raise TypeError
- # Types that are neither copyable nor movable can't be passed to a struct
- # constructor so only generate a default constructor.
- if any(IsTypemappedKind(field.kind) and _current_typemap[
- GetFullMojomNameForKind(field.kind)]["non_copyable_non_movable"]
- for field in struct.fields):
- return [StructConstructor(struct.fields, [])]
-
- param_counts = [0]
- for version in struct.versions:
- if param_counts[-1] != version.num_fields:
- param_counts.append(version.num_fields)
-
- ordinal_fields = sorted(struct.fields, key=lambda field: field.ordinal)
- return (StructConstructor(struct.fields, ordinal_fields[:param_count])
- for param_count in param_counts)
-
-
def GetContainerValidateParamsCtorArgs(kind):
if mojom.IsStringKind(kind):
expected_num_elements = 0
@@ -579,8 +378,7 @@
element_validate_params = GetNewContainerValidateParams(kind.kind)
if mojom.IsEnumKind(kind.kind):
enum_validate_func = ("%s::Validate" %
- GetQualifiedNameForKind(kind.kind, internal=True,
- flatten_nested_kind=True))
+ GetQualifiedNameForKind(kind.kind, internal=True))
else:
enum_validate_func = "nullptr"
@@ -605,143 +403,57 @@
class Generator(generator.Generator):
cpp_filters = {
- "all_enum_values": AllEnumValues,
"constant_value": ConstantValue,
- "contains_handles_or_interfaces": mojom.ContainsHandlesOrInterfaces,
- "contains_move_only_members": ContainsMoveOnlyMembers,
"cpp_wrapper_param_type": GetCppWrapperParamType,
- "cpp_data_view_type": GetCppDataViewType,
"cpp_field_type": GetCppFieldType,
"cpp_union_field_type": GetCppUnionFieldType,
"cpp_pod_type": GetCppPodType,
"cpp_union_getter_return_type": GetUnionGetterReturnType,
- "cpp_union_trait_getter_return_type": GetUnionTraitGetterReturnType,
"cpp_wrapper_type": GetCppWrapperType,
"default_value": DefaultValue,
"expression_to_text": ExpressionToText,
- "format_constant_declaration": FormatConstantDeclaration,
"get_container_validate_params_ctor_args":
- GetContainerValidateParamsCtorArgs,
+ GetContainerValidateParamsCtorArgs,
"get_name_for_kind": GetNameForKind,
"get_pad": pack.GetPad,
"get_qualified_name_for_kind": GetQualifiedNameForKind,
"has_callbacks": mojom.HasCallbacks,
"has_sync_methods": mojom.HasSyncMethods,
- "requires_context_for_data_view": RequiresContextForDataView,
"should_inline": ShouldInlineStruct,
"should_inline_union": ShouldInlineUnion,
"is_array_kind": mojom.IsArrayKind,
"is_enum_kind": mojom.IsEnumKind,
"is_integral_kind": mojom.IsIntegralKind,
"is_native_only_kind": IsNativeOnlyKind,
- "is_any_handle_kind": mojom.IsAnyHandleKind,
- "is_any_interface_kind": mojom.IsAnyInterfaceKind,
"is_any_handle_or_interface_kind": mojom.IsAnyHandleOrInterfaceKind,
"is_associated_kind": mojom.IsAssociatedKind,
- "is_hashable": IsHashableKind,
"is_map_kind": mojom.IsMapKind,
"is_nullable_kind": mojom.IsNullableKind,
"is_object_kind": mojom.IsObjectKind,
- "is_reference_kind": mojom.IsReferenceKind,
"is_string_kind": mojom.IsStringKind,
"is_struct_kind": mojom.IsStructKind,
"is_typemapped_kind": IsTypemappedKind,
"is_union_kind": mojom.IsUnionKind,
"passes_associated_kinds": mojom.PassesAssociatedKinds,
- "struct_constructors": GetStructConstructors,
+ "struct_size": lambda ps: ps.GetTotalSize() + _HEADER_SIZE,
"stylize_method": generator.StudlyCapsToCamel,
"under_to_camel": generator.UnderToCamel,
"unmapped_type_for_serializer": GetUnmappedTypeForSerializer,
- "wtf_hash_fn_name_for_enum": GetWtfHashFnNameForEnum,
}
def GetExtraTraitsHeaders(self):
extra_headers = set()
- for typemap in self._GetAllUsedTypemaps():
- extra_headers.update(typemap.get("traits_headers", []))
- return sorted(extra_headers)
-
- def _GetAllUsedTypemaps(self):
- """Returns the typemaps for types needed for serialization in this module.
-
- A type is needed for serialization if it is contained by a struct or union
- defined in this module, is a parameter of a message in an interface in
- this module or is contained within another type needed for serialization.
- """
- used_typemaps = []
- seen_types = set()
- def AddKind(kind):
- if (mojom.IsIntegralKind(kind) or mojom.IsStringKind(kind) or
- mojom.IsDoubleKind(kind) or mojom.IsFloatKind(kind) or
- mojom.IsAnyHandleKind(kind) or
- mojom.IsInterfaceKind(kind) or
- mojom.IsInterfaceRequestKind(kind) or
- mojom.IsAssociatedKind(kind)):
- pass
- elif mojom.IsArrayKind(kind):
- AddKind(kind.kind)
- elif mojom.IsMapKind(kind):
- AddKind(kind.key_kind)
- AddKind(kind.value_kind)
- else:
- name = GetFullMojomNameForKind(kind)
- if name in seen_types:
- return
- seen_types.add(name)
-
- typemap = _current_typemap.get(name, None)
- if typemap:
- used_typemaps.append(typemap)
- if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
- for field in kind.fields:
- AddKind(field.kind)
-
- for kind in self.module.structs + self.module.unions:
- for field in kind.fields:
- AddKind(field.kind)
-
- for interface in self.module.interfaces:
- for method in interface.methods:
- for parameter in method.parameters + (method.response_parameters or []):
- AddKind(parameter.kind)
-
- return used_typemaps
+ for entry in self.typemap.itervalues():
+ extra_headers.update(entry.get("traits_headers", []))
+ return list(extra_headers)
def GetExtraPublicHeaders(self):
- all_enums = list(self.module.enums)
- for struct in self.module.structs:
- all_enums.extend(struct.enums)
- for interface in self.module.interfaces:
- all_enums.extend(interface.enums)
-
- types = set(GetFullMojomNameForKind(typename)
- for typename in
- self.module.structs + all_enums + self.module.unions)
- headers = set()
- for typename, typemap in self.typemap.iteritems():
- if typename in types:
- headers.update(typemap.get("public_headers", []))
- return sorted(headers)
-
- def _GetDirectlyUsedKinds(self):
- for struct in self.module.structs + self.module.unions:
- for field in struct.fields:
- yield field.kind
-
- for interface in self.module.interfaces:
- for method in interface.methods:
- for param in method.parameters + (method.response_parameters or []):
- yield param.kind
+ extra_headers = set()
+ for entry in self.typemap.itervalues():
+ extra_headers.update(entry.get("public_headers", []))
+ return list(extra_headers)
def GetJinjaExports(self):
- structs = self.GetStructs()
- interfaces = self.GetInterfaces()
- all_enums = list(self.module.enums)
- for struct in structs:
- all_enums.extend(struct.enums)
- for interface in interfaces:
- all_enums.extend(interface.enums)
-
return {
"module": self.module,
"namespace": self.module.namespace,
@@ -749,17 +461,14 @@
"imports": self.module.imports,
"kinds": self.module.kinds,
"enums": self.module.enums,
- "all_enums": all_enums,
- "structs": structs,
+ "structs": self.GetStructs(),
"unions": self.GetUnions(),
- "interfaces": interfaces,
+ "interfaces": self.GetInterfaces(),
"variant": self.variant,
"extra_traits_headers": self.GetExtraTraitsHeaders(),
"extra_public_headers": self.GetExtraPublicHeaders(),
"for_blink": self.for_blink,
- "use_once_callback": self.use_once_callback,
- "export_attribute": self.export_attribute,
- "export_header": self.export_header,
+ "use_new_wrapper_types": self.use_new_wrapper_types,
}
@staticmethod
@@ -774,45 +483,27 @@
def GenerateModuleHeader(self):
return self.GetJinjaExports()
+ @UseJinja("module-internal.h.tmpl")
+ def GenerateModuleInternalHeader(self):
+ return self.GetJinjaExports()
+
@UseJinja("module.cc.tmpl")
def GenerateModuleSource(self):
return self.GetJinjaExports()
- @UseJinja("module-shared.h.tmpl")
- def GenerateModuleSharedHeader(self):
- return self.GetJinjaExports()
-
- @UseJinja("module-shared-internal.h.tmpl")
- def GenerateModuleSharedInternalHeader(self):
- return self.GetJinjaExports()
-
- @UseJinja("module-shared.cc.tmpl")
- def GenerateModuleSharedSource(self):
- return self.GetJinjaExports()
-
def GenerateFiles(self, args):
- if self.generate_non_variant_code:
- self.Write(self.GenerateModuleSharedHeader(),
- self.MatchMojomFilePath("%s-shared.h" % self.module.name))
- self.Write(
- self.GenerateModuleSharedInternalHeader(),
- self.MatchMojomFilePath("%s-shared-internal.h" % self.module.name))
- self.Write(self.GenerateModuleSharedSource(),
- self.MatchMojomFilePath("%s-shared.cc" % self.module.name))
- else:
- global _current_typemap
- _current_typemap = self.typemap
- global _for_blink
- _for_blink = self.for_blink
- global _use_once_callback
- _use_once_callback = self.use_once_callback
- global _variant
- _variant = self.variant
- global _export_attribute
- _export_attribute = self.export_attribute
- suffix = "-%s" % self.variant if self.variant else ""
- self.Write(self.GenerateModuleHeader(),
- self.MatchMojomFilePath("%s%s.h" % (self.module.name, suffix)))
- self.Write(
- self.GenerateModuleSource(),
- self.MatchMojomFilePath("%s%s.cc" % (self.module.name, suffix)))
+ global _current_typemap
+ _current_typemap = self.typemap
+ global _for_blink
+ _for_blink = self.for_blink
+ global _use_new_wrapper_types
+ _use_new_wrapper_types = self.use_new_wrapper_types
+ global _variant
+ _variant = self.variant
+ suffix = "-%s" % self.variant if self.variant else ""
+ self.Write(self.GenerateModuleHeader(),
+ self.MatchMojomFilePath("%s%s.h" % (self.module.name, suffix)))
+ self.Write(self.GenerateModuleInternalHeader(),
+ self.MatchMojomFilePath("%s%s-internal.h" % (self.module.name, suffix)))
+ self.Write(self.GenerateModuleSource(),
+ self.MatchMojomFilePath("%s%s.cc" % (self.module.name, suffix)))
diff --git a/mojo/public/tools/bindings/generators/mojom_java_generator.py b/mojo/public/tools/bindings/generators/mojom_java_generator.py
index 9265b3a..481efbc 100644
--- a/mojo/public/tools/bindings/generators/mojom_java_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_java_generator.py
@@ -10,8 +10,8 @@
import os
import re
import shutil
-import sys
import tempfile
+import zipfile
from jinja2 import contextfilter
@@ -20,12 +20,6 @@
import mojom.generate.module as mojom
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
-
GENERATOR_PREFIX = 'java'
@@ -225,8 +219,8 @@
return ParseStringAttribute(module.attributes['JavaPackage'])
# Default package.
if module.namespace:
- return 'org.chromium.' + module.namespace
- return 'org.chromium'
+ return 'org.chromium.mojom.' + module.namespace
+ return 'org.chromium.mojom'
def GetNameForKind(context, kind):
def _GetNameHierachy(kind):
@@ -403,6 +397,14 @@
finally:
shutil.rmtree(dirname)
+def ZipContentInto(root, zip_filename):
+ with zipfile.ZipFile(zip_filename, 'w') as zip_file:
+ for dirname, _, files in os.walk(root):
+ for filename in files:
+ path = os.path.join(dirname, filename)
+ path_in_archive = os.path.relpath(path, root)
+ zip_file.write(path, path_in_archive)
+
class Generator(generator.Generator):
java_filters = {
@@ -531,7 +533,7 @@
with TempDir() as temp_java_root:
self.output_dir = os.path.join(temp_java_root, package_path)
self.DoGenerateFiles();
- build_utils.ZipDir(zip_filename, temp_java_root)
+ ZipContentInto(temp_java_root, zip_filename)
if args.java_output_directory:
# If requested, generate the java files directly into indicated directory.
diff --git a/mojo/public/tools/bindings/generators/mojom_js_generator.py b/mojo/public/tools/bindings/generators/mojom_js_generator.py
index 0eedb31..e9a4883 100644
--- a/mojo/public/tools/bindings/generators/mojom_js_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_js_generator.py
@@ -37,13 +37,9 @@
def JavaScriptType(kind):
- name = []
if kind.imported_from:
- name.append(kind.imported_from["unique_name"])
- if kind.parent_kind:
- name.append(kind.parent_kind.name)
- name.append(kind.name)
- return ".".join(name)
+ return kind.imported_from["unique_name"] + "." + kind.name
+ return kind.name
def JavaScriptDefaultValue(field):
@@ -62,10 +58,9 @@
return "null"
if mojom.IsMapKind(field.kind):
return "null"
- if mojom.IsInterfaceKind(field.kind):
- return "new %sPtr()" % JavaScriptType(field.kind)
- if mojom.IsInterfaceRequestKind(field.kind):
- return "new bindings.InterfaceRequest()"
+ if mojom.IsInterfaceKind(field.kind) or \
+ mojom.IsInterfaceRequestKind(field.kind):
+ return _kind_to_javascript_default_value[mojom.MSGPIPE]
if mojom.IsAssociatedKind(field.kind):
return "null"
if mojom.IsEnumKind(field.kind):
@@ -125,19 +120,16 @@
element_type = ElementCodecType(kind.kind)
return "new codec.%s(%s%s)" % (array_type, element_type, array_length)
if mojom.IsInterfaceKind(kind):
- return "new codec.%s(%sPtr)" % (
- "NullableInterface" if mojom.IsNullableKind(kind) else "Interface",
- JavaScriptType(kind))
+ return "codec.%s" % ("NullableInterface" if mojom.IsNullableKind(kind)
+ else "Interface")
if mojom.IsInterfaceRequestKind(kind):
- return "codec.%s" % (
- "NullableInterfaceRequest" if mojom.IsNullableKind(kind)
- else "InterfaceRequest")
+ return CodecType(mojom.MSGPIPE)
if mojom.IsAssociatedInterfaceKind(kind):
return "codec.AssociatedInterfaceNotSupported"
if mojom.IsAssociatedInterfaceRequestKind(kind):
return "codec.AssociatedInterfaceRequestNotSupported"
if mojom.IsEnumKind(kind):
- return "new codec.Enum(%s)" % JavaScriptType(kind)
+ return _kind_to_codec_type[mojom.INT32]
if mojom.IsMapKind(kind):
map_type = "NullableMapOf" if mojom.IsNullableKind(kind) else "MapOf"
key_type = ElementCodecType(kind.key_kind)
@@ -149,10 +141,9 @@
def ElementCodecType(kind):
return "codec.PackedBool" if mojom.IsBoolKind(kind) else CodecType(kind)
-
def JavaScriptDecodeSnippet(kind):
if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or
- mojom.IsAnyInterfaceKind(kind)):
+ mojom.IsInterfaceKind(kind) or mojom.IsAssociatedKind(kind)):
return "decodeStruct(%s)" % CodecType(kind)
if mojom.IsStructKind(kind):
return "decodeStructPointer(%s)" % JavaScriptType(kind)
@@ -165,6 +156,8 @@
return "decodeArrayPointer(%s)" % CodecType(kind.kind)
if mojom.IsUnionKind(kind):
return "decodeUnion(%s)" % CodecType(kind)
+ if mojom.IsInterfaceRequestKind(kind):
+ return JavaScriptDecodeSnippet(mojom.MSGPIPE)
if mojom.IsEnumKind(kind):
return JavaScriptDecodeSnippet(mojom.INT32)
raise Exception("No decode snippet for %s" % kind)
@@ -172,7 +165,7 @@
def JavaScriptEncodeSnippet(kind):
if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or
- mojom.IsAnyInterfaceKind(kind)):
+ mojom.IsInterfaceKind(kind) or mojom.IsAssociatedKind(kind)):
return "encodeStruct(%s, " % CodecType(kind)
if mojom.IsUnionKind(kind):
return "encodeStruct(%s, " % JavaScriptType(kind)
@@ -185,6 +178,8 @@
return "encodeArrayPointer(codec.PackedBool, ";
if mojom.IsArrayKind(kind):
return "encodeArrayPointer(%s, " % CodecType(kind.kind)
+ if mojom.IsInterfaceRequestKind(kind):
+ return JavaScriptEncodeSnippet(mojom.MSGPIPE)
if mojom.IsEnumKind(kind):
return JavaScriptEncodeSnippet(mojom.INT32)
raise Exception("No encode snippet for %s" % kind)
@@ -233,9 +228,6 @@
expected_dimension_sizes)
-def JavaScriptValidateEnumParams(field):
- return JavaScriptType(field.kind)
-
def JavaScriptValidateStructParams(field):
nullable = JavaScriptNullableParam(field)
struct_type = JavaScriptType(field.kind)
@@ -256,6 +248,42 @@
(nullable, keys_type, values_type, values_nullable)
+def JavaScriptValidateStringParams(field):
+ nullable = JavaScriptNullableParam(field)
+ return "%s" % (nullable)
+
+
+def JavaScriptValidateHandleParams(field):
+ nullable = JavaScriptNullableParam(field)
+ return "%s" % (nullable)
+
+def JavaScriptValidateInterfaceParams(field):
+ return JavaScriptValidateHandleParams(field)
+
+def JavaScriptProxyMethodParameterValue(parameter):
+ name = parameter.name;
+ if (mojom.IsInterfaceKind(parameter.kind)):
+ type = JavaScriptType(parameter.kind)
+ return "core.isHandle(%s) ? %s : connection.bindImpl" \
+ "(%s, %s)" % (name, name, name, type)
+ if (mojom.IsInterfaceRequestKind(parameter.kind)):
+ type = JavaScriptType(parameter.kind.kind)
+ return "core.isHandle(%s) ? %s : connection.bindProxy" \
+ "(%s, %s)" % (name, name, name, type)
+ return name;
+
+
+def JavaScriptStubMethodParameterValue(parameter):
+ name = parameter.name;
+ if (mojom.IsInterfaceKind(parameter.kind)):
+ type = JavaScriptType(parameter.kind)
+ return "connection.bindHandleToProxy(%s, %s)" % (name, type)
+ if (mojom.IsInterfaceRequestKind(parameter.kind)):
+ type = JavaScriptType(parameter.kind.kind)
+ return "connection.bindHandleToStub(%s, %s)" % (name, type)
+ return name;
+
+
def TranslateConstants(token):
if isinstance(token, (mojom.EnumValue, mojom.NamedValue)):
# Both variable and enum constants are constructed like:
@@ -288,9 +316,6 @@
def IsArrayPointerField(field):
return mojom.IsArrayKind(field.kind)
-def IsEnumField(field):
- return mojom.IsEnumKind(field.kind)
-
def IsStringPointerField(field):
return mojom.IsStringKind(field.kind)
@@ -306,55 +331,38 @@
def IsInterfaceField(field):
return mojom.IsInterfaceKind(field.kind)
-def IsInterfaceRequestField(field):
- return mojom.IsInterfaceRequestKind(field.kind)
-
def IsUnionField(field):
return mojom.IsUnionKind(field.kind)
-def IsBoolField(field):
- return mojom.IsBoolKind(field.kind)
-
-def IsObjectField(field):
- return mojom.IsObjectKind(field.kind)
-
-def IsAnyHandleOrInterfaceField(field):
- return mojom.IsAnyHandleOrInterfaceKind(field.kind)
-
-def IsEnumField(field):
- return mojom.IsEnumKind(field.kind)
-
class Generator(generator.Generator):
js_filters = {
- "decode_snippet": JavaScriptDecodeSnippet,
"default_value": JavaScriptDefaultValue,
+ "payload_size": JavaScriptPayloadSize,
+ "decode_snippet": JavaScriptDecodeSnippet,
"encode_snippet": JavaScriptEncodeSnippet,
+ "union_decode_snippet": JavaScriptUnionDecodeSnippet,
+ "union_encode_snippet": JavaScriptUnionEncodeSnippet,
"expression_to_text": ExpressionToText,
"field_offset": JavaScriptFieldOffset,
"has_callbacks": mojom.HasCallbacks,
- "is_any_handle_or_interface_field": IsAnyHandleOrInterfaceField,
"is_array_pointer_field": IsArrayPointerField,
- "is_bool_field": IsBoolField,
- "is_enum_field": IsEnumField,
+ "is_map_pointer_field": IsMapPointerField,
+ "is_struct_pointer_field": IsStructPointerField,
+ "is_string_pointer_field": IsStringPointerField,
+ "is_union_field": IsUnionField,
"is_handle_field": IsHandleField,
"is_interface_field": IsInterfaceField,
- "is_interface_request_field": IsInterfaceRequestField,
- "is_map_pointer_field": IsMapPointerField,
- "is_object_field": IsObjectField,
- "is_string_pointer_field": IsStringPointerField,
- "is_struct_pointer_field": IsStructPointerField,
- "is_union_field": IsUnionField,
"js_type": JavaScriptType,
- "payload_size": JavaScriptPayloadSize,
+ "js_proxy_method_parameter_value": JavaScriptProxyMethodParameterValue,
+ "js_stub_method_parameter_value": JavaScriptStubMethodParameterValue,
"stylize_method": generator.StudlyCapsToCamel,
- "union_decode_snippet": JavaScriptUnionDecodeSnippet,
- "union_encode_snippet": JavaScriptUnionEncodeSnippet,
"validate_array_params": JavaScriptValidateArrayParams,
- "validate_enum_params": JavaScriptValidateEnumParams,
+ "validate_handle_params": JavaScriptValidateHandleParams,
+ "validate_interface_params": JavaScriptValidateInterfaceParams,
"validate_map_params": JavaScriptValidateMapParams,
- "validate_nullable_params": JavaScriptNullableParam,
+ "validate_string_params": JavaScriptValidateStringParams,
"validate_struct_params": JavaScriptValidateStructParams,
"validate_union_params": JavaScriptValidateUnionParams,
}
diff --git a/mojo/public/tools/bindings/generators/run_cpp_generator.py b/mojo/public/tools/bindings/generators/run_cpp_generator.py
new file mode 100755
index 0000000..4c6f597
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/run_cpp_generator.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import ast
+import os
+import sys
+
+script_dir = os.path.dirname(os.path.realpath(__file__))
+sys.path.insert(0, os.path.join(script_dir, os.pardir, "pylib"))
+
+from mojom.generate.data
+import mojom_cpp_generator
+
+def ReadDict(file):
+ with open(file, 'r') as f:
+ s = f.read()
+ dict = ast.literal_eval(s)
+ return dict
+
+dict = ReadDict(sys.argv[1])
+module = mojom.generate.data.ModuleFromData(dict)
+dir = None
+if len(sys.argv) > 2:
+ dir = sys.argv[2]
+cpp = mojom_cpp_generator.Generator(module, ".", dir)
+cpp.GenerateFiles([])
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni
index 2466636..d6f4f17 100644
--- a/mojo/public/tools/bindings/mojom.gni
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -2,18 +2,6 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-declare_args() {
- # Indicates whether typemapping should be supported in this build
- # configuration. This may be disabled when building external projects which
- # depend on //mojo but which do not need/want all of the Chromium tree
- # dependencies that come with typemapping.
- #
- # Note that (perhaps obviously) a huge amount of Chromium code will not build
- # with typemapping disabled, so it is never valid to set this to |false| in
- # any Chromium build configuration.
- enable_mojom_typemapping = true
-}
-
mojom_generator_root = "//mojo/public/tools/bindings"
mojom_generator_script = "$mojom_generator_root/mojom_bindings_generator.py"
mojom_generator_sources = [
@@ -24,79 +12,42 @@
"$mojom_generator_root/pylib/mojom/error.py",
"$mojom_generator_root/pylib/mojom/generate/__init__.py",
"$mojom_generator_root/pylib/mojom/generate/constant_resolver.py",
+ "$mojom_generator_root/pylib/mojom/generate/data.py",
"$mojom_generator_root/pylib/mojom/generate/generator.py",
"$mojom_generator_root/pylib/mojom/generate/module.py",
"$mojom_generator_root/pylib/mojom/generate/pack.py",
"$mojom_generator_root/pylib/mojom/generate/template_expander.py",
- "$mojom_generator_root/pylib/mojom/generate/translate.py",
"$mojom_generator_root/pylib/mojom/parse/__init__.py",
"$mojom_generator_root/pylib/mojom/parse/ast.py",
"$mojom_generator_root/pylib/mojom/parse/lexer.py",
"$mojom_generator_root/pylib/mojom/parse/parser.py",
+ "$mojom_generator_root/pylib/mojom/parse/translate.py",
"$mojom_generator_script",
]
-if (enable_mojom_typemapping) {
- if (!is_ios) {
- _bindings_configuration_files = [
- "//mojo/public/tools/bindings/chromium_bindings_configuration.gni",
- "//mojo/public/tools/bindings/blink_bindings_configuration.gni",
- ]
- } else {
- _bindings_configuration_files =
- [ "//mojo/public/tools/bindings/chromium_bindings_configuration.gni" ]
- }
- _bindings_configurations = []
- foreach(config_file, _bindings_configuration_files) {
- _bindings_configurations += [ read_file(config_file, "scope") ]
- }
- foreach(configuration, _bindings_configurations) {
- # Check that the mojom field of each typemap refers to a mojom that exists.
- foreach(typemap, configuration.typemaps) {
- _typemap_config = {
- }
- _typemap_config = typemap.config
- read_file(_typemap_config.mojom, "")
- }
- if (is_mac && defined(configuration.typemaps_mac)) {
- foreach(typemap, configuration.typemaps_mac) {
- _typemap_config = {
- }
- _typemap_config = typemap.config
- read_file(_typemap_config.mojom, "")
- }
- }
- }
-} else {
- _bindings_configuration_files = []
- _bindings_configurations = [
- {
- typemaps = []
- },
- {
- variant = "blink"
- for_blink = true
- typemaps = []
- },
+if (!is_ios) {
+ _bindings_configuration_files = [
+ "//mojo/public/tools/bindings/chromium_bindings_configuration.gni",
+ "//mojo/public/tools/bindings/blink_bindings_configuration.gni",
]
+} else {
+ _bindings_configuration_files =
+ [ "//mojo/public/tools/bindings/chromium_bindings_configuration.gni" ]
+}
+_bindings_configurations = []
+foreach(config_file, _bindings_configuration_files) {
+ _bindings_configurations += [ read_file(config_file, "scope") ]
+}
+foreach(configuration, _bindings_configurations) {
+ foreach(typemap, configuration.typemaps) {
+ # Check that the mojom field of each typemap refers to a mojom that exists.
+ read_file(typemap.mojom, "")
+ }
}
-# Generates targets for building C++, JavaScript and Java bindings from mojom
-# files. The output files will go under the generated file directory tree with
-# the same path as each input file.
-#
-# Other targets should depend on one of these generated targets (where "foo"
-# is the target name):
-#
-# foo
-# C++ and Javascript bindings. Other mojom targets should also depend on
-# this target.
-#
-# foo_blink
-# C++ bindings using Blink standard types.
-#
-# foo_java
-# Java bindings.
+# Generate C++/JavaScript/Java source files from mojom files. The output files
+# will go under the generated file directory tree with the same path as each
+# input file.
#
# Parameters:
#
@@ -119,78 +70,20 @@
#
# visibility (optional)
#
-# visibility_blink (optional)
-# The value to use for visibility for the blink variant. If unset,
-# |visibility| is used.
-#
-# use_once_callback (optional)
-# If set to true, generated classes will use base::OnceCallback instead of
-# base::RepeatingCallback.
+# use_new_wrapper_types (optional)
+# If set to true, mojom array/map/string will be mapped to STL (for
+# chromium variant) or WTF (for blink) types. Otherwise, they will be
+# mapped to mojo::Array/Map/String/etc.
# Default value is false.
-# TODO(dcheng):
-# - Convert everything to use OnceCallback.
-# - Remove support for the old mode.
-#
-# cpp_only (optional)
-# If set to true, only the C++ bindings targets will be generated.
-#
-# The following parameters are used to support the component build. They are
-# needed so that bindings which are linked with a component can use the same
-# export settings for classes. The first three are for the chromium variant, and
-# the last three are for the blink variant.
-# export_class_attribute (optional)
-# The attribute to add to the class declaration. e.g. "CONTENT_EXPORT"
-# export_define (optional)
-# A define to be added to the source_set which is needed by the export
-# header. e.g. "CONTENT_IMPLEMENTATION=1"
-# export_header (optional)
-# A header to be added to the generated bindings to support the component
-# build. e.g. "content/common/content_export.h"
-# export_class_attribute_blink (optional)
-# export_define_blink (optional)
-# export_header_blink (optional)
-# These three parameters are the blink variants of the previous 3.
-#
-# The following parameters are used to correct component build dependencies.
-# They are needed so mojom-mojom dependencies follow the rule that dependencies
-# on a source set in another component are replaced by a dependency on the
-# containing component. The first two are for the chromium variant; the other
-# two are for the blink variant.
-# overridden_deps (optional)
-# The list of mojom deps to be overridden.
-# component_deps (optional)
-# The list of component deps to add to replace overridden_deps.
-# overridden_deps_blink (optional)
-# component_deps_blink (optional)
-# These two parameters are the blink variants of the previous two.
+# TODO(yzshen):
+# - flip the flag and make use_new_wrapper_types=true the default;
+# - convert all users to use the new mode;
+# - remove support for the old mode.
template("mojom") {
assert(
defined(invoker.sources) || defined(invoker.deps) ||
defined(invoker.public_deps),
"\"sources\" or \"deps\" must be defined for the $target_name template.")
- if (defined(invoker.export_class_attribute) ||
- defined(invoker.export_define) || defined(invoker.export_header)) {
- assert(defined(invoker.export_class_attribute))
- assert(defined(invoker.export_define))
- assert(defined(invoker.export_header))
- }
- if (defined(invoker.export_class_attribute_blink) ||
- defined(invoker.export_define_blink) ||
- defined(invoker.export_header_blink)) {
- assert(defined(invoker.export_class_attribute_blink))
- assert(defined(invoker.export_define_blink))
- assert(defined(invoker.export_header_blink))
- }
- if (defined(invoker.overridden_deps) || defined(invoker.component_deps)) {
- assert(defined(invoker.overridden_deps))
- assert(defined(invoker.component_deps))
- }
-
- if (defined(invoker.overridden_deps_blink) ||
- defined(invoker.component_deps_blink)) {
- assert(defined(invoker.overridden_deps_blink))
- assert(defined(invoker.component_deps_blink))
- }
all_deps = []
if (defined(invoker.deps)) {
@@ -214,89 +107,8 @@
}
}
- # Generate code that is shared by different variants.
- if (defined(invoker.sources)) {
- common_generator_args = [
- "--use_bundled_pylibs",
- "generate",
- "{{source}}",
- "-d",
- rebase_path("//", root_build_dir),
- "-I",
- rebase_path("//", root_build_dir),
- "-o",
- rebase_path(root_gen_dir),
- "--bytecode_path",
- rebase_path("$root_gen_dir/mojo/public/tools/bindings"),
- ]
-
- if (defined(invoker.import_dirs)) {
- foreach(import_dir, invoker.import_dirs) {
- common_generator_args += [
- "-I",
- rebase_path(import_dir, root_build_dir),
- ]
- }
- }
-
- generator_shared_cpp_outputs = [
- "{{source_gen_dir}}/{{source_name_part}}.mojom-shared-internal.h",
- "{{source_gen_dir}}/{{source_name_part}}.mojom-shared.cc",
- "{{source_gen_dir}}/{{source_name_part}}.mojom-shared.h",
- ]
- generator_shared_target_name = "${target_name}_shared__generator"
- action_foreach(generator_shared_target_name) {
- script = mojom_generator_script
- inputs = mojom_generator_sources
- sources = invoker.sources
- deps = [
- "//mojo/public/tools/bindings:precompile_templates",
- ]
- outputs = generator_shared_cpp_outputs
- args = common_generator_args
- args += [
- "--generate_non_variant_code",
- "-g",
- "c++",
- ]
- depfile = "{{source_gen_dir}}/${generator_shared_target_name}_{{source_name_part}}.d"
- args += [
- "--depfile",
- depfile,
- "--depfile_target",
- "{{source_gen_dir}}/{{source_name_part}}.mojom-shared-internal.h",
- ]
- }
- }
-
- shared_cpp_sources_suffix = "shared_cpp_sources"
- shared_cpp_sources_target_name = "${target_name}_${shared_cpp_sources_suffix}"
- source_set(shared_cpp_sources_target_name) {
- if (defined(invoker.testonly)) {
- testonly = invoker.testonly
- }
- deps = []
- if (defined(invoker.sources)) {
- sources =
- process_file_template(invoker.sources, generator_shared_cpp_outputs)
- deps += [ ":$generator_shared_target_name" ]
- }
- public_deps = []
- foreach(d, all_deps) {
- # Resolve the name, so that a target //mojo/something becomes
- # //mojo/something:something and we can append shared_cpp_sources_suffix
- # to get the cpp dependency name.
- full_name = get_label_info("$d", "label_no_toolchain")
- public_deps += [ "${full_name}_${shared_cpp_sources_suffix}" ]
- }
- }
-
- # Generate code for variants.
foreach(bindings_configuration, _bindings_configurations) {
cpp_only = false
- if (defined(invoker.cpp_only)) {
- cpp_only = invoker.cpp_only
- }
variant_suffix = ""
if (defined(bindings_configuration.variant)) {
variant = bindings_configuration.variant
@@ -307,6 +119,9 @@
type_mappings_path =
"$target_gen_dir/${target_name}${variant_suffix}__type_mappings"
active_typemaps = []
+ cpp_sources_suffix = "cpp_sources"
+ cpp_sources_target_name =
+ "${target_name}${variant_suffix}_${cpp_sources_suffix}"
enabled_sources = []
if (defined(invoker.sources)) {
generator_cpp_outputs = []
@@ -319,6 +134,7 @@
generator_cpp_outputs += [
"{{source_gen_dir}}/{{source_name_part}}.mojom${variant_dash_suffix}.cc",
"{{source_gen_dir}}/{{source_name_part}}.mojom${variant_dash_suffix}.h",
+ "{{source_gen_dir}}/{{source_name_part}}.mojom${variant_dash_suffix}-internal.h",
]
enabled_sources = []
if (defined(bindings_configuration.blacklist)) {
@@ -339,23 +155,10 @@
foreach(source, enabled_sources) {
# TODO(sammc): Use a map instead of a linear scan when GN supports maps.
foreach(typemap, bindings_configuration.typemaps) {
- _typemap_config = {
- }
- _typemap_config = typemap.config
- if (get_path_info(source, "abspath") == _typemap_config.mojom) {
+ if (get_path_info(source, "abspath") == typemap.mojom) {
active_typemaps += [ typemap ]
}
}
- if (is_mac && defined(bindings_configuration.typemaps_mac)) {
- foreach(typemap, bindings_configuration.typemaps_mac) {
- _typemap_config = {
- }
- _typemap_config = typemap.config
- if (get_path_info(source, "abspath") == _typemap_config.mojom) {
- active_typemaps += [ typemap ]
- }
- }
- }
}
if (!cpp_only) {
@@ -375,7 +178,28 @@
]
outputs = generator_cpp_outputs + generator_java_outputs +
generator_js_outputs
- args = common_generator_args
+ args = [
+ "--use_bundled_pylibs",
+ "generate",
+ "{{source}}",
+ "-d",
+ rebase_path("//", root_build_dir),
+ "-I",
+ rebase_path("//", root_build_dir),
+ "-o",
+ rebase_path(root_gen_dir),
+ "--bytecode_path",
+ rebase_path("$root_gen_dir/mojo/public/tools/bindings"),
+ ]
+
+ if (defined(invoker.import_dirs)) {
+ foreach(import_dir, invoker.import_dirs) {
+ args += [
+ "-I",
+ rebase_path(import_dir, root_build_dir),
+ ]
+ }
+ }
if (cpp_only) {
args += [
@@ -395,14 +219,6 @@
bindings_configuration.variant,
]
}
- depfile =
- "{{source_gen_dir}}/${generator_target_name}_{{source_name_part}}.d"
- args += [
- "--depfile",
- depfile,
- "--depfile_target",
- "{{source_gen_dir}}/{{source_name_part}}.mojom${variant_dash_suffix}.cc",
- ]
args += [
"--typemap",
@@ -412,27 +228,11 @@
if (defined(bindings_configuration.for_blink) &&
bindings_configuration.for_blink) {
args += [ "--for_blink" ]
- if (defined(invoker.export_class_attribute_blink)) {
- args += [
- "--export_attribute",
- invoker.export_class_attribute_blink,
- "--export_header",
- invoker.export_header_blink,
- ]
- }
- } else {
- if (defined(invoker.export_class_attribute)) {
- args += [
- "--export_attribute",
- invoker.export_class_attribute,
- "--export_header",
- invoker.export_header,
- ]
- }
}
- if (defined(invoker.use_once_callback) && invoker.use_once_callback) {
- args += [ "--use_once_callback" ]
+ if (defined(invoker.use_new_wrapper_types) &&
+ invoker.use_new_wrapper_types) {
+ args += [ "--use_new_wrapper_types" ]
}
}
}
@@ -472,39 +272,27 @@
# line length limitations.
typemap_description = []
foreach(typemap, active_typemaps) {
- _typemap_config = {
- }
- _typemap_config = typemap.config
typemap_description += [ "--start-typemap" ]
- if (defined(_typemap_config.public_headers)) {
- foreach(value, _typemap_config.public_headers) {
+ if (defined(typemap.public_headers)) {
+ foreach(value, typemap.public_headers) {
typemap_description += [ "public_headers=$value" ]
}
}
- if (defined(_typemap_config.traits_headers)) {
- foreach(value, _typemap_config.traits_headers) {
+ if (defined(typemap.traits_headers)) {
+ foreach(value, typemap.traits_headers) {
typemap_description += [ "traits_headers=$value" ]
}
}
- foreach(value, _typemap_config.type_mappings) {
+ foreach(value, typemap.type_mappings) {
typemap_description += [ "type_mappings=$value" ]
}
-
- # The typemap configuration files are not actually used as inputs here
- # but this establishes a necessary build dependency to ensure that
- # typemap changes force a rebuild of affected targets.
- inputs += [ typemap.filename ]
}
args += typemap_description
}
}
source_set("${target_name}${variant_suffix}") {
- if (defined(bindings_configuration.for_blink) &&
- bindings_configuration.for_blink &&
- defined(invoker.visibility_blink)) {
- visibility = invoker.visibility_blink
- } else if (defined(invoker.visibility)) {
+ if (defined(invoker.visibility)) {
visibility = invoker.visibility
}
if (defined(invoker.testonly)) {
@@ -513,81 +301,69 @@
if (defined(invoker.sources) && !defined(bindings_configuration.variant)) {
data = process_file_template(enabled_sources, generator_js_outputs)
}
- defines = []
+
+ public_deps = [
+ ":${cpp_sources_target_name}",
+ "//mojo/public/cpp/bindings",
+ ]
+ if (defined(invoker.deps)) {
+ public_deps += invoker.deps
+ }
+ if (defined(invoker.public_deps)) {
+ public_deps += invoker.public_deps
+ }
+
+ deps = []
+ if (defined(invoker.sources)) {
+ public_deps += [ ":$generator_target_name" ]
+ }
+ }
+
+ # The generated C++ source files. The main reason to introduce this target
+ # is so that mojo/public/cpp/bindings can depend on mojom interfaces without
+ # circular dependencies. It means that the target is missing the dependency
+ # on mojo/public/cpp/bindings. No external targets should depend directly on
+ # this target *except* mojo/public/cpp/bindings and other *_cpp_sources
+ # targets.
+ source_set(cpp_sources_target_name) {
if (defined(invoker.testonly)) {
testonly = invoker.testonly
}
- if (defined(invoker.export_define)) {
- defines += [ invoker.export_define ]
- }
- if (defined(invoker.export_define_blink)) {
- defines += [ invoker.export_define_blink ]
- }
if (enabled_sources != []) {
sources = process_file_template(enabled_sources, generator_cpp_outputs)
}
deps = [
"//mojo/public/cpp/bindings:struct_traits",
"//mojo/public/interfaces/bindings:bindings__generator",
- "//mojo/public/interfaces/bindings:bindings_shared__generator",
- ]
- public_deps = [
- ":$shared_cpp_sources_target_name",
- "//base",
- "//mojo/public/cpp/bindings",
]
if (enabled_sources != []) {
- public_deps += [ ":$generator_target_name" ]
+ deps += [ ":$generator_target_name" ]
}
+ public_deps = [
+ "//base",
+ ]
foreach(d, all_deps) {
# Resolve the name, so that a target //mojo/something becomes
- # //mojo/something:something and we can append variant_suffix to
+ # //mojo/something:something and we can append cpp_sources_suffix to
# get the cpp dependency name.
full_name = get_label_info("$d", "label_no_toolchain")
- public_deps += [ "${full_name}${variant_suffix}" ]
- }
- if (defined(bindings_configuration.for_blink) &&
- bindings_configuration.for_blink) {
- if (defined(invoker.overridden_deps_blink)) {
- foreach(d, invoker.overridden_deps_blink) {
- # Resolve the name, so that a target //mojo/something becomes
- # //mojo/something:something and we can append variant_suffix
- # to get the cpp dependency name.
- full_name = get_label_info("$d", "label_no_toolchain")
- public_deps -= [ "${full_name}${variant_suffix}" ]
- }
- public_deps += invoker.component_deps_blink
- }
- } else {
- if (defined(invoker.overridden_deps)) {
- foreach(d, invoker.overridden_deps) {
- # Resolve the name, so that a target //mojo/something becomes
- # //mojo/something:something and we can append variant_suffix
- # to get the cpp dependency name.
- full_name = get_label_info("$d", "label_no_toolchain")
- public_deps -= [ "${full_name}${variant_suffix}" ]
- }
- public_deps += invoker.component_deps
- }
+ public_deps += [ "${full_name}${variant_suffix}_${cpp_sources_suffix}" ]
}
foreach(typemap, active_typemaps) {
- _typemap_config = {
+ if (defined(typemap.public_headers)) {
+ sources += typemap.public_headers
}
- _typemap_config = typemap.config
- if (defined(_typemap_config.public_headers)) {
- sources += _typemap_config.public_headers
+ if (defined(typemap.traits_headers)) {
+ sources += typemap.traits_headers
}
- if (defined(_typemap_config.traits_headers)) {
- sources += _typemap_config.traits_headers
+ if (defined(typemap.sources)) {
+ sources += typemap.sources
}
- if (defined(_typemap_config.sources)) {
- sources += _typemap_config.sources
+ if (defined(typemap.public_deps)) {
+ public_deps += typemap.public_deps
}
- if (defined(_typemap_config.public_deps)) {
- public_deps += _typemap_config.public_deps
- }
- if (defined(_typemap_config.deps)) {
- deps += _typemap_config.deps
+ if (defined(typemap.deps)) {
+ deps += typemap.deps
}
}
if (defined(bindings_configuration.for_blink) &&
@@ -629,8 +405,8 @@
android_library(java_target_name) {
deps = [
"//base:base_java",
- "//mojo/public/java:bindings_java",
- "//mojo/public/java:system_java",
+ "//mojo/public/java:bindings",
+ "//mojo/public/java:system",
]
foreach(d, all_deps) {
diff --git a/mojo/public/tools/bindings/mojom_bindings_generator.py b/mojo/public/tools/bindings/mojom_bindings_generator.py
index f90ce01..12196ba 100755
--- a/mojo/public/tools/bindings/mojom_bindings_generator.py
+++ b/mojo/public/tools/bindings/mojom_bindings_generator.py
@@ -37,9 +37,10 @@
from mojom.error import Error
import mojom.fileutil as fileutil
-from mojom.generate import translate
+from mojom.generate.data import OrderedModuleFromData
from mojom.generate import template_expander
from mojom.parse.parser import Parse
+from mojom.parse.translate import Translate
_BUILTIN_GENERATORS = {
@@ -101,12 +102,6 @@
class MojomProcessor(object):
- """Parses mojom files and creates ASTs for them.
-
- Attributes:
- _processed_files: {Dict[str, mojom.generate.module.Module]} Mapping from
- relative mojom filename paths to the module AST for that mojom file.
- """
def __init__(self, should_generate):
self._should_generate = should_generate
self._processed_files = {}
@@ -141,18 +136,20 @@
tree = self._parsed_files[rel_filename.path]
dirname, name = os.path.split(rel_filename.path)
+ mojom = Translate(tree, name)
+ if args.debug_print_intermediate:
+ pprint.PrettyPrinter().pprint(mojom)
# Process all our imports first and collect the module object for each.
# We use these to generate proper type info.
- imports = {}
- for parsed_imp in tree.import_list:
+ for import_data in mojom['imports']:
rel_import_file = FindImportFile(
RelativePath(dirname, rel_filename.source_root),
import_data['filename'], args.import_directories)
import_data['module'] = self._GenerateModule(
args, remaining_args, generator_modules, rel_import_file)
- module = translate.OrderedModule(tree, name, imports)
+ module = OrderedModuleFromData(mojom)
# Set the path as relative to the source root.
module.path = rel_filename.relative_path()
@@ -166,10 +163,7 @@
module, args.output_dir, typemap=self._typemap.get(language, {}),
variant=args.variant, bytecode_path=args.bytecode_path,
for_blink=args.for_blink,
- use_once_callback=args.use_once_callback,
- export_attribute=args.export_attribute,
- export_header=args.export_header,
- generate_non_variant_code=args.generate_non_variant_code)
+ use_new_wrapper_types=args.use_new_wrapper_types)
filtered_args = []
if hasattr(generator_module, 'GENERATOR_PREFIX'):
prefix = '--' + generator_module.GENERATOR_PREFIX + '_'
@@ -196,7 +190,7 @@
with open(rel_filename.path) as f:
source = f.read()
except IOError as e:
- print "%s: Error: %s" % (rel_filename.path, e.strerror) + \
+ print "%s: Error: %s" % (e.rel_filename.path, e.strerror) + \
MakeImportStackMessage(imported_filename_stack + [rel_filename.path])
sys.exit(1)
@@ -236,12 +230,6 @@
processor.LoadTypemaps(set(args.typemaps))
for filename in args.filename:
processor.ProcessFile(args, remaining_args, generator_modules, filename)
- if args.depfile:
- assert args.depfile_target
- with open(args.depfile, 'w') as f:
- f.write('%s: %s' % (
- args.depfile_target,
- ' '.join(processor._parsed_files.keys())))
return 0
@@ -270,6 +258,9 @@
generate_parser.add_argument("-o", "--output_dir", dest="output_dir",
default=".",
help="output directory for generated files")
+ generate_parser.add_argument("--debug_print_intermediate",
+ action="store_true",
+ help="print the intermediate representation")
generate_parser.add_argument("-g", "--generators",
dest="generators_string",
metavar="GENERATORS",
@@ -295,25 +286,9 @@
help="Use WTF types as generated types for mojo "
"string/array/map.")
generate_parser.add_argument(
- "--use_once_callback", action="store_true",
- help="Use base::OnceCallback instead of base::RepeatingCallback.")
- generate_parser.add_argument(
- "--export_attribute", type=str, default="",
- help="Optional attribute to specify on class declaration to export it "
- "for the component build.")
- generate_parser.add_argument(
- "--export_header", type=str, default="",
- help="Optional header to include in the generated headers to support the "
- "component build.")
- generate_parser.add_argument(
- "--generate_non_variant_code", action="store_true",
- help="Generate code that is shared by different variants.")
- generate_parser.add_argument(
- "--depfile", type=str,
- help="A file into which the list of input files will be written.")
- generate_parser.add_argument(
- "--depfile_target", type=str,
- help="The target name to use in the depfile.")
+ "--use_new_wrapper_types", action="store_true",
+ help="Map mojom array/map/string to STL (for chromium variant) or WTF "
+ "(for blink variant) types directly.")
generate_parser.set_defaults(func=_Generate)
precompile_parser = subparsers.add_parser("precompile",
diff --git a/mojo/public/tools/bindings/mojom_list_outputs.py b/mojo/public/tools/bindings/mojom_list_outputs.py
new file mode 100755
index 0000000..267bd80
--- /dev/null
+++ b/mojo/public/tools/bindings/mojom_list_outputs.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import argparse
+import os.path
+import sys
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="GYP helper script for mapping mojoms => generated outputs.")
+ parser.add_argument("--basedir", required=True)
+ parser.add_argument("--variant", required=True)
+ parser.add_argument("mojom", nargs="*")
+
+ args = parser.parse_args()
+
+ variant = args.variant if args.variant != "none" else None
+
+ for mojom in args.mojom:
+ full = os.path.join("<(SHARED_INTERMEDIATE_DIR)", args.basedir, mojom)
+ base, ext = os.path.splitext(full)
+
+ # Ignore non-mojom files.
+ if ext != ".mojom":
+ continue
+
+ # Fix filename escaping issues on Windows.
+ base = base.replace("\\", "/")
+ if variant:
+ print base + ".mojom-%s.cc" % variant
+ print base + ".mojom-%s.h" % variant
+ print base + ".mojom-%s-internal.h" % variant
+ else:
+ print base + ".mojom.cc"
+ print base + ".mojom.h"
+ print base + ".mojom-internal.h"
+ print base + ".mojom.js"
+
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/data.py b/mojo/public/tools/bindings/pylib/mojom/generate/data.py
new file mode 100644
index 0000000..9ceb2e9
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/data.py
@@ -0,0 +1,530 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# TODO(vtl): "data" is a pretty vague name. Rename it?
+
+import copy
+
+import module as mojom
+
+# This module provides a mechanism to turn mojom Modules to dictionaries and
+# back again. This can be used to persist a mojom Module created progromatically
+# or to read a dictionary from code or a file.
+# Example:
+# test_dict = {
+# 'name': 'test',
+# 'namespace': 'testspace',
+# 'structs': [{
+# 'name': 'teststruct',
+# 'fields': [
+# {'name': 'testfield1', 'kind': 'i32'},
+# {'name': 'testfield2', 'kind': 'a:i32', 'ordinal': 42}]}],
+# 'interfaces': [{
+# 'name': 'Server',
+# 'methods': [{
+# 'name': 'Foo',
+# 'parameters': [{
+# 'name': 'foo', 'kind': 'i32'},
+# {'name': 'bar', 'kind': 'a:x:teststruct'}],
+# 'ordinal': 42}]}]
+# }
+# test_module = data.ModuleFromData(test_dict)
+
+# Used to create a subclass of str that supports sorting by index, to make
+# pretty printing maintain the order.
+def istr(index, string):
+ class IndexedString(str):
+ def __lt__(self, other):
+ return self.__index__ < other.__index__
+
+ rv = IndexedString(string)
+ rv.__index__ = index
+ return rv
+
+def AddOptional(dictionary, key, value):
+ if value is not None:
+ dictionary[key] = value;
+
+builtin_values = frozenset([
+ "double.INFINITY",
+ "double.NEGATIVE_INFINITY",
+ "double.NAN",
+ "float.INFINITY",
+ "float.NEGATIVE_INFINITY",
+ "float.NAN"])
+
+def IsBuiltinValue(value):
+ return value in builtin_values
+
+def LookupKind(kinds, spec, scope):
+ """Tries to find which Kind a spec refers to, given the scope in which its
+ referenced. Starts checking from the narrowest scope to most general. For
+ example, given a struct field like
+ Foo.Bar x;
+ Foo.Bar could refer to the type 'Bar' in the 'Foo' namespace, or an inner
+ type 'Bar' in the struct 'Foo' in the current namespace.
+
+ |scope| is a tuple that looks like (namespace, struct/interface), referring
+ to the location where the type is referenced."""
+ if spec.startswith('x:'):
+ name = spec[2:]
+ for i in xrange(len(scope), -1, -1):
+ test_spec = 'x:'
+ if i > 0:
+ test_spec += '.'.join(scope[:i]) + '.'
+ test_spec += name
+ kind = kinds.get(test_spec)
+ if kind:
+ return kind
+
+ return kinds.get(spec)
+
+def LookupValue(values, name, scope, kind):
+ """Like LookupKind, but for constant values."""
+ # If the type is an enum, the value can be specified as a qualified name, in
+ # which case the form EnumName.ENUM_VALUE must be used. We use the presence
+ # of a '.' in the requested name to identify this. Otherwise, we prepend the
+ # enum name.
+ if isinstance(kind, mojom.Enum) and '.' not in name:
+ name = '%s.%s' % (kind.spec.split(':', 1)[1], name)
+ for i in reversed(xrange(len(scope) + 1)):
+ test_spec = '.'.join(scope[:i])
+ if test_spec:
+ test_spec += '.'
+ test_spec += name
+ value = values.get(test_spec)
+ if value:
+ return value
+
+ return values.get(name)
+
+def FixupExpression(module, value, scope, kind):
+ """Translates an IDENTIFIER into a built-in value or structured NamedValue
+ object."""
+ if isinstance(value, tuple) and value[0] == 'IDENTIFIER':
+ # Allow user defined values to shadow builtins.
+ result = LookupValue(module.values, value[1], scope, kind)
+ if result:
+ if isinstance(result, tuple):
+ raise Exception('Unable to resolve expression: %r' % value[1])
+ return result
+ if IsBuiltinValue(value[1]):
+ return mojom.BuiltinValue(value[1])
+ return value
+
+def KindToData(kind):
+ return kind.spec
+
+def KindFromData(kinds, data, scope):
+ kind = LookupKind(kinds, data, scope)
+ if kind:
+ return kind
+
+ if data.startswith('?'):
+ kind = KindFromData(kinds, data[1:], scope).MakeNullableKind()
+ elif data.startswith('a:'):
+ kind = mojom.Array(KindFromData(kinds, data[2:], scope))
+ elif data.startswith('asso:'):
+ inner_kind = KindFromData(kinds, data[5:], scope)
+ if isinstance(inner_kind, mojom.InterfaceRequest):
+ kind = mojom.AssociatedInterfaceRequest(inner_kind)
+ else:
+ kind = mojom.AssociatedInterface(inner_kind)
+ elif data.startswith('a'):
+ colon = data.find(':')
+ length = int(data[1:colon])
+ kind = mojom.Array(KindFromData(kinds, data[colon+1:], scope), length)
+ elif data.startswith('r:'):
+ kind = mojom.InterfaceRequest(KindFromData(kinds, data[2:], scope))
+ elif data.startswith('m['):
+ # Isolate the two types from their brackets.
+
+ # It is not allowed to use map as key, so there shouldn't be nested ']'s
+ # inside the key type spec.
+ key_end = data.find(']')
+ assert key_end != -1 and key_end < len(data) - 1
+ assert data[key_end+1] == '[' and data[-1] == ']'
+
+ first_kind = data[2:key_end]
+ second_kind = data[key_end+2:-1]
+
+ kind = mojom.Map(KindFromData(kinds, first_kind, scope),
+ KindFromData(kinds, second_kind, scope))
+ else:
+ kind = mojom.Kind(data)
+
+ kinds[data] = kind
+ return kind
+
+def KindFromImport(original_kind, imported_from):
+ """Used with 'import module' - clones the kind imported from the given
+ module's namespace. Only used with Structs, Unions, Interfaces and Enums."""
+ kind = copy.copy(original_kind)
+ # |shared_definition| is used to store various properties (see
+ # |AddSharedProperty()| in module.py), including |imported_from|. We don't
+ # want the copy to share these with the original, so copy it if necessary.
+ if hasattr(original_kind, 'shared_definition'):
+ kind.shared_definition = copy.copy(original_kind.shared_definition)
+ kind.imported_from = imported_from
+ return kind
+
+def ImportFromData(module, data):
+ import_module = data['module']
+
+ import_item = {}
+ import_item['module_name'] = import_module.name
+ import_item['namespace'] = import_module.namespace
+ import_item['module'] = import_module
+
+ # Copy the struct kinds from our imports into the current module.
+ importable_kinds = (mojom.Struct, mojom.Union, mojom.Enum, mojom.Interface)
+ for kind in import_module.kinds.itervalues():
+ if (isinstance(kind, importable_kinds) and
+ kind.imported_from is None):
+ kind = KindFromImport(kind, import_item)
+ module.kinds[kind.spec] = kind
+ # Ditto for values.
+ for value in import_module.values.itervalues():
+ if value.imported_from is None:
+ # Values don't have shared definitions (since they're not nullable), so no
+ # need to do anything special.
+ value = copy.copy(value)
+ value.imported_from = import_item
+ module.values[value.GetSpec()] = value
+
+ return import_item
+
+def StructToData(struct):
+ data = {
+ istr(0, 'name'): struct.name,
+ istr(1, 'fields'): map(FieldToData, struct.fields),
+ # TODO(yzshen): EnumToData() and ConstantToData() are missing.
+ istr(2, 'enums'): [],
+ istr(3, 'constants'): []
+ }
+ AddOptional(data, istr(4, 'attributes'), struct.attributes)
+ return data
+
+def StructFromData(module, data):
+ struct = mojom.Struct(module=module)
+ struct.name = data['name']
+ struct.native_only = data['native_only']
+ struct.spec = 'x:' + module.namespace + '.' + struct.name
+ module.kinds[struct.spec] = struct
+ if struct.native_only:
+ struct.enums = []
+ struct.constants = []
+ struct.fields_data = []
+ else:
+ struct.enums = map(lambda enum:
+ EnumFromData(module, enum, struct), data['enums'])
+ struct.constants = map(lambda constant:
+ ConstantFromData(module, constant, struct), data['constants'])
+ # Stash fields data here temporarily.
+ struct.fields_data = data['fields']
+ struct.attributes = data.get('attributes')
+
+ # Enforce that a [Native] attribute is set to make native-only struct
+ # declarations more explicit.
+ if struct.native_only:
+ if not struct.attributes or not struct.attributes.get('Native', False):
+ raise Exception("Native-only struct declarations must include a " +
+ "Native attribute.")
+
+ return struct
+
+def UnionToData(union):
+ data = {
+ istr(0, 'name'): union.name,
+ istr(1, 'fields'): map(FieldToData, union.fields)
+ }
+ AddOptional(data, istr(2, 'attributes'), union.attributes)
+ return data
+
+def UnionFromData(module, data):
+ union = mojom.Union(module=module)
+ union.name = data['name']
+ union.spec = 'x:' + module.namespace + '.' + union.name
+ module.kinds[union.spec] = union
+ # Stash fields data here temporarily.
+ union.fields_data = data['fields']
+ union.attributes = data.get('attributes')
+ return union
+
+def FieldToData(field):
+ data = {
+ istr(0, 'name'): field.name,
+ istr(1, 'kind'): KindToData(field.kind)
+ }
+ AddOptional(data, istr(2, 'ordinal'), field.ordinal)
+ AddOptional(data, istr(3, 'default'), field.default)
+ AddOptional(data, istr(4, 'attributes'), field.attributes)
+ return data
+
+def StructFieldFromData(module, data, struct):
+ field = mojom.StructField()
+ PopulateField(field, module, data, struct)
+ return field
+
+def UnionFieldFromData(module, data, union):
+ field = mojom.UnionField()
+ PopulateField(field, module, data, union)
+ return field
+
+def PopulateField(field, module, data, parent):
+ field.name = data['name']
+ field.kind = KindFromData(
+ module.kinds, data['kind'], (module.namespace, parent.name))
+ field.ordinal = data.get('ordinal')
+ field.default = FixupExpression(
+ module, data.get('default'), (module.namespace, parent.name), field.kind)
+ field.attributes = data.get('attributes')
+
+def ParameterToData(parameter):
+ data = {
+ istr(0, 'name'): parameter.name,
+ istr(1, 'kind'): parameter.kind.spec
+ }
+ AddOptional(data, istr(2, 'ordinal'), parameter.ordinal)
+ AddOptional(data, istr(3, 'default'), parameter.default)
+ AddOptional(data, istr(4, 'attributes'), parameter.attributes)
+ return data
+
+def ParameterFromData(module, data, interface):
+ parameter = mojom.Parameter()
+ parameter.name = data['name']
+ parameter.kind = KindFromData(
+ module.kinds, data['kind'], (module.namespace, interface.name))
+ parameter.ordinal = data.get('ordinal')
+ parameter.default = data.get('default')
+ parameter.attributes = data.get('attributes')
+ return parameter
+
+def MethodToData(method):
+ data = {
+ istr(0, 'name'): method.name,
+ istr(1, 'parameters'): map(ParameterToData, method.parameters)
+ }
+ if method.response_parameters is not None:
+ data[istr(2, 'response_parameters')] = map(
+ ParameterToData, method.response_parameters)
+ AddOptional(data, istr(3, 'ordinal'), method.ordinal)
+ AddOptional(data, istr(4, 'attributes'), method.attributes)
+ return data
+
+def MethodFromData(module, data, interface):
+ method = mojom.Method(interface, data['name'], ordinal=data.get('ordinal'))
+ method.parameters = map(lambda parameter:
+ ParameterFromData(module, parameter, interface), data['parameters'])
+ if data.has_key('response_parameters'):
+ method.response_parameters = map(
+ lambda parameter: ParameterFromData(module, parameter, interface),
+ data['response_parameters'])
+ method.attributes = data.get('attributes')
+
+ # Enforce that only methods with response can have a [Sync] attribute.
+ if method.sync and method.response_parameters is None:
+ raise Exception("Only methods with response can include a [Sync] "
+ "attribute. If no response parameters are needed, you "
+ "could use an empty response parameter list, i.e., "
+ "\"=> ()\".")
+
+ return method
+
+def InterfaceToData(interface):
+ data = {
+ istr(0, 'name'): interface.name,
+ istr(1, 'methods'): map(MethodToData, interface.methods),
+ # TODO(yzshen): EnumToData() and ConstantToData() are missing.
+ istr(2, 'enums'): [],
+ istr(3, 'constants'): []
+ }
+ AddOptional(data, istr(4, 'attributes'), interface.attributes)
+ return data
+
+def InterfaceFromData(module, data):
+ interface = mojom.Interface(module=module)
+ interface.name = data['name']
+ interface.spec = 'x:' + module.namespace + '.' + interface.name
+ module.kinds[interface.spec] = interface
+ interface.enums = map(lambda enum:
+ EnumFromData(module, enum, interface), data['enums'])
+ interface.constants = map(lambda constant:
+ ConstantFromData(module, constant, interface), data['constants'])
+ # Stash methods data here temporarily.
+ interface.methods_data = data['methods']
+ interface.attributes = data.get('attributes')
+ return interface
+
+def EnumFieldFromData(module, enum, data, parent_kind):
+ field = mojom.EnumField()
+ field.name = data['name']
+ # TODO(mpcomplete): FixupExpression should be done in the second pass,
+ # so constants and enums can refer to each other.
+ # TODO(mpcomplete): But then, what if constants are initialized to an enum? Or
+ # vice versa?
+ if parent_kind:
+ field.value = FixupExpression(
+ module, data.get('value'), (module.namespace, parent_kind.name), enum)
+ else:
+ field.value = FixupExpression(
+ module, data.get('value'), (module.namespace, ), enum)
+ field.attributes = data.get('attributes')
+ value = mojom.EnumValue(module, enum, field)
+ module.values[value.GetSpec()] = value
+ return field
+
+def ResolveNumericEnumValues(enum_fields):
+ """
+ Given a reference to a list of mojom.EnumField, resolves and assigns their
+ values to EnumField.numeric_value.
+ """
+
+ # map of <name> -> integral value
+ resolved_enum_values = {}
+ prev_value = -1
+ for field in enum_fields:
+ # This enum value is +1 the previous enum value (e.g: BEGIN).
+ if field.value is None:
+ prev_value += 1
+
+ # Integral value (e.g: BEGIN = -0x1).
+ elif type(field.value) is str:
+ prev_value = int(field.value, 0)
+
+ # Reference to a previous enum value (e.g: INIT = BEGIN).
+ elif type(field.value) is mojom.EnumValue:
+ prev_value = resolved_enum_values[field.value.name]
+ else:
+ raise Exception("Unresolved enum value.")
+
+ resolved_enum_values[field.name] = prev_value
+ field.numeric_value = prev_value
+
+def EnumFromData(module, data, parent_kind):
+ enum = mojom.Enum(module=module)
+ enum.name = data['name']
+ enum.native_only = data['native_only']
+ name = enum.name
+ if parent_kind:
+ name = parent_kind.name + '.' + name
+ enum.spec = 'x:%s.%s' % (module.namespace, name)
+ enum.parent_kind = parent_kind
+ enum.attributes = data.get('attributes')
+ if enum.native_only:
+ enum.fields = []
+ else:
+ enum.fields = map(
+ lambda field: EnumFieldFromData(module, enum, field, parent_kind),
+ data['fields'])
+ ResolveNumericEnumValues(enum.fields)
+
+ module.kinds[enum.spec] = enum
+
+ # Enforce that a [Native] attribute is set to make native-only enum
+ # declarations more explicit.
+ if enum.native_only:
+ if not enum.attributes or not enum.attributes.get('Native', False):
+ raise Exception("Native-only enum declarations must include a " +
+ "Native attribute.")
+
+ return enum
+
+def ConstantFromData(module, data, parent_kind):
+ constant = mojom.Constant()
+ constant.name = data['name']
+ if parent_kind:
+ scope = (module.namespace, parent_kind.name)
+ else:
+ scope = (module.namespace, )
+ # TODO(mpcomplete): maybe we should only support POD kinds.
+ constant.kind = KindFromData(module.kinds, data['kind'], scope)
+ constant.parent_kind = parent_kind
+ constant.value = FixupExpression(module, data.get('value'), scope, None)
+
+ value = mojom.ConstantValue(module, parent_kind, constant)
+ module.values[value.GetSpec()] = value
+ return constant
+
+def ModuleToData(module):
+ data = {
+ istr(0, 'name'): module.name,
+ istr(1, 'namespace'): module.namespace,
+ # TODO(yzshen): Imports information is missing.
+ istr(2, 'imports'): [],
+ istr(3, 'structs'): map(StructToData, module.structs),
+ istr(4, 'unions'): map(UnionToData, module.unions),
+ istr(5, 'interfaces'): map(InterfaceToData, module.interfaces),
+ # TODO(yzshen): EnumToData() and ConstantToData() are missing.
+ istr(6, 'enums'): [],
+ istr(7, 'constants'): []
+ }
+ AddOptional(data, istr(8, 'attributes'), module.attributes)
+ return data
+
+def ModuleFromData(data):
+ module = mojom.Module()
+ module.kinds = {}
+ for kind in mojom.PRIMITIVES:
+ module.kinds[kind.spec] = kind
+
+ module.values = {}
+
+ module.name = data['name']
+ module.namespace = data['namespace']
+ # Imports must come first, because they add to module.kinds which is used
+ # by by the others.
+ module.imports = map(
+ lambda import_data: ImportFromData(module, import_data),
+ data['imports'])
+ module.attributes = data.get('attributes')
+
+ # First pass collects kinds.
+ module.enums = map(
+ lambda enum: EnumFromData(module, enum, None), data['enums'])
+ module.structs = map(
+ lambda struct: StructFromData(module, struct), data['structs'])
+ module.unions = map(
+ lambda union: UnionFromData(module, union), data.get('unions', []))
+ module.interfaces = map(
+ lambda interface: InterfaceFromData(module, interface),
+ data['interfaces'])
+ module.constants = map(
+ lambda constant: ConstantFromData(module, constant, None),
+ data['constants'])
+
+ # Second pass expands fields and methods. This allows fields and parameters
+ # to refer to kinds defined anywhere in the mojom.
+ for struct in module.structs:
+ struct.fields = map(lambda field:
+ StructFieldFromData(module, field, struct), struct.fields_data)
+ del struct.fields_data
+ for union in module.unions:
+ union.fields = map(lambda field:
+ UnionFieldFromData(module, field, union), union.fields_data)
+ del union.fields_data
+ for interface in module.interfaces:
+ interface.methods = map(lambda method:
+ MethodFromData(module, method, interface), interface.methods_data)
+ del interface.methods_data
+
+ return module
+
+def OrderedModuleFromData(data):
+ """Convert Mojom IR to a module.
+
+ Args:
+ data: The Mojom IR as a dict.
+
+ Returns:
+ A mojom.generate.module.Module object.
+ """
+ module = ModuleFromData(data)
+ for interface in module.interfaces:
+ next_ordinal = 0
+ for method in interface.methods:
+ if method.ordinal is None:
+ method.ordinal = next_ordinal
+ next_ordinal = method.ordinal + 1
+ return module
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py b/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py
new file mode 100644
index 0000000..096554c
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py
@@ -0,0 +1,86 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import sys
+
+import data
+import test_support
+
+EXPECT_EQ = test_support.EXPECT_EQ
+EXPECT_TRUE = test_support.EXPECT_TRUE
+RunTest = test_support.RunTest
+
+
+def DeepEquals(d1, d2):
+ if d1 == d2:
+ return True
+ if d2.__class__ != d2.__class__:
+ return False
+ if isinstance(d1, dict):
+ if set(d1.keys()) != set(d2.keys()):
+ return False
+ for key in d1.keys():
+ if not DeepEquals(d1[key], d2[key]):
+ return False
+ return True
+ if isinstance(d1, (list, tuple)):
+ if len(d1) != len(d2):
+ return False
+ for i in range(len(d1)):
+ if not DeepEquals(d1[i], d2[i]):
+ return False
+ return True
+ return False
+
+
+test_dict = {
+ 'name': 'test',
+ 'namespace': 'testspace',
+ 'structs': [{
+ 'name': 'teststruct',
+ 'fields': [
+ {'name': 'testfield1', 'kind': 'i32'},
+ {'name': 'testfield2', 'kind': 'a:i32', 'ordinal': 42}]}],
+ 'interfaces': [{
+ 'name': 'Server',
+ 'client': None,
+ 'methods': [{
+ 'name': 'Foo',
+ 'parameters': [
+ {'name': 'foo', 'kind': 'i32'},
+ {'name': 'bar', 'kind': 'a:x:teststruct'}],
+ 'ordinal': 42}]}]
+}
+
+
+def TestRead():
+ module = data.ModuleFromData(test_dict)
+ return test_support.TestTestModule(module)
+
+
+def TestWrite():
+ module = test_support.BuildTestModule()
+ d = data.ModuleToData(module)
+ return EXPECT_TRUE(DeepEquals(test_dict, d))
+
+
+def TestWriteRead():
+ module1 = test_support.BuildTestModule()
+
+ dict1 = data.ModuleToData(module1)
+ module2 = data.ModuleFromData(dict1)
+ return EXPECT_TRUE(test_support.ModulesAreEqual(module1, module2))
+
+
+def Main(args):
+ errors = 0
+ errors += RunTest(TestWriteRead)
+ errors += RunTest(TestRead)
+ errors += RunTest(TestWrite)
+
+ return errors
+
+
+if __name__ == '__main__':
+ sys.exit(Main(sys.argv[1:]))
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
index e4ab373..ccb8363 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py
@@ -37,19 +37,15 @@
# Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all
# files to stdout.
def __init__(self, module, output_dir=None, typemap=None, variant=None,
- bytecode_path=None, for_blink=False, use_once_callback=False,
- export_attribute=None, export_header=None,
- generate_non_variant_code=False):
+ bytecode_path=None, for_blink=False,
+ use_new_wrapper_types=False):
self.module = module
self.output_dir = output_dir
self.typemap = typemap or {}
self.variant = variant
self.bytecode_path = bytecode_path
self.for_blink = for_blink
- self.use_once_callback = use_once_callback
- self.export_attribute = export_attribute
- self.export_header = export_header
- self.generate_non_variant_code = generate_non_variant_code
+ self.use_new_wrapper_types = use_new_wrapper_types
def GetStructsFromMethods(self):
result = []
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/module.py b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
index 3a5f188..2775b6c 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/module.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/module.py
@@ -294,20 +294,6 @@
class Struct(ReferenceKind):
- """A struct with typed fields.
-
- Attributes:
- name: {str} The name of the struct type.
- native_only: {bool} Does the struct have a body (i.e. any fields) or is it
- purely a native struct.
- module: {Module} The defining module.
- imported_from: {dict} Information about where this union was
- imported from.
- fields: {List[StructField]} The members of the struct.
- attributes: {dict} Additional information about the struct, such as
- if it's a native struct.
- """
-
ReferenceKind.AddSharedProperty('name')
ReferenceKind.AddSharedProperty('native_only')
ReferenceKind.AddSharedProperty('module')
@@ -439,9 +425,14 @@
']')
if IsNullableKind(key_kind):
raise Exception("Nullable kinds cannot be keys in maps.")
+ if IsStructKind(key_kind):
+ # TODO(erg): It would sometimes be nice if we could key on struct
+ # values. However, what happens if the struct has a handle in it? Or
+ # non-copyable data like an array?
+ raise Exception("Structs cannot be keys in maps.")
if IsAnyHandleKind(key_kind):
raise Exception("Handles cannot be keys in maps.")
- if IsAnyInterfaceKind(key_kind):
+ if IsInterfaceKind(key_kind):
raise Exception("Interfaces cannot be keys in maps.")
if IsArrayKind(key_kind):
raise Exception("Arrays cannot be keys in maps.")
@@ -690,10 +681,6 @@
return kind.spec == FLOAT.spec
-def IsDoubleKind(kind):
- return kind.spec == DOUBLE.spec
-
-
def IsIntegralKind(kind):
return (kind.spec == BOOL.spec or
kind.spec == INT8.spec or
@@ -784,22 +771,20 @@
IsMapKind(kind))
-# Please note that it doesn't include any interface kind.
+# Please note that interface is not considered as handle kind, since it is an
+# aggregate type consisting of a handle and a version number.
def IsAnyHandleKind(kind):
return (IsGenericHandleKind(kind) or
IsDataPipeConsumerKind(kind) or
IsDataPipeProducerKind(kind) or
IsMessagePipeKind(kind) or
- IsSharedBufferKind(kind))
-
-
-def IsAnyInterfaceKind(kind):
- return (IsInterfaceKind(kind) or IsInterfaceRequestKind(kind) or
- IsAssociatedKind(kind))
+ IsSharedBufferKind(kind) or
+ IsInterfaceRequestKind(kind))
def IsAnyHandleOrInterfaceKind(kind):
- return IsAnyHandleKind(kind) or IsAnyInterfaceKind(kind)
+ return (IsAnyHandleKind(kind) or IsInterfaceKind(kind) or
+ IsAssociatedKind(kind))
def IsAssociatedKind(kind):
@@ -853,39 +838,3 @@
if method.sync:
return True
return False
-
-
-def ContainsHandlesOrInterfaces(kind):
- """Check if the kind contains any handles.
-
- This check is recursive so it checks all struct fields, containers elements,
- etc.
-
- Args:
- struct: {Kind} The kind to check.
-
- Returns:
- {bool}: True if the kind contains handles.
- """
- # We remember the types we already checked to avoid infinite recursion when
- # checking recursive (or mutually recursive) types:
- checked = set()
- def Check(kind):
- if kind.spec in checked:
- return False
- checked.add(kind.spec)
- if IsStructKind(kind):
- return any(Check(field.kind) for field in kind.fields)
- elif IsUnionKind(kind):
- return any(Check(field.kind) for field in kind.fields)
- elif IsAnyHandleKind(kind):
- return True
- elif IsAnyInterfaceKind(kind):
- return True
- elif IsArrayKind(kind):
- return Check(kind.kind)
- elif IsMapKind(kind):
- return Check(kind.key_kind) or Check(kind.value_kind)
- else:
- return False
- return Check(kind)
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py b/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
index 66f8954..9ea6cf8 100644
--- a/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
+++ b/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py
@@ -2,7 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-# Based on third_party/WebKit/Source/build/scripts/template_expander.py.
+# Based on:
+# http://src.chromium.org/viewvc/blink/trunk/Source/build/scripts/template_expander.py
import imp
import os.path
diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/translate.py b/mojo/public/tools/bindings/pylib/mojom/generate/translate.py
deleted file mode 100644
index ffad744..0000000
--- a/mojo/public/tools/bindings/pylib/mojom/generate/translate.py
+++ /dev/null
@@ -1,639 +0,0 @@
-# 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.
-
-"""Convert parse tree to AST.
-
-This module converts the parse tree to the AST we use for code generation. The
-main entry point is OrderedModule, which gets passed the parser
-representation of a mojom file. When called it's assumed that all imports have
-already been parsed and converted to ASTs before.
-"""
-
-import copy
-import re
-
-import module as mojom
-from mojom.parse import ast
-
-def _DuplicateName(values):
- """Returns the 'name' of the first entry in |values| whose 'name' has already
- been encountered. If there are no duplicates, returns None."""
- names = set()
- for value in values:
- if value.name in names:
- return value.name
- names.add(value.name)
- return None
-
-def _ElemsOfType(elems, elem_type, scope):
- """Find all elements of the given type.
-
- Args:
- elems: {Sequence[Any]} Sequence of elems.
- elem_type: {Type[C]} Extract all elems of this type.
- scope: {str} The name of the surrounding scope (e.g. struct
- definition). Used in error messages.
-
- Returns:
- {List[C]} All elems of matching type.
- """
- assert isinstance(elem_type, type)
- result = [elem for elem in elems if isinstance(elem, elem_type)]
- duplicate_name = _DuplicateName(result)
- if duplicate_name:
- raise Exception('Names in mojom must be unique within a scope. The name '
- '"%s" is used more than once within the scope "%s".' %
- (duplicate_name, scope))
- return result
-
-def _MapKind(kind):
- map_to_kind = {'bool': 'b',
- 'int8': 'i8',
- 'int16': 'i16',
- 'int32': 'i32',
- 'int64': 'i64',
- 'uint8': 'u8',
- 'uint16': 'u16',
- 'uint32': 'u32',
- 'uint64': 'u64',
- 'float': 'f',
- 'double': 'd',
- 'string': 's',
- 'handle': 'h',
- 'handle<data_pipe_consumer>': 'h:d:c',
- 'handle<data_pipe_producer>': 'h:d:p',
- 'handle<message_pipe>': 'h:m',
- 'handle<shared_buffer>': 'h:s'}
- if kind.endswith('?'):
- base_kind = _MapKind(kind[0:-1])
- # NOTE: This doesn't rule out enum types. Those will be detected later, when
- # cross-reference is established.
- reference_kinds = ('m', 's', 'h', 'a', 'r', 'x', 'asso')
- if re.split('[^a-z]', base_kind, 1)[0] not in reference_kinds:
- raise Exception(
- 'A type (spec "%s") cannot be made nullable' % base_kind)
- return '?' + base_kind
- if kind.endswith('}'):
- lbracket = kind.rfind('{')
- value = kind[0:lbracket]
- return 'm[' + _MapKind(kind[lbracket+1:-1]) + '][' + _MapKind(value) + ']'
- if kind.endswith(']'):
- lbracket = kind.rfind('[')
- typename = kind[0:lbracket]
- return 'a' + kind[lbracket+1:-1] + ':' + _MapKind(typename)
- if kind.endswith('&'):
- return 'r:' + _MapKind(kind[0:-1])
- if kind.startswith('asso<'):
- assert kind.endswith('>')
- return 'asso:' + _MapKind(kind[5:-1])
- if kind in map_to_kind:
- return map_to_kind[kind]
- return 'x:' + kind
-
-def _AttributeListToDict(attribute_list):
- if attribute_list is None:
- return None
- assert isinstance(attribute_list, ast.AttributeList)
- # TODO(vtl): Check for duplicate keys here.
- return dict([(attribute.key, attribute.value)
- for attribute in attribute_list])
-
-builtin_values = frozenset([
- "double.INFINITY",
- "double.NEGATIVE_INFINITY",
- "double.NAN",
- "float.INFINITY",
- "float.NEGATIVE_INFINITY",
- "float.NAN"])
-
-def _IsBuiltinValue(value):
- return value in builtin_values
-
-def _LookupKind(kinds, spec, scope):
- """Tries to find which Kind a spec refers to, given the scope in which its
- referenced. Starts checking from the narrowest scope to most general. For
- example, given a struct field like
- Foo.Bar x;
- Foo.Bar could refer to the type 'Bar' in the 'Foo' namespace, or an inner
- type 'Bar' in the struct 'Foo' in the current namespace.
-
- |scope| is a tuple that looks like (namespace, struct/interface), referring
- to the location where the type is referenced."""
- if spec.startswith('x:'):
- name = spec[2:]
- for i in xrange(len(scope), -1, -1):
- test_spec = 'x:'
- if i > 0:
- test_spec += '.'.join(scope[:i]) + '.'
- test_spec += name
- kind = kinds.get(test_spec)
- if kind:
- return kind
-
- return kinds.get(spec)
-
-def _LookupValue(values, name, scope, kind):
- """Like LookupKind, but for constant values."""
- # If the type is an enum, the value can be specified as a qualified name, in
- # which case the form EnumName.ENUM_VALUE must be used. We use the presence
- # of a '.' in the requested name to identify this. Otherwise, we prepend the
- # enum name.
- if isinstance(kind, mojom.Enum) and '.' not in name:
- name = '%s.%s' % (kind.spec.split(':', 1)[1], name)
- for i in reversed(xrange(len(scope) + 1)):
- test_spec = '.'.join(scope[:i])
- if test_spec:
- test_spec += '.'
- test_spec += name
- value = values.get(test_spec)
- if value:
- return value
-
- return values.get(name)
-
-def _FixupExpression(module, value, scope, kind):
- """Translates an IDENTIFIER into a built-in value or structured NamedValue
- object."""
- if isinstance(value, tuple) and value[0] == 'IDENTIFIER':
- # Allow user defined values to shadow builtins.
- result = _LookupValue(module.values, value[1], scope, kind)
- if result:
- if isinstance(result, tuple):
- raise Exception('Unable to resolve expression: %r' % value[1])
- return result
- if _IsBuiltinValue(value[1]):
- return mojom.BuiltinValue(value[1])
- return value
-
-def _Kind(kinds, spec, scope):
- """Convert a type name into a mojom.Kind object.
-
- As a side-effect this function adds the result to 'kinds'.
-
- Args:
- kinds: {Dict[str, mojom.Kind]} All known kinds up to this point, indexed by
- their names.
- spec: {str} A name uniquely identifying a type.
- scope: {Tuple[str, str]} A tuple that looks like (namespace,
- struct/interface), referring to the location where the type is
- referenced.
-
- Returns:
- {mojom.Kind} The type corresponding to 'spec'.
- """
- kind = _LookupKind(kinds, spec, scope)
- if kind:
- return kind
-
- if spec.startswith('?'):
- kind = _Kind(kinds, spec[1:], scope).MakeNullableKind()
- elif spec.startswith('a:'):
- kind = mojom.Array(_Kind(kinds, spec[2:], scope))
- elif spec.startswith('asso:'):
- inner_kind = _Kind(kinds, spec[5:], scope)
- if isinstance(inner_kind, mojom.InterfaceRequest):
- kind = mojom.AssociatedInterfaceRequest(inner_kind)
- else:
- kind = mojom.AssociatedInterface(inner_kind)
- elif spec.startswith('a'):
- colon = spec.find(':')
- length = int(spec[1:colon])
- kind = mojom.Array(_Kind(kinds, spec[colon+1:], scope), length)
- elif spec.startswith('r:'):
- kind = mojom.InterfaceRequest(_Kind(kinds, spec[2:], scope))
- elif spec.startswith('m['):
- # Isolate the two types from their brackets.
-
- # It is not allowed to use map as key, so there shouldn't be nested ']'s
- # inside the key type spec.
- key_end = spec.find(']')
- assert key_end != -1 and key_end < len(spec) - 1
- assert spec[key_end+1] == '[' and spec[-1] == ']'
-
- first_kind = spec[2:key_end]
- second_kind = spec[key_end+2:-1]
-
- kind = mojom.Map(_Kind(kinds, first_kind, scope),
- _Kind(kinds, second_kind, scope))
- else:
- kind = mojom.Kind(spec)
-
- kinds[spec] = kind
- return kind
-
-def _KindFromImport(original_kind, imported_from):
- """Used with 'import module' - clones the kind imported from the given
- module's namespace. Only used with Structs, Unions, Interfaces and Enums."""
- kind = copy.copy(original_kind)
- # |shared_definition| is used to store various properties (see
- # |AddSharedProperty()| in module.py), including |imported_from|. We don't
- # want the copy to share these with the original, so copy it if necessary.
- if hasattr(original_kind, 'shared_definition'):
- kind.shared_definition = copy.copy(original_kind.shared_definition)
- kind.imported_from = imported_from
- return kind
-
-def _Import(module, import_module):
- import_item = {}
- import_item['module_name'] = import_module.name
- import_item['namespace'] = import_module.namespace
- import_item['module'] = import_module
-
- # Copy the struct kinds from our imports into the current module.
- importable_kinds = (mojom.Struct, mojom.Union, mojom.Enum, mojom.Interface)
- for kind in import_module.kinds.itervalues():
- if (isinstance(kind, importable_kinds) and
- kind.imported_from is None):
- kind = _KindFromImport(kind, import_item)
- module.kinds[kind.spec] = kind
- # Ditto for values.
- for value in import_module.values.itervalues():
- if value.imported_from is None:
- # Values don't have shared definitions (since they're not nullable), so no
- # need to do anything special.
- value = copy.copy(value)
- value.imported_from = import_item
- module.values[value.GetSpec()] = value
-
- return import_item
-
-def _Struct(module, parsed_struct):
- """
- Args:
- module: {mojom.Module} Module currently being constructed.
- parsed_struct: {ast.Struct} Parsed struct.
-
- Returns:
- {mojom.Struct} AST struct.
- """
- struct = mojom.Struct(module=module)
- struct.name = parsed_struct.name
- struct.native_only = parsed_struct.body is None
- struct.spec = 'x:' + module.namespace + '.' + struct.name
- module.kinds[struct.spec] = struct
- if struct.native_only:
- struct.enums = []
- struct.constants = []
- struct.fields_data = []
- else:
- struct.enums = map(
- lambda enum: _Enum(module, enum, struct),
- _ElemsOfType(parsed_struct.body, ast.Enum, parsed_struct.name))
- struct.constants = map(
- lambda constant: _Constant(module, constant, struct),
- _ElemsOfType(parsed_struct.body, ast.Const, parsed_struct.name))
- # Stash fields parsed_struct here temporarily.
- struct.fields_data = _ElemsOfType(
- parsed_struct.body, ast.StructField, parsed_struct.name)
- struct.attributes = _AttributeListToDict(parsed_struct.attribute_list)
-
- # Enforce that a [Native] attribute is set to make native-only struct
- # declarations more explicit.
- if struct.native_only:
- if not struct.attributes or not struct.attributes.get('Native', False):
- raise Exception("Native-only struct declarations must include a " +
- "Native attribute.")
-
- return struct
-
-def _Union(module, parsed_union):
- """
- Args:
- module: {mojom.Module} Module currently being constructed.
- parsed_union: {ast.Union} Parsed union.
-
- Returns:
- {mojom.Union} AST union.
- """
- union = mojom.Union(module=module)
- union.name = parsed_union.name
- union.spec = 'x:' + module.namespace + '.' + union.name
- module.kinds[union.spec] = union
- # Stash fields parsed_union here temporarily.
- union.fields_data = _ElemsOfType(
- parsed_union.body, ast.UnionField, parsed_union.name)
- union.attributes = _AttributeListToDict(parsed_union.attribute_list)
- return union
-
-def _StructField(module, parsed_field, struct):
- """
- Args:
- module: {mojom.Module} Module currently being constructed.
- parsed_field: {ast.StructField} Parsed struct field.
- struct: {mojom.Struct} Struct this field belongs to.
-
- Returns:
- {mojom.StructField} AST struct field.
- """
- field = mojom.StructField()
- field.name = parsed_field.name
- field.kind = _Kind(
- module.kinds, _MapKind(parsed_field.typename),
- (module.namespace, struct.name))
- field.ordinal = parsed_field.ordinal.value if parsed_field.ordinal else None
- field.default = _FixupExpression(
- module, parsed_field.default_value, (module.namespace, struct.name),
- field.kind)
- field.attributes = _AttributeListToDict(parsed_field.attribute_list)
- return field
-
-def _UnionField(module, parsed_field, union):
- """
- Args:
- module: {mojom.Module} Module currently being constructed.
- parsed_field: {ast.UnionField} Parsed union field.
- union: {mojom.Union} Union this fields belong to.
-
- Returns:
- {mojom.UnionField} AST union.
- """
- field = mojom.UnionField()
- field.name = parsed_field.name
- field.kind = _Kind(
- module.kinds, _MapKind(parsed_field.typename),
- (module.namespace, union.name))
- field.ordinal = parsed_field.ordinal.value if parsed_field.ordinal else None
- field.default = _FixupExpression(
- module, None, (module.namespace, union.name), field.kind)
- field.attributes = _AttributeListToDict(parsed_field.attribute_list)
- return field
-
-def _Parameter(module, parsed_param, interface):
- """
- Args:
- module: {mojom.Module} Module currently being constructed.
- parsed_param: {ast.Parameter} Parsed parameter.
- union: {mojom.Interface} Interface this parameter belongs to.
-
- Returns:
- {mojom.Parameter} AST parameter.
- """
- parameter = mojom.Parameter()
- parameter.name = parsed_param.name
- parameter.kind = _Kind(
- module.kinds, _MapKind(parsed_param.typename),
- (module.namespace, interface.name))
- parameter.ordinal = (
- parsed_param.ordinal.value if parsed_param.ordinal else None)
- parameter.default = None # TODO(tibell): We never have these. Remove field?
- parameter.attributes = _AttributeListToDict(parsed_param.attribute_list)
- return parameter
-
-def _Method(module, parsed_method, interface):
- """
- Args:
- module: {mojom.Module} Module currently being constructed.
- parsed_method: {ast.Method} Parsed method.
- interface: {mojom.Interface} Interface this method belongs to.
-
- Returns:
- {mojom.Method} AST method.
- """
- method = mojom.Method(
- interface, parsed_method.name,
- ordinal=parsed_method.ordinal.value if parsed_method.ordinal else None)
- method.parameters = map(
- lambda parameter: _Parameter(module, parameter, interface),
- parsed_method.parameter_list)
- if parsed_method.response_parameter_list is not None:
- method.response_parameters = map(
- lambda parameter: _Parameter(module, parameter, interface),
- parsed_method.response_parameter_list)
- method.attributes = _AttributeListToDict(parsed_method.attribute_list)
-
- # Enforce that only methods with response can have a [Sync] attribute.
- if method.sync and method.response_parameters is None:
- raise Exception("Only methods with response can include a [Sync] "
- "attribute. If no response parameters are needed, you "
- "could use an empty response parameter list, i.e., "
- "\"=> ()\".")
-
- return method
-
-def _Interface(module, parsed_iface):
- """
- Args:
- module: {mojom.Module} Module currently being constructed.
- parsed_iface: {ast.Interface} Parsed interface.
-
- Returns:
- {mojom.Interface} AST interface.
- """
- interface = mojom.Interface(module=module)
- interface.name = parsed_iface.name
- interface.spec = 'x:' + module.namespace + '.' + interface.name
- module.kinds[interface.spec] = interface
- interface.enums = map(
- lambda enum: _Enum(module, enum, interface),
- _ElemsOfType(parsed_iface.body, ast.Enum, parsed_iface.name))
- interface.constants = map(
- lambda constant: _Constant(module, constant, interface),
- _ElemsOfType(parsed_iface.body, ast.Const, parsed_iface.name))
- # Stash methods parsed_iface here temporarily.
- interface.methods_data = _ElemsOfType(
- parsed_iface.body, ast.Method, parsed_iface.name)
- interface.attributes = _AttributeListToDict(parsed_iface.attribute_list)
- return interface
-
-def _EnumField(module, enum, parsed_field, parent_kind):
- """
- Args:
- module: {mojom.Module} Module currently being constructed.
- enum: {mojom.Enum} Enum this field belongs to.
- parsed_field: {ast.EnumValue} Parsed enum value.
- parent_kind: {mojom.Kind} The enclosing type.
-
- Returns:
- {mojom.EnumField} AST enum field.
- """
- field = mojom.EnumField()
- field.name = parsed_field.name
- # TODO(mpcomplete): FixupExpression should be done in the second pass,
- # so constants and enums can refer to each other.
- # TODO(mpcomplete): But then, what if constants are initialized to an enum? Or
- # vice versa?
- if parent_kind:
- field.value = _FixupExpression(
- module, parsed_field.value, (module.namespace, parent_kind.name), enum)
- else:
- field.value = _FixupExpression(
- module, parsed_field.value, (module.namespace, ), enum)
- field.attributes = _AttributeListToDict(parsed_field.attribute_list)
- value = mojom.EnumValue(module, enum, field)
- module.values[value.GetSpec()] = value
- return field
-
-def _ResolveNumericEnumValues(enum_fields):
- """
- Given a reference to a list of mojom.EnumField, resolves and assigns their
- values to EnumField.numeric_value.
- """
-
- # map of <name> -> integral value
- resolved_enum_values = {}
- prev_value = -1
- for field in enum_fields:
- # This enum value is +1 the previous enum value (e.g: BEGIN).
- if field.value is None:
- prev_value += 1
-
- # Integral value (e.g: BEGIN = -0x1).
- elif type(field.value) is str:
- prev_value = int(field.value, 0)
-
- # Reference to a previous enum value (e.g: INIT = BEGIN).
- elif type(field.value) is mojom.EnumValue:
- prev_value = resolved_enum_values[field.value.name]
- else:
- raise Exception("Unresolved enum value.")
-
- resolved_enum_values[field.name] = prev_value
- field.numeric_value = prev_value
-
-def _Enum(module, parsed_enum, parent_kind):
- """
- Args:
- module: {mojom.Module} Module currently being constructed.
- parsed_enum: {ast.Enum} Parsed enum.
-
- Returns:
- {mojom.Enum} AST enum.
- """
- enum = mojom.Enum(module=module)
- enum.name = parsed_enum.name
- enum.native_only = parsed_enum.enum_value_list is None
- name = enum.name
- if parent_kind:
- name = parent_kind.name + '.' + name
- enum.spec = 'x:%s.%s' % (module.namespace, name)
- enum.parent_kind = parent_kind
- enum.attributes = _AttributeListToDict(parsed_enum.attribute_list)
- if enum.native_only:
- enum.fields = []
- else:
- enum.fields = map(
- lambda field: _EnumField(module, enum, field, parent_kind),
- parsed_enum.enum_value_list)
- _ResolveNumericEnumValues(enum.fields)
-
- module.kinds[enum.spec] = enum
-
- # Enforce that a [Native] attribute is set to make native-only enum
- # declarations more explicit.
- if enum.native_only:
- if not enum.attributes or not enum.attributes.get('Native', False):
- raise Exception("Native-only enum declarations must include a " +
- "Native attribute.")
-
- return enum
-
-def _Constant(module, parsed_const, parent_kind):
- """
- Args:
- module: {mojom.Module} Module currently being constructed.
- parsed_const: {ast.Const} Parsed constant.
-
- Returns:
- {mojom.Constant} AST constant.
- """
- constant = mojom.Constant()
- constant.name = parsed_const.name
- if parent_kind:
- scope = (module.namespace, parent_kind.name)
- else:
- scope = (module.namespace, )
- # TODO(mpcomplete): maybe we should only support POD kinds.
- constant.kind = _Kind(module.kinds, _MapKind(parsed_const.typename), scope)
- constant.parent_kind = parent_kind
- constant.value = _FixupExpression(module, parsed_const.value, scope, None)
-
- value = mojom.ConstantValue(module, parent_kind, constant)
- module.values[value.GetSpec()] = value
- return constant
-
-def _Module(tree, name, imports):
- """
- Args:
- tree: {ast.Mojom} The parse tree.
- name: {str} The mojom filename, excluding the path.
- imports: {Dict[str, mojom.Module]} Mapping from filenames, as they appear in
- the import list, to already processed modules. Used to process imports.
-
- Returns:
- {mojom.Module} An AST for the mojom.
- """
- module = mojom.Module()
- module.kinds = {}
- for kind in mojom.PRIMITIVES:
- module.kinds[kind.spec] = kind
-
- module.values = {}
-
- module.name = name
- module.namespace = tree.module.name[1] if tree.module else ''
- # Imports must come first, because they add to module.kinds which is used
- # by by the others.
- module.imports = [
- _Import(module, imports[imp.import_filename])
- for imp in tree.import_list]
- if tree.module and tree.module.attribute_list:
- assert isinstance(tree.module.attribute_list, ast.AttributeList)
- # TODO(vtl): Check for duplicate keys here.
- module.attributes = dict((attribute.key, attribute.value)
- for attribute in tree.module.attribute_list)
-
- # First pass collects kinds.
- module.enums = map(
- lambda enum: _Enum(module, enum, None),
- _ElemsOfType(tree.definition_list, ast.Enum, name))
- module.structs = map(
- lambda struct: _Struct(module, struct),
- _ElemsOfType(tree.definition_list, ast.Struct, name))
- module.unions = map(
- lambda union: _Union(module, union),
- _ElemsOfType(tree.definition_list, ast.Union, name))
- module.interfaces = map(
- lambda interface: _Interface(module, interface),
- _ElemsOfType(tree.definition_list, ast.Interface, name))
- module.constants = map(
- lambda constant: _Constant(module, constant, None),
- _ElemsOfType(tree.definition_list, ast.Const, name))
-
- # Second pass expands fields and methods. This allows fields and parameters
- # to refer to kinds defined anywhere in the mojom.
- for struct in module.structs:
- struct.fields = map(lambda field:
- _StructField(module, field, struct), struct.fields_data)
- del struct.fields_data
- for union in module.unions:
- union.fields = map(lambda field:
- _UnionField(module, field, union), union.fields_data)
- del union.fields_data
- for interface in module.interfaces:
- interface.methods = map(lambda method:
- _Method(module, method, interface), interface.methods_data)
- del interface.methods_data
-
- return module
-
-def OrderedModule(tree, name, imports):
- """Convert parse tree to AST module.
-
- Args:
- tree: {ast.Mojom} The parse tree.
- name: {str} The mojom filename, excluding the path.
- imports: {Dict[str, mojom.Module]} Mapping from filenames, as they appear in
- the import list, to already processed modules. Used to process imports.
-
- Returns:
- {mojom.Module} An AST for the mojom.
- """
- module = _Module(tree, name, imports)
- for interface in module.interfaces:
- next_ordinal = 0
- for method in interface.methods:
- if method.ordinal is None:
- method.ordinal = next_ordinal
- next_ordinal = method.ordinal + 1
- return module
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/parser.py b/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
index 868fb45..5cf20fe 100644
--- a/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
@@ -346,7 +346,7 @@
p[0] = ast.Ordinal(value, filename=self.filename, lineno=p.lineno(1))
def p_enum_1(self, p):
- """enum : attribute_section ENUM NAME LBRACE enum_value_list \
+ """enum : attribute_section ENUM NAME LBRACE nonempty_enum_value_list \
RBRACE SEMI
| attribute_section ENUM NAME LBRACE nonempty_enum_value_list \
COMMA RBRACE SEMI"""
@@ -358,14 +358,6 @@
p[0] = ast.Enum(p[3], p[1], None, filename=self.filename,
lineno=p.lineno(2))
- def p_enum_value_list_1(self, p):
- """enum_value_list : """
- p[0] = ast.EnumValueList()
-
- def p_enum_value_list_2(self, p):
- """enum_value_list : nonempty_enum_value_list"""
- p[0] = p[1]
-
def p_nonempty_enum_value_list_1(self, p):
"""nonempty_enum_value_list : enum_value"""
p[0] = ast.EnumValueList(p[1])
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/translate.py b/mojo/public/tools/bindings/pylib/mojom/parse/translate.py
new file mode 100644
index 0000000..66e8443
--- /dev/null
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/translate.py
@@ -0,0 +1,236 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Translates parse tree to Mojom IR."""
+
+import re
+
+from . import ast
+
+
+def _DuplicateName(values):
+ """Returns the 'name' of the first entry in |values| whose 'name' has already
+ been encountered. If there are no duplicates, returns None."""
+ names = set()
+ for value in values:
+ if value['name'] in names:
+ return value['name']
+ names.add(value['name'])
+ return None
+
+def _MapTreeForType(func, tree, type_to_map, scope):
+ assert isinstance(type_to_map, type)
+ if not tree:
+ return []
+ result = [func(subtree)
+ for subtree in tree if isinstance(subtree, type_to_map)]
+ duplicate_name = _DuplicateName(result)
+ if duplicate_name:
+ raise Exception('Names in mojom must be unique within a scope. The name '
+ '"%s" is used more than once within the scope "%s".' %
+ (duplicate_name, scope))
+ return result
+
+def _MapKind(kind):
+ map_to_kind = {'bool': 'b',
+ 'int8': 'i8',
+ 'int16': 'i16',
+ 'int32': 'i32',
+ 'int64': 'i64',
+ 'uint8': 'u8',
+ 'uint16': 'u16',
+ 'uint32': 'u32',
+ 'uint64': 'u64',
+ 'float': 'f',
+ 'double': 'd',
+ 'string': 's',
+ 'handle': 'h',
+ 'handle<data_pipe_consumer>': 'h:d:c',
+ 'handle<data_pipe_producer>': 'h:d:p',
+ 'handle<message_pipe>': 'h:m',
+ 'handle<shared_buffer>': 'h:s'}
+ if kind.endswith('?'):
+ base_kind = _MapKind(kind[0:-1])
+ # NOTE: This doesn't rule out enum types. Those will be detected later, when
+ # cross-reference is established.
+ reference_kinds = ('m', 's', 'h', 'a', 'r', 'x', 'asso')
+ if re.split('[^a-z]', base_kind, 1)[0] not in reference_kinds:
+ raise Exception(
+ 'A type (spec "%s") cannot be made nullable' % base_kind)
+ return '?' + base_kind
+ if kind.endswith('}'):
+ lbracket = kind.rfind('{')
+ value = kind[0:lbracket]
+ return 'm[' + _MapKind(kind[lbracket+1:-1]) + '][' + _MapKind(value) + ']'
+ if kind.endswith(']'):
+ lbracket = kind.rfind('[')
+ typename = kind[0:lbracket]
+ return 'a' + kind[lbracket+1:-1] + ':' + _MapKind(typename)
+ if kind.endswith('&'):
+ return 'r:' + _MapKind(kind[0:-1])
+ if kind.startswith('asso<'):
+ assert kind.endswith('>')
+ return 'asso:' + _MapKind(kind[5:-1])
+ if kind in map_to_kind:
+ return map_to_kind[kind]
+ return 'x:' + kind
+
+def _AddOptional(dictionary, key, value):
+ if value is not None:
+ dictionary[key] = value;
+
+def _AttributeListToDict(attribute_list):
+ if attribute_list is None:
+ return None
+ assert isinstance(attribute_list, ast.AttributeList)
+ # TODO(vtl): Check for duplicate keys here.
+ return dict([(attribute.key, attribute.value)
+ for attribute in attribute_list])
+
+def _EnumToDict(enum):
+ def EnumValueToDict(enum_value):
+ assert isinstance(enum_value, ast.EnumValue)
+ data = {'name': enum_value.name}
+ _AddOptional(data, 'value', enum_value.value)
+ _AddOptional(data, 'attributes',
+ _AttributeListToDict(enum_value.attribute_list))
+ return data
+
+ assert isinstance(enum, ast.Enum)
+ data = {'name': enum.name,
+ 'native_only': enum.enum_value_list is None }
+ if not data['native_only']:
+ data.update({'fields': map(EnumValueToDict, enum.enum_value_list)})
+ _AddOptional(data, 'attributes', _AttributeListToDict(enum.attribute_list))
+ return data
+
+def _ConstToDict(const):
+ assert isinstance(const, ast.Const)
+ return {'name': const.name,
+ 'kind': _MapKind(const.typename),
+ 'value': const.value}
+
+
+class _MojomBuilder(object):
+ def __init__(self):
+ self.mojom = {}
+
+ def Build(self, tree, name):
+ def StructToDict(struct):
+ def StructFieldToDict(struct_field):
+ assert isinstance(struct_field, ast.StructField)
+ data = {'name': struct_field.name,
+ 'kind': _MapKind(struct_field.typename)}
+ _AddOptional(data, 'ordinal',
+ struct_field.ordinal.value
+ if struct_field.ordinal else None)
+ _AddOptional(data, 'default', struct_field.default_value)
+ _AddOptional(data, 'attributes',
+ _AttributeListToDict(struct_field.attribute_list))
+ return data
+
+ assert isinstance(struct, ast.Struct)
+ data = {'name': struct.name,
+ 'native_only': struct.body is None}
+ if not data['native_only']:
+ data.update({
+ 'fields': _MapTreeForType(StructFieldToDict, struct.body,
+ ast.StructField, struct.name),
+ 'enums': _MapTreeForType(_EnumToDict, struct.body, ast.Enum,
+ struct.name),
+ 'constants': _MapTreeForType(_ConstToDict, struct.body,
+ ast.Const, struct.name)})
+ _AddOptional(data, 'attributes',
+ _AttributeListToDict(struct.attribute_list))
+ return data
+
+ def UnionToDict(union):
+ def UnionFieldToDict(union_field):
+ assert isinstance(union_field, ast.UnionField)
+ data = {'name': union_field.name,
+ 'kind': _MapKind(union_field.typename)}
+ _AddOptional(data, 'ordinal',
+ union_field.ordinal.value
+ if union_field.ordinal else None)
+ _AddOptional(data, 'attributes',
+ _AttributeListToDict(union_field.attribute_list))
+ return data
+
+ assert isinstance(union, ast.Union)
+ data = {'name': union.name,
+ 'fields': _MapTreeForType(UnionFieldToDict, union.body,
+ ast.UnionField, union.name)}
+ _AddOptional(data, 'attributes',
+ _AttributeListToDict(union.attribute_list))
+ return data
+
+ def InterfaceToDict(interface):
+ def MethodToDict(method):
+ def ParameterToDict(param):
+ assert isinstance(param, ast.Parameter)
+ data = {'name': param.name,
+ 'kind': _MapKind(param.typename)}
+ _AddOptional(data, 'ordinal',
+ param.ordinal.value if param.ordinal else None)
+ _AddOptional(data, 'attributes',
+ _AttributeListToDict(param.attribute_list))
+ return data
+
+ assert isinstance(method, ast.Method)
+ data = {'name': method.name,
+ 'parameters': map(ParameterToDict, method.parameter_list)}
+ if method.response_parameter_list is not None:
+ data['response_parameters'] = map(ParameterToDict,
+ method.response_parameter_list)
+ _AddOptional(data, 'ordinal',
+ method.ordinal.value if method.ordinal else None)
+ _AddOptional(data, 'attributes',
+ _AttributeListToDict(method.attribute_list))
+ return data
+
+ assert isinstance(interface, ast.Interface)
+ data = {'name': interface.name,
+ 'methods': _MapTreeForType(MethodToDict, interface.body,
+ ast.Method, interface.name),
+ 'enums': _MapTreeForType(_EnumToDict, interface.body, ast.Enum,
+ interface.name),
+ 'constants': _MapTreeForType(_ConstToDict, interface.body,
+ ast.Const, interface.name)}
+ _AddOptional(data, 'attributes',
+ _AttributeListToDict(interface.attribute_list))
+ return data
+
+ assert isinstance(tree, ast.Mojom)
+ self.mojom['name'] = name
+ self.mojom['namespace'] = tree.module.name[1] if tree.module else ''
+ self.mojom['imports'] = \
+ [{'filename': imp.import_filename} for imp in tree.import_list]
+ self.mojom['structs'] = \
+ _MapTreeForType(StructToDict, tree.definition_list, ast.Struct, name)
+ self.mojom['unions'] = \
+ _MapTreeForType(UnionToDict, tree.definition_list, ast.Union, name)
+ self.mojom['interfaces'] = \
+ _MapTreeForType(InterfaceToDict, tree.definition_list, ast.Interface,
+ name)
+ self.mojom['enums'] = \
+ _MapTreeForType(_EnumToDict, tree.definition_list, ast.Enum, name)
+ self.mojom['constants'] = \
+ _MapTreeForType(_ConstToDict, tree.definition_list, ast.Const, name)
+ _AddOptional(self.mojom, 'attributes',
+ _AttributeListToDict(tree.module.attribute_list)
+ if tree.module else None)
+ return self.mojom
+
+
+def Translate(tree, name):
+ """Translate AST to Mojom IR.
+
+ Args:
+ tree: The AST as a mojom.parse.ast.Mojom object.
+ name: The filename as a str.
+
+ Returns:
+ The Mojom IR as a dict.
+ """
+ return _MojomBuilder().Build(tree, name)
diff --git a/mojo/public/tools/gn/zip.py b/mojo/public/tools/gn/zip.py
index adc9cb1..0d4960f 100755
--- a/mojo/public/tools/gn/zip.py
+++ b/mojo/public/tools/gn/zip.py
@@ -20,30 +20,25 @@
"build"))
import gn_helpers
-sys.path.append(os.path.join(os.path.dirname(__file__),
- os.pardir, os.pardir, os.pardir, os.pardir,
- 'build', 'android', 'gyp'))
-from util import build_utils
-
-
def DoZip(inputs, link_inputs, zip_inputs, output, base_dir):
files = []
with zipfile.ZipFile(output, 'w', zipfile.ZIP_DEFLATED) as outfile:
for f in inputs:
file_name = os.path.relpath(f, base_dir)
files.append(file_name)
- build_utils.AddToZipHermetic(outfile, file_name, f)
+ outfile.write(f, file_name)
for f in link_inputs:
realf = os.path.realpath(f) # Resolve symlinks.
file_name = os.path.relpath(realf, base_dir)
files.append(file_name)
- build_utils.AddToZipHermetic(outfile, file_name, realf)
+ outfile.write(realf, file_name)
for zf_name in zip_inputs:
with zipfile.ZipFile(zf_name, 'r') as zf:
for f in zf.namelist():
if f not in files:
files.append(f)
- build_utils.AddToZipHermetic(outfile, f, data=zf.read(f))
+ with zf.open(f) as zff:
+ outfile.writestr(f, zff.read())
def main():
diff --git a/mojo/public/tools/manifest/manifest_collator.py b/mojo/public/tools/manifest/manifest_collator.py
new file mode 100755
index 0000000..9a6d0e9
--- /dev/null
+++ b/mojo/public/tools/manifest/manifest_collator.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+# 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.
+
+""" A collator for Mojo Application Manifests """
+
+import argparse
+import json
+import os
+import shutil
+import sys
+import urlparse
+
+eater_relative = '../../../../../tools/json_comment_eater'
+eater_relative = os.path.join(os.path.abspath(__file__), eater_relative)
+sys.path.insert(0, os.path.normpath(eater_relative))
+try:
+ import json_comment_eater
+finally:
+ sys.path.pop(0)
+
+def ParseJSONFile(filename):
+ with open(filename) as json_file:
+ try:
+ return json.loads(json_comment_eater.Nom(json_file.read()))
+ except ValueError:
+ print "%s is not a valid JSON document" % filename
+ return None
+
+def MergeDicts(left, right):
+ for k, v in right.iteritems():
+ if k not in left:
+ left[k] = v
+ else:
+ if isinstance(v, dict):
+ assert isinstance(left[k], dict)
+ MergeDicts(left[k], v)
+ elif isinstance(v, list):
+ assert isinstance(left[k], list)
+ left[k].extend(v)
+ else:
+ raise "Refusing to merge conflicting non-collection values."
+ return left
+
+
+def MergeBaseManifest(parent, base):
+ MergeDicts(parent["capabilities"], base["capabilities"])
+
+ if "applications" in base:
+ if "applications" not in parent:
+ parent["applications"] = []
+ parent["applications"].extend(base["applications"])
+
+ if "process-group" in base:
+ parent["process-group"] = base["process-group"]
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Collate Mojo application manifests.")
+ parser.add_argument("--parent")
+ parser.add_argument("--output")
+ parser.add_argument("--application-name")
+ parser.add_argument("--base-manifest", default=None)
+ args, children = parser.parse_known_args()
+
+ parent = ParseJSONFile(args.parent)
+ if parent == None:
+ return 1
+
+ if args.base_manifest:
+ base = ParseJSONFile(args.base_manifest)
+ if base == None:
+ return 1
+ MergeBaseManifest(parent, base)
+
+ app_path = parent['name'].split(':')[1]
+ if app_path.startswith('//'):
+ raise ValueError("Application name path component '%s' must not start " \
+ "with //" % app_path)
+
+ if args.application_name != app_path:
+ raise ValueError("Application name '%s' specified in build file does not " \
+ "match application name '%s' specified in manifest." %
+ (args.application_name, app_path))
+
+ applications = []
+ for child in children:
+ application = ParseJSONFile(child)
+ if application == None:
+ return 1
+ applications.append(application)
+
+ if len(applications) > 0:
+ parent['applications'] = applications
+
+ with open(args.output, 'w') as output_file:
+ json.dump(parent, output_file)
+
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/mojo/public/tools/prepend.py b/mojo/public/tools/prepend.py
new file mode 100755
index 0000000..de70a82
--- /dev/null
+++ b/mojo/public/tools/prepend.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+#
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Prepends a given file with a given line. This can be used to add a shebang line
+to a generated file.
+"""
+
+import optparse
+import os
+import shutil
+import sys
+
+
+def main():
+ parser = optparse.OptionParser()
+ parser.add_option('--input', help='The file to prepend the line to.')
+ parser.add_option('--line', help='The line to be prepended.')
+ parser.add_option('--output', help='The output file.')
+
+ options, _ = parser.parse_args()
+ input_path = options.input
+ output_path = options.output
+ line = options.line
+
+ # Warning - this reads all of the input file into memory.
+ with open(output_path, 'w') as output_file:
+ output_file.write(line + '\n')
+ with open(input_path, 'r') as input_file:
+ shutil.copyfileobj(input_file, output_file)
+
+
+if __name__ == '__main__':
+ sys.exit(main())