summaryrefslogtreecommitdiffstats
path: root/mojo/edk/js
diff options
context:
space:
mode:
Diffstat (limited to 'mojo/edk/js')
-rw-r--r--mojo/edk/js/BUILD.gn19
-rw-r--r--mojo/edk/js/DEPS1
-rw-r--r--mojo/edk/js/core.cc130
-rw-r--r--mojo/edk/js/core.h6
-rw-r--r--mojo/edk/js/drain_data.h6
-rw-r--r--mojo/edk/js/handle.cc18
-rw-r--r--mojo/edk/js/handle.h12
-rw-r--r--mojo/edk/js/handle_close_observer.h6
-rw-r--r--mojo/edk/js/mojo_runner_delegate.cc78
-rw-r--r--mojo/edk/js/mojo_runner_delegate.h33
-rw-r--r--mojo/edk/js/support.h6
-rw-r--r--mojo/edk/js/test/BUILD.gn39
-rw-r--r--mojo/edk/js/test/hexdump.js34
-rw-r--r--mojo/edk/js/test/run_js_integration_tests.cc57
-rw-r--r--mojo/edk/js/test/run_js_tests.cc (renamed from mojo/edk/js/tests/run_js_tests.cc)2
-rw-r--r--mojo/edk/js/tests/BUILD.gn21
-rw-r--r--mojo/edk/js/tests/DEPS7
-rw-r--r--mojo/edk/js/tests/connection_tests.js261
-rw-r--r--mojo/edk/js/tests/js_to_cpp.mojom53
-rw-r--r--mojo/edk/js/tests/js_to_cpp_tests.cc418
-rw-r--r--mojo/edk/js/tests/js_to_cpp_tests.js221
-rw-r--r--mojo/edk/js/tests/sample_service_tests.js168
-rw-r--r--mojo/edk/js/threading.cc47
-rw-r--r--mojo/edk/js/threading.h25
-rw-r--r--mojo/edk/js/waiting_callback.h6
25 files changed, 1551 insertions, 123 deletions
diff --git a/mojo/edk/js/BUILD.gn b/mojo/edk/js/BUILD.gn
index b8d60ec..e652b80 100644
--- a/mojo/edk/js/BUILD.gn
+++ b/mojo/edk/js/BUILD.gn
@@ -11,8 +11,12 @@ source_set("js") {
"handle.cc",
"handle.h",
"handle_close_observer.h",
+ "mojo_runner_delegate.cc",
+ "mojo_runner_delegate.h",
"support.cc",
"support.h",
+ "threading.cc",
+ "threading.h",
"waiting_callback.cc",
"waiting_callback.h",
]
@@ -22,15 +26,24 @@ source_set("js") {
"//gin",
"//v8",
]
+
+ deps = [
+ "//mojo/public/cpp/environment",
+ "//mojo/public/cpp/system",
+ ]
}
source_set("js_unittests") {
testonly = true
+ sources = [
+ "handle_unittest.cc",
+ ]
+
deps = [
+ "//mojo/edk/js",
"//mojo/edk/test:test_support",
+ "//mojo/public/cpp/system",
+ "//testing/gtest",
]
- sources = [
- "handle_unittest.cc",
- ]
}
diff --git a/mojo/edk/js/DEPS b/mojo/edk/js/DEPS
index d974b68..c350edf 100644
--- a/mojo/edk/js/DEPS
+++ b/mojo/edk/js/DEPS
@@ -1,4 +1,5 @@
include_rules = [
+ "+base",
"+gin",
"+v8",
]
diff --git a/mojo/edk/js/core.cc b/mojo/edk/js/core.cc
index 91a746c..232f0e3 100644
--- a/mojo/edk/js/core.cc
+++ b/mojo/edk/js/core.cc
@@ -247,69 +247,73 @@ v8::Local<v8::Value> Core::GetModule(v8::Isolate* isolate) {
&g_wrapper_info);
if (templ.IsEmpty()) {
- templ = gin::ObjectTemplateBuilder(isolate)
- // TODO(mpcomplete): Should these just be methods on the JS Handle
- // object?
- .SetMethod("close", CloseHandle)
- .SetMethod("wait", WaitHandle)
- .SetMethod("waitMany", WaitMany)
- .SetMethod("createMessagePipe", CreateMessagePipe)
- .SetMethod("writeMessage", WriteMessage)
- .SetMethod("readMessage", ReadMessage)
- .SetMethod("createDataPipe", CreateDataPipe)
- .SetMethod("writeData", WriteData)
- .SetMethod("readData", ReadData)
- .SetMethod("drainData", DoDrainData)
-
- .SetValue("RESULT_OK", MOJO_RESULT_OK)
- .SetValue("RESULT_CANCELLED", MOJO_RESULT_CANCELLED)
- .SetValue("RESULT_UNKNOWN", MOJO_RESULT_UNKNOWN)
- .SetValue("RESULT_INVALID_ARGUMENT", MOJO_RESULT_INVALID_ARGUMENT)
- .SetValue("RESULT_DEADLINE_EXCEEDED", MOJO_RESULT_DEADLINE_EXCEEDED)
- .SetValue("RESULT_NOT_FOUND", MOJO_RESULT_NOT_FOUND)
- .SetValue("RESULT_ALREADY_EXISTS", MOJO_RESULT_ALREADY_EXISTS)
- .SetValue("RESULT_PERMISSION_DENIED", MOJO_RESULT_PERMISSION_DENIED)
- .SetValue("RESULT_RESOURCE_EXHAUSTED", MOJO_RESULT_RESOURCE_EXHAUSTED)
- .SetValue("RESULT_FAILED_PRECONDITION", MOJO_RESULT_FAILED_PRECONDITION)
- .SetValue("RESULT_ABORTED", MOJO_RESULT_ABORTED)
- .SetValue("RESULT_OUT_OF_RANGE", MOJO_RESULT_OUT_OF_RANGE)
- .SetValue("RESULT_UNIMPLEMENTED", MOJO_RESULT_UNIMPLEMENTED)
- .SetValue("RESULT_INTERNAL", MOJO_RESULT_INTERNAL)
- .SetValue("RESULT_UNAVAILABLE", MOJO_RESULT_UNAVAILABLE)
- .SetValue("RESULT_DATA_LOSS", MOJO_RESULT_DATA_LOSS)
- .SetValue("RESULT_BUSY", MOJO_RESULT_BUSY)
- .SetValue("RESULT_SHOULD_WAIT", MOJO_RESULT_SHOULD_WAIT)
-
- .SetValue("DEADLINE_INDEFINITE", MOJO_DEADLINE_INDEFINITE)
-
- .SetValue("HANDLE_SIGNAL_NONE", MOJO_HANDLE_SIGNAL_NONE)
- .SetValue("HANDLE_SIGNAL_READABLE", MOJO_HANDLE_SIGNAL_READABLE)
- .SetValue("HANDLE_SIGNAL_WRITABLE", MOJO_HANDLE_SIGNAL_WRITABLE)
-
- .SetValue("CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE",
- MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE)
-
- .SetValue("WRITE_MESSAGE_FLAG_NONE", MOJO_WRITE_MESSAGE_FLAG_NONE)
-
- .SetValue("READ_MESSAGE_FLAG_NONE", MOJO_READ_MESSAGE_FLAG_NONE)
- .SetValue("READ_MESSAGE_FLAG_MAY_DISCARD",
- MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)
-
- .SetValue("CREATE_DATA_PIPE_OPTIONS_FLAG_NONE",
- MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE)
- .SetValue("CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD",
- MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD)
-
- .SetValue("WRITE_DATA_FLAG_NONE", MOJO_WRITE_DATA_FLAG_NONE)
- .SetValue("WRITE_DATA_FLAG_ALL_OR_NONE",
- MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)
-
- .SetValue("READ_DATA_FLAG_NONE", MOJO_READ_DATA_FLAG_NONE)
- .SetValue("READ_DATA_FLAG_ALL_OR_NONE",
- MOJO_READ_DATA_FLAG_ALL_OR_NONE)
- .SetValue("READ_DATA_FLAG_DISCARD", MOJO_READ_DATA_FLAG_DISCARD)
- .SetValue("READ_DATA_FLAG_QUERY", MOJO_READ_DATA_FLAG_QUERY)
- .Build();
+ templ =
+ gin::ObjectTemplateBuilder(isolate)
+ // TODO(mpcomplete): Should these just be methods on the JS Handle
+ // object?
+ .SetMethod("close", CloseHandle)
+ .SetMethod("wait", WaitHandle)
+ .SetMethod("waitMany", WaitMany)
+ .SetMethod("createMessagePipe", CreateMessagePipe)
+ .SetMethod("writeMessage", WriteMessage)
+ .SetMethod("readMessage", ReadMessage)
+ .SetMethod("createDataPipe", CreateDataPipe)
+ .SetMethod("writeData", WriteData)
+ .SetMethod("readData", ReadData)
+ .SetMethod("drainData", DoDrainData)
+
+ .SetValue("RESULT_OK", MOJO_RESULT_OK)
+ .SetValue("RESULT_CANCELLED", MOJO_RESULT_CANCELLED)
+ .SetValue("RESULT_UNKNOWN", MOJO_RESULT_UNKNOWN)
+ .SetValue("RESULT_INVALID_ARGUMENT", MOJO_RESULT_INVALID_ARGUMENT)
+ .SetValue("RESULT_DEADLINE_EXCEEDED", MOJO_RESULT_DEADLINE_EXCEEDED)
+ .SetValue("RESULT_NOT_FOUND", MOJO_RESULT_NOT_FOUND)
+ .SetValue("RESULT_ALREADY_EXISTS", MOJO_RESULT_ALREADY_EXISTS)
+ .SetValue("RESULT_PERMISSION_DENIED", MOJO_RESULT_PERMISSION_DENIED)
+ .SetValue("RESULT_RESOURCE_EXHAUSTED",
+ MOJO_RESULT_RESOURCE_EXHAUSTED)
+ .SetValue("RESULT_FAILED_PRECONDITION",
+ MOJO_RESULT_FAILED_PRECONDITION)
+ .SetValue("RESULT_ABORTED", MOJO_RESULT_ABORTED)
+ .SetValue("RESULT_OUT_OF_RANGE", MOJO_RESULT_OUT_OF_RANGE)
+ .SetValue("RESULT_UNIMPLEMENTED", MOJO_RESULT_UNIMPLEMENTED)
+ .SetValue("RESULT_INTERNAL", MOJO_RESULT_INTERNAL)
+ .SetValue("RESULT_UNAVAILABLE", MOJO_RESULT_UNAVAILABLE)
+ .SetValue("RESULT_DATA_LOSS", MOJO_RESULT_DATA_LOSS)
+ .SetValue("RESULT_BUSY", MOJO_RESULT_BUSY)
+ .SetValue("RESULT_SHOULD_WAIT", MOJO_RESULT_SHOULD_WAIT)
+
+ .SetValue("DEADLINE_INDEFINITE", MOJO_DEADLINE_INDEFINITE)
+
+ .SetValue("HANDLE_SIGNAL_NONE", MOJO_HANDLE_SIGNAL_NONE)
+ .SetValue("HANDLE_SIGNAL_READABLE", MOJO_HANDLE_SIGNAL_READABLE)
+ .SetValue("HANDLE_SIGNAL_WRITABLE", MOJO_HANDLE_SIGNAL_WRITABLE)
+
+ .SetValue("CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE",
+ MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE)
+
+ .SetValue("WRITE_MESSAGE_FLAG_NONE", MOJO_WRITE_MESSAGE_FLAG_NONE)
+
+ .SetValue("READ_MESSAGE_FLAG_NONE", MOJO_READ_MESSAGE_FLAG_NONE)
+ .SetValue("READ_MESSAGE_FLAG_MAY_DISCARD",
+ MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)
+
+ .SetValue("CREATE_DATA_PIPE_OPTIONS_FLAG_NONE",
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE)
+ .SetValue("CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD",
+ MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD)
+
+ .SetValue("WRITE_DATA_FLAG_NONE", MOJO_WRITE_DATA_FLAG_NONE)
+ .SetValue("WRITE_DATA_FLAG_ALL_OR_NONE",
+ MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)
+
+ .SetValue("READ_DATA_FLAG_NONE", MOJO_READ_DATA_FLAG_NONE)
+ .SetValue("READ_DATA_FLAG_ALL_OR_NONE",
+ MOJO_READ_DATA_FLAG_ALL_OR_NONE)
+ .SetValue("READ_DATA_FLAG_DISCARD", MOJO_READ_DATA_FLAG_DISCARD)
+ .SetValue("READ_DATA_FLAG_QUERY", MOJO_READ_DATA_FLAG_QUERY)
+ .SetValue("READ_DATA_FLAG_PEEK", MOJO_READ_DATA_FLAG_PEEK)
+ .Build();
data->SetObjectTemplate(&g_wrapper_info, templ);
}
diff --git a/mojo/edk/js/core.h b/mojo/edk/js/core.h
index bde327c..445fb00 100644
--- a/mojo/edk/js/core.h
+++ b/mojo/edk/js/core.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef MOJO_BINDINGS_JS_CORE_H_
-#define MOJO_BINDINGS_JS_CORE_H_
+#ifndef MOJO_EDK_JS_CORE_H_
+#define MOJO_EDK_JS_CORE_H_
#include "v8/include/v8.h"
@@ -19,4 +19,4 @@ class Core {
} // namespace js
} // namespace mojo
-#endif // MOJO_BINDINGS_JS_CORE_H_
+#endif // MOJO_EDK_JS_CORE_H_
diff --git a/mojo/edk/js/drain_data.h b/mojo/edk/js/drain_data.h
index 27d8f52..48f1de2 100644
--- a/mojo/edk/js/drain_data.h
+++ b/mojo/edk/js/drain_data.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef MOJO_BINDINGS_JS_DRAIN_DATA_H_
-#define MOJO_BINDINGS_JS_DRAIN_DATA_H_
+#ifndef MOJO_EDK_JS_DRAIN_DATA_H_
+#define MOJO_EDK_JS_DRAIN_DATA_H_
#include "base/memory/scoped_vector.h"
#include "gin/runner.h"
@@ -61,4 +61,4 @@ class DrainData {
} // namespace js
} // namespace mojo
-#endif // MOJO_BINDINGS_JS_DRAIN_DATA_H_
+#endif // MOJO_EDK_JS_DRAIN_DATA_H_
diff --git a/mojo/edk/js/handle.cc b/mojo/edk/js/handle.cc
index ae3415f..de8f338 100644
--- a/mojo/edk/js/handle.cc
+++ b/mojo/edk/js/handle.cc
@@ -4,7 +4,6 @@
#include "mojo/edk/js/handle.h"
-#include <sstream>
#include "mojo/edk/js/handle_close_observer.h"
namespace mojo {
@@ -20,23 +19,6 @@ HandleWrapper::~HandleWrapper() {
NotifyCloseObservers();
}
-std::string HandleWrapper::ToString() {
- std::ostringstream oss;
- oss << "[mojo::Handle ";
- if (handle_.is_valid())
- oss << handle_.get().value();
- else
- oss << "null";
- oss << "]";
- return oss.str();
-}
-
-gin::ObjectTemplateBuilder HandleWrapper::GetObjectTemplateBuilder(
- v8::Isolate* isolate) {
- return Wrappable<HandleWrapper>::GetObjectTemplateBuilder(isolate)
- .SetMethod("toString", &HandleWrapper::ToString);
-}
-
void HandleWrapper::Close() {
NotifyCloseObservers();
handle_.reset();
diff --git a/mojo/edk/js/handle.h b/mojo/edk/js/handle.h
index e363b7c..200b322 100644
--- a/mojo/edk/js/handle.h
+++ b/mojo/edk/js/handle.h
@@ -2,13 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef MOJO_BINDINGS_JS_HANDLE_H_
-#define MOJO_BINDINGS_JS_HANDLE_H_
+#ifndef MOJO_EDK_JS_HANDLE_H_
+#define MOJO_EDK_JS_HANDLE_H_
#include "base/observer_list.h"
#include "gin/converter.h"
#include "gin/handle.h"
-#include "gin/object_template_builder.h"
#include "gin/wrappable.h"
#include "mojo/public/cpp/system/core.h"
@@ -27,11 +26,6 @@ class HandleWrapper : public gin::Wrappable<HandleWrapper> {
return gin::CreateHandle(isolate, new HandleWrapper(handle));
}
- std::string ToString();
-
- gin::ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate)
- override;
-
mojo::Handle get() const { return handle_.get(); }
mojo::Handle release() { return handle_.release(); }
void Close();
@@ -101,4 +95,4 @@ struct Converter<gin::Handle<mojo::js::HandleWrapper> > {
} // namespace gin
-#endif // MOJO_BINDINGS_JS_HANDLE_H_
+#endif // MOJO_EDK_JS_HANDLE_H_
diff --git a/mojo/edk/js/handle_close_observer.h b/mojo/edk/js/handle_close_observer.h
index 8f19466..3e537fd 100644
--- a/mojo/edk/js/handle_close_observer.h
+++ b/mojo/edk/js/handle_close_observer.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef MOJO_BINDINGS_JS_HANDLE_CLOSE_OBSERVER_H_
-#define MOJO_BINDINGS_JS_HANDLE_CLOSE_OBSERVER_H_
+#ifndef MOJO_EDK_JS_HANDLE_CLOSE_OBSERVER_H_
+#define MOJO_EDK_JS_HANDLE_CLOSE_OBSERVER_H_
namespace mojo {
namespace js {
@@ -19,4 +19,4 @@ class HandleCloseObserver {
} // namespace js
} // namespace mojo
-#endif // MOJO_BINDINGS_JS_HANDLE_CLOSE_OBSERVER_H_
+#endif // MOJO_EDK_JS_HANDLE_CLOSE_OBSERVER_H_
diff --git a/mojo/edk/js/mojo_runner_delegate.cc b/mojo/edk/js/mojo_runner_delegate.cc
new file mode 100644
index 0000000..152b12c
--- /dev/null
+++ b/mojo/edk/js/mojo_runner_delegate.cc
@@ -0,0 +1,78 @@
+// 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/edk/js/mojo_runner_delegate.h"
+
+#include "base/bind.h"
+#include "base/path_service.h"
+#include "gin/converter.h"
+#include "gin/modules/console.h"
+#include "gin/modules/module_registry.h"
+#include "gin/modules/timer.h"
+#include "gin/try_catch.h"
+#include "mojo/edk/js/core.h"
+#include "mojo/edk/js/handle.h"
+#include "mojo/edk/js/support.h"
+#include "mojo/edk/js/threading.h"
+
+namespace mojo {
+namespace js {
+
+namespace {
+
+// TODO(abarth): Rather than loading these modules from the file system, we
+// should load them from the network via Mojo IPC.
+std::vector<base::FilePath> GetModuleSearchPaths() {
+ std::vector<base::FilePath> search_paths(2);
+ PathService::Get(base::DIR_SOURCE_ROOT, &search_paths[0]);
+ PathService::Get(base::DIR_EXE, &search_paths[1]);
+ search_paths[1] = search_paths[1].AppendASCII("gen");
+ return search_paths;
+}
+
+void StartCallback(base::WeakPtr<gin::Runner> runner,
+ MojoHandle pipe,
+ v8::Handle<v8::Value> module) {
+ v8::Isolate* isolate = runner->GetContextHolder()->isolate();
+ v8::Handle<v8::Function> start;
+ CHECK(gin::ConvertFromV8(isolate, module, &start));
+
+ v8::Handle<v8::Value> args[] = {
+ gin::ConvertToV8(isolate, Handle(pipe)) };
+ runner->Call(start, runner->global(), 1, args);
+}
+
+} // namespace
+
+MojoRunnerDelegate::MojoRunnerDelegate()
+ : ModuleRunnerDelegate(GetModuleSearchPaths()) {
+ AddBuiltinModule(gin::Console::kModuleName, gin::Console::GetModule);
+ AddBuiltinModule(gin::TimerModule::kName, gin::TimerModule::GetModule);
+ AddBuiltinModule(js::Core::kModuleName, js::Core::GetModule);
+ AddBuiltinModule(js::Support::kModuleName, js::Support::GetModule);
+ AddBuiltinModule(js::Threading::kModuleName, js::Threading::GetModule);
+}
+
+MojoRunnerDelegate::~MojoRunnerDelegate() {
+}
+
+void MojoRunnerDelegate::Start(gin::Runner* runner,
+ MojoHandle pipe,
+ const std::string& module) {
+ gin::Runner::Scope scope(runner);
+ gin::ModuleRegistry* registry =
+ gin::ModuleRegistry::From(runner->GetContextHolder()->context());
+ registry->LoadModule(runner->GetContextHolder()->isolate(), module,
+ base::Bind(StartCallback, runner->GetWeakPtr(), pipe));
+ AttemptToLoadMoreModules(runner);
+}
+
+void MojoRunnerDelegate::UnhandledException(gin::ShellRunner* runner,
+ gin::TryCatch& try_catch) {
+ gin::ModuleRunnerDelegate::UnhandledException(runner, try_catch);
+ LOG(ERROR) << try_catch.GetStackTrace();
+}
+
+} // namespace js
+} // namespace mojo
diff --git a/mojo/edk/js/mojo_runner_delegate.h b/mojo/edk/js/mojo_runner_delegate.h
new file mode 100644
index 0000000..423eefb
--- /dev/null
+++ b/mojo/edk/js/mojo_runner_delegate.h
@@ -0,0 +1,33 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_EDK_JS_MOJO_RUNNER_DELEGATE_H_
+#define MOJO_EDK_JS_MOJO_RUNNER_DELEGATE_H_
+
+#include "base/macros.h"
+#include "gin/modules/module_runner_delegate.h"
+#include "mojo/public/c/system/core.h"
+
+namespace mojo {
+namespace js {
+
+class MojoRunnerDelegate : public gin::ModuleRunnerDelegate {
+ public:
+ MojoRunnerDelegate();
+ ~MojoRunnerDelegate() override;
+
+ void Start(gin::Runner* runner, MojoHandle pipe, const std::string& module);
+
+ private:
+ // From ModuleRunnerDelegate:
+ void UnhandledException(gin::ShellRunner* runner,
+ gin::TryCatch& try_catch) override;
+
+ DISALLOW_COPY_AND_ASSIGN(MojoRunnerDelegate);
+};
+
+} // namespace js
+} // namespace mojo
+
+#endif // MOJO_EDK_JS_MOJO_RUNNER_DELEGATE_H_
diff --git a/mojo/edk/js/support.h b/mojo/edk/js/support.h
index 0f6eb07c..b49dd23 100644
--- a/mojo/edk/js/support.h
+++ b/mojo/edk/js/support.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef MOJO_BINDINGS_JS_SUPPORT_H_
-#define MOJO_BINDINGS_JS_SUPPORT_H_
+#ifndef MOJO_EDK_JS_SUPPORT_H_
+#define MOJO_EDK_JS_SUPPORT_H_
#include "v8/include/v8.h"
@@ -19,4 +19,4 @@ class Support {
} // namespace js
} // namespace mojo
-#endif // MOJO_BINDINGS_JS_SUPPORT_H_
+#endif // MOJO_EDK_JS_SUPPORT_H_
diff --git a/mojo/edk/js/test/BUILD.gn b/mojo/edk/js/test/BUILD.gn
new file mode 100644
index 0000000..e63704b
--- /dev/null
+++ b/mojo/edk/js/test/BUILD.gn
@@ -0,0 +1,39 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+test("js_unittests") {
+ deps = [
+ "//base",
+ "//gin:gin_test",
+ "//mojo/edk/js",
+ "//mojo/edk/js:js_unittests",
+ "//mojo/edk/test:run_all_unittests",
+ "//mojo/edk/test:test_support",
+ "//mojo/public/cpp/environment",
+ "//mojo/public/cpp/system",
+ "//mojo/public/cpp/utility",
+ "//mojo/environment:chromium",
+ "//mojo/public/interfaces/bindings/tests:test_interfaces",
+ ]
+
+ sources = [ "run_js_tests.cc" ]
+}
+
+test("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/environment:chromium",
+ "//mojo/public/interfaces/bindings/tests:test_interfaces",
+ ]
+
+ 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..1a6f1d6
--- /dev/null
+++ b/mojo/edk/js/test/run_js_integration_tests.cc
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "gin/modules/console.h"
+#include "gin/modules/module_registry.h"
+#include "gin/modules/timer.h"
+#include "gin/test/file_runner.h"
+#include "gin/test/gtest.h"
+#include "mojo/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 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);
+ }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestRunnerDelegate);
+};
+
+void RunTest(std::string test, bool addSupportModule) {
+ base::FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ path = path.AppendASCII("mojo")
+ .AppendASCII("edk")
+ .AppendASCII("js")
+ .AppendASCII("tests")
+ .AppendASCII(test);
+ TestRunnerDelegate delegate;
+ if (addSupportModule)
+ delegate.AddBuiltinModule(Support::kModuleName, Support::GetModule);
+ gin::RunTestFromFile(path, &delegate, true);
+}
+
+TEST(JSTest, connection) {
+ RunTest("connection_tests.js", false);
+}
+
+TEST(JSTest, sample_service) {
+ RunTest("sample_service_tests.js", true);
+}
+
+} // namespace
+} // namespace js
+} // namespace mojo
diff --git a/mojo/edk/js/tests/run_js_tests.cc b/mojo/edk/js/test/run_js_tests.cc
index 4246f8e..b574902 100644
--- a/mojo/edk/js/tests/run_js_tests.cc
+++ b/mojo/edk/js/test/run_js_tests.cc
@@ -6,7 +6,6 @@
#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"
@@ -31,7 +30,6 @@ class TestRunnerDelegate : public gin::FileRunnerDelegate {
};
void RunTest(std::string test, bool run_until_idle) {
- Environment env;
base::FilePath path;
PathService::Get(base::DIR_SOURCE_ROOT, &path);
path = path.AppendASCII("mojo")
diff --git a/mojo/edk/js/tests/BUILD.gn b/mojo/edk/js/tests/BUILD.gn
index 1555328..c61ba37 100644
--- a/mojo/edk/js/tests/BUILD.gn
+++ b/mojo/edk/js/tests/BUILD.gn
@@ -2,17 +2,26 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-test("js_unittests") {
+import("//mojo/public/tools/bindings/mojom.gni")
+
+source_set("js_to_cpp_tests") {
+ testonly = true
+
deps = [
+ ":js_to_cpp_bindings",
"//gin:gin_test",
"//mojo/edk/js",
- "//mojo/edk/js:js_unittests",
- "//mojo/edk/test:run_all_unittests",
"//mojo/edk/test:test_support",
- "//mojo/public/cpp/environment:standalone",
- "//mojo/public/cpp/utility",
+ "//mojo/public/cpp/bindings",
+ "//mojo/public/cpp/system",
"//mojo/public/interfaces/bindings/tests:test_interfaces",
]
- sources = [ "run_js_tests.cc" ]
+ sources = [
+ "js_to_cpp_tests.cc",
+ ]
+}
+
+mojom("js_to_cpp_bindings") {
+ sources = [ "js_to_cpp.mojom" ]
}
diff --git a/mojo/edk/js/tests/DEPS b/mojo/edk/js/tests/DEPS
deleted file mode 100644
index 190ee62..0000000
--- a/mojo/edk/js/tests/DEPS
+++ /dev/null
@@ -1,7 +0,0 @@
-include_rules = [
- "+base",
- "+gin",
- "+v8",
- "+mojo/edk/js/core.h",
- "+mojo/edk/js/support.h",
-]
diff --git a/mojo/edk/js/tests/connection_tests.js b/mojo/edk/js/tests/connection_tests.js
new file mode 100644
index 0000000..6649dfe
--- /dev/null
+++ b/mojo/edk/js/tests/connection_tests.js
@@ -0,0 +1,261 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Mock out the support module to avoid depending on the message loop.
+define("mojo/public/js/support", ["timer"], function(timer) {
+ var waitingCallbacks = [];
+
+ function WaitCookie(id) {
+ this.id = id;
+ }
+
+ function asyncWait(handle, flags, callback) {
+ var id = waitingCallbacks.length;
+ waitingCallbacks.push(callback);
+ return new WaitCookie(id);
+ }
+
+ function cancelWait(cookie) {
+ waitingCallbacks[cookie.id] = null;
+ }
+
+ function numberOfWaitingCallbacks() {
+ var count = 0;
+ for (var i = 0; i < waitingCallbacks.length; ++i) {
+ if (waitingCallbacks[i])
+ ++count;
+ }
+ return count;
+ }
+
+ function pumpOnce(result) {
+ var callbacks = waitingCallbacks;
+ waitingCallbacks = [];
+ for (var i = 0; i < callbacks.length; ++i) {
+ if (callbacks[i])
+ callbacks[i](result);
+ }
+ }
+
+ // Queue up a pumpOnce call to execute after the stack unwinds. Use
+ // this to trigger a pump after all Promises are executed.
+ function queuePump(result) {
+ timer.createOneShot(0, pumpOnce.bind(undefined, result));
+ }
+
+ var exports = {};
+ exports.asyncWait = asyncWait;
+ exports.cancelWait = cancelWait;
+ exports.numberOfWaitingCallbacks = numberOfWaitingCallbacks;
+ exports.pumpOnce = pumpOnce;
+ exports.queuePump = queuePump;
+ return exports;
+});
+
+define([
+ "gin/test/expect",
+ "mojo/public/js/support",
+ "mojo/public/js/core",
+ "mojo/public/js/connection",
+ "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom",
+ "mojo/public/interfaces/bindings/tests/sample_service.mojom",
+ "mojo/public/js/threading",
+ "gc",
+], function(expect,
+ mockSupport,
+ core,
+ connection,
+ sample_interfaces,
+ sample_service,
+ threading,
+ gc) {
+ testClientServer();
+ testWriteToClosedPipe();
+ testRequestResponse().then(function() {
+ this.result = "PASS";
+ gc.collectGarbage(); // should not crash
+ threading.quit();
+ }.bind(this)).catch(function(e) {
+ this.result = "FAIL: " + (e.stack || e);
+ threading.quit();
+ }.bind(this));
+
+ function testClientServer() {
+ var receivedFrobinate = false;
+ var receivedDidFrobinate = false;
+
+ // ServiceImpl ------------------------------------------------------------
+
+ function ServiceImpl(peer) {
+ this.peer = peer;
+ }
+
+ ServiceImpl.prototype = Object.create(
+ sample_service.Service.stubClass.prototype);
+
+ ServiceImpl.prototype.frobinate = function(foo, baz, port) {
+ receivedFrobinate = true;
+
+ expect(foo.name).toBe("Example name");
+ expect(baz).toBeTruthy();
+ expect(core.close(port)).toBe(core.RESULT_OK);
+
+ this.peer.didFrobinate(42);
+ };
+
+ // ServiceClientImpl ------------------------------------------------------
+
+ function ServiceClientImpl(peer) {
+ this.peer = peer;
+ }
+
+ ServiceClientImpl.prototype =
+ Object.create(sample_service.ServiceClient.stubClass.prototype);
+
+ ServiceClientImpl.prototype.didFrobinate = function(result) {
+ receivedDidFrobinate = true;
+
+ expect(result).toBe(42);
+ };
+
+ var pipe = core.createMessagePipe();
+ var anotherPipe = core.createMessagePipe();
+ var sourcePipe = core.createMessagePipe();
+
+ var connection0 = new connection.Connection(
+ pipe.handle0, ServiceImpl, sample_service.ServiceClient.proxyClass);
+
+ var connection1 = new connection.Connection(
+ pipe.handle1, ServiceClientImpl, sample_service.Service.proxyClass);
+
+ var foo = new sample_service.Foo();
+ foo.bar = new sample_service.Bar();
+ foo.name = "Example name";
+ foo.source = sourcePipe.handle0;
+ connection1.remote.frobinate(foo, true, anotherPipe.handle0);
+
+ mockSupport.pumpOnce(core.RESULT_OK);
+
+ expect(receivedFrobinate).toBeTruthy();
+ expect(receivedDidFrobinate).toBeTruthy();
+
+ connection0.close();
+ connection1.close();
+
+ expect(mockSupport.numberOfWaitingCallbacks()).toBe(0);
+
+ // sourcePipe.handle0 was closed automatically when sent over IPC.
+ expect(core.close(sourcePipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT);
+ // sourcePipe.handle1 hasn't been closed yet.
+ expect(core.close(sourcePipe.handle1)).toBe(core.RESULT_OK);
+
+ // anotherPipe.handle0 was closed automatically when sent over IPC.
+ expect(core.close(anotherPipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT);
+ // anotherPipe.handle1 hasn't been closed yet.
+ expect(core.close(anotherPipe.handle1)).toBe(core.RESULT_OK);
+
+ // The Connection object is responsible for closing these handles.
+ expect(core.close(pipe.handle0)).toBe(core.RESULT_INVALID_ARGUMENT);
+ expect(core.close(pipe.handle1)).toBe(core.RESULT_INVALID_ARGUMENT);
+ }
+
+ function testWriteToClosedPipe() {
+ var pipe = core.createMessagePipe();
+
+ var connection1 = new connection.Connection(
+ pipe.handle1, function() {}, sample_service.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 foo = new sample_service.Foo();
+ foo.bar = new sample_service.Bar();
+ // TODO(darin): crbug.com/357043: pass null in place of |foo| here.
+ connection1.remote.frobinate(foo, true, null);
+
+ // Write failures are not reported.
+ expect(connection1.encounteredError()).toBeFalsy();
+
+ // Pump events, and then we should start observing the closed pipe.
+ mockSupport.pumpOnce(core.RESULT_OK);
+
+ expect(connection1.encounteredError()).toBeTruthy();
+
+ connection1.close();
+ }
+
+ function testRequestResponse() {
+
+ // ProviderImpl ------------------------------------------------------------
+
+ function ProviderImpl(peer) {
+ this.peer = peer;
+ }
+
+ ProviderImpl.prototype =
+ Object.create(sample_interfaces.Provider.stubClass.prototype);
+
+ ProviderImpl.prototype.echoString = function(a) {
+ mockSupport.queuePump(core.RESULT_OK);
+ return Promise.resolve({a: a});
+ };
+
+ ProviderImpl.prototype.echoStrings = function(a, b) {
+ mockSupport.queuePump(core.RESULT_OK);
+ return Promise.resolve({a: a, b: b});
+ };
+
+ // ProviderClientImpl ------------------------------------------------------
+
+ function ProviderClientImpl(peer) {
+ this.peer = peer;
+ }
+
+ ProviderClientImpl.prototype =
+ Object.create(sample_interfaces.ProviderClient.stubClass.prototype);
+
+ var pipe = core.createMessagePipe();
+
+ var connection0 = new connection.Connection(
+ pipe.handle0,
+ ProviderImpl,
+ sample_interfaces.ProviderClient.proxyClass);
+
+ var connection1 = new connection.Connection(
+ pipe.handle1,
+ ProviderClientImpl,
+ sample_interfaces.Provider.proxyClass);
+
+ var origReadMessage = core.readMessage;
+ // echoString
+ mockSupport.queuePump(core.RESULT_OK);
+ return connection1.remote.echoString("hello").then(function(response) {
+ expect(response.a).toBe("hello");
+ }).then(function() {
+ // echoStrings
+ mockSupport.queuePump(core.RESULT_OK);
+ return connection1.remote.echoStrings("hello", "world");
+ }).then(function(response) {
+ expect(response.a).toBe("hello");
+ expect(response.b).toBe("world");
+ }).then(function() {
+ // Mock a read failure, expect it to fail.
+ core.readMessage = function() {
+ return { result: core.RESULT_UNKNOWN };
+ };
+ mockSupport.queuePump(core.RESULT_OK);
+ return connection1.remote.echoString("goodbye");
+ }).then(function() {
+ throw Error("Expected echoString to fail.");
+ }, function(error) {
+ expect(error.message).toBe("Connection error: " + core.RESULT_UNKNOWN);
+
+ // Clean up.
+ core.readMessage = origReadMessage;
+ });
+ }
+});
diff --git a/mojo/edk/js/tests/js_to_cpp.mojom b/mojo/edk/js/tests/js_to_cpp.mojom
new file mode 100644
index 0000000..69f67b6
--- /dev/null
+++ b/mojo/edk/js/tests/js_to_cpp.mojom
@@ -0,0 +1,53 @@
+module js_to_cpp;
+
+// This struct encompasses all of the basic types, so that they
+// may be sent from C++ to JS and back for validation.
+struct EchoArgs {
+ int64 si64;
+ int32 si32;
+ int16 si16;
+ int8 si8;
+ uint64 ui64;
+ uint32 ui32;
+ uint16 ui16;
+ uint8 ui8;
+ float float_val;
+ float float_inf;
+ float float_nan;
+ double double_val;
+ double double_inf;
+ double double_nan;
+ string? name;
+ array<string>? string_array;
+ handle<message_pipe>? message_handle;
+ handle<data_pipe_consumer>? data_handle;
+};
+
+struct EchoArgsList {
+ EchoArgsList? next;
+ EchoArgs? item;
+};
+
+// Note: For messages which control test flow, pick numbers that are unlikely
+// to be hit as a result of our deliberate corruption of response messages.
+interface CppSide {
+ // Sent for all tests to notify that the JS side is now ready.
+ StartTest@88888888();
+
+ // Indicates end for echo, bit-flip, and back-pointer tests.
+ TestFinished@99999999();
+
+ // Responses from specific tests.
+ PingResponse();
+ EchoResponse(EchoArgsList list);
+ BitFlipResponse(EchoArgsList arg);
+ BackPointerResponse(EchoArgsList arg);
+};
+
+[Client=CppSide]
+interface JsSide {
+ Ping();
+ Echo(int32 numIterations, EchoArgs arg);
+ BitFlip(EchoArgs arg);
+ BackPointer(EchoArgs arg);
+};
diff --git a/mojo/edk/js/tests/js_to_cpp_tests.cc b/mojo/edk/js/tests/js_to_cpp_tests.cc
new file mode 100644
index 0000000..1da70c2
--- /dev/null
+++ b/mojo/edk/js/tests/js_to_cpp_tests.cc
@@ -0,0 +1,418 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/at_exit.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "gin/array_buffer.h"
+#include "gin/public/isolate_holder.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/system/core.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace js {
+
+// Global value updated by some checks to prevent compilers from optimizing
+// reads out of existence.
+uint32 g_waste_accumulator = 0;
+
+namespace {
+
+// Negative numbers with different values in each byte, the last of
+// which can survive promotion to double and back.
+const int8 kExpectedInt8Value = -65;
+const int16 kExpectedInt16Value = -16961;
+const int32 kExpectedInt32Value = -1145258561;
+const int64 kExpectedInt64Value = -77263311946305LL;
+
+// Positive numbers with different values in each byte, the last of
+// which can survive promotion to double and back.
+const uint8 kExpectedUInt8Value = 65;
+const uint16 kExpectedUInt16Value = 16961;
+const uint32 kExpectedUInt32Value = 1145258561;
+const uint64 kExpectedUInt64Value = 77263311946305LL;
+
+// Double/float values, including special case constants.
+const double kExpectedDoubleVal = 3.14159265358979323846;
+const double kExpectedDoubleInf = std::numeric_limits<double>::infinity();
+const double kExpectedDoubleNan = std::numeric_limits<double>::quiet_NaN();
+const float kExpectedFloatVal = static_cast<float>(kExpectedDoubleVal);
+const float kExpectedFloatInf = std::numeric_limits<float>::infinity();
+const float kExpectedFloatNan = std::numeric_limits<float>::quiet_NaN();
+
+// NaN has the property that it is not equal to itself.
+#define EXPECT_NAN(x) EXPECT_NE(x, x)
+
+void CheckDataPipe(MojoHandle data_pipe_handle) {
+ unsigned char buffer[100];
+ uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ MojoResult result = MojoReadData(
+ data_pipe_handle, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE);
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(64u, buffer_size);
+ for (int i = 0; i < 64; ++i) {
+ EXPECT_EQ(i, buffer[i]);
+ }
+}
+
+void CheckMessagePipe(MojoHandle message_pipe_handle) {
+ unsigned char buffer[100];
+ uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ MojoResult result = MojoReadMessage(
+ message_pipe_handle, buffer, &buffer_size, 0, 0, 0);
+ EXPECT_EQ(MOJO_RESULT_OK, result);
+ EXPECT_EQ(64u, buffer_size);
+ for (int i = 0; i < 64; ++i) {
+ EXPECT_EQ(255 - i, buffer[i]);
+ }
+}
+
+js_to_cpp::EchoArgsPtr BuildSampleEchoArgs() {
+ js_to_cpp::EchoArgsPtr args(js_to_cpp::EchoArgs::New());
+ args->si64 = kExpectedInt64Value;
+ args->si32 = kExpectedInt32Value;
+ args->si16 = kExpectedInt16Value;
+ args->si8 = kExpectedInt8Value;
+ args->ui64 = kExpectedUInt64Value;
+ args->ui32 = kExpectedUInt32Value;
+ args->ui16 = kExpectedUInt16Value;
+ args->ui8 = kExpectedUInt8Value;
+ args->float_val = kExpectedFloatVal;
+ args->float_inf = kExpectedFloatInf;
+ args->float_nan = kExpectedFloatNan;
+ args->double_val = kExpectedDoubleVal;
+ args->double_inf = kExpectedDoubleInf;
+ args->double_nan = kExpectedDoubleNan;
+ args->name = "coming";
+ Array<String> string_array(3);
+ string_array[0] = "one";
+ string_array[1] = "two";
+ string_array[2] = "three";
+ args->string_array = string_array.Pass();
+ return args.Pass();
+}
+
+void CheckSampleEchoArgs(const js_to_cpp::EchoArgs& arg) {
+ EXPECT_EQ(kExpectedInt64Value, arg.si64);
+ EXPECT_EQ(kExpectedInt32Value, arg.si32);
+ EXPECT_EQ(kExpectedInt16Value, arg.si16);
+ EXPECT_EQ(kExpectedInt8Value, arg.si8);
+ EXPECT_EQ(kExpectedUInt64Value, arg.ui64);
+ EXPECT_EQ(kExpectedUInt32Value, arg.ui32);
+ EXPECT_EQ(kExpectedUInt16Value, arg.ui16);
+ EXPECT_EQ(kExpectedUInt8Value, arg.ui8);
+ EXPECT_EQ(kExpectedFloatVal, arg.float_val);
+ EXPECT_EQ(kExpectedFloatInf, arg.float_inf);
+ EXPECT_NAN(arg.float_nan);
+ EXPECT_EQ(kExpectedDoubleVal, arg.double_val);
+ EXPECT_EQ(kExpectedDoubleInf, arg.double_inf);
+ EXPECT_NAN(arg.double_nan);
+ 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(arg.data_handle.get().value());
+ CheckMessagePipe(arg.message_handle.get().value());
+}
+
+void CheckSampleEchoArgsList(const js_to_cpp::EchoArgsListPtr& list) {
+ if (list.is_null())
+ return;
+ CheckSampleEchoArgs(*list->item);
+ CheckSampleEchoArgsList(list->next);
+}
+
+// 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 String& arg) {
+ if (arg.is_null())
+ return;
+ for (size_t i = 0; i < arg.size(); ++i)
+ g_waste_accumulator += arg[i];
+}
+
+void CheckCorruptedStringArray(const Array<String>& string_array) {
+ if (string_array.is_null())
+ return;
+ for (size_t i = 0; i < string_array.size(); ++i)
+ CheckCorruptedString(string_array[i]);
+}
+
+void CheckCorruptedDataPipe(MojoHandle data_pipe_handle) {
+ unsigned char buffer[100];
+ uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ MojoResult result = MojoReadData(
+ data_pipe_handle, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE);
+ if (result != MOJO_RESULT_OK)
+ return;
+ for (uint32_t i = 0; i < buffer_size; ++i)
+ g_waste_accumulator += buffer[i];
+}
+
+void CheckCorruptedMessagePipe(MojoHandle message_pipe_handle) {
+ unsigned char buffer[100];
+ uint32_t buffer_size = static_cast<uint32_t>(sizeof(buffer));
+ MojoResult result = MojoReadMessage(
+ message_pipe_handle, buffer, &buffer_size, 0, 0, 0);
+ if (result != MOJO_RESULT_OK)
+ return;
+ for (uint32_t i = 0; i < buffer_size; ++i)
+ g_waste_accumulator += buffer[i];
+}
+
+void CheckCorruptedEchoArgs(const js_to_cpp::EchoArgsPtr& arg) {
+ if (arg.is_null())
+ return;
+ CheckCorruptedString(arg->name);
+ CheckCorruptedStringArray(arg->string_array);
+ if (arg->data_handle.is_valid())
+ CheckCorruptedDataPipe(arg->data_handle.get().value());
+ if (arg->message_handle.is_valid())
+ CheckCorruptedMessagePipe(arg->message_handle.get().value());
+}
+
+void CheckCorruptedEchoArgsList(const js_to_cpp::EchoArgsListPtr& list) {
+ if (list.is_null())
+ return;
+ CheckCorruptedEchoArgs(list->item);
+ CheckCorruptedEchoArgsList(list->next);
+}
+
+// Base Provider implementation class. It's expected that tests subclass and
+// override the appropriate Provider functions. When test is done quit the
+// run_loop().
+class CppSideConnection : public js_to_cpp::CppSide {
+ public:
+ CppSideConnection() :
+ run_loop_(NULL),
+ js_side_(NULL),
+ mishandled_messages_(0) {
+ }
+ ~CppSideConnection() override {}
+
+ void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; }
+ base::RunLoop* run_loop() { return run_loop_; }
+
+ void set_js_side(js_to_cpp::JsSide* js_side) { js_side_ = js_side; }
+ js_to_cpp::JsSide* js_side() { return js_side_; }
+
+ // js_to_cpp::CppSide:
+ void StartTest() override { NOTREACHED(); }
+
+ void TestFinished() override { NOTREACHED(); }
+
+ void PingResponse() override { mishandled_messages_ += 1; }
+
+ void EchoResponse(js_to_cpp::EchoArgsListPtr list) override {
+ mishandled_messages_ += 1;
+ }
+
+ void BitFlipResponse(js_to_cpp::EchoArgsListPtr list) override {
+ mishandled_messages_ += 1;
+ }
+
+ void BackPointerResponse(js_to_cpp::EchoArgsListPtr list) override {
+ mishandled_messages_ += 1;
+ }
+
+ protected:
+ base::RunLoop* run_loop_;
+ js_to_cpp::JsSide* js_side_;
+ int mishandled_messages_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CppSideConnection);
+};
+
+// Trivial test to verify a message sent from JS is received.
+class PingCppSideConnection : public CppSideConnection {
+ public:
+ PingCppSideConnection() : got_message_(false) {}
+ ~PingCppSideConnection() override {}
+
+ // js_to_cpp::CppSide:
+ void StartTest() override { js_side_->Ping(); }
+
+ void PingResponse() override {
+ got_message_ = true;
+ run_loop()->Quit();
+ }
+
+ bool DidSucceed() {
+ return got_message_ && !mishandled_messages_;
+ }
+
+ private:
+ bool got_message_;
+ DISALLOW_COPY_AND_ASSIGN(PingCppSideConnection);
+};
+
+// Test that parameters are passed with correct values.
+class EchoCppSideConnection : public CppSideConnection {
+ public:
+ EchoCppSideConnection() :
+ message_count_(0),
+ termination_seen_(false) {
+ }
+ ~EchoCppSideConnection() override {}
+
+ // js_to_cpp::CppSide:
+ void StartTest() override {
+ js_side_->Echo(kExpectedMessageCount, BuildSampleEchoArgs());
+ }
+
+ void EchoResponse(js_to_cpp::EchoArgsListPtr list) override {
+ const js_to_cpp::EchoArgsPtr& special_arg = list->item;
+ message_count_ += 1;
+ EXPECT_EQ(-1, special_arg->si64);
+ 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.To<std::string>());
+ CheckSampleEchoArgsList(list->next);
+ }
+
+ void TestFinished() override {
+ termination_seen_ = true;
+ run_loop()->Quit();
+ }
+
+ bool DidSucceed() {
+ return termination_seen_ &&
+ !mishandled_messages_ &&
+ message_count_ == kExpectedMessageCount;
+ }
+
+ private:
+ static const int kExpectedMessageCount = 10;
+ int message_count_;
+ bool termination_seen_;
+ DISALLOW_COPY_AND_ASSIGN(EchoCppSideConnection);
+};
+
+// Test that corrupted messages don't wreak havoc.
+class BitFlipCppSideConnection : public CppSideConnection {
+ public:
+ BitFlipCppSideConnection() : termination_seen_(false) {}
+ ~BitFlipCppSideConnection() override {}
+
+ // js_to_cpp::CppSide:
+ void StartTest() override { js_side_->BitFlip(BuildSampleEchoArgs()); }
+
+ void BitFlipResponse(js_to_cpp::EchoArgsListPtr list) override {
+ CheckCorruptedEchoArgsList(list);
+ }
+
+ void TestFinished() override {
+ termination_seen_ = true;
+ run_loop()->Quit();
+ }
+
+ bool DidSucceed() {
+ return termination_seen_;
+ }
+
+ private:
+ bool termination_seen_;
+ DISALLOW_COPY_AND_ASSIGN(BitFlipCppSideConnection);
+};
+
+// Test that severely random messages don't wreak havoc.
+class BackPointerCppSideConnection : public CppSideConnection {
+ public:
+ BackPointerCppSideConnection() : termination_seen_(false) {}
+ ~BackPointerCppSideConnection() override {}
+
+ // js_to_cpp::CppSide:
+ void StartTest() override { js_side_->BackPointer(BuildSampleEchoArgs()); }
+
+ void BackPointerResponse(js_to_cpp::EchoArgsListPtr list) override {
+ CheckCorruptedEchoArgsList(list);
+ }
+
+ void TestFinished() override {
+ termination_seen_ = true;
+ run_loop()->Quit();
+ }
+
+ bool DidSucceed() {
+ return termination_seen_;
+ }
+
+ private:
+ bool termination_seen_;
+ DISALLOW_COPY_AND_ASSIGN(BackPointerCppSideConnection);
+};
+
+} // namespace
+
+class JsToCppTest : public testing::Test {
+ public:
+ JsToCppTest() {}
+
+ void RunTest(const std::string& test, CppSideConnection* cpp_side) {
+ cpp_side->set_run_loop(&run_loop_);
+
+ MessagePipe pipe;
+ js_to_cpp::JsSidePtr js_side =
+ MakeProxy<js_to_cpp::JsSide>(pipe.handle0.Pass());
+ js_side.set_client(cpp_side);
+
+ js_side.internal_state()->router_for_testing()->EnableTestingMode();
+
+ cpp_side->set_js_side(js_side.get());
+
+ gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode,
+ gin::ArrayBufferAllocator::SharedInstance());
+ gin::IsolateHolder instance;
+ MojoRunnerDelegate delegate;
+ gin::ShellRunner runner(&delegate, instance.isolate());
+ delegate.Start(&runner, pipe.handle1.release().value(), test);
+
+ run_loop_.Run();
+ }
+
+ private:
+ base::ShadowingAtExitManager at_exit_;
+ base::MessageLoop loop;
+ base::RunLoop run_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(JsToCppTest);
+};
+
+TEST_F(JsToCppTest, Ping) {
+ PingCppSideConnection cpp_side_connection;
+ RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
+ EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+TEST_F(JsToCppTest, Echo) {
+ EchoCppSideConnection cpp_side_connection;
+ RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
+ EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+TEST_F(JsToCppTest, BitFlip) {
+ 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) {
+ BackPointerCppSideConnection cpp_side_connection;
+ RunTest("mojo/edk/js/tests/js_to_cpp_tests", &cpp_side_connection);
+ EXPECT_TRUE(cpp_side_connection.DidSucceed());
+}
+
+} // namespace js
+} // namespace mojo
diff --git a/mojo/edk/js/tests/js_to_cpp_tests.js b/mojo/edk/js/tests/js_to_cpp_tests.js
new file mode 100644
index 0000000..c32f0af
--- /dev/null
+++ b/mojo/edk/js/tests/js_to_cpp_tests.js
@@ -0,0 +1,221 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define('mojo/edk/js/tests/js_to_cpp_tests', [
+ 'console',
+ 'mojo/edk/js/tests/js_to_cpp.mojom',
+ 'mojo/public/js/connection',
+ 'mojo/public/js/connector',
+ 'mojo/public/js/core',
+], function (console, jsToCpp, connection, connector, core) {
+ var retainedConnection;
+ var sampleData;
+ var sampleMessage;
+ var BAD_VALUE = 13;
+ var DATA_PIPE_PARAMS = {
+ flags: core.CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
+ elementNumBytes: 1,
+ capacityNumBytes: 64
+ };
+
+ function JsSideConnection(cppSide) {
+ this.cppSide_ = cppSide;
+ cppSide.startTest();
+ }
+
+ JsSideConnection.prototype =
+ Object.create(jsToCpp.JsSide.stubClass.prototype);
+
+ JsSideConnection.prototype.ping = function (arg) {
+ this.cppSide_.pingResponse();
+ };
+
+ JsSideConnection.prototype.echo = function (numIterations, arg) {
+ var dataPipe1;
+ var dataPipe2;
+ var i;
+ var messagePipe1;
+ var messagePipe2;
+ var specialArg;
+
+ // Ensure expected negative values are negative.
+ if (arg.si64 > 0)
+ arg.si64 = BAD_VALUE;
+
+ if (arg.si32 > 0)
+ arg.si32 = BAD_VALUE;
+
+ if (arg.si16 > 0)
+ arg.si16 = BAD_VALUE;
+
+ if (arg.si8 > 0)
+ arg.si8 = BAD_VALUE;
+
+ for (i = 0; i < numIterations; ++i) {
+ dataPipe1 = core.createDataPipe(DATA_PIPE_PARAMS);
+ dataPipe2 = core.createDataPipe(DATA_PIPE_PARAMS);
+ messagePipe1 = core.createMessagePipe();
+ messagePipe2 = core.createMessagePipe();
+
+ arg.data_handle = dataPipe1.consumerHandle;
+ arg.message_handle = messagePipe1.handle1;
+
+ specialArg = new jsToCpp.EchoArgs();
+ specialArg.si64 = -1;
+ specialArg.si32 = -1;
+ specialArg.si16 = -1;
+ specialArg.si8 = -1;
+ specialArg.name = 'going';
+ specialArg.data_handle = dataPipe2.consumerHandle;
+ specialArg.message_handle = messagePipe2.handle1;
+
+ writeDataPipe(dataPipe1, sampleData);
+ writeDataPipe(dataPipe2, sampleData);
+ writeMessagePipe(messagePipe1, sampleMessage);
+ writeMessagePipe(messagePipe2, sampleMessage);
+
+ this.cppSide_.echoResponse(createEchoArgsList(specialArg, arg));
+
+ core.close(dataPipe1.producerHandle);
+ core.close(dataPipe2.producerHandle);
+ core.close(messagePipe1.handle0);
+ core.close(messagePipe2.handle0);
+ }
+ this.cppSide_.testFinished();
+ };
+
+ JsSideConnection.prototype.bitFlip = function (arg) {
+ var iteration = 0;
+ var dataPipe;
+ var messagePipe;
+ var proto = connector.Connector.prototype;
+ var stopSignalled = false;
+
+ proto.realAccept = proto.accept;
+ proto.accept = function (message) {
+ var offset = iteration / 8;
+ var mask;
+ var value;
+ if (offset < message.buffer.arrayBuffer.byteLength) {
+ mask = 1 << (iteration % 8);
+ value = message.buffer.getUint8(offset) ^ mask;
+ message.buffer.setUint8(offset, value);
+ return this.realAccept(message);
+ }
+ stopSignalled = true;
+ return false;
+ };
+
+ while (!stopSignalled) {
+ dataPipe = core.createDataPipe(DATA_PIPE_PARAMS);
+ messagePipe = core.createMessagePipe();
+ writeDataPipe(dataPipe, sampleData);
+ writeMessagePipe(messagePipe, sampleMessage);
+ arg.data_handle = dataPipe.consumerHandle;
+ arg.message_handle = messagePipe.handle1;
+
+ this.cppSide_.bitFlipResponse(createEchoArgsList(arg));
+
+ core.close(dataPipe.producerHandle);
+ core.close(messagePipe.handle0);
+ iteration += 1;
+ }
+
+ proto.accept = proto.realAccept;
+ proto.realAccept = null;
+ this.cppSide_.testFinished();
+ };
+
+ JsSideConnection.prototype.backPointer = function (arg) {
+ var iteration = 0;
+ var dataPipe;
+ var messagePipe;
+ var proto = connector.Connector.prototype;
+ var stopSignalled = false;
+
+ proto.realAccept = proto.accept;
+ proto.accept = function (message) {
+ var delta = 8 * (1 + iteration % 32);
+ var offset = 8 * ((iteration / 32) | 0);
+ if (offset < message.buffer.arrayBuffer.byteLength - 4) {
+ message.buffer.dataView.setUint32(offset, 0x100000000 - delta, true);
+ message.buffer.dataView.setUint32(offset + 4, 0xffffffff, true);
+ return this.realAccept(message);
+ }
+ stopSignalled = true;
+ return false;
+ };
+
+ while (!stopSignalled) {
+ dataPipe = core.createDataPipe(DATA_PIPE_PARAMS);
+ messagePipe = core.createMessagePipe();
+ writeDataPipe(dataPipe, sampleData);
+ writeMessagePipe(messagePipe, sampleMessage);
+ arg.data_handle = dataPipe.consumerHandle;
+ arg.message_handle = messagePipe.handle1;
+
+ this.cppSide_.backPointerResponse(createEchoArgsList(arg));
+
+ core.close(dataPipe.producerHandle);
+ core.close(messagePipe.handle0);
+ iteration += 1;
+ }
+
+ proto.accept = proto.realAccept;
+ proto.realAccept = null;
+ this.cppSide_.testFinished();
+ };
+
+ function writeDataPipe(pipe, data) {
+ var writeResult = core.writeData(
+ pipe.producerHandle, data, core.WRITE_DATA_FLAG_ALL_OR_NONE);
+
+ if (writeResult.result != core.RESULT_OK) {
+ console.log('ERROR: Data pipe write result was ' + writeResult.result);
+ return false;
+ }
+ if (writeResult.numBytes != data.length) {
+ console.log('ERROR: Data pipe write length was ' + writeResult.numBytes);
+ return false;
+ }
+ return true;
+ }
+
+ function writeMessagePipe(pipe, arrayBuffer) {
+ var result = core.writeMessage(pipe.handle0, arrayBuffer, [], 0);
+ if (result != core.RESULT_OK) {
+ console.log('ERROR: Message pipe write result was ' + result);
+ return false;
+ }
+ return true;
+ }
+
+ function createEchoArgsListElement(item, next) {
+ var list = new jsToCpp.EchoArgsList();
+ list.item = item;
+ list.next = next;
+ return list;
+ }
+
+ function createEchoArgsList() {
+ var genuineArray = Array.prototype.slice.call(arguments);
+ return genuineArray.reduceRight(function (previous, current) {
+ return createEchoArgsListElement(current, previous);
+ }, null);
+ }
+
+ return function(handle) {
+ var i;
+ sampleData = new Uint8Array(DATA_PIPE_PARAMS.capacityNumBytes);
+ for (i = 0; i < sampleData.length; ++i) {
+ sampleData[i] = i;
+ }
+ sampleMessage = new Uint8Array(DATA_PIPE_PARAMS.capacityNumBytes);
+ for (i = 0; i < sampleMessage.length; ++i) {
+ sampleMessage[i] = 255 - i;
+ }
+ retainedConnection = new connection.Connection(handle, JsSideConnection,
+ jsToCpp.CppSide.proxyClass);
+ };
+});
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..ca4f8e6
--- /dev/null
+++ b/mojo/edk/js/tests/sample_service_tests.js
@@ -0,0 +1,168 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+define([
+ "console",
+ "mojo/edk/js/test/hexdump",
+ "gin/test/expect",
+ "mojo/public/interfaces/bindings/tests/sample_service.mojom",
+ "mojo/public/interfaces/bindings/tests/sample_import.mojom",
+ "mojo/public/interfaces/bindings/tests/sample_import2.mojom",
+ ], function(console, hexdump, expect, sample, imported, imported2) {
+
+ var global = this;
+
+ // Set this variable to true to print the binary message in hex.
+ var dumpMessageAsHex = false;
+
+ function makeFoo() {
+ var bar = new sample.Bar();
+ bar.alpha = 20;
+ bar.beta = 40;
+ bar.gamma = 60;
+ bar.type = sample.Bar.Type.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 source = 0xFFFF; // Invent a dummy handle.
+
+ var foo = new sample.Foo();
+ foo.name = "foopy";
+ foo.x = 1;
+ foo.y = 2;
+ foo.a = false;
+ foo.b = true;
+ foo.c = false;
+ foo.bar = bar;
+ foo.extra_bars = extra_bars;
+ foo.data = data;
+ foo.source = source;
+ return foo;
+ }
+
+ // Check that the given |Foo| is identical to the one made by |MakeFoo()|.
+ function checkFoo(foo) {
+ expect(foo.name).toBe("foopy");
+ expect(foo.x).toBe(1);
+ expect(foo.y).toBe(2);
+ expect(foo.a).toBeFalsy();
+ expect(foo.b).toBeTruthy();
+ expect(foo.c).toBeFalsy();
+ expect(foo.bar.alpha).toBe(20);
+ expect(foo.bar.beta).toBe(40);
+ expect(foo.bar.gamma).toBe(60);
+ expect(foo.bar.type).toBe(sample.Bar.Type.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(foo.source).toBe(0xFFFF);
+ }
+
+ // Check that values are set to the defaults if we don't override them.
+ function checkDefaultValues() {
+ var bar = new sample.Bar();
+ expect(bar.alpha).toBe(255);
+ expect(bar.type).toBe(sample.Bar.Type.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);
+ }
+
+ 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(port).toBe(10);
+ global.result = "PASS";
+ };
+
+ function SimpleMessageReceiver() {
+ }
+
+ SimpleMessageReceiver.prototype.accept = function(message) {
+ if (dumpMessageAsHex) {
+ var uint8Array = new Uint8Array(message.buffer.arrayBuffer);
+ console.log(hexdump.dumpArray(uint8Array));
+ }
+ // Imagine some IPC happened here.
+ var serviceImpl = new ServiceImpl();
+ serviceImpl.accept(message);
+ };
+
+ var receiver = new SimpleMessageReceiver();
+ var serviceProxy = new sample.Service.proxyClass(receiver);
+
+ checkDefaultValues();
+
+ var foo = makeFoo();
+ checkFoo(foo);
+
+ var port = 10;
+ serviceProxy.frobinate(foo, sample.Service.BazOptions.EXTRA, port);
+});
diff --git a/mojo/edk/js/threading.cc b/mojo/edk/js/threading.cc
new file mode 100644
index 0000000..b571e3e
--- /dev/null
+++ b/mojo/edk/js/threading.cc
@@ -0,0 +1,47 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/edk/js/threading.h"
+
+#include "base/message_loop/message_loop.h"
+#include "gin/object_template_builder.h"
+#include "gin/per_isolate_data.h"
+#include "mojo/edk/js/handle.h"
+
+namespace mojo {
+namespace js {
+
+namespace {
+
+void Quit() {
+ base::MessageLoop::current()->QuitNow();
+}
+
+gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
+
+} // namespace
+
+const char Threading::kModuleName[] = "mojo/public/js/threading";
+
+v8::Local<v8::Value> Threading::GetModule(v8::Isolate* isolate) {
+ gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
+ v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(
+ &g_wrapper_info);
+
+ if (templ.IsEmpty()) {
+ templ = gin::ObjectTemplateBuilder(isolate)
+ .SetMethod("quit", Quit)
+ .Build();
+
+ data->SetObjectTemplate(&g_wrapper_info, templ);
+ }
+
+ return templ->NewInstance();
+}
+
+Threading::Threading() {
+}
+
+} // namespace js
+} // namespace mojo
diff --git a/mojo/edk/js/threading.h b/mojo/edk/js/threading.h
new file mode 100644
index 0000000..7cf0d53
--- /dev/null
+++ b/mojo/edk/js/threading.h
@@ -0,0 +1,25 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_EDK_JS_THREADING_H_
+#define MOJO_EDK_JS_THREADING_H_
+
+#include "gin/public/wrapper_info.h"
+#include "v8/include/v8.h"
+
+namespace mojo {
+namespace js {
+
+class Threading {
+ public:
+ static const char kModuleName[];
+ static v8::Local<v8::Value> GetModule(v8::Isolate* isolate);
+ private:
+ Threading();
+};
+
+} // namespace js
+} // namespace mojo
+
+#endif // MOJO_EDK_JS_THREADING_H_
diff --git a/mojo/edk/js/waiting_callback.h b/mojo/edk/js/waiting_callback.h
index fdffde5..6b2ccc7 100644
--- a/mojo/edk/js/waiting_callback.h
+++ b/mojo/edk/js/waiting_callback.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef MOJO_BINDINGS_JS_WAITING_CALLBACK_H_
-#define MOJO_BINDINGS_JS_WAITING_CALLBACK_H_
+#ifndef MOJO_EDK_JS_WAITING_CALLBACK_H_
+#define MOJO_EDK_JS_WAITING_CALLBACK_H_
#include "gin/handle.h"
#include "gin/runner.h"
@@ -60,4 +60,4 @@ class WaitingCallback : public gin::Wrappable<WaitingCallback>,
} // namespace js
} // namespace mojo
-#endif // MOJO_BINDINGS_JS_WAITING_CALLBACK_H_
+#endif // MOJO_EDK_JS_WAITING_CALLBACK_H_