diff options
author | Hidehiko Abe <hidehiko@google.com> | 2017-12-13 19:00:02 +0900 |
---|---|---|
committer | Hidehiko Abe <hidehiko@google.com> | 2017-12-13 23:42:13 +0900 |
commit | 0411add1e14cc2dc041f5f1776f05a8c4b787d73 (patch) | |
tree | 19a081baf4afcba8d442b5e839cb1ff247f31b7f | |
parent | c96e5aa6f5f1f6df03ef539b4b85c78bf7c292e5 (diff) | |
download | libmojo-0411add1e14cc2dc041f5f1776f05a8c4b787d73.tar.gz |
Revert "Revert "libmojo: Uprev the library to r456626 from Chromium""
This reverts commit 21a249e4d9cb0b2ec6f0ff84ed5f7939ea67ac52.
Test: Build.
Change-Id: Id31199817067e933eb08889b21b83c787b0d4c0d
Merged-In: I2f77e0bb4541d6520dac974cd499b30561c6658f
580 files changed, 19024 insertions, 24791 deletions
@@ -14,9 +14,13 @@ LOCAL_MODULE_TAGS := optional LOCAL_CPP_EXTENSION := .cc LOCAL_MOJOM_FILES := \ - mojo/common/common_custom_types.mojom \ + ipc/ipc.mojom \ + mojo/common/file.mojom \ mojo/common/string16.mojom \ + mojo/common/text_direction.mojom \ mojo/common/time.mojom \ + mojo/common/unguessable_token.mojom \ + mojo/common/version.mojom \ mojo/public/interfaces/bindings/interface_control_messages.mojom \ mojo/public/interfaces/bindings/pipe_control_messages.mojom \ @@ -44,6 +48,7 @@ LOCAL_JAVA_JNI_FILES := \ jni/java/lang/Runtime.class \ mojo/android/system/src/org/chromium/mojo/system/impl/BaseRunLoop.java \ mojo/android/system/src/org/chromium/mojo/system/impl/CoreImpl.java \ + mojo/android/system/src/org/chromium/mojo/system/impl/WatcherImpl.java \ # Generate all JNI header files. include $(LOCAL_PATH)/build_generated_jni.mk @@ -64,10 +69,9 @@ LOCAL_SRC_FILES := \ base/files/file_util_android.cc \ base/message_loop/message_pump_android.cc \ base/path_service.cc \ - base/threading/thread_local_android.cc \ base/trace_event/java_heap_dump_provider_android.cc \ base/trace_event/trace_event_android.cc \ - ipc/brokerable_attachment.cc \ + base/unguessable_token.cc \ ipc/ipc_message.cc \ ipc/ipc_message_attachment.cc \ ipc/ipc_message_attachment_set.cc \ @@ -76,11 +80,12 @@ LOCAL_SRC_FILES := \ ipc/ipc_mojo_message_helper.cc \ ipc/ipc_mojo_param_traits.cc \ ipc/ipc_platform_file_attachment_posix.cc \ - ipc/placeholder_brokerable_attachment.cc \ mojo/android/system/base_run_loop.cc \ mojo/android/system/core_impl.cc \ - mojo/edk/embedder/embedder.cc \ + mojo/android/system/watcher_impl.cc \ mojo/common/common_custom_types_struct_traits.cc \ + mojo/edk/embedder/connection_params.cc \ + mojo/edk/embedder/embedder.cc \ mojo/edk/embedder/entrypoints.cc \ mojo/edk/embedder/platform_channel_pair.cc \ mojo/edk/embedder/platform_channel_pair_posix.cc \ @@ -88,9 +93,10 @@ LOCAL_SRC_FILES := \ mojo/edk/embedder/platform_handle.cc \ mojo/edk/embedder/platform_handle_utils_posix.cc \ mojo/edk/embedder/platform_shared_buffer.cc \ + mojo/edk/embedder/pending_process_connection.cc \ mojo/edk/embedder/test_embedder.cc \ mojo/edk/system/awakable_list.cc \ - mojo/edk/system/broker_host_posix.cc \ + mojo/edk/system/broker_host.cc \ mojo/edk/system/broker_posix.cc \ mojo/edk/system/channel.cc \ mojo/edk/system/channel_posix.cc \ @@ -121,14 +127,11 @@ LOCAL_SRC_FILES := \ mojo/edk/system/waiter.cc \ mojo/edk/system/watcher.cc \ mojo/edk/system/watcher_set.cc \ - mojo/message_pump/handle_watcher.cc \ - mojo/message_pump/message_pump_mojo.cc \ - mojo/message_pump/time_helper.cc \ mojo/public/c/system/thunks.cc \ mojo/public/cpp/bindings/lib/array_internal.cc \ mojo/public/cpp/bindings/lib/associated_group.cc \ mojo/public/cpp/bindings/lib/associated_group_controller.cc \ - mojo/public/cpp/bindings/lib/bindings_internal.cc \ + mojo/public/cpp/bindings/lib/binding_state.cc \ mojo/public/cpp/bindings/lib/connector.cc \ mojo/public/cpp/bindings/lib/control_message_handler.cc \ mojo/public/cpp/bindings/lib/control_message_proxy.cc \ @@ -138,16 +141,13 @@ LOCAL_SRC_FILES := \ mojo/public/cpp/bindings/lib/message.cc \ mojo/public/cpp/bindings/lib/message_buffer.cc \ mojo/public/cpp/bindings/lib/message_builder.cc \ - mojo/public/cpp/bindings/lib/message_filter.cc \ mojo/public/cpp/bindings/lib/message_header_validator.cc \ mojo/public/cpp/bindings/lib/multiplex_router.cc \ mojo/public/cpp/bindings/lib/native_struct.cc \ mojo/public/cpp/bindings/lib/native_struct_data.cc \ mojo/public/cpp/bindings/lib/native_struct_serialization.cc \ - mojo/public/cpp/bindings/lib/no_interface.cc \ mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc \ mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc \ - mojo/public/cpp/bindings/lib/router.cc \ mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc \ mojo/public/cpp/bindings/lib/serialization_context.cc \ mojo/public/cpp/bindings/lib/sync_handle_registry.cc \ @@ -202,6 +202,7 @@ LOCAL_SRC_FILES := \ base/android/java/src/org/chromium/base/BuildInfo.java \ base/android/java/src/org/chromium/base/ContextUtils.java \ base/android/java/src/org/chromium/base/PackageUtils.java \ + base/android/java/src/org/chromium/base/Log.java \ base/android/java/src/org/chromium/base/VisibleForTesting.java \ $(call all-java-files-under, mojo/android/system/src) \ $(call all-java-files-under, mojo/public/java/system/src) \ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cd08c43 --- /dev/null +++ b/Makefile @@ -0,0 +1,181 @@ +# Copyright 2016 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +include common.mk + +PC_DEPS = libchrome-$(BASE_VER) +PC_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) +PC_LIBS := $(shell $(PKG_CONFIG) --libs $(PC_DEPS)) + +CPPFLAGS += -Wno-unused-parameter -Wno-missing-field-initializers $(PC_CFLAGS) -I$(OUT) + +CXXFLAGS += -std=c++11 \ + -Wno-sign-promo \ + -Wno-non-virtual-dtor \ + -Wno-ignored-qualifiers \ + -Wno-format-nonliteral \ + -Wno-extra \ + -Wno-deprecated-register \ + -Wno-char-subscripts \ + -DOS_POSIX \ + -DNO_TCMALLOC \ + +LDLIBS += $(PC_LIBS) + +CXX_OBJECTS := \ + base/base_paths.o \ + base/debug/proc_maps_linux.o \ + base/path_service.o \ + base/unguessable_token.o \ + ipc/ipc_message.o \ + ipc/ipc_message_attachment.o \ + ipc/ipc_message_attachment_set.o \ + ipc/ipc_message_utils.o \ + ipc/ipc_mojo_handle_attachment.o \ + ipc/ipc_mojo_message_helper.o \ + ipc/ipc_mojo_param_traits.o \ + ipc/ipc_platform_file_attachment_posix.o \ + mojo/common/common_custom_types_struct_traits.o \ + mojo/edk/embedder/connection_params.o \ + mojo/edk/embedder/embedder.o \ + mojo/edk/embedder/entrypoints.o \ + mojo/edk/embedder/platform_channel_pair.o \ + mojo/edk/embedder/platform_channel_pair_posix.o \ + mojo/edk/embedder/platform_channel_utils_posix.o \ + mojo/edk/embedder/platform_handle.o \ + mojo/edk/embedder/platform_handle_utils_posix.o \ + mojo/edk/embedder/platform_shared_buffer.o \ + mojo/edk/system/awakable_list.o \ + mojo/edk/system/broker_host.o \ + mojo/edk/system/broker_posix.o \ + mojo/edk/system/channel.o \ + mojo/edk/system/channel_posix.o \ + mojo/edk/system/configuration.o \ + mojo/edk/system/core.o \ + mojo/edk/system/data_pipe_consumer_dispatcher.o \ + mojo/edk/system/data_pipe_control_message.o \ + mojo/edk/system/data_pipe_producer_dispatcher.o \ + mojo/edk/system/dispatcher.o \ + mojo/edk/system/handle_table.o \ + mojo/edk/system/mapping_table.o \ + mojo/edk/system/message_for_transit.o \ + mojo/edk/system/message_pipe_dispatcher.o \ + mojo/edk/system/node_channel.o \ + mojo/edk/system/node_controller.o \ + mojo/edk/system/platform_handle_dispatcher.o \ + mojo/edk/system/ports/event.o \ + mojo/edk/system/ports/message.o \ + mojo/edk/system/ports/message_queue.o \ + mojo/edk/system/ports/name.o \ + mojo/edk/system/ports/node.o \ + mojo/edk/system/ports/port.o \ + mojo/edk/system/ports/port_ref.o \ + mojo/edk/system/ports_message.o \ + mojo/edk/system/request_context.o \ + mojo/edk/system/shared_buffer_dispatcher.o \ + mojo/edk/system/wait_set_dispatcher.o \ + mojo/edk/system/waiter.o \ + mojo/edk/system/watcher.o \ + mojo/edk/system/watcher_set.o \ + mojo/public/c/system/thunks.o \ + mojo/public/cpp/bindings/lib/array_internal.o \ + mojo/public/cpp/bindings/lib/associated_group.o \ + mojo/public/cpp/bindings/lib/associated_group_controller.o \ + mojo/public/cpp/bindings/lib/connector.o \ + mojo/public/cpp/bindings/lib/control_message_handler.o \ + mojo/public/cpp/bindings/lib/control_message_proxy.o \ + mojo/public/cpp/bindings/lib/filter_chain.o \ + mojo/public/cpp/bindings/lib/fixed_buffer.o \ + mojo/public/cpp/bindings/lib/interface_endpoint_client.o \ + mojo/public/cpp/bindings/lib/message.o \ + mojo/public/cpp/bindings/lib/message_buffer.o \ + mojo/public/cpp/bindings/lib/message_builder.o \ + mojo/public/cpp/bindings/lib/message_header_validator.o \ + mojo/public/cpp/bindings/lib/multiplex_router.o \ + mojo/public/cpp/bindings/lib/native_struct.o \ + mojo/public/cpp/bindings/lib/native_struct_data.o \ + mojo/public/cpp/bindings/lib/native_struct_serialization.o \ + mojo/public/cpp/bindings/lib/pipe_control_message_handler.o \ + mojo/public/cpp/bindings/lib/pipe_control_message_proxy.o \ + mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.o \ + mojo/public/cpp/bindings/lib/serialization_context.o \ + mojo/public/cpp/bindings/lib/sync_handle_registry.o \ + mojo/public/cpp/bindings/lib/sync_handle_watcher.o \ + mojo/public/cpp/bindings/lib/validation_context.o \ + mojo/public/cpp/bindings/lib/validation_errors.o \ + mojo/public/cpp/bindings/lib/validation_util.o \ + mojo/public/cpp/system/platform_handle.o \ + mojo/public/cpp/system/watcher.o + +MOJOM_FILES := ipc/ipc.mojom \ + mojo/common/file.mojom \ + mojo/common/string16.mojom \ + mojo/common/text_direction.mojom \ + mojo/common/time.mojom \ + mojo/common/unguessable_token.mojom \ + mojo/common/version.mojom \ + mojo/public/interfaces/bindings/pipe_control_messages.mojom \ + mojo/public/interfaces/bindings/interface_control_messages.mojom \ + +MOJOM_BINDINGS_GENERATOR := \ + $(S)/mojo/public/tools/bindings/mojom_bindings_generator.py + +GEN_TEMPLATES_DIR := $(S)/templates + +gen_templates: + @echo generate_mojo_templates: $(GEN_TEMPLATES_DIR) + @rm -rf $(GEN_TEMPLATES_DIR) + @mkdir -p $(GEN_TEMPLATES_DIR) + @python $(MOJOM_BINDINGS_GENERATOR) --use_bundled_pylibs precompile \ + -o $(GEN_TEMPLATES_DIR) + +templates: gen_templates + cd $(S) && \ + python $(abspath $(MOJOM_BINDINGS_GENERATOR)) \ + --use_bundled_pylibs generate \ + $(MOJOM_FILES) \ + -o $(abspath $(S)) \ + --bytecode_path $(abspath $(GEN_TEMPLATES_DIR)) \ + -g c++ + cd $(S) && \ + python $(abspath $(MOJOM_BINDINGS_GENERATOR)) \ + --use_bundled_pylibs generate \ + $(MOJOM_FILES) \ + -o $(abspath $(S)) \ + --bytecode_path $(abspath $(GEN_TEMPLATES_DIR)) \ + --generate_non_variant_code \ + -g c++ + +GENERATED_OBJECTS := $(patsubst %.mojom,%.mojom.o,$(MOJOM_FILES)) +GENERATED_SHARED_OBJECTS := $(patsubst %.mojom,%.mojom-shared.o,$(MOJOM_FILES)) + +$(eval $(call add_object_rules,$(GENERATED_OBJECTS),CXX,cc,CXXFLAGS,$(SRC)/)) +$(eval $(call add_object_rules,$(GENERATED_SHARED_OBJECTS),CXX,cc,CXXFLAGS,$(SRC)/)) +# Note: I don't know why we need the line below (it's in commmon.mk), but without it, +# .o files from CXX_OBJECTS won't be built. +$(eval $(call add_object_rules,$(CXX_OBJECTS),CXX,cc,CXXFLAGS,$(SRC)/)) + +LIB ?= lib +LIB_PIE_NAME = libmojo-$(BASE_VER).pie.a +LIB_PIC_NAME = libmojo-$(BASE_VER).pic.a + +CXX_STATIC_LIBRARY($(LIB_PIE_NAME)): $(GENERATED_SHARED_OBJECTS) $(GENERATED_OBJECTS) $(CXX_OBJECTS) +CXX_STATIC_LIBRARY($(LIB_PIC_NAME)): $(GENERATED_SHARED_OBJECTS) $(GENERATED_OBJECTS) $(CXX_OBJECTS) + +all: CXX_STATIC_LIBRARY($(LIB_PIE_NAME)) CXX_STATIC_LIBRARY($(LIB_PIC_NAME)) + +clean: CLEAN($(LIB_PIE_NAME)) CLEAN($(LIB_PIC_NAME)) + +install_lib: all + install -D -m 755 $(OUT)/$(LIB_PIE_NAME) $(DESTDIR)/usr/$(LIB)/$(LIB_PIE_NAME) + install -D -m 755 $(OUT)/$(LIB_PIC_NAME) $(DESTDIR)/usr/$(LIB)/$(LIB_PIC_NAME) + sed -e "s:@LIB@:$(LIB):g" -e "s:@BSLOT@:$(BASE_VER):g" $(S)/libmojo.pc.in > $(S)/libmojo-$(BASE_VER).pc + +install_tool: + install -d $(DESTDIR)/usr/src/libmojo-$(BASE_VER)/mojo/ + cp -r --preserve=mode $(SRC)/third_party $(DESTDIR)/usr/src/libmojo-$(BASE_VER)/ + cp -r --preserve=mode $(SRC)/mojo/public/tools/bindings/* \ + $(DESTDIR)/usr/src/libmojo-$(BASE_VER)/mojo/ + +install: install_lib install_tool diff --git a/base/android/animation_frame_time_histogram.cc b/base/android/animation_frame_time_histogram.cc index 2cf7516..c2b48d3 100644 --- a/base/android/animation_frame_time_histogram.cc +++ b/base/android/animation_frame_time_histogram.cc @@ -8,6 +8,8 @@ #include "base/metrics/histogram_macros.h" #include "jni/AnimationFrameTimeHistogram_jni.h" +using base::android::JavaParamRef; + // static void SaveHistogram(JNIEnv* env, const JavaParamRef<jobject>& jcaller, diff --git a/base/android/apk_assets.cc b/base/android/apk_assets.cc index 5319e73..19a202c 100644 --- a/base/android/apk_assets.cc +++ b/base/android/apk_assets.cc @@ -10,24 +10,20 @@ #include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "base/android/scoped_java_ref.h" +#include "base/file_descriptor_store.h" #include "jni/ApkAssets_jni.h" namespace base { namespace android { -bool RegisterApkAssets(JNIEnv* env) { - return RegisterNativesImpl(env); -} - int OpenApkAsset(const std::string& file_path, base::MemoryMappedFile::Region* region) { - // The AAssetManager API of the NDK is does not expose a method for accessing - // raw resources :(. + // The AssetManager API of the NDK does not expose a method for accessing raw + // resources :( JNIEnv* env = base::android::AttachCurrentThread(); ScopedJavaLocalRef<jlongArray> jarr = Java_ApkAssets_open( - env, - base::android::GetApplicationContext(), - base::android::ConvertUTF8ToJavaString(env, file_path).obj()); + env, base::android::GetApplicationContext(), + base::android::ConvertUTF8ToJavaString(env, file_path)); std::vector<jlong> results; base::android::JavaLongArrayToLongVector(env, jarr.obj(), &results); CHECK_EQ(3U, results.size()); @@ -37,15 +33,16 @@ int OpenApkAsset(const std::string& file_path, return fd; } -bool RegisterApkAssetWithGlobalDescriptors(base::GlobalDescriptors::Key key, - const std::string& file_path) { +bool RegisterApkAssetWithFileDescriptorStore(const std::string& key, + const base::FilePath& file_path) { base::MemoryMappedFile::Region region = base::MemoryMappedFile::Region::kWholeFile; - int asset_fd = OpenApkAsset(file_path, ®ion); - if (asset_fd != -1) { - base::GlobalDescriptors::GetInstance()->Set(key, asset_fd, region); - } - return asset_fd != -1; + int asset_fd = OpenApkAsset(file_path.value(), ®ion); + if (asset_fd == -1) + return false; + base::FileDescriptorStore::GetInstance().Set(key, base::ScopedFD(asset_fd), + region); + return true; } } // namespace android diff --git a/base/android/apk_assets.h b/base/android/apk_assets.h index 6eb5da3..cdac000 100644 --- a/base/android/apk_assets.h +++ b/base/android/apk_assets.h @@ -8,14 +8,12 @@ #include <string> #include "base/android/jni_android.h" +#include "base/files/file_path.h" #include "base/files/memory_mapped_file.h" -#include "base/posix/global_descriptors.h" namespace base { namespace android { -bool RegisterApkAssets(JNIEnv* env); - // Opens an asset (e.g. a .pak file) from the apk. // Can be used from renderer process. // Fails if the asset is not stored uncompressed within the .apk. @@ -28,11 +26,12 @@ BASE_EXPORT int OpenApkAsset( const std::string& file_path, base::MemoryMappedFile::Region* region); -// Registers an uncompressed asset from within the apk with GlobalDescriptors. +// Registers an uncompressed asset from within the apk in the +// FileDescriptorStore. // Returns: true in case of success, false otherwise. -BASE_EXPORT bool RegisterApkAssetWithGlobalDescriptors( - base::GlobalDescriptors::Key key, - const std::string& file_path); +BASE_EXPORT bool RegisterApkAssetWithFileDescriptorStore( + const std::string& key, + const base::FilePath& file_path); } // namespace android } // namespace base diff --git a/base/android/base_jni_onload.cc b/base/android/base_jni_onload.cc index 7ab4982..0a82db4 100644 --- a/base/android/base_jni_onload.cc +++ b/base/android/base_jni_onload.cc @@ -12,13 +12,11 @@ namespace base { namespace android { -namespace { - -bool RegisterJNI(JNIEnv* env) { +bool OnJNIOnLoadRegisterJNI(JNIEnv* env) { return RegisterLibraryLoaderEntryHook(env); } -bool Init() { +bool OnJNIOnLoadInit() { InitAtExitManager(); JNIEnv* env = base::android::AttachCurrentThread(); base::android::InitReplacementClassLoader(env, @@ -26,32 +24,5 @@ bool Init() { return true; } -} // namespace - - -bool OnJNIOnLoadRegisterJNI(JavaVM* vm, - std::vector<RegisterCallback> callbacks) { - base::android::InitVM(vm); - JNIEnv* env = base::android::AttachCurrentThread(); - - callbacks.push_back(base::Bind(&RegisterJNI)); - for (std::vector<RegisterCallback>::reverse_iterator i = - callbacks.rbegin(); i != callbacks.rend(); ++i) { - if (!i->Run(env)) - return false; - } - return true; -} - -bool OnJNIOnLoadInit(std::vector<InitCallback> callbacks) { - callbacks.push_back(base::Bind(&Init)); - for (std::vector<InitCallback>::reverse_iterator i = - callbacks.rbegin(); i != callbacks.rend(); ++i) { - if (!i->Run()) - return false; - } - return true; -} - } // namespace android } // namespace base diff --git a/base/android/base_jni_onload.h b/base/android/base_jni_onload.h index dcc7756..be637d5 100644 --- a/base/android/base_jni_onload.h +++ b/base/android/base_jni_onload.h @@ -14,17 +14,12 @@ namespace base { namespace android { -// Returns whether JNI registration succeeded. Caller shall put the -// RegisterCallback into |callbacks| in reverse order. +// Returns whether JNI registration succeeded. typedef base::Callback<bool(JNIEnv*)> RegisterCallback; -BASE_EXPORT bool OnJNIOnLoadRegisterJNI( - JavaVM* vm, - std::vector<RegisterCallback> callbacks); - -// Returns whether initialization succeeded. Caller shall put the -// InitCallback into |callbacks| in reverse order. -typedef base::Callback<bool(void)> InitCallback; -BASE_EXPORT bool OnJNIOnLoadInit(std::vector<InitCallback> callbacks); +BASE_EXPORT bool OnJNIOnLoadRegisterJNI(JNIEnv* env); + +// Returns whether initialization succeeded. +BASE_EXPORT bool OnJNIOnLoadInit(); } // namespace android } // namespace base diff --git a/base/android/base_jni_registrar.cc b/base/android/base_jni_registrar.cc index bc34979..972c333 100644 --- a/base/android/base_jni_registrar.cc +++ b/base/android/base_jni_registrar.cc @@ -5,30 +5,23 @@ #include "base/android/base_jni_registrar.h" #include "base/android/animation_frame_time_histogram.h" -#include "base/android/apk_assets.h" #include "base/android/application_status_listener.h" -#include "base/android/build_info.h" -#include "base/android/callback_android.h" #include "base/android/command_line_android.h" -#include "base/android/content_uri_utils.h" #include "base/android/context_utils.h" #include "base/android/cpu_features.h" -#include "base/android/event_log.h" +#include "base/android/early_trace_event_binding.h" #include "base/android/field_trial_list.h" #include "base/android/important_file_writer_android.h" +#include "base/android/java_exception_reporter.h" #include "base/android/java_handler_thread.h" -#include "base/android/java_runtime.h" #include "base/android/jni_android.h" #include "base/android/jni_registrar.h" -#include "base/android/jni_utils.h" -#include "base/android/locale_utils.h" #include "base/android/memory_pressure_listener_android.h" #include "base/android/path_service_android.h" -#include "base/android/path_utils.h" #include "base/android/record_histogram.h" #include "base/android/record_user_action.h" -#include "base/android/sys_utils.h" -#include "base/android/thread_utils.h" +#include "base/android/statistics_recorder_android.h" +#include "base/android/time_utils.h" #include "base/android/trace_event_binding.h" #include "base/macros.h" #include "base/message_loop/message_pump_android.h" @@ -41,33 +34,27 @@ namespace android { static RegistrationMethod kBaseRegisteredMethods[] = { {"AnimationFrameTimeHistogram", base::android::RegisterAnimationFrameTimeHistogram}, - {"ApkAssets", base::android::RegisterApkAssets}, {"ApplicationStatusListener", base::android::ApplicationStatusListener::RegisterBindings}, - {"BuildInfo", base::android::BuildInfo::RegisterBindings}, - {"CallbackAndroid", base::android::RegisterCallbackAndroid}, {"CommandLine", base::android::RegisterCommandLine}, - {"ContentUriUtils", base::RegisterContentUriUtils}, {"ContextUtils", base::android::RegisterContextUtils}, {"CpuFeatures", base::android::RegisterCpuFeatures}, - {"EventLog", base::android::RegisterEventLog}, + {"EarlyTraceEvent", base::android::RegisterEarlyTraceEvent}, {"FieldTrialList", base::android::RegisterFieldTrialList}, {"ImportantFileWriterAndroid", base::android::RegisterImportantFileWriterAndroid}, - {"JNIUtils", base::android::RegisterJNIUtils}, - {"LocaleUtils", base::android::RegisterLocaleUtils}, {"MemoryPressureListenerAndroid", base::android::MemoryPressureListenerAndroid::Register}, + {"JavaExceptionReporter", base::android::RegisterJavaExceptionReporterJni}, {"JavaHandlerThread", base::android::JavaHandlerThread::RegisterBindings}, {"PathService", base::android::RegisterPathService}, - {"PathUtils", base::android::RegisterPathUtils}, {"PowerMonitor", base::RegisterPowerMonitor}, {"RecordHistogram", base::android::RegisterRecordHistogram}, {"RecordUserAction", base::android::RegisterRecordUserAction}, - {"Runtime", base::android::JavaRuntime::Register}, + {"StatisticsRecorderAndroid", + base::android::RegisterStatisticsRecorderAndroid}, {"SystemMessageHandler", base::MessagePumpForUI::RegisterBindings}, - {"SysUtils", base::android::SysUtils::Register}, - {"ThreadUtils", base::RegisterThreadUtils}, + {"TimeUtils", base::android::RegisterTimeUtils}, {"TraceEvent", base::android::RegisterTraceEvent}, }; diff --git a/base/android/build_info.cc b/base/android/build_info.cc index ef8f572..80b9e0a 100644 --- a/base/android/build_info.cc +++ b/base/android/build_info.cc @@ -38,7 +38,7 @@ struct BuildInfoSingletonTraits { } static const bool kRegisterAtExit = false; -#ifndef NDEBUG +#if DCHECK_IS_ON() static const bool kAllowedToAccessOnNonjoinableThread = true; #endif }; @@ -81,10 +81,5 @@ void BuildInfo::ClearJavaExceptionInfo() { java_exception_info_ = nullptr; } -// static -bool BuildInfo::RegisterBindings(JNIEnv* env) { - return RegisterNativesImpl(env); -} - } // namespace android } // namespace base diff --git a/base/android/build_info.h b/base/android/build_info.h index 838d6f8..cce74f4 100644 --- a/base/android/build_info.h +++ b/base/android/build_info.h @@ -26,7 +26,8 @@ enum SdkVersion { SDK_VERSION_KITKAT_WEAR = 20, SDK_VERSION_LOLLIPOP = 21, SDK_VERSION_LOLLIPOP_MR1 = 22, - SDK_VERSION_MARSHMALLOW = 23 + SDK_VERSION_MARSHMALLOW = 23, + SDK_VERSION_NOUGAT = 24 }; // BuildInfo is a singleton class that stores android build and device @@ -110,8 +111,6 @@ class BASE_EXPORT BuildInfo { void ClearJavaExceptionInfo(); - static bool RegisterBindings(JNIEnv* env); - private: friend struct BuildInfoSingletonTraits; diff --git a/base/android/command_line_android.cc b/base/android/command_line_android.cc index e196aed..21ae182 100644 --- a/base/android/command_line_android.cc +++ b/base/android/command_line_android.cc @@ -12,6 +12,8 @@ using base::android::ConvertUTF8ToJavaString; using base::android::ConvertJavaStringToUTF8; +using base::android::JavaParamRef; +using base::android::ScopedJavaLocalRef; using base::CommandLine; namespace { diff --git a/base/android/content_uri_utils.cc b/base/android/content_uri_utils.cc index 31f7b4f..f7484cf 100644 --- a/base/android/content_uri_utils.cc +++ b/base/android/content_uri_utils.cc @@ -10,19 +10,16 @@ #include "jni/ContentUriUtils_jni.h" using base::android::ConvertUTF8ToJavaString; +using base::android::ScopedJavaLocalRef; namespace base { -bool RegisterContentUriUtils(JNIEnv* env) { - return RegisterNativesImpl(env); -} - bool ContentUriExists(const FilePath& content_uri) { JNIEnv* env = base::android::AttachCurrentThread(); ScopedJavaLocalRef<jstring> j_uri = ConvertUTF8ToJavaString(env, content_uri.value()); return Java_ContentUriUtils_contentUriExists( - env, base::android::GetApplicationContext(), j_uri.obj()); + env, base::android::GetApplicationContext(), j_uri); } File OpenContentUriForRead(const FilePath& content_uri) { @@ -30,7 +27,7 @@ File OpenContentUriForRead(const FilePath& content_uri) { ScopedJavaLocalRef<jstring> j_uri = ConvertUTF8ToJavaString(env, content_uri.value()); jint fd = Java_ContentUriUtils_openContentUriForRead( - env, base::android::GetApplicationContext(), j_uri.obj()); + env, base::android::GetApplicationContext(), j_uri); if (fd < 0) return File(); return File(fd); @@ -40,9 +37,8 @@ std::string GetContentUriMimeType(const FilePath& content_uri) { JNIEnv* env = base::android::AttachCurrentThread(); ScopedJavaLocalRef<jstring> j_uri = ConvertUTF8ToJavaString(env, content_uri.value()); - ScopedJavaLocalRef<jstring> j_mime = - Java_ContentUriUtils_getMimeType( - env, base::android::GetApplicationContext(), j_uri.obj()); + ScopedJavaLocalRef<jstring> j_mime = Java_ContentUriUtils_getMimeType( + env, base::android::GetApplicationContext(), j_uri); if (j_mime.is_null()) return std::string(); diff --git a/base/android/content_uri_utils.h b/base/android/content_uri_utils.h index 59fa1e6..6d817c0 100644 --- a/base/android/content_uri_utils.h +++ b/base/android/content_uri_utils.h @@ -13,8 +13,6 @@ namespace base { -bool RegisterContentUriUtils(JNIEnv* env); - // Opens a content URI for read and returns the file descriptor to the caller. // Returns -1 if the URI is invalid. BASE_EXPORT File OpenContentUriForRead(const FilePath& content_uri); diff --git a/base/android/context_utils.cc b/base/android/context_utils.cc index e9ab723..e2c4ed0 100644 --- a/base/android/context_utils.cc +++ b/base/android/context_utils.cc @@ -33,9 +33,9 @@ void SetNativeApplicationContext(JNIEnv* env, const JavaRef<jobject>& context) { } // namespace -jobject GetApplicationContext() { +const JavaRef<jobject>& GetApplicationContext() { DCHECK(!g_application_context.Get().is_null()); - return g_application_context.Get().obj(); + return g_application_context.Get(); } static void InitNativeSideApplicationContext( diff --git a/base/android/context_utils.h b/base/android/context_utils.h index f172d93..c5289f1 100644 --- a/base/android/context_utils.h +++ b/base/android/context_utils.h @@ -16,7 +16,7 @@ namespace android { // Gets a global ref to the application context set with // InitApplicationContext(). Ownership is retained by the function - the caller // must NOT release it. -BASE_EXPORT jobject GetApplicationContext(); +BASE_EXPORT const JavaRef<jobject>& GetApplicationContext(); bool RegisterContextUtils(JNIEnv* env); diff --git a/base/android/event_log.cc b/base/android/event_log.cc index a4b1dd1..3eb5926 100644 --- a/base/android/event_log.cc +++ b/base/android/event_log.cc @@ -12,9 +12,5 @@ void EventLogWriteInt(int tag, int value) { Java_EventLog_writeEvent(AttachCurrentThread(), tag, value); } -bool RegisterEventLog(JNIEnv* env) { - return RegisterNativesImpl(env); -} - } // namespace android } // namespace base diff --git a/base/android/event_log.h b/base/android/event_log.h index dad4e4c..ebd5919 100644 --- a/base/android/event_log.h +++ b/base/android/event_log.h @@ -14,8 +14,6 @@ namespace android { void BASE_EXPORT EventLogWriteInt(int tag, int value); -bool RegisterEventLog(JNIEnv* env); - } // namespace android } // namespace base diff --git a/base/android/field_trial_list.cc b/base/android/field_trial_list.cc index 9731a48..5150b81 100644 --- a/base/android/field_trial_list.cc +++ b/base/android/field_trial_list.cc @@ -12,6 +12,8 @@ using base::android::ConvertJavaStringToUTF8; using base::android::ConvertUTF8ToJavaString; +using base::android::JavaParamRef; +using base::android::ScopedJavaLocalRef; static ScopedJavaLocalRef<jstring> FindFullName( JNIEnv* env, diff --git a/base/android/fifo_utils.cc b/base/android/fifo_utils.cc deleted file mode 100644 index 8f3e95f..0000000 --- a/base/android/fifo_utils.cc +++ /dev/null @@ -1,25 +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. - -#include "base/android/fifo_utils.h" - -#include <sys/stat.h> - -#include "base/files/file_path.h" - -namespace base { -namespace android { - -bool CreateFIFO(const FilePath& path, int mode) { - // Default permissions for mkfifo() is ignored, chmod() is required. - return mkfifo(path.value().c_str(), mode) == 0 && - chmod(path.value().c_str(), mode) == 0; -} - -bool RedirectStream(FILE* stream, const FilePath& path, const char* mode) { - return freopen(path.value().c_str(), mode, stream) != NULL; -} - -} // namespace android -} // namespace base diff --git a/base/android/fifo_utils.h b/base/android/fifo_utils.h deleted file mode 100644 index 0bad8e2..0000000 --- a/base/android/fifo_utils.h +++ /dev/null @@ -1,31 +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. - -#ifndef BASE_ANDROID_FIFO_UTILS_H_ -#define BASE_ANDROID_FIFO_UTILS_H_ - -#include <stdio.h> - -#include "base/base_export.h" - -namespace base { - -class FilePath; - -namespace android { - -// Creates a fifo at the given |path| with POSIX permissions set to |mode|, -// returning true if it was successfully created and permissions were set. -BASE_EXPORT bool CreateFIFO(const FilePath& path, int mode); - -// Redirects the |stream| to the file provided by |path| with |mode| -// permissions, returning true if successful. -BASE_EXPORT bool RedirectStream(FILE* stream, - const FilePath& path, - const char* mode); - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_FIFO_UTILS_H_ diff --git a/base/android/java/src/org/chromium/base/ActivityState.java b/base/android/java/src/org/chromium/base/ActivityState.java index 98aff62..f4e6413 100644 --- a/base/android/java/src/org/chromium/base/ActivityState.java +++ b/base/android/java/src/org/chromium/base/ActivityState.java @@ -4,10 +4,20 @@ package org.chromium.base; +import android.support.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * A set of states that represent the last state change of an Activity. */ public interface ActivityState { + + @Retention(RetentionPolicy.SOURCE) + @IntDef({CREATED, STARTED, RESUMED, PAUSED, STOPPED, DESTROYED}) + public @interface ActivityStateEnum {} + /** * Represents Activity#onCreate(). */ diff --git a/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java index 9c9c16b..f773859 100644 --- a/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java +++ b/base/android/java/src/org/chromium/base/ApiCompatibilityUtils.java @@ -21,6 +21,7 @@ import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Build; import android.os.PowerManager; import android.os.Process; @@ -31,8 +32,10 @@ import android.view.View; import android.view.ViewGroup.MarginLayoutParams; import android.view.Window; import android.view.WindowManager; +import android.view.inputmethod.InputMethodSubtype; import android.widget.TextView; +import java.io.File; import java.lang.reflect.Method; /** @@ -44,6 +47,22 @@ public class ApiCompatibilityUtils { } /** + * Compares two long values numerically. The value returned is identical to what would be + * returned by {@link Long#compare(long, long)} which is available since API level 19. + */ + public static int compareLong(long lhs, long rhs) { + return lhs < rhs ? -1 : (lhs == rhs ? 0 : 1); + } + + /** + * Compares two boolean values. The value returned is identical to what would be returned by + * {@link Boolean#compare(boolean, boolean)} which is available since API level 19. + */ + public static int compareBoolean(boolean lhs, boolean rhs) { + return lhs == rhs ? 0 : lhs ? 1 : -1; + } + + /** * Returns true if view's layout direction is right-to-left. * * @param view the View whose layout is being considered @@ -394,13 +413,7 @@ public class ApiCompatibilityUtils { */ public static void setStatusBarColor(Window window, int statusBarColor) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - // If both system bars are black, we can remove these from our layout, - // removing or shrinking the SurfaceFlinger overlay required for our views. - if (statusBarColor == Color.BLACK && window.getNavigationBarColor() == Color.BLACK) { - window.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - } else { - window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - } + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(statusBarColor); } } @@ -515,7 +528,19 @@ public class ApiCompatibilityUtils { } /** - * See {@link android.os.StatFs#getBlockCount()}. + * See {@link android.os.StatFs#getAvailableBlocksLong}. + */ + @SuppressWarnings("deprecation") + public static long getAvailableBlocks(StatFs statFs) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + return statFs.getAvailableBlocksLong(); + } else { + return statFs.getAvailableBlocks(); + } + } + + /** + * See {@link android.os.StatFs#getBlockCount}. */ @SuppressWarnings("deprecation") public static long getBlockCount(StatFs statFs) { @@ -527,7 +552,7 @@ public class ApiCompatibilityUtils { } /** - * See {@link android.os.StatFs#getBlockSize()}. + * See {@link android.os.StatFs#getBlockSize}. */ @SuppressWarnings("deprecation") public static long getBlockSize(StatFs statFs) { @@ -559,4 +584,68 @@ public class ApiCompatibilityUtils { return false; } + + /** + * @see Context#checkPermission(String, int, int) + */ + public static int checkPermission(Context context, String permission, int pid, int uid) { + try { + return context.checkPermission(permission, pid, uid); + } catch (RuntimeException e) { + // Some older versions of Android throw odd errors when checking for permissions, so + // just swallow the exception and treat it as the permission is denied. + // crbug.com/639099 + return PackageManager.PERMISSION_DENIED; + } + } + + /** + * @see android.view.inputmethod.InputMethodSubType#getLocate() + */ + @SuppressWarnings("deprecation") + public static String getLocale(InputMethodSubtype inputMethodSubType) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return inputMethodSubType.getLanguageTag(); + } else { + return inputMethodSubType.getLocale(); + } + } + + /** + * Get a URI for |file| which has the image capture. This function assumes that path of |file| + * is based on the result of UiUtils.getDirectoryForImageCapture(). + * + * @param context The application context. + * @param file image capture file. + * @return URI for |file|. + */ + public static Uri getUriForImageCaptureFile(Context context, File file) { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 + ? ContentUriUtils.getContentUriFromFile(context, file) + : Uri.fromFile(file); + } + + /** + * @see android.view.Window#FEATURE_INDETERMINATE_PROGRESS + */ + public static void setWindowIndeterminateProgress(Window window) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + @SuppressWarnings("deprecation") + int featureNumber = Window.FEATURE_INDETERMINATE_PROGRESS; + + @SuppressWarnings("deprecation") + int featureValue = Window.PROGRESS_VISIBILITY_OFF; + + window.setFeatureInt(featureNumber, featureValue); + } + } + + /** + * Null-safe equivalent of {@code a.equals(b)}. + * + * @see Objects#equals(Object, Object) + */ + public static boolean objectEquals(Object a, Object b) { + return (a == null) ? (b == null) : a.equals(b); + } } diff --git a/base/android/java/src/org/chromium/base/ApplicationStatus.java b/base/android/java/src/org/chromium/base/ApplicationStatus.java index 37af12d..8d82ed7 100644 --- a/base/android/java/src/org/chromium/base/ApplicationStatus.java +++ b/base/android/java/src/org/chromium/base/ApplicationStatus.java @@ -4,11 +4,14 @@ package org.chromium.base; +import android.annotation.SuppressLint; import android.app.Activity; import android.app.Application; import android.app.Application.ActivityLifecycleCallbacks; import android.os.Bundle; +import org.chromium.base.ActivityState.ActivityStateEnum; +import org.chromium.base.ApplicationState.ApplicationStateEnum; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.MainDex; @@ -34,6 +37,7 @@ public class ApplicationStatus { /** * @return The current {@link ActivityState} of the activity. */ + @ActivityStateEnum public int getStatus() { return mStatus; } @@ -41,7 +45,7 @@ public class ApplicationStatus { /** * @param status The new {@link ActivityState} of the activity. */ - public void setStatus(int status) { + public void setStatus(@ActivityStateEnum int status) { mStatus = status; } @@ -54,6 +58,7 @@ public class ApplicationStatus { } private static Object sCachedApplicationStateLock = new Object(); + @ApplicationStateEnum private static Integer sCachedApplicationState; /** Last activity that was shown (or null if none or it was destroyed). */ @@ -89,7 +94,7 @@ public class ApplicationStatus { * Called when the application's state changes. * @param newState The application state. */ - public void onApplicationStateChange(int newState); + public void onApplicationStateChange(@ApplicationStateEnum int newState); } /** @@ -101,7 +106,7 @@ public class ApplicationStatus { * @param activity The activity that had a state change. * @param newState New activity state. */ - public void onActivityStateChange(Activity activity, int newState); + public void onActivityStateChange(Activity activity, @ActivityStateEnum int newState); } private ApplicationStatus() {} @@ -170,7 +175,7 @@ public class ApplicationStatus { * @param activity Current activity. * @param newState New state value. */ - private static void onStateChange(Activity activity, int newState) { + private static void onStateChange(Activity activity, @ActivityStateEnum int newState) { if (activity == null) throw new IllegalArgumentException("null activity is not supported"); if (sActivity == null @@ -183,7 +188,13 @@ public class ApplicationStatus { int oldApplicationState = getStateForApplication(); if (newState == ActivityState.CREATED) { - assert !sActivityInfo.containsKey(activity); + // TODO(tedchoc): crbug/691100. The timing of application callback lifecycles were + // changed in O and the activity info may have been lazily created + // on first access to avoid a crash on startup. This should be removed + // once the new lifecycle APIs are available. + if (!BuildInfo.isAtLeastO()) { + assert !sActivityInfo.containsKey(activity); + } sActivityInfo.put(activity, new ActivityInfo()); } @@ -195,6 +206,13 @@ public class ApplicationStatus { ActivityInfo info = sActivityInfo.get(activity); info.setStatus(newState); + // Remove before calling listeners so that isEveryActivityDestroyed() returns false when + // this was the last activity. + if (newState == ActivityState.DESTROYED) { + sActivityInfo.remove(activity); + if (activity == sActivity) sActivity = null; + } + // Notify all state observers that are specifically listening to this activity. for (ActivityStateListener listener : info.getListeners()) { listener.onActivityStateChange(activity, newState); @@ -212,11 +230,6 @@ public class ApplicationStatus { listener.onApplicationStateChange(applicationState); } } - - if (newState == ActivityState.DESTROYED) { - sActivityInfo.remove(activity); - if (activity == sActivity) sActivity = null; - } } /** @@ -289,6 +302,7 @@ public class ApplicationStatus { * @param activity The activity whose state is to be returned. * @return The state of the specified activity (see {@link ActivityState}). */ + @ActivityStateEnum public static int getStateForActivity(Activity activity) { ActivityInfo info = sActivityInfo.get(activity); return info != null ? info.getStatus() : ActivityState.DESTROYED; @@ -297,6 +311,7 @@ public class ApplicationStatus { /** * @return The state of the application (see {@link ApplicationState}). */ + @ApplicationStateEnum @CalledByNative public static int getStateForApplication() { synchronized (sCachedApplicationStateLock) { @@ -343,11 +358,19 @@ public class ApplicationStatus { * @param listener Listener to receive state changes. * @param activity Activity to track or {@code null} to track all activities. */ + @SuppressLint("NewApi") public static void registerStateListenerForActivity(ActivityStateListener listener, Activity activity) { assert activity != null; ActivityInfo info = sActivityInfo.get(activity); + // TODO(tedchoc): crbug/691100. The timing of application callback lifecycles were changed + // in O and the activity info may need to be lazily created if the onCreate + // event has not yet been received. + if (BuildInfo.isAtLeastO() && info == null && !activity.isDestroyed()) { + info = new ActivityInfo(); + sActivityInfo.put(activity, info); + } assert info != null && info.getStatus() != ActivityState.DESTROYED; info.getListeners().addObserver(listener); } @@ -430,6 +453,7 @@ public class ApplicationStatus { * HAS_STOPPED_ACTIVITIES if none are running/paused and one is stopped. * HAS_DESTROYED_ACTIVITIES if none are running/paused/stopped. */ + @ApplicationStateEnum private static int determineApplicationState() { boolean hasPausedActivity = false; boolean hasStoppedActivity = false; @@ -454,5 +478,5 @@ public class ApplicationStatus { // Called to notify the native side of state changes. // IMPORTANT: This is always called on the main thread! - private static native void nativeOnApplicationStateChange(int newState); + private static native void nativeOnApplicationStateChange(@ApplicationStateEnum int newState); } diff --git a/base/android/java/src/org/chromium/base/BaseChromiumApplication.java b/base/android/java/src/org/chromium/base/BaseChromiumApplication.java index 44c69dc..6b97bdd 100644 --- a/base/android/java/src/org/chromium/base/BaseChromiumApplication.java +++ b/base/android/java/src/org/chromium/base/BaseChromiumApplication.java @@ -10,6 +10,7 @@ import android.content.Context; import android.os.Bundle; import android.view.Window; +import org.chromium.base.annotations.SuppressFBWarnings; import org.chromium.base.multidex.ChromiumMultiDexInstaller; import java.lang.reflect.InvocationHandler; @@ -21,8 +22,7 @@ import java.lang.reflect.Proxy; * Basic application functionality that should be shared among all browser applications. */ public class BaseChromiumApplication extends Application { - - private static final String TAG = "cr.base"; + private static final String TAG = "base"; private static final String TOOLBAR_CALLBACK_INTERNAL_WRAPPER_CLASS = "android.support.v7.internal.app.ToolbarActionBar$ToolbarCallbackWrapper"; // In builds using the --use_unpublished_apis flag, the ToolbarActionBar class name does not @@ -42,6 +42,8 @@ public class BaseChromiumApplication extends Application { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); + assert getBaseContext() != null; + checkAppBeingReplaced(); ChromiumMultiDexInstaller.install(this); } @@ -58,7 +60,7 @@ public class BaseChromiumApplication extends Application { } private ObserverList<WindowFocusChangedListener> mWindowFocusListeners = - new ObserverList<WindowFocusChangedListener>(); + new ObserverList<>(); /** * Intercepts calls to an existing Window.Callback. Most invocations are passed on directly @@ -144,6 +146,18 @@ public class BaseChromiumApplication extends Application { ((BaseChromiumApplication) context.getApplicationContext()).initCommandLine(); } + /** Ensure this application object is not out-of-date. */ + @SuppressFBWarnings("DM_EXIT") + private void checkAppBeingReplaced() { + // During app update the old apk can still be triggered by broadcasts and spin up an + // out-of-date application. Kill old applications in this bad state. See + // http://crbug.com/658130 for more context and http://b.android.com/56296 for the bug. + if (getResources() == null) { + Log.e(TAG, "getResources() null, closing app."); + System.exit(0); + } + } + /** Register hooks and listeners to start tracking the application status. */ private void startTrackingApplicationStatus() { ApplicationStatus.initialize(this); @@ -158,7 +172,7 @@ public class BaseChromiumApplication extends Application { @Override public void onActivityDestroyed(Activity activity) { - if (BuildConfig.IS_DEBUG) { + if (BuildConfig.DCHECK_IS_ON) { assert (Proxy.isProxyClass(activity.getWindow().getCallback().getClass()) || activity.getWindow().getCallback().getClass().getName().equals( TOOLBAR_CALLBACK_WRAPPER_CLASS) @@ -169,7 +183,7 @@ public class BaseChromiumApplication extends Application { @Override public void onActivityPaused(Activity activity) { - if (BuildConfig.IS_DEBUG) { + if (BuildConfig.DCHECK_IS_ON) { assert (Proxy.isProxyClass(activity.getWindow().getCallback().getClass()) || activity.getWindow().getCallback().getClass().getName().equals( TOOLBAR_CALLBACK_WRAPPER_CLASS) @@ -180,7 +194,7 @@ public class BaseChromiumApplication extends Application { @Override public void onActivityResumed(Activity activity) { - if (BuildConfig.IS_DEBUG) { + if (BuildConfig.DCHECK_IS_ON) { assert (Proxy.isProxyClass(activity.getWindow().getCallback().getClass()) || activity.getWindow().getCallback().getClass().getName().equals( TOOLBAR_CALLBACK_WRAPPER_CLASS) @@ -191,7 +205,7 @@ public class BaseChromiumApplication extends Application { @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { - if (BuildConfig.IS_DEBUG) { + if (BuildConfig.DCHECK_IS_ON) { assert (Proxy.isProxyClass(activity.getWindow().getCallback().getClass()) || activity.getWindow().getCallback().getClass().getName().equals( TOOLBAR_CALLBACK_WRAPPER_CLASS) @@ -202,7 +216,7 @@ public class BaseChromiumApplication extends Application { @Override public void onActivityStarted(Activity activity) { - if (BuildConfig.IS_DEBUG) { + if (BuildConfig.DCHECK_IS_ON) { assert (Proxy.isProxyClass(activity.getWindow().getCallback().getClass()) || activity.getWindow().getCallback().getClass().getName().equals( TOOLBAR_CALLBACK_WRAPPER_CLASS) @@ -213,7 +227,7 @@ public class BaseChromiumApplication extends Application { @Override public void onActivityStopped(Activity activity) { - if (BuildConfig.IS_DEBUG) { + if (BuildConfig.DCHECK_IS_ON) { assert (Proxy.isProxyClass(activity.getWindow().getCallback().getClass()) || activity.getWindow().getCallback().getClass().getName().equals( TOOLBAR_CALLBACK_WRAPPER_CLASS) diff --git a/base/android/java/src/org/chromium/base/BuildInfo.java b/base/android/java/src/org/chromium/base/BuildInfo.java index ea6a019..141b62b 100644 --- a/base/android/java/src/org/chromium/base/BuildInfo.java +++ b/base/android/java/src/org/chromium/base/BuildInfo.java @@ -11,7 +11,6 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Build; import android.os.StrictMode; -import android.util.Log; import org.chromium.base.annotations.CalledByNative; @@ -72,7 +71,7 @@ public class BuildInfo { PackageInfo packageInfo = packageManager.getPackageInfo("com.google.android.gms", 0); msg = Integer.toString(packageInfo.versionCode); } catch (NameNotFoundException e) { - Log.d(TAG, "GMS package is not found: %s", e); + Log.d(TAG, "GMS package is not found.", e); } return msg; } @@ -137,6 +136,13 @@ public class BuildInfo { return Build.TYPE; } + /** + * Check if this is a debuggable build of Android. Use this to enable developer-only features. + */ + public static boolean isDebugAndroid() { + return "eng".equals(Build.TYPE) || "userdebug".equals(Build.TYPE); + } + @CalledByNative public static int getSdkInt() { return Build.VERSION.SDK_INT; @@ -148,4 +154,21 @@ public class BuildInfo { public static boolean isGreaterThanN() { return Build.VERSION.SDK_INT > 24 || Build.VERSION.CODENAME.equals("NMR1"); } + + /** + * @return Whether the current device is running Android O release or newer. + */ + public static boolean isAtLeastO() { + return !"REL".equals(Build.VERSION.CODENAME) + && ("O".equals(Build.VERSION.CODENAME) || Build.VERSION.CODENAME.startsWith("OMR")); + } + + /** + * @return Whether the current app targets the SDK for at least O + */ + public static boolean targetsAtLeastO(Context appContext) { + return isAtLeastO() + && appContext.getApplicationInfo().targetSdkVersion + == Build.VERSION_CODES.CUR_DEVELOPMENT; + } } diff --git a/base/android/java/src/org/chromium/base/CommandLine.java b/base/android/java/src/org/chromium/base/CommandLine.java index efef22a..b6246f3 100644 --- a/base/android/java/src/org/chromium/base/CommandLine.java +++ b/base/android/java/src/org/chromium/base/CommandLine.java @@ -134,8 +134,8 @@ public abstract class CommandLine { * @param file The fully qualified command line file. */ public static void initFromFile(String file) { - // Arbitrary clamp of 8k on the amount of file we read in. - char[] buffer = readUtf8FileFully(file, 8 * 1024); + // Arbitrary clamp of 16k on the amount of file we read in. + char[] buffer = readUtf8FileFullyCrashIfTooBig(file, 16 * 1024); init(buffer == null ? null : tokenizeQuotedAruments(buffer)); } @@ -238,10 +238,10 @@ public abstract class CommandLine { /** * @param fileName the file to read in. * @param sizeLimit cap on the file size. - * @return Array of chars read from the file, or null if the file cannot be read - * or if its length exceeds |sizeLimit|. + * @return Array of chars read from the file, or null if the file cannot be read. + * @throws RuntimeException if the file size exceeds |sizeLimit|. */ - private static char[] readUtf8FileFully(String fileName, int sizeLimit) { + private static char[] readUtf8FileFullyCrashIfTooBig(String fileName, int sizeLimit) { Reader reader = null; File f = new File(fileName); long fileLength = f.length(); @@ -251,9 +251,8 @@ public abstract class CommandLine { } if (fileLength > sizeLimit) { - Log.w(TAG, "File " + fileName + " length " + fileLength + " exceeds limit " - + sizeLimit); - return null; + throw new RuntimeException( + "File " + fileName + " length " + fileLength + " exceeds limit " + sizeLimit); } try { diff --git a/base/android/java/src/org/chromium/base/CommandLineInitUtil.java b/base/android/java/src/org/chromium/base/CommandLineInitUtil.java index 6aa227c..bec9b40 100644 --- a/base/android/java/src/org/chromium/base/CommandLineInitUtil.java +++ b/base/android/java/src/org/chromium/base/CommandLineInitUtil.java @@ -51,7 +51,9 @@ public final class CommandLineInitUtil { public static void initCommandLine(Context context, String fileName) { if (!CommandLine.isInitialized()) { File commandLineFile = getAlternativeCommandLinePath(context, fileName); - if (commandLineFile == null) { + if (commandLineFile != null) { + Log.i(TAG, "Using alternative command line file in " + commandLineFile.getPath()); + } else { commandLineFile = new File(COMMAND_LINE_FILE_PATH, fileName); } CommandLine.initFromFile(commandLineFile.getPath()); @@ -59,7 +61,9 @@ public final class CommandLineInitUtil { } /** - * Use an alternative path if adb is enabled and this is the debug app. + * Use an alternative path if: + * - The current build is "eng" or "userdebug", OR + * - adb is enabled and this is the debug app. */ @SuppressFBWarnings("DMI_HARDCODED_ABSOLUTE_FILENAME") private static File getAlternativeCommandLinePath(Context context, String fileName) { @@ -67,13 +71,15 @@ public final class CommandLineInitUtil { new File(COMMAND_LINE_FILE_PATH_DEBUG_APP, fileName); if (!alternativeCommandLineFile.exists()) return null; try { + if (BuildInfo.isDebugAndroid()) { + return alternativeCommandLineFile; + } + String debugApp = Build.VERSION.SDK_INT < 17 ? getDebugAppPreJBMR1(context) : getDebugAppJBMR1(context); if (debugApp != null && debugApp.equals(context.getApplicationContext().getPackageName())) { - Log.i(TAG, "Using alternative command line file in " - + alternativeCommandLineFile.getPath()); return alternativeCommandLineFile; } } catch (RuntimeException e) { diff --git a/base/android/java/src/org/chromium/base/ContentUriUtils.java b/base/android/java/src/org/chromium/base/ContentUriUtils.java index 5448aa0..9253f28 100644 --- a/base/android/java/src/org/chromium/base/ContentUriUtils.java +++ b/base/android/java/src/org/chromium/base/ContentUriUtils.java @@ -6,15 +6,20 @@ package org.chromium.base; import android.content.ContentResolver; import android.content.Context; +import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.net.Uri; +import android.os.Build; import android.os.ParcelFileDescriptor; +import android.provider.DocumentsContract; import android.util.Log; +import android.webkit.MimeTypeMap; import org.chromium.base.annotations.CalledByNative; import java.io.File; import java.io.FileNotFoundException; +import java.io.IOException; /** * This class provides methods to access content URI schemes. @@ -67,9 +72,9 @@ public abstract class ContentUriUtils { */ @CalledByNative public static int openContentUriForRead(Context context, String uriString) { - ParcelFileDescriptor pfd = getParcelFileDescriptor(context, uriString); - if (pfd != null) { - return pfd.detachFd(); + AssetFileDescriptor afd = getAssetFileDescriptor(context, uriString); + if (afd != null) { + return afd.getParcelFileDescriptor().detachFd(); } return -1; } @@ -83,7 +88,21 @@ public abstract class ContentUriUtils { */ @CalledByNative public static boolean contentUriExists(Context context, String uriString) { - return getParcelFileDescriptor(context, uriString) != null; + AssetFileDescriptor asf = null; + try { + asf = getAssetFileDescriptor(context, uriString); + return asf != null; + } finally { + // Do not use StreamUtil.closeQuietly here, as AssetFileDescriptor + // does not implement Closeable until KitKat. + if (asf != null) { + try { + asf.close(); + } catch (IOException e) { + // Closing quietly. + } + } + } } /** @@ -96,8 +115,11 @@ public abstract class ContentUriUtils { @CalledByNative public static String getMimeType(Context context, String uriString) { ContentResolver resolver = context.getContentResolver(); - if (resolver == null) return null; Uri uri = Uri.parse(uriString); + if (isVirtualDocument(uri, context)) { + String[] streamTypes = resolver.getStreamTypes(uri, "*/*"); + return (streamTypes != null && streamTypes.length > 0) ? streamTypes[0] : null; + } return resolver.getType(uri); } @@ -106,15 +128,36 @@ public abstract class ContentUriUtils { * * @param context {@link Context} in interest. * @param uriString the content URI to open. - * @return ParcelFileDescriptor of the content URI, or NULL if the file does not exist. + * @return AssetFileDescriptor of the content URI, or NULL if the file does not exist. */ - private static ParcelFileDescriptor getParcelFileDescriptor(Context context, String uriString) { + private static AssetFileDescriptor getAssetFileDescriptor(Context context, String uriString) { ContentResolver resolver = context.getContentResolver(); Uri uri = Uri.parse(uriString); - ParcelFileDescriptor pfd = null; try { - pfd = resolver.openFileDescriptor(uri, "r"); + if (isVirtualDocument(uri, context)) { + String[] streamTypes = resolver.getStreamTypes(uri, "*/*"); + if (streamTypes != null && streamTypes.length > 0) { + AssetFileDescriptor afd = + resolver.openTypedAssetFileDescriptor(uri, streamTypes[0], null); + if (afd.getStartOffset() != 0) { + // Do not use StreamUtil.closeQuietly here, as AssetFileDescriptor + // does not implement Closeable until KitKat. + try { + afd.close(); + } catch (IOException e) { + // Closing quietly. + } + throw new SecurityException("Cannot open files with non-zero offset type."); + } + return afd; + } + } else { + ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r"); + if (pfd != null) { + return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH); + } + } } catch (FileNotFoundException e) { Log.w(TAG, "Cannot find content uri: " + uriString, e); } catch (SecurityException e) { @@ -124,37 +167,103 @@ public abstract class ContentUriUtils { } catch (IllegalStateException e) { Log.w(TAG, "Unknown content uri: " + uriString, e); } - return pfd; + + return null; } /** * Method to resolve the display name of a content URI. * * @param uri the content URI to be resolved. - * @param contentResolver the content resolver to query. + * @param context {@link Context} in interest. * @param columnField the column field to query. * @return the display name of the @code uri if present in the database * or an empty string otherwise. */ - public static String getDisplayName( - Uri uri, ContentResolver contentResolver, String columnField) { - if (contentResolver == null || uri == null) return ""; + public static String getDisplayName(Uri uri, Context context, String columnField) { + if (uri == null) return ""; + ContentResolver contentResolver = context.getContentResolver(); Cursor cursor = null; try { cursor = contentResolver.query(uri, null, null, null, null); if (cursor != null && cursor.getCount() >= 1) { cursor.moveToFirst(); - int index = cursor.getColumnIndex(columnField); - if (index > -1) return cursor.getString(index); + int displayNameIndex = cursor.getColumnIndex(columnField); + if (displayNameIndex == -1) { + return ""; + } + String displayName = cursor.getString(displayNameIndex); + // For Virtual documents, try to modify the file extension so it's compatible + // with the alternative MIME type. + if (hasVirtualFlag(cursor)) { + String[] mimeTypes = contentResolver.getStreamTypes(uri, "*/*"); + if (mimeTypes != null && mimeTypes.length > 0) { + String ext = + MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeTypes[0]); + if (ext != null) { + // Just append, it's simpler and more secure than altering an + // existing extension. + displayName += "." + ext; + } + } + } + return displayName; } } catch (NullPointerException e) { // Some android models don't handle the provider call correctly. // see crbug.com/345393 return ""; } finally { - if (cursor != null) cursor.close(); + StreamUtil.closeQuietly(cursor); } return ""; } + + /** + * Checks whether the passed Uri represents a virtual document. + * + * @param uri the content URI to be resolved. + * @param contentResolver the content resolver to query. + * @return True for virtual file, false for any other file. + */ + private static boolean isVirtualDocument(Uri uri, Context context) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return false; + if (uri == null) return false; + if (!DocumentsContract.isDocumentUri(context, uri)) return false; + ContentResolver contentResolver = context.getContentResolver(); + Cursor cursor = null; + try { + cursor = contentResolver.query(uri, null, null, null, null); + + if (cursor != null && cursor.getCount() >= 1) { + cursor.moveToFirst(); + return hasVirtualFlag(cursor); + } + } catch (NullPointerException e) { + // Some android models don't handle the provider call correctly. + // see crbug.com/345393 + return false; + } finally { + StreamUtil.closeQuietly(cursor); + } + return false; + } + + /** + * Checks whether the passed cursor for a document has a virtual document flag. + * + * The called must close the passed cursor. + * + * @param cursor Cursor with COLUMN_FLAGS. + * @return True for virtual file, false for any other file. + */ + private static boolean hasVirtualFlag(Cursor cursor) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return false; + int index = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAGS); + if (index > -1) { + return (cursor.getLong(index) & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0; + } + return false; + } } diff --git a/base/android/java/src/org/chromium/base/ContextUtils.java b/base/android/java/src/org/chromium/base/ContextUtils.java index 4b615cb..448eff9 100644 --- a/base/android/java/src/org/chromium/base/ContextUtils.java +++ b/base/android/java/src/org/chromium/base/ContextUtils.java @@ -9,11 +9,13 @@ import android.content.SharedPreferences; import android.preference.PreferenceManager; import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.MainDex; /** * This class provides Android application context related utility methods. */ @JNINamespace("base::android") +@MainDex public class ContextUtils { private static final String TAG = "ContextUtils"; private static Context sApplicationContext; diff --git a/base/android/java/src/org/chromium/base/FileUtils.java b/base/android/java/src/org/chromium/base/FileUtils.java index 2c0d8f5..2ad70dd 100644 --- a/base/android/java/src/org/chromium/base/FileUtils.java +++ b/base/android/java/src/org/chromium/base/FileUtils.java @@ -12,6 +12,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.List; /** * Helper methods for dealing with Files. @@ -37,6 +38,18 @@ public class FileUtils { } /** + * Delete the given files or directories by calling {@link #recursivelyDeleteFile(File)}. + * @param files The files to delete. + */ + public static void batchDeleteFiles(List<File> files) { + assert !ThreadUtils.runningOnUiThread(); + + for (File file : files) { + if (file.exists()) recursivelyDeleteFile(file); + } + } + + /** * Extracts an asset from the app's APK to a file. * @param context * @param assetName Name of the asset to extract. diff --git a/base/android/java/src/org/chromium/base/JNIUtils.java b/base/android/java/src/org/chromium/base/JNIUtils.java index f971b5e..3fcec91 100644 --- a/base/android/java/src/org/chromium/base/JNIUtils.java +++ b/base/android/java/src/org/chromium/base/JNIUtils.java @@ -12,6 +12,8 @@ import org.chromium.base.annotations.MainDex; */ @MainDex public class JNIUtils { + private static Boolean sSelectiveJniRegistrationEnabled; + /** * This returns a ClassLoader that is capable of loading Chromium Java code. Such a ClassLoader * is needed for the few cases where the JNI mechanism is unable to automatically determine the @@ -21,4 +23,24 @@ public class JNIUtils { public static Object getClassLoader() { return JNIUtils.class.getClassLoader(); } + + /** + * @return whether or not the current process supports selective JNI registration. + */ + @CalledByNative + public static boolean isSelectiveJniRegistrationEnabled() { + if (sSelectiveJniRegistrationEnabled == null) { + sSelectiveJniRegistrationEnabled = false; + } + return sSelectiveJniRegistrationEnabled; + } + + /** + * Allow this process to selectively perform JNI registration. This must be called before + * loading native libraries or it will have no effect. + */ + public static void enableSelectiveJniRegistration() { + assert sSelectiveJniRegistrationEnabled == null; + sSelectiveJniRegistrationEnabled = true; + } } diff --git a/base/android/java/src/org/chromium/base/LocaleUtils.java b/base/android/java/src/org/chromium/base/LocaleUtils.java index 5c26e7a..2f51455 100644 --- a/base/android/java/src/org/chromium/base/LocaleUtils.java +++ b/base/android/java/src/org/chromium/base/LocaleUtils.java @@ -4,9 +4,18 @@ package org.chromium.base; +import android.annotation.TargetApi; +import android.os.Build; +import android.os.LocaleList; +import android.text.TextUtils; + import org.chromium.base.annotations.CalledByNative; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.Locale; +import java.util.Map; /** * This class provides the locale related methods. @@ -18,41 +27,176 @@ public class LocaleUtils { private LocaleUtils() { } + private static final Map<String, String> LANGUAGE_MAP_FOR_CHROMIUM; + private static final Map<String, String> LANGUAGE_MAP_FOR_ANDROID; + + static { + // A variation of this mapping also exists in: + // build/android/gyp/package_resources.py + HashMap<String, String> mapForChromium = new HashMap<>(); + mapForChromium.put("iw", "he"); // Hebrew + mapForChromium.put("ji", "yi"); // Yiddish + mapForChromium.put("in", "id"); // Indonesian + mapForChromium.put("tl", "fil"); // Filipino + LANGUAGE_MAP_FOR_CHROMIUM = Collections.unmodifiableMap(mapForChromium); + } + + static { + HashMap<String, String> mapForAndroid = new HashMap<>(); + mapForAndroid.put("und", ""); // Undefined + mapForAndroid.put("fil", "tl"); // Filipino + LANGUAGE_MAP_FOR_ANDROID = Collections.unmodifiableMap(mapForAndroid); + } + /** - * @return the string for the given locale, translating - * Android deprecated language codes into the modern ones - * used by Chromium. + * Java keeps deprecated language codes for Hebrew, Yiddish and Indonesian but Chromium uses + * updated ones. Similarly, Android uses "tl" while Chromium uses "fil" for Tagalog/Filipino. + * So apply a mapping here. + * See http://developer.android.com/reference/java/util/Locale.html + * @return a updated language code for Chromium with given language string. */ - public static String getLocale(Locale locale) { - String language = locale.getLanguage(); - String country = locale.getCountry(); + public static String getUpdatedLanguageForChromium(String language) { + String updatedLanguageCode = LANGUAGE_MAP_FOR_CHROMIUM.get(language); + return updatedLanguageCode == null ? language : updatedLanguageCode; + } + + /** + * @return a locale with updated language codes for Chromium, with translated modern language + * codes used by Chromium. + */ + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @VisibleForTesting + public static Locale getUpdatedLocaleForChromium(Locale locale) { + String languageForChrome = LANGUAGE_MAP_FOR_CHROMIUM.get(locale.getLanguage()); + if (languageForChrome == null) { + return locale; + } + return new Locale.Builder().setLocale(locale).setLanguage(languageForChrome).build(); + } + + /** + * Android uses "tl" while Chromium uses "fil" for Tagalog/Filipino. + * So apply a mapping here. + * See http://developer.android.com/reference/java/util/Locale.html + * @return a updated language code for Android with given language string. + */ + public static String getUpdatedLanguageForAndroid(String language) { + String updatedLanguageCode = LANGUAGE_MAP_FOR_ANDROID.get(language); + return updatedLanguageCode == null ? language : updatedLanguageCode; + } + + /** + * @return a locale with updated language codes for Android, from translated modern language + * codes used by Chromium. + */ + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @VisibleForTesting + public static Locale getUpdatedLocaleForAndroid(Locale locale) { + String languageForAndroid = LANGUAGE_MAP_FOR_ANDROID.get(locale.getLanguage()); + if (languageForAndroid == null) { + return locale; + } + return new Locale.Builder().setLocale(locale).setLanguage(languageForAndroid).build(); + } + + /** + * This function creates a Locale object from xx-XX style string where xx is language code + * and XX is a country code. This works for API level lower than 21. + * @return the locale that best represents the language tag. + */ + public static Locale forLanguageTagCompat(String languageTag) { + String[] tag = languageTag.split("-"); + if (tag.length == 0) { + return new Locale(""); + } + String language = getUpdatedLanguageForAndroid(tag[0]); + if ((language.length() != 2 && language.length() != 3) || language.equals("und")) { + return new Locale(""); + } + if (tag.length == 1) { + return new Locale(language); + } + String country = tag[1]; + if (country.length() != 2 && country.length() != 3) { + return new Locale(language); + } + return new Locale(language, country); + } - // Android uses deprecated lanuages codes for Hebrew and Indonesian but Chromium uses the - // updated codes. Also, Android uses "tl" while Chromium uses "fil" for Tagalog/Filipino. - // So apply a mapping. - // See http://developer.android.com/reference/java/util/Locale.html - if ("iw".equals(language)) { - language = "he"; - } else if ("in".equals(language)) { - language = "id"; - } else if ("tl".equals(language)) { - language = "fil"; + /** + * This function creates a Locale object from xx-XX style string where xx is language code + * and XX is a country code. + * @return the locale that best represents the language tag. + */ + public static Locale forLanguageTag(String languageTag) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Locale locale = Locale.forLanguageTag(languageTag); + return getUpdatedLocaleForAndroid(locale); + } + return forLanguageTagCompat(languageTag); + } + + /** + * Converts Locale object to the BCP 47 compliant string format. + * This works for API level lower than 24. + * + * Note that for Android M or before, we cannot use Locale.getLanguage() and + * Locale.toLanguageTag() for this purpose. Since Locale.getLanguage() returns deprecated + * language code even if the Locale object is constructed with updated language code. As for + * Locale.toLanguageTag(), it does a special conversion from deprecated language code to updated + * one, but it is only usable for Android N or after. + * @return a well-formed IETF BCP 47 language tag with language and country code that + * represents this locale. + */ + public static String toLanguageTag(Locale locale) { + String language = getUpdatedLanguageForChromium(locale.getLanguage()); + String country = locale.getCountry(); + if (language.equals("no") && country.equals("NO") && locale.getVariant().equals("NY")) { + return "nn-NO"; } return country.isEmpty() ? language : language + "-" + country; } /** - * @return the default locale, translating Android deprecated - * language codes into the modern ones used by Chromium. + * Converts LocaleList object to the comma separated BCP 47 compliant string format. + * + * @return a well-formed IETF BCP 47 language tag with language and country code that + * represents this locale list. + */ + @TargetApi(Build.VERSION_CODES.N) + public static String toLanguageTags(LocaleList localeList) { + ArrayList<String> newLocaleList = new ArrayList<>(); + for (int i = 0; i < localeList.size(); i++) { + Locale locale = getUpdatedLocaleForChromium(localeList.get(i)); + newLocaleList.add(toLanguageTag(locale)); + } + return TextUtils.join(",", newLocaleList); + } + + /** + * @return a comma separated language tags string that represents a default locale. + * Each language tag is well-formed IETF BCP 47 language tag with language and country + * code. */ @CalledByNative - public static String getDefaultLocale() { - return getLocale(Locale.getDefault()); + public static String getDefaultLocaleString() { + return toLanguageTag(Locale.getDefault()); + } + + /** + * @return a comma separated language tags string that represents a default locale or locales. + * Each language tag is well-formed IETF BCP 47 language tag with language and country + * code. + */ + public static String getDefaultLocaleListString() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return toLanguageTags(LocaleList.getDefault()); + } + return getDefaultLocaleString(); } /** - * Get the default country code set during install. - * @return country code. + * @return The default country code set during install. */ @CalledByNative private static String getDefaultCountryCode() { diff --git a/base/android/java/src/org/chromium/base/Log.java b/base/android/java/src/org/chromium/base/Log.java index 8815e63..399f16d 100644 --- a/base/android/java/src/org/chromium/base/Log.java +++ b/base/android/java/src/org/chromium/base/Log.java @@ -347,6 +347,11 @@ public class Log { } } + /** Handy function to get a loggable stack trace from a Throwable. */ + public static String getStackTraceString(Throwable tr) { + return android.util.Log.getStackTraceString(tr); + } + private static Throwable getThrowableToLog(Object[] args) { if (args == null || args.length == 0) return null; diff --git a/base/android/java/src/org/chromium/base/ObserverList.java b/base/android/java/src/org/chromium/base/ObserverList.java index 7a2ab98..59276c6 100644 --- a/base/android/java/src/org/chromium/base/ObserverList.java +++ b/base/android/java/src/org/chromium/base/ObserverList.java @@ -21,7 +21,7 @@ import javax.annotation.concurrent.NotThreadSafe; * The implementation (and the interface) is heavily influenced by the C++ ObserverList. * Notable differences: * - The iterator implements NOTIFY_EXISTING_ONLY. - * - The FOR_EACH_OBSERVER closure is left to the clients to implement in terms of iterator(). + * - The range-based for loop is left to the clients to implement in terms of iterator(). * <p/> * This class is not threadsafe. Observers MUST be added, removed and will be notified on the same * thread this is created. @@ -44,9 +44,9 @@ public class ObserverList<E> implements Iterable<E> { } public final List<E> mObservers = new ArrayList<E>(); - private int mIterationDepth = 0; - private int mCount = 0; - private boolean mNeedsCompact = false; + private int mIterationDepth; + private int mCount; + private boolean mNeedsCompact; public ObserverList() {} @@ -190,8 +190,8 @@ public class ObserverList<E> implements Iterable<E> { private class ObserverListIterator implements RewindableIterator<E> { private int mListEndMarker; - private int mIndex = 0; - private boolean mIsExhausted = false; + private int mIndex; + private boolean mIsExhausted; private ObserverListIterator() { ObserverList.this.incrementIterationDepth(); diff --git a/base/android/java/src/org/chromium/base/PathUtils.java b/base/android/java/src/org/chromium/base/PathUtils.java index c3d3e7e..00977d5 100644 --- a/base/android/java/src/org/chromium/base/PathUtils.java +++ b/base/android/java/src/org/chromium/base/PathUtils.java @@ -34,13 +34,9 @@ public abstract class PathUtils { private static final AtomicBoolean sInitializationStarted = new AtomicBoolean(); private static AsyncTask<Void, Void, String[]> sDirPathFetchTask; - // In setPrivateDataDirectorySuffix(), we store the app's context. If the AsyncTask started in - // setPrivateDataDirectorySuffix() fails to complete by the time we need the values, we will - // need the context so that we can restart the task synchronously on the UI thread. - private static Context sDataDirectoryAppContext; - - // We also store the directory path suffix from setPrivateDataDirectorySuffix() for the same - // reason as above. + // If the AsyncTask started in setPrivateDataDirectorySuffix() fails to complete by the time we + // need the values, we will need the suffix so that we can restart the task synchronously on + // the UI thread. private static String sDataDirectorySuffix; // Prevent instantiation. @@ -101,13 +97,14 @@ public abstract class PathUtils { */ private static String[] setPrivateDataDirectorySuffixInternal() { String[] paths = new String[NUM_DIRECTORIES]; - paths[DATA_DIRECTORY] = sDataDirectoryAppContext.getDir(sDataDirectorySuffix, - Context.MODE_PRIVATE).getPath(); - paths[THUMBNAIL_DIRECTORY] = sDataDirectoryAppContext.getDir( + Context appContext = ContextUtils.getApplicationContext(); + paths[DATA_DIRECTORY] = appContext.getDir( + sDataDirectorySuffix, Context.MODE_PRIVATE).getPath(); + paths[THUMBNAIL_DIRECTORY] = appContext.getDir( THUMBNAIL_DIRECTORY_NAME, Context.MODE_PRIVATE).getPath(); - paths[DATABASE_DIRECTORY] = sDataDirectoryAppContext.getDatabasePath("foo").getParent(); - if (sDataDirectoryAppContext.getCacheDir() != null) { - paths[CACHE_DIRECTORY] = sDataDirectoryAppContext.getCacheDir().getPath(); + paths[DATABASE_DIRECTORY] = appContext.getDatabasePath("foo").getParent(); + if (appContext.getCacheDir() != null) { + paths[CACHE_DIRECTORY] = appContext.getCacheDir().getPath(); } return paths; } @@ -124,12 +121,12 @@ public abstract class PathUtils { * @param suffix The private data directory suffix. * @see Context#getDir(String, int) */ - public static void setPrivateDataDirectorySuffix(String suffix, Context context) { + public static void setPrivateDataDirectorySuffix(String suffix) { // This method should only be called once, but many tests end up calling it multiple times, // so adding a guard here. if (!sInitializationStarted.getAndSet(true)) { + assert ContextUtils.getApplicationContext() != null; sDataDirectorySuffix = suffix; - sDataDirectoryAppContext = context.getApplicationContext(); sDirPathFetchTask = new AsyncTask<Void, Void, String[]>() { @Override protected String[] doInBackground(Void... unused) { @@ -151,7 +148,7 @@ public abstract class PathUtils { * @return the private directory that is used to store application data. */ @CalledByNative - public static String getDataDirectory(Context appContext) { + public static String getDataDirectory() { assert sDirPathFetchTask != null : "setDataDirectorySuffix must be called first."; return getDirectoryPath(DATA_DIRECTORY); } @@ -160,7 +157,7 @@ public abstract class PathUtils { * @return the private directory that is used to store application database. */ @CalledByNative - public static String getDatabaseDirectory(Context appContext) { + public static String getDatabaseDirectory() { assert sDirPathFetchTask != null : "setDataDirectorySuffix must be called first."; return getDirectoryPath(DATABASE_DIRECTORY); } @@ -168,15 +165,14 @@ public abstract class PathUtils { /** * @return the cache directory. */ - @SuppressWarnings("unused") @CalledByNative - public static String getCacheDirectory(Context appContext) { + public static String getCacheDirectory() { assert sDirPathFetchTask != null : "setDataDirectorySuffix must be called first."; return getDirectoryPath(CACHE_DIRECTORY); } @CalledByNative - public static String getThumbnailCacheDirectory(Context appContext) { + public static String getThumbnailCacheDirectory() { assert sDirPathFetchTask != null : "setDataDirectorySuffix must be called first."; return getDirectoryPath(THUMBNAIL_DIRECTORY); } @@ -186,7 +182,7 @@ public abstract class PathUtils { */ @SuppressWarnings("unused") @CalledByNative - private static String getDownloadsDirectory(Context appContext) { + private static String getDownloadsDirectory() { // Temporarily allowing disk access while fixing. TODO: http://crbug.com/508615 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); String downloadsPath; @@ -207,8 +203,8 @@ public abstract class PathUtils { */ @SuppressWarnings("unused") @CalledByNative - private static String getNativeLibraryDirectory(Context appContext) { - ApplicationInfo ai = appContext.getApplicationInfo(); + private static String getNativeLibraryDirectory() { + ApplicationInfo ai = ContextUtils.getApplicationContext().getApplicationInfo(); if ((ai.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 || (ai.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { return ai.nativeLibraryDir; diff --git a/base/android/java/src/org/chromium/base/PerfTraceEvent.java b/base/android/java/src/org/chromium/base/PerfTraceEvent.java index c0e4b21..8a87773 100644 --- a/base/android/java/src/org/chromium/base/PerfTraceEvent.java +++ b/base/android/java/src/org/chromium/base/PerfTraceEvent.java @@ -8,12 +8,12 @@ import android.os.Debug; import android.os.Debug.MemoryInfo; import android.util.Log; -import org.chromium.base.annotations.SuppressFBWarnings; - import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.chromium.base.annotations.SuppressFBWarnings; + import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -42,7 +42,7 @@ import java.util.List; public class PerfTraceEvent { private static final int MAX_NAME_LENGTH = 40; private static final String MEMORY_TRACE_NAME_SUFFIX = "_BZR_PSS"; - private static File sOutputFile = null; + private static File sOutputFile; /** The event types understood by the perf trace scripts. */ private enum EventType { @@ -63,9 +63,9 @@ public class PerfTraceEvent { } } - private static boolean sEnabled = false; + private static boolean sEnabled; private static boolean sTrackTiming = true; - private static boolean sTrackMemory = false; + private static boolean sTrackMemory; // A list of performance trace event strings. // Events are stored as a JSON dict much like TraceEvent. diff --git a/base/android/java/src/org/chromium/base/PowerMonitor.java b/base/android/java/src/org/chromium/base/PowerMonitor.java index 5d8fa0c..ae36a75 100644 --- a/base/android/java/src/org/chromium/base/PowerMonitor.java +++ b/base/android/java/src/org/chromium/base/PowerMonitor.java @@ -4,74 +4,61 @@ package org.chromium.base; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; -import android.os.Handler; -import android.os.Looper; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; - /** * Integrates native PowerMonitor with the java side. */ @JNINamespace("base::android") -public class PowerMonitor implements ApplicationStatus.ApplicationStateListener { - private static final long SUSPEND_DELAY_MS = 1 * 60 * 1000; // 1 minute. - private static class LazyHolder { - private static final PowerMonitor INSTANCE = new PowerMonitor(); - } +public class PowerMonitor { private static PowerMonitor sInstance; private boolean mIsBatteryPower; - private final Handler mHandler = new Handler(Looper.getMainLooper()); - - // Asynchronous task used to fire the "paused" event to the native side 1 minute after the main - // activity transitioned to the "paused" state. This event is not sent immediately because it - // would be too aggressive. An Android activity can be in the "paused" state quite often. This - // can happen when a dialog window shows up for instance. - private static final Runnable sSuspendTask = new Runnable() { - @Override - public void run() { - nativeOnMainActivitySuspended(); - } - }; - public static void createForTests(Context context) { + public static void createForTests() { // Applications will create this once the JNI side has been fully wired up both sides. For // tests, we just need native -> java, that is, we don't need to notify java -> native on // creation. - sInstance = LazyHolder.INSTANCE; + sInstance = new PowerMonitor(); } /** * Create a PowerMonitor instance if none exists. - * @param context The context to register broadcast receivers for. The application context - * will be used from this parameter. */ - public static void create(Context context) { - context = context.getApplicationContext(); - if (sInstance == null) { - sInstance = LazyHolder.INSTANCE; - ApplicationStatus.registerApplicationStateListener(sInstance); - IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); - Intent batteryStatusIntent = context.registerReceiver(null, ifilter); - if (batteryStatusIntent != null) onBatteryChargingChanged(batteryStatusIntent); - } + public static void create() { + ThreadUtils.assertOnUiThread(); + + if (sInstance != null) return; + + Context context = ContextUtils.getApplicationContext(); + sInstance = new PowerMonitor(); + IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); + Intent batteryStatusIntent = context.registerReceiver(null, ifilter); + if (batteryStatusIntent != null) onBatteryChargingChanged(batteryStatusIntent); + + IntentFilter powerConnectedFilter = new IntentFilter(); + powerConnectedFilter.addAction(Intent.ACTION_POWER_CONNECTED); + powerConnectedFilter.addAction(Intent.ACTION_POWER_DISCONNECTED); + context.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + PowerMonitor.onBatteryChargingChanged(intent); + } + }, powerConnectedFilter); } private PowerMonitor() { } - public static void onBatteryChargingChanged(Intent intent) { - if (sInstance == null) { - // We may be called by the framework intent-filter before being fully initialized. This - // is not a problem, since our constructor will check for the state later on. - return; - } + private static void onBatteryChargingChanged(Intent intent) { + assert sInstance != null; int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); // If we're not plugged, assume we're running on battery power. sInstance.mIsBatteryPower = chargePlug != BatteryManager.BATTERY_PLUGGED_USB @@ -79,23 +66,15 @@ public class PowerMonitor implements ApplicationStatus.ApplicationStateListener nativeOnBatteryChargingChanged(); } - @Override - public void onApplicationStateChange(int newState) { - if (newState == ApplicationState.HAS_RUNNING_ACTIVITIES) { - // Remove the callback from the message loop in case it hasn't been executed yet. - mHandler.removeCallbacks(sSuspendTask); - nativeOnMainActivityResumed(); - } else if (newState == ApplicationState.HAS_PAUSED_ACTIVITIES) { - mHandler.postDelayed(sSuspendTask, SUSPEND_DELAY_MS); - } - } - @CalledByNative private static boolean isBatteryPower() { + // Creation of the PowerMonitor can be deferred based on the browser startup path. If the + // battery power is requested prior to the browser triggering the creation, force it to be + // created now. + if (sInstance == null) create(); + return sInstance.mIsBatteryPower; } private static native void nativeOnBatteryChargingChanged(); - private static native void nativeOnMainActivitySuspended(); - private static native void nativeOnMainActivityResumed(); } diff --git a/base/android/java/src/org/chromium/base/PowerStatusReceiver.java b/base/android/java/src/org/chromium/base/PowerStatusReceiver.java deleted file mode 100644 index 904a740..0000000 --- a/base/android/java/src/org/chromium/base/PowerStatusReceiver.java +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - - -/** - * A BroadcastReceiver that listens to changes in power status and notifies - * PowerMonitor. - * It's instantiated by the framework via the application intent-filter - * declared in its manifest. - */ -public class PowerStatusReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - PowerMonitor.onBatteryChargingChanged(intent); - } -} diff --git a/base/android/java/src/org/chromium/base/ResourceExtractor.java b/base/android/java/src/org/chromium/base/ResourceExtractor.java index 2854b02..c1d1856 100644 --- a/base/android/java/src/org/chromium/base/ResourceExtractor.java +++ b/base/android/java/src/org/chromium/base/ResourceExtractor.java @@ -4,18 +4,12 @@ package org.chromium.base; -import android.annotation.TargetApi; -import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.AsyncTask; -import android.os.Build; import android.os.Handler; import android.os.Looper; -import android.os.Trace; - -import org.chromium.base.annotations.SuppressFBWarnings; import java.io.File; import java.io.FileOutputStream; @@ -23,7 +17,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Locale; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; @@ -38,23 +34,7 @@ public class ResourceExtractor { private static final String V8_NATIVES_DATA_FILENAME = "natives_blob.bin"; private static final String V8_SNAPSHOT_DATA_FILENAME = "snapshot_blob.bin"; private static final String APP_VERSION_PREF = "org.chromium.base.ResourceExtractor.Version"; - - private static ResourceEntry[] sResourcesToExtract = new ResourceEntry[0]; - - /** - * Holds information about a res/raw file (e.g. locale .pak files). - */ - public static final class ResourceEntry { - public final int resourceId; - public final String pathWithinApk; - public final String extractedFileName; - - public ResourceEntry(int resourceId, String pathWithinApk, String extractedFileName) { - this.resourceId = resourceId; - this.pathWithinApk = pathWithinApk; - this.extractedFileName = extractedFileName; - } - } + private static final String FALLBACK_LOCALE = "en-US"; private class ExtractTask extends AsyncTask<Void, Void, Void> { private static final int BUFFER_SIZE = 16 * 1024; @@ -92,12 +72,12 @@ public class ResourceExtractor { return; } - beginTraceSection("checkPakTimeStamp"); + TraceEvent.begin("checkPakTimeStamp"); long curAppVersion = getApkVersion(); SharedPreferences sharedPrefs = ContextUtils.getAppSharedPreferences(); long prevAppVersion = sharedPrefs.getLong(APP_VERSION_PREF, 0); boolean versionChanged = curAppVersion != prevAppVersion; - endTraceSection(); + TraceEvent.end("checkPakTimeStamp"); if (versionChanged) { deleteFiles(); @@ -107,23 +87,23 @@ public class ResourceExtractor { sharedPrefs.edit().putLong(APP_VERSION_PREF, curAppVersion).apply(); } - beginTraceSection("WalkAssets"); + TraceEvent.begin("WalkAssets"); byte[] buffer = new byte[BUFFER_SIZE]; try { - for (ResourceEntry entry : sResourcesToExtract) { - File output = new File(outputDir, entry.extractedFileName); + for (String assetName : mAssetsToExtract) { + File output = new File(outputDir, assetName); // TODO(agrieve): It would be better to check that .length == expectedLength. // http://crbug.com/606413 if (output.length() != 0) { continue; } - beginTraceSection("ExtractResource"); - InputStream inputStream = mContext.getResources().openRawResource( - entry.resourceId); + TraceEvent.begin("ExtractResource"); + InputStream inputStream = + ContextUtils.getApplicationContext().getAssets().open(assetName); try { extractResourceHelper(inputStream, output, buffer); } finally { - endTraceSection(); // ExtractResource + TraceEvent.end("ExtractResource"); } } } catch (IOException e) { @@ -131,25 +111,21 @@ public class ResourceExtractor { // Try to recover here, can we try again after deleting files instead of // returning null? It might be useful to gather UMA here too to track if // this happens with regularity. - Log.w(TAG, "Exception unpacking required pak resources: %s", e.getMessage()); + Log.w(TAG, "Exception unpacking required pak asset: %s", e.getMessage()); deleteFiles(); return; } finally { - endTraceSection(); // WalkAssets + TraceEvent.end("WalkAssets"); } } @Override protected Void doInBackground(Void... unused) { - // TODO(lizeb): Use chrome tracing here (and above in - // doInBackgroundImpl) when it will be possible. This is currently - // not doable since the native library is not loaded yet, and the - // TraceEvent calls are dropped before this point. - beginTraceSection("ResourceExtractor.ExtractTask.doInBackground"); + TraceEvent.begin("ResourceExtractor.ExtractTask.doInBackground"); try { doInBackgroundImpl(); } finally { - endTraceSection(); + TraceEvent.end("ResourceExtractor.ExtractTask.doInBackground"); } return null; } @@ -163,64 +139,60 @@ public class ResourceExtractor { @Override protected void onPostExecute(Void result) { - beginTraceSection("ResourceExtractor.ExtractTask.onPostExecute"); + TraceEvent.begin("ResourceExtractor.ExtractTask.onPostExecute"); try { onPostExecuteImpl(); } finally { - endTraceSection(); + TraceEvent.end("ResourceExtractor.ExtractTask.onPostExecute"); } } /** Returns a number that is different each time the apk changes. */ private long getApkVersion() { - PackageManager pm = mContext.getPackageManager(); + PackageManager pm = ContextUtils.getApplicationContext().getPackageManager(); try { - // More appropriate would be versionCode, but it doesn't change while developing. - PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), 0); - return pi.lastUpdateTime; + // Use lastUpdateTime since versionCode does not change when developing locally, + // but also use versionCode since it is possible for Chrome to be updated without + // the lastUpdateTime being changed (http://crbug.org/673458). + PackageInfo pi = + pm.getPackageInfo(ContextUtils.getApplicationContext().getPackageName(), 0); + // Xor'ing versionCode into upper half of the long to ensure it doesn't somehow + // exactly offset an increase in time. + return pi.lastUpdateTime ^ (((long) pi.versionCode) << 32); } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException(e); } } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) - private void beginTraceSection(String section) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) return; - Trace.beginSection(section); - } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) - private void endTraceSection() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) return; - Trace.endSection(); - } } - private final Context mContext; private ExtractTask mExtractTask; + private final String[] mAssetsToExtract = detectFilesToExtract(); private static ResourceExtractor sInstance; - public static ResourceExtractor get(Context context) { + public static ResourceExtractor get() { if (sInstance == null) { - sInstance = new ResourceExtractor(context); + sInstance = new ResourceExtractor(); } return sInstance; } - /** - * Specifies the files that should be extracted from the APK. - * and moved to {@link #getOutputDir()}. - */ - @SuppressFBWarnings("EI_EXPOSE_STATIC_REP2") - public static void setResourcesToExtract(ResourceEntry[] entries) { - assert (sInstance == null || sInstance.mExtractTask == null) - : "Must be called before startExtractingResources is called"; - sResourcesToExtract = entries; - } - - private ResourceExtractor(Context context) { - mContext = context.getApplicationContext(); + private static String[] detectFilesToExtract() { + Locale defaultLocale = Locale.getDefault(); + String language = LocaleUtils.getUpdatedLanguageForChromium(defaultLocale.getLanguage()); + // Currenty (Oct 2016), this array can be as big as 4 entries, so using a capacity + // that allows a bit of growth, but is still in the right ballpark.. + ArrayList<String> activeLocalePakFiles = new ArrayList<String>(6); + for (String locale : BuildConfig.COMPRESSED_LOCALES) { + if (locale.startsWith(language)) { + activeLocalePakFiles.add(locale + ".pak"); + } + } + if (activeLocalePakFiles.isEmpty() && BuildConfig.COMPRESSED_LOCALES.length > 0) { + assert Arrays.asList(BuildConfig.COMPRESSED_LOCALES).contains(FALLBACK_LOCALE); + activeLocalePakFiles.add(FALLBACK_LOCALE + ".pak"); + } + return activeLocalePakFiles.toArray(new String[activeLocalePakFiles.size()]); } /** @@ -301,7 +273,7 @@ public class ResourceExtractor { } private File getAppDataDir() { - return new File(PathUtils.getDataDirectory(mContext)); + return new File(PathUtils.getDataDirectory()); } private File getOutputDir() { @@ -348,6 +320,6 @@ public class ResourceExtractor { * Pak extraction not necessarily required by the embedder. */ private static boolean shouldSkipPakExtraction() { - return sResourcesToExtract.length == 0; + return get().mAssetsToExtract.length == 0; } } diff --git a/base/android/java/src/org/chromium/base/SecureRandomInitializer.java b/base/android/java/src/org/chromium/base/SecureRandomInitializer.java index 457e2ef..294b1e6 100644 --- a/base/android/java/src/org/chromium/base/SecureRandomInitializer.java +++ b/base/android/java/src/org/chromium/base/SecureRandomInitializer.java @@ -4,6 +4,8 @@ package org.chromium.base; +import android.annotation.SuppressLint; + import java.io.FileInputStream; import java.io.IOException; import java.security.SecureRandom; @@ -13,30 +15,25 @@ import java.security.SecureRandom; * <= 4.3. See * {@link http://android-developers.blogspot.com/2013/08/some-securerandom-thoughts.html}. */ +// TODO(crbug.com/635567): Fix this properly. +@SuppressLint("SecureRandom") public class SecureRandomInitializer { private static final int NUM_RANDOM_BYTES = 16; - private static byte[] sSeedBytes = new byte[NUM_RANDOM_BYTES]; - /** * Safely initializes the random number generator, by seeding it with data from /dev/urandom. */ public static void initialize(SecureRandom generator) throws IOException { FileInputStream fis = null; try { + byte[] seedBytes = new byte[NUM_RANDOM_BYTES]; fis = new FileInputStream("/dev/urandom"); - if (fis.read(sSeedBytes) != sSeedBytes.length) { + if (fis.read(seedBytes) != seedBytes.length) { throw new IOException("Failed to get enough random data."); } - generator.setSeed(sSeedBytes); + generator.setSeed(seedBytes); } finally { - try { - if (fis != null) { - fis.close(); - } - } catch (IOException e) { - // Ignore exception closing the device. - } + StreamUtil.closeQuietly(fis); } } } diff --git a/base/android/java/src/org/chromium/base/SysUtils.java b/base/android/java/src/org/chromium/base/SysUtils.java index 04a8332..5ab77df 100644 --- a/base/android/java/src/org/chromium/base/SysUtils.java +++ b/base/android/java/src/org/chromium/base/SysUtils.java @@ -4,6 +4,9 @@ package org.chromium.base; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Build; import android.os.StrictMode; import android.util.Log; @@ -99,6 +102,24 @@ public class SysUtils { return sLowEndDevice.booleanValue(); } + /** + * Resets the cached value, if any. + */ + @VisibleForTesting + public static void reset() { + sLowEndDevice = null; + } + + public static boolean hasCamera(final Context context) { + final PackageManager pm = context.getPackageManager(); + // JellyBean support. + boolean hasCamera = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + hasCamera |= pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY); + } + return hasCamera; + } + private static boolean detectLowEndDevice() { assert CommandLine.isInitialized(); if (CommandLine.getInstance().hasSwitch(BaseSwitches.ENABLE_LOW_END_DEVICE_MODE)) { diff --git a/base/android/java/src/org/chromium/base/SystemMessageHandler.java b/base/android/java/src/org/chromium/base/SystemMessageHandler.java index ebcc0d9..abfa2d8 100644 --- a/base/android/java/src/org/chromium/base/SystemMessageHandler.java +++ b/base/android/java/src/org/chromium/base/SystemMessageHandler.java @@ -25,10 +25,12 @@ class SystemMessageHandler extends Handler { // Native class pointer set by the constructor of the SharedClient native class. private long mMessagePumpDelegateNative = 0; + private long mMessagePumpNative = 0; private long mDelayedScheduledTimeTicks = 0; - private SystemMessageHandler(long messagePumpDelegateNative) { + protected SystemMessageHandler(long messagePumpDelegateNative, long messagePumpNative) { mMessagePumpDelegateNative = messagePumpDelegateNative; + mMessagePumpNative = messagePumpNative; } @Override @@ -36,7 +38,8 @@ class SystemMessageHandler extends Handler { if (msg.what == DELAYED_SCHEDULED_WORK) { mDelayedScheduledTimeTicks = 0; } - nativeDoRunLoopOnce(mMessagePumpDelegateNative, mDelayedScheduledTimeTicks); + nativeDoRunLoopOnce( + mMessagePumpDelegateNative, mMessagePumpNative, mDelayedScheduledTimeTicks); } @SuppressWarnings("unused") @@ -153,10 +156,11 @@ class SystemMessageHandler extends Handler { } @CalledByNative - private static SystemMessageHandler create(long messagePumpDelegateNative) { - return new SystemMessageHandler(messagePumpDelegateNative); + private static SystemMessageHandler create( + long messagePumpDelegateNative, long messagePumpNative) { + return new SystemMessageHandler(messagePumpDelegateNative, messagePumpNative); } private native void nativeDoRunLoopOnce( - long messagePumpDelegateNative, long delayedScheduledTimeTicks); + long messagePumpDelegateNative, long messagePumpNative, long delayedScheduledTimeTicks); } diff --git a/base/android/java/src/org/chromium/base/ThreadUtils.java b/base/android/java/src/org/chromium/base/ThreadUtils.java index ef2887a..737d7f6 100644 --- a/base/android/java/src/org/chromium/base/ThreadUtils.java +++ b/base/android/java/src/org/chromium/base/ThreadUtils.java @@ -34,6 +34,11 @@ public class ThreadUtils { @VisibleForTesting public static void setUiThread(Looper looper) { synchronized (sLock) { + if (looper == null) { + // Used to reset the looper after tests. + sUiThreadHandler = null; + return; + } if (sUiThreadHandler != null && sUiThreadHandler.getLooper() != looper) { throw new RuntimeException("UI thread looper is already set to " + sUiThreadHandler.getLooper() + " (Main thread looper is " @@ -189,7 +194,9 @@ public class ThreadUtils { * Asserts that the current thread is running on the main thread. */ public static void assertOnUiThread() { - assert runningOnUiThread(); + if (BuildConfig.DCHECK_IS_ON && !runningOnUiThread()) { + throw new IllegalStateException("Must be called on the Ui thread."); + } } /** diff --git a/base/android/java/src/org/chromium/base/TraceEvent.java b/base/android/java/src/org/chromium/base/TraceEvent.java index 878275c..71bfac1 100644 --- a/base/android/java/src/org/chromium/base/TraceEvent.java +++ b/base/android/java/src/org/chromium/base/TraceEvent.java @@ -12,18 +12,19 @@ import android.util.Printer; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.MainDex; /** * Java mirror of Chrome trace event API. See base/trace_event/trace_event.h. Unlike the native * version, Java does not have stack objects, so a TRACE_EVENT() which does both TRACE_EVENT_BEGIN() * and TRACE_EVENT_END() in ctor/dtor is not possible. - * It is OK to use tracing before the native library has loaded, but such traces will - * be ignored. (Perhaps we could devise to buffer them up in future?). + * It is OK to use tracing before the native library has loaded, in a slightly restricted fashion. + * @see EarlyTraceEvent for details. */ @JNINamespace("base::android") +@MainDex public class TraceEvent { - - private static volatile boolean sEnabled = false; - private static volatile boolean sATraceEnabled = false; // True when taking an Android systrace. + private static volatile boolean sEnabled; + private static volatile boolean sATraceEnabled; // True when taking an Android systrace. private static class BasicLooperMonitor implements Printer { @Override @@ -82,14 +83,14 @@ public class TraceEvent { MIN_INTERESTING_DURATION_MILLIS * 3; // Stats tracking - private long mLastIdleStartedAt = 0L; - private long mLastWorkStartedAt = 0L; - private int mNumTasksSeen = 0; - private int mNumIdlesSeen = 0; - private int mNumTasksSinceLastIdle = 0; + private long mLastIdleStartedAt; + private long mLastWorkStartedAt; + private int mNumTasksSeen; + private int mNumIdlesSeen; + private int mNumTasksSinceLastIdle; // State - private boolean mIdleMonitorAttached = false; + private boolean mIdleMonitorAttached; // Called from within the begin/end methods only. // This method can only execute on the looper thread, because that is @@ -179,11 +180,26 @@ public class TraceEvent { */ @CalledByNative public static void setEnabled(boolean enabled) { - sEnabled = enabled; - // Android M+ systrace logs this on its own. Only log it if not writing to Android systrace. - if (sATraceEnabled) return; - ThreadUtils.getUiThreadLooper().setMessageLogging( - enabled ? LooperMonitorHolder.sInstance : null); + if (enabled) EarlyTraceEvent.disable(); + // Only disable logging if Chromium enabled it originally, so as to not disrupt logging done + // by other applications + if (sEnabled != enabled) { + sEnabled = enabled; + // Android M+ systrace logs this on its own. Only log it if not writing to Android + // systrace. + if (sATraceEnabled) return; + ThreadUtils.getUiThreadLooper().setMessageLogging( + enabled ? LooperMonitorHolder.sInstance : null); + } + } + + /** + * May enable early tracing depending on the environment. + * + * Must be called after the command-line has been read. + */ + public static void maybeEnableEarlyTracing() { + EarlyTraceEvent.maybeEnable(); } /** @@ -254,7 +270,7 @@ public class TraceEvent { * @param name The name of the event. */ public static void begin(String name) { - if (sEnabled) nativeBegin(name, null); + begin(name, null); } /** @@ -263,6 +279,7 @@ public class TraceEvent { * @param arg The arguments of the event. */ public static void begin(String name, String arg) { + EarlyTraceEvent.begin(name); if (sEnabled) nativeBegin(name, arg); } @@ -271,7 +288,7 @@ public class TraceEvent { * @param name The name of the event. */ public static void end(String name) { - if (sEnabled) nativeEnd(name, null); + end(name, null); } /** @@ -280,6 +297,7 @@ public class TraceEvent { * @param arg The arguments of the event. */ public static void end(String name, String arg) { + EarlyTraceEvent.end(name); if (sEnabled) nativeEnd(name, arg); } diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java index f6bf867..d3e0fb9 100644 --- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java +++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java @@ -4,6 +4,7 @@ package org.chromium.base.library_loader; +import android.annotation.SuppressLint; import android.content.Context; import android.os.AsyncTask; import android.os.SystemClock; @@ -12,8 +13,10 @@ import org.chromium.base.CommandLine; import org.chromium.base.ContextUtils; import org.chromium.base.Log; import org.chromium.base.TraceEvent; +import org.chromium.base.VisibleForTesting; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.MainDex; import org.chromium.base.metrics.RecordHistogram; import java.util.concurrent.atomic.AtomicBoolean; @@ -35,6 +38,7 @@ import javax.annotation.Nullable; * the native counterpart to this class. */ @JNINamespace("base::android") +@MainDex public class LibraryLoader { private static final String TAG = "LibraryLoader"; @@ -129,18 +133,14 @@ public class LibraryLoader { /** * This method blocks until the library is fully loaded and initialized. - * - * @param context The context in which the method is called. */ - public void ensureInitialized(Context context) throws ProcessInitException { - // TODO(wnwen): Move this call appropriately down to the tests that need it. - ContextUtils.initApplicationContext(context.getApplicationContext()); + public void ensureInitialized() throws ProcessInitException { synchronized (sLock) { if (mInitialized) { // Already initialized, nothing to do. return; } - loadAlreadyLocked(context); + loadAlreadyLocked(ContextUtils.getApplicationContext()); initializeAlreadyLocked(); } } @@ -159,13 +159,26 @@ public class LibraryLoader { * this is called on will be the thread that runs the native code's static initializers. * See the comment in doInBackground() for more considerations on this. * - * @param context The context the code is running. - * * @throws ProcessInitException if the native library failed to load. */ - public void loadNow(Context context) throws ProcessInitException { + public void loadNow() throws ProcessInitException { + loadNowOverrideApplicationContext(ContextUtils.getApplicationContext()); + } + + /** + * Override kept for callers that need to load from a different app context. Do not use unless + * specifically required to load from another context that is not the current process's app + * context. + * + * @param appContext The overriding app context to be used to load libraries. + * @throws ProcessInitException if the native library failed to load with this context. + */ + public void loadNowOverrideApplicationContext(Context appContext) throws ProcessInitException { synchronized (sLock) { - loadAlreadyLocked(context); + if (mLoaded && appContext != ContextUtils.getApplicationContext()) { + throw new IllegalStateException("Attempt to load again from alternate context."); + } + loadAlreadyLocked(appContext); } } @@ -197,7 +210,10 @@ public class LibraryLoader { TraceEvent.begin("LibraryLoader.asyncPrefetchLibrariesToMemory"); int percentage = nativePercentageOfResidentNativeLibraryCode(); boolean success = false; - if (coldStart) { + // Arbitrary percentage threshold. If most of the native library is already + // resident (likely with monochrome), don't bother creating a prefetch process. + boolean prefetch = coldStart && percentage < 90; + if (prefetch) { success = nativeForkAndPrefetchNativeLibrary(); if (!success) { Log.w(TAG, "Forking a process to prefetch the native library failed."); @@ -206,7 +222,7 @@ public class LibraryLoader { // As this runs in a background thread, it can be called before histograms are // initialized. In this instance, histograms are dropped. RecordHistogram.initialize(); - if (coldStart) { + if (prefetch) { RecordHistogram.recordBooleanHistogram("LibraryLoader.PrefetchStatus", success); } if (percentage != -1) { @@ -247,7 +263,9 @@ public class LibraryLoader { // Invoke either Linker.loadLibrary(...) or System.loadLibrary(...), triggering // JNI_OnLoad in native code - private void loadAlreadyLocked(Context context) throws ProcessInitException { + // TODO(crbug.com/635567): Fix this properly. + @SuppressLint("DefaultLocale") + private void loadAlreadyLocked(Context appContext) throws ProcessInitException { try { if (!mLoaded) { assert !mInitialized; @@ -273,25 +291,35 @@ public class LibraryLoader { String libFilePath = System.mapLibraryName(library); if (Linker.isInZipFile()) { // Load directly from the APK. - zipFilePath = context.getApplicationInfo().sourceDir; + zipFilePath = appContext.getApplicationInfo().sourceDir; Log.i(TAG, "Loading " + library + " from within " + zipFilePath); } else { // The library is in its own file. Log.i(TAG, "Loading " + library); } - // Load the library using this Linker. May throw UnsatisfiedLinkError. - loadLibrary(linker, zipFilePath, libFilePath); + try { + // Load the library using this Linker. May throw UnsatisfiedLinkError. + loadLibrary(linker, zipFilePath, libFilePath); + } catch (UnsatisfiedLinkError e) { + Log.e(TAG, "Unable to load library: " + library); + throw(e); + } } linker.finishLibraryLoad(); } else { if (sLibraryPreloader != null) { - mLibraryPreloaderStatus = sLibraryPreloader.loadLibrary(context); + mLibraryPreloaderStatus = sLibraryPreloader.loadLibrary(appContext); } // Load libraries using the system linker. for (String library : NativeLibraries.LIBRARIES) { - System.loadLibrary(library); + try { + System.loadLibrary(library); + } catch (UnsatisfiedLinkError e) { + Log.e(TAG, "Unable to load library: " + library); + throw(e); + } } } @@ -307,21 +335,6 @@ public class LibraryLoader { } catch (UnsatisfiedLinkError e) { throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_LOAD_FAILED, e); } - // Check that the version of the library we have loaded matches the version we expect - Log.i(TAG, String.format( - "Expected native library version number \"%s\", " - + "actual native library version number \"%s\"", - NativeLibraries.sVersionNumber, - nativeGetVersionNumber())); - if (!NativeLibraries.sVersionNumber.equals(nativeGetVersionNumber())) { - throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_WRONG_VERSION); - } - } - - // Returns whether the given split name is that of the ABI split. - private static boolean isAbiSplit(String splitName) { - // The split name for the ABI split is manually set in the build rules. - return splitName.startsWith("abi_"); } // The WebView requires the Command Line to be switched over before @@ -363,6 +376,14 @@ public class LibraryLoader { throw new ProcessInitException(LoaderErrors.LOADER_ERROR_FAILED_TO_REGISTER_JNI); } + // Check that the version of the library we have loaded matches the version we expect + Log.i(TAG, String.format("Expected native library version number \"%s\", " + + "actual native library version number \"%s\"", + NativeLibraries.sVersionNumber, nativeGetVersionNumber())); + if (!NativeLibraries.sVersionNumber.equals(nativeGetVersionNumber())) { + throw new ProcessInitException(LoaderErrors.LOADER_ERROR_NATIVE_LIBRARY_WRONG_VERSION); + } + // From now on, keep tracing in sync with native. TraceEvent.registerNativeEnabledObserver(); @@ -375,18 +396,19 @@ public class LibraryLoader { } // Called after all native initializations are complete. - public void onNativeInitializationComplete(Context context) { - recordBrowserProcessHistogram(context); + public void onNativeInitializationComplete() { + recordBrowserProcessHistogram(); } // Record Chromium linker histogram state for the main browser process. Called from // onNativeInitializationComplete(). - private void recordBrowserProcessHistogram(Context context) { + private void recordBrowserProcessHistogram() { if (Linker.getInstance().isUsed()) { - nativeRecordChromiumAndroidLinkerBrowserHistogram(mIsUsingBrowserSharedRelros, - mLoadAtFixedAddressFailed, - getLibraryLoadFromApkStatus(context), - mLibraryLoadTimeMs); + nativeRecordChromiumAndroidLinkerBrowserHistogram( + mIsUsingBrowserSharedRelros, + mLoadAtFixedAddressFailed, + getLibraryLoadFromApkStatus(), + mLibraryLoadTimeMs); } if (sLibraryPreloader != null) { nativeRecordLibraryPreloaderBrowserHistogram(mLibraryPreloaderStatus); @@ -395,7 +417,7 @@ public class LibraryLoader { // Returns the device's status for loading a library directly from the APK file. // This method can only be called when the Chromium linker is used. - private int getLibraryLoadFromApkStatus(Context context) { + private int getLibraryLoadFromApkStatus() { assert Linker.getInstance().isUsed(); if (mLibraryWasLoadedFromApk) { @@ -432,6 +454,15 @@ public class LibraryLoader { return sInstance.mLibraryProcessType; } + /** + * Override the library loader (normally with a mock) for testing. + * @param loader the mock library loader. + */ + @VisibleForTesting + public static void setLibraryLoaderForTesting(LibraryLoader loader) { + sInstance = loader; + } + private native void nativeInitCommandLine(String[] initCommandLine); // Only methods needed before or during normal JNI registration are during System.OnLoad. diff --git a/base/android/java/src/org/chromium/base/library_loader/Linker.java b/base/android/java/src/org/chromium/base/library_loader/Linker.java index 271e6cb..7d19995 100644 --- a/base/android/java/src/org/chromium/base/library_loader/Linker.java +++ b/base/android/java/src/org/chromium/base/library_loader/Linker.java @@ -503,32 +503,24 @@ public abstract class Linker { } /** - * Determine whether a library is the linker library. Also deal with the - * component build that adds a .cr suffix to the name. + * Determine whether a library is the linker library. * * @param library the name of the library. * @return true is the library is the Linker's own JNI library. */ public boolean isChromiumLinkerLibrary(String library) { - return library.equals(LINKER_JNI_LIBRARY) || library.equals(LINKER_JNI_LIBRARY + ".cr"); + return library.equals(LINKER_JNI_LIBRARY); } /** * Load the Linker JNI library. Throws UnsatisfiedLinkError on error. - * In a component build, the suffix ".cr" is added to each library name, so - * if the initial load fails we retry with a suffix. */ protected static void loadLinkerJniLibrary() { String libName = "lib" + LINKER_JNI_LIBRARY + ".so"; if (DEBUG) { Log.i(TAG, "Loading " + libName); } - try { - System.loadLibrary(LINKER_JNI_LIBRARY); - } catch (UnsatisfiedLinkError e) { - Log.w(TAG, "Couldn't load " + libName + ", trying " + libName + ".cr"); - System.loadLibrary(LINKER_JNI_LIBRARY + ".cr"); - } + System.loadLibrary(LINKER_JNI_LIBRARY); } /** diff --git a/base/android/java/src/org/chromium/base/library_loader/ModernLinker.java b/base/android/java/src/org/chromium/base/library_loader/ModernLinker.java index 7716a8d..15021c7 100644 --- a/base/android/java/src/org/chromium/base/library_loader/ModernLinker.java +++ b/base/android/java/src/org/chromium/base/library_loader/ModernLinker.java @@ -251,8 +251,6 @@ class ModernLinker extends Linker { Log.i(TAG, "disableSharedRelros() called"); } synchronized (mLock) { - assert !mPrepareLibraryLoadCalled; - // Mark this as a service process, and disable wait for shared RELRO. mInBrowserProcess = false; mWaitForSharedRelros = false; @@ -388,7 +386,7 @@ class ModernLinker extends Linker { // We are in the browser, and with a current load address that indicates that // there is enough address space for shared RELRO to operate. Create the // shared RELRO, and store it in the map. - String relroPath = PathUtils.getDataDirectory(null) + "/RELRO:" + libFilePath; + String relroPath = PathUtils.getDataDirectory() + "/RELRO:" + libFilePath; if (nativeCreateSharedRelro(dlopenExtPath, mCurrentLoadAddress, relroPath, libInfo)) { mSharedRelros.put(dlopenExtPath, libInfo); diff --git a/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java b/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java index 56a5803..eaf57b7 100644 --- a/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java +++ b/base/android/java/src/org/chromium/base/metrics/RecordHistogram.java @@ -25,16 +25,23 @@ import java.util.concurrent.TimeUnit; */ @JNINamespace("base::android") public class RecordHistogram { - private static boolean sIsDisabledForTests = false; + private static Throwable sDisabledBy; private static Map<String, Long> sCache = Collections.synchronizedMap(new HashMap<String, Long>()); /** - * Tests may not have native initialized, so they may need to disable metrics. + * Tests may not have native initialized, so they may need to disable metrics. The value should + * be reset after the test done, to avoid carrying over state to unrelated tests. + * + * In JUnit tests this can be done automatically using + * {@link org.chromium.chrome.browser.DisableHistogramsRule} */ @VisibleForTesting - public static void disableForTests() { - sIsDisabledForTests = true; + public static void setDisabledForTests(boolean disabled) { + if (disabled && sDisabledBy != null) { + throw new IllegalStateException("Histograms are already disabled.", sDisabledBy); + } + sDisabledBy = disabled ? new Throwable() : null; } private static long getCachedHistogramKey(String name) { @@ -54,7 +61,7 @@ public class RecordHistogram { * @param sample sample to be recorded, either true or false */ public static void recordBooleanHistogram(String name, boolean sample) { - if (sIsDisabledForTests) return; + if (sDisabledBy != null) return; long key = getCachedHistogramKey(name); long result = nativeRecordBooleanHistogram(name, key, sample); if (result != key) sCache.put(name, result); @@ -70,7 +77,7 @@ public class RecordHistogram { * lower than |boundary| */ public static void recordEnumeratedHistogram(String name, int sample, int boundary) { - if (sIsDisabledForTests) return; + if (sDisabledBy != null) return; long key = getCachedHistogramKey(name); long result = nativeRecordEnumeratedHistogram(name, key, sample, boundary); if (result != key) sCache.put(name, result); @@ -111,13 +118,13 @@ public class RecordHistogram { * UMA_HISTOGRAM_CUSTOM_COUNTS C++ macro. * @param name name of the histogram * @param sample sample to be recorded, at least |min| and at most |max| - 1 - * @param min lower bound for expected sample values + * @param min lower bound for expected sample values. It must be >= 1 * @param max upper bounds for expected sample values * @param numBuckets the number of buckets */ public static void recordCustomCountHistogram( String name, int sample, int min, int max, int numBuckets) { - if (sIsDisabledForTests) return; + if (sDisabledBy != null) return; long key = getCachedHistogramKey(name); long result = nativeRecordCustomCountHistogram(name, key, sample, min, max, numBuckets); if (result != key) sCache.put(name, result); @@ -134,7 +141,7 @@ public class RecordHistogram { */ public static void recordLinearCountHistogram( String name, int sample, int min, int max, int numBuckets) { - if (sIsDisabledForTests) return; + if (sDisabledBy != null) return; long key = getCachedHistogramKey(name); long result = nativeRecordLinearCountHistogram(name, key, sample, min, max, numBuckets); if (result != key) sCache.put(name, result); @@ -147,7 +154,7 @@ public class RecordHistogram { * @param sample sample to be recorded, at least 0 and at most 100. */ public static void recordPercentageHistogram(String name, int sample) { - if (sIsDisabledForTests) return; + if (sDisabledBy != null) return; long key = getCachedHistogramKey(name); long result = nativeRecordEnumeratedHistogram(name, key, sample, 101); if (result != key) sCache.put(name, result); @@ -160,7 +167,7 @@ public class RecordHistogram { * values. */ public static void recordSparseSlowlyHistogram(String name, int sample) { - if (sIsDisabledForTests) return; + if (sDisabledBy != null) return; long key = getCachedHistogramKey(name); long result = nativeRecordSparseHistogram(name, key, sample); if (result != key) sCache.put(name, result); @@ -218,6 +225,19 @@ public class RecordHistogram { timeUnit.toMillis(min), timeUnit.toMillis(max), numBuckets); } + /** + * Records a sample in a histogram of sizes in KB. This is the Java equivalent of the + * UMA_HISTOGRAM_MEMORY_KB C++ macro. + * + * Good for sizes up to about 500MB. + * + * @param name name of the histogram. + * @param sizeInkB Sample to record in KB. + */ + public static void recordMemoryKBHistogram(String name, int sizeInKB) { + recordCustomCountHistogram(name, sizeInKB, 1000, 500000, 50); + } + private static int clampToInt(long value) { if (value > Integer.MAX_VALUE) return Integer.MAX_VALUE; // Note: Clamping to MIN_VALUE rather than 0, to let base/ histograms code @@ -228,7 +248,7 @@ public class RecordHistogram { private static void recordCustomTimesHistogramMilliseconds( String name, long duration, long min, long max, int numBuckets) { - if (sIsDisabledForTests) return; + if (sDisabledBy != null) return; long key = getCachedHistogramKey(name); // Note: Duration, min and max are clamped to int here because that's what's expected by // the native histograms API. Callers of these functions still pass longs because that's @@ -253,7 +273,7 @@ public class RecordHistogram { * Initializes the metrics system. */ public static void initialize() { - if (sIsDisabledForTests) return; + if (sDisabledBy != null) return; nativeInitialize(); } diff --git a/base/android/java_handler_thread.cc b/base/android/java_handler_thread.cc index 7527034..76ee301 100644 --- a/base/android/java_handler_thread.cc +++ b/base/android/java_handler_thread.cc @@ -20,8 +20,8 @@ namespace android { JavaHandlerThread::JavaHandlerThread(const char* name) { JNIEnv* env = base::android::AttachCurrentThread(); - java_thread_.Reset(Java_JavaHandlerThread_create( - env, ConvertUTF8ToJavaString(env, name).obj())); + java_thread_.Reset( + Java_JavaHandlerThread_create(env, ConvertUTF8ToJavaString(env, name))); } JavaHandlerThread::~JavaHandlerThread() { @@ -35,8 +35,7 @@ void JavaHandlerThread::Start() { base::WaitableEvent initialize_event( WaitableEvent::ResetPolicy::AUTOMATIC, WaitableEvent::InitialState::NOT_SIGNALED); - Java_JavaHandlerThread_start(env, - java_thread_.obj(), + Java_JavaHandlerThread_start(env, java_thread_, reinterpret_cast<intptr_t>(this), reinterpret_cast<intptr_t>(&initialize_event)); // Wait for thread to be initialized so it is ready to be used when Start @@ -49,8 +48,7 @@ void JavaHandlerThread::Stop() { JNIEnv* env = base::android::AttachCurrentThread(); base::WaitableEvent shutdown_event(WaitableEvent::ResetPolicy::AUTOMATIC, WaitableEvent::InitialState::NOT_SIGNALED); - Java_JavaHandlerThread_stop(env, - java_thread_.obj(), + Java_JavaHandlerThread_stop(env, java_thread_, reinterpret_cast<intptr_t>(this), reinterpret_cast<intptr_t>(&shutdown_event)); // Wait for thread to shut down before returning. @@ -63,17 +61,25 @@ void JavaHandlerThread::InitializeThread(JNIEnv* env, jlong event) { // TYPE_JAVA to get the Android java style message loop. message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_JAVA)); - static_cast<MessageLoopForUI*>(message_loop_.get())->Start(); + StartMessageLoop(); reinterpret_cast<base::WaitableEvent*>(event)->Signal(); } void JavaHandlerThread::StopThread(JNIEnv* env, const JavaParamRef<jobject>& obj, jlong event) { - static_cast<MessageLoopForUI*>(message_loop_.get())->QuitWhenIdle(); + StopMessageLoop(); reinterpret_cast<base::WaitableEvent*>(event)->Signal(); } +void JavaHandlerThread::StartMessageLoop() { + static_cast<MessageLoopForUI*>(message_loop_.get())->Start(); +} + +void JavaHandlerThread::StopMessageLoop() { + static_cast<MessageLoopForUI*>(message_loop_.get())->QuitWhenIdle(); +} + // static bool JavaHandlerThread::RegisterBindings(JNIEnv* env) { return RegisterNativesImpl(env); diff --git a/base/android/java_handler_thread.h b/base/android/java_handler_thread.h index 1709ff4..96f2f0a 100644 --- a/base/android/java_handler_thread.h +++ b/base/android/java_handler_thread.h @@ -14,7 +14,6 @@ namespace base { class MessageLoop; -class WaitableEvent; namespace android { @@ -41,10 +40,15 @@ class BASE_EXPORT JavaHandlerThread { const JavaParamRef<jobject>& obj, jlong event); + virtual void StartMessageLoop(); + virtual void StopMessageLoop(); + static bool RegisterBindings(JNIEnv* env); - private: + protected: std::unique_ptr<base::MessageLoop> message_loop_; + + private: ScopedJavaGlobalRef<jobject> java_thread_; }; diff --git a/base/android/java_message_handler_factory.h b/base/android/java_message_handler_factory.h new file mode 100644 index 0000000..801617d --- /dev/null +++ b/base/android/java_message_handler_factory.h @@ -0,0 +1,33 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_ANDROID_JAVA_MESSAGE_HANDLER_FACTORY_H_ +#define BASE_ANDROID_JAVA_MESSAGE_HANDLER_FACTORY_H_ + +#include "base/android/scoped_java_ref.h" +#include "base/message_loop/message_pump.h" + +namespace base { + +class MessagePumpForUI; +class WaitableEvent; + +namespace android { + +// Factory for creating the Java-side system message handler - only used for +// testing. +class JavaMessageHandlerFactory { + public: + virtual ~JavaMessageHandlerFactory() {} + virtual base::android::ScopedJavaLocalRef<jobject> CreateMessageHandler( + JNIEnv* env, + base::MessagePump::Delegate* delegate, + MessagePumpForUI* message_pump, + WaitableEvent* test_done_event) = 0; +}; + +} // namespace android +} // namespace base + +#endif // BASE_ANDROID_JAVA_MESSAGE_HANDLER_FACTORY_H_ diff --git a/base/android/java_runtime.cc b/base/android/java_runtime.cc index 5be9adf..5fae49a 100644 --- a/base/android/java_runtime.cc +++ b/base/android/java_runtime.cc @@ -9,16 +9,12 @@ namespace base { namespace android { -bool JavaRuntime::Register(JNIEnv* env) { - return JNI_Runtime::RegisterNativesImpl(env); -} - void JavaRuntime::GetMemoryUsage(long* total_memory, long* free_memory) { JNIEnv* env = base::android::AttachCurrentThread(); base::android::ScopedJavaLocalRef<jobject> runtime = JNI_Runtime::Java_Runtime_getRuntime(env); - *total_memory = JNI_Runtime::Java_Runtime_totalMemory(env, runtime.obj()); - *free_memory = JNI_Runtime::Java_Runtime_freeMemory(env, runtime.obj()); + *total_memory = JNI_Runtime::Java_Runtime_totalMemory(env, runtime); + *free_memory = JNI_Runtime::Java_Runtime_freeMemory(env, runtime); } } // namespace android diff --git a/base/android/java_runtime.h b/base/android/java_runtime.h index 4ca889e..2034fb9 100644 --- a/base/android/java_runtime.h +++ b/base/android/java_runtime.h @@ -14,9 +14,6 @@ namespace android { // Wrapper class for using the java.lang.Runtime object from jni. class BASE_EXPORT JavaRuntime { public: - // Registers the jni class (once per process). - static bool Register(JNIEnv* env); - // Fills the total memory used and memory allocated for objects by the java // heap in the current process. Returns true on success. static void GetMemoryUsage(long* total_memory, long* free_memory); diff --git a/base/android/javatests/src/org/chromium/base/AdvancedMockContextTest.java b/base/android/javatests/src/org/chromium/base/AdvancedMockContextTest.java index 02dbeb1..20c626d 100644 --- a/base/android/javatests/src/org/chromium/base/AdvancedMockContextTest.java +++ b/base/android/javatests/src/org/chromium/base/AdvancedMockContextTest.java @@ -1,4 +1,4 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -9,15 +9,21 @@ import android.content.ComponentCallbacks; import android.content.ComponentCallbacks2; import android.content.Context; import android.content.res.Configuration; -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.BaseJUnit4ClassRunner; import org.chromium.base.test.util.AdvancedMockContext; /** * Tests for {@link org.chromium.base.test.util.AdvancedMockContext}. */ -public class AdvancedMockContextTest extends InstrumentationTestCase { +@RunWith(BaseJUnit4ClassRunner.class) +public class AdvancedMockContextTest { private static class Callback1 implements ComponentCallbacks { protected Configuration mConfiguration; protected boolean mOnLowMemoryCalled; @@ -42,9 +48,10 @@ public class AdvancedMockContextTest extends InstrumentationTestCase { } } + @Test @SmallTest public void testComponentCallbacksForTargetContext() { - Context targetContext = getInstrumentation().getTargetContext(); + Context targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); Application targetApplication = (Application) targetContext.getApplicationContext(); AdvancedMockContext context = new AdvancedMockContext(targetContext); Callback1 callback1 = new Callback1(); @@ -53,18 +60,18 @@ public class AdvancedMockContextTest extends InstrumentationTestCase { context.registerComponentCallbacks(callback2); targetApplication.onLowMemory(); - assertTrue("onLowMemory should have been called.", callback1.mOnLowMemoryCalled); - assertTrue("onLowMemory should have been called.", callback2.mOnLowMemoryCalled); + Assert.assertTrue("onLowMemory should have been called.", callback1.mOnLowMemoryCalled); + Assert.assertTrue("onLowMemory should have been called.", callback2.mOnLowMemoryCalled); Configuration configuration = new Configuration(); targetApplication.onConfigurationChanged(configuration); - assertEquals("onConfigurationChanged should have been called.", configuration, + Assert.assertEquals("onConfigurationChanged should have been called.", configuration, callback1.mConfiguration); - assertEquals("onConfigurationChanged should have been called.", configuration, + Assert.assertEquals("onConfigurationChanged should have been called.", configuration, callback2.mConfiguration); targetApplication.onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_MODERATE); - assertEquals("onTrimMemory should have been called.", ComponentCallbacks2 - .TRIM_MEMORY_MODERATE, callback2.mLevel); + Assert.assertEquals("onTrimMemory should have been called.", + ComponentCallbacks2.TRIM_MEMORY_MODERATE, callback2.mLevel); } } diff --git a/base/android/javatests/src/org/chromium/base/ApiCompatibilityUtilsTest.java b/base/android/javatests/src/org/chromium/base/ApiCompatibilityUtilsTest.java index 45b3e21..de0858a 100644 --- a/base/android/javatests/src/org/chromium/base/ApiCompatibilityUtilsTest.java +++ b/base/android/javatests/src/org/chromium/base/ApiCompatibilityUtilsTest.java @@ -1,4 +1,4 @@ -// Copyright 2015 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. @@ -8,13 +8,20 @@ import android.annotation.TargetApi; import android.app.Activity; import android.os.Build; import android.os.SystemClock; -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.BaseJUnit4ClassRunner; /** * Test of ApiCompatibilityUtils */ -public class ApiCompatibilityUtilsTest extends InstrumentationTestCase { +@RunWith(BaseJUnit4ClassRunner.class) +public class ApiCompatibilityUtilsTest { private static final long WAIT_TIMEOUT_IN_MS = 5000; private static final long SLEEP_INTERVAL_IN_MS = 50; @@ -42,30 +49,40 @@ public class ApiCompatibilityUtilsTest extends InstrumentationTestCase { } } + @Test @SmallTest - public void testFinishAndRemoveTask() throws InterruptedException { - MockActivity activity = new MockActivity(); - ApiCompatibilityUtils.finishAndRemoveTask(activity); + public void testFinishAndRemoveTask() { + InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + MockActivity activity = new MockActivity(); + ApiCompatibilityUtils.finishAndRemoveTask(activity); - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { - assertEquals(1, activity.mFinishAndRemoveTaskCallbackCount); - assertEquals(0, activity.mFinishCallbackCount); - } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) { - long startTime = SystemClock.uptimeMillis(); - while (activity.mFinishCallbackCount == 0 - && SystemClock.uptimeMillis() - startTime < WAIT_TIMEOUT_IN_MS) { - Thread.sleep(SLEEP_INTERVAL_IN_MS); - } + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { + Assert.assertEquals(1, activity.mFinishAndRemoveTaskCallbackCount); + Assert.assertEquals(0, activity.mFinishCallbackCount); + } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) { + long startTime = SystemClock.uptimeMillis(); + while (activity.mFinishCallbackCount == 0 + && SystemClock.uptimeMillis() - startTime < WAIT_TIMEOUT_IN_MS) { + try { + Thread.sleep(SLEEP_INTERVAL_IN_MS); + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted thread sleep", e); + } + } - // MockActivity#finishAndRemoveTask() never sets isFinishing() to true for LOLLIPOP to - // simulate an exceptional case. In that case, MockActivity#finish() should be called - // after 3 tries. - assertEquals(3, activity.mFinishAndRemoveTaskCallbackCount); - assertEquals(1, activity.mFinishCallbackCount); - } else { - assertEquals(0, activity.mFinishAndRemoveTaskCallbackCount); - assertEquals(1, activity.mFinishCallbackCount); - } - assertTrue(activity.mIsFinishing); + // MockActivity#finishAndRemoveTask() never sets isFinishing() to true for + // LOLLIPOP to simulate an exceptional case. In that case, MockActivity#finish() + // should be called after 3 tries. + Assert.assertEquals(3, activity.mFinishAndRemoveTaskCallbackCount); + Assert.assertEquals(1, activity.mFinishCallbackCount); + } else { + Assert.assertEquals(0, activity.mFinishAndRemoveTaskCallbackCount); + Assert.assertEquals(1, activity.mFinishCallbackCount); + } + Assert.assertTrue(activity.mIsFinishing); + } + }); } } diff --git a/base/android/javatests/src/org/chromium/base/CommandLineTest.java b/base/android/javatests/src/org/chromium/base/CommandLineTest.java index 2b1a967..47749fb 100644 --- a/base/android/javatests/src/org/chromium/base/CommandLineTest.java +++ b/base/android/javatests/src/org/chromium/base/CommandLineTest.java @@ -1,15 +1,21 @@ -// Copyright 2013 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. package org.chromium.base; -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.SmallTest; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.BaseJUnit4ClassRunner; import org.chromium.base.test.util.Feature; -public class CommandLineTest extends InstrumentationTestCase { +@RunWith(BaseJUnit4ClassRunner.class) +public class CommandLineTest { // A reference command line. Note that switch2 is [brea\d], switch3 is [and "butter"], // and switch4 is [a "quoted" 'food'!] static final String INIT_SWITCHES[] = { "init_command", "--SWITCH", "Arg", @@ -27,60 +33,61 @@ public class CommandLineTest extends InstrumentationTestCase { static final String CL_ADDED_SWITCH_2 = "username"; static final String CL_ADDED_VALUE_2 = "bozo"; - @Override + @Before public void setUp() throws Exception { CommandLine.reset(); } void checkInitSwitches() { CommandLine cl = CommandLine.getInstance(); - assertFalse(cl.hasSwitch("init_command")); - assertFalse(cl.hasSwitch("switch")); - assertTrue(cl.hasSwitch("SWITCH")); - assertFalse(cl.hasSwitch("--SWITCH")); - assertFalse(cl.hasSwitch("Arg")); - assertFalse(cl.hasSwitch("actually_an_arg")); - assertEquals("brea\\d", cl.getSwitchValue("switch2")); - assertEquals("and \"butter\"", cl.getSwitchValue("switch3")); - assertEquals("a \"quoted\" 'food'!", cl.getSwitchValue("switch4")); - assertNull(cl.getSwitchValue("SWITCH")); - assertNull(cl.getSwitchValue("non-existant")); + Assert.assertFalse(cl.hasSwitch("init_command")); + Assert.assertFalse(cl.hasSwitch("switch")); + Assert.assertTrue(cl.hasSwitch("SWITCH")); + Assert.assertFalse(cl.hasSwitch("--SWITCH")); + Assert.assertFalse(cl.hasSwitch("Arg")); + Assert.assertFalse(cl.hasSwitch("actually_an_arg")); + Assert.assertEquals("brea\\d", cl.getSwitchValue("switch2")); + Assert.assertEquals("and \"butter\"", cl.getSwitchValue("switch3")); + Assert.assertEquals("a \"quoted\" 'food'!", cl.getSwitchValue("switch4")); + Assert.assertNull(cl.getSwitchValue("SWITCH")); + Assert.assertNull(cl.getSwitchValue("non-existant")); } void checkSettingThenGetting() { CommandLine cl = CommandLine.getInstance(); // Add a plain switch. - assertFalse(cl.hasSwitch(CL_ADDED_SWITCH)); + Assert.assertFalse(cl.hasSwitch(CL_ADDED_SWITCH)); cl.appendSwitch(CL_ADDED_SWITCH); - assertTrue(cl.hasSwitch(CL_ADDED_SWITCH)); + Assert.assertTrue(cl.hasSwitch(CL_ADDED_SWITCH)); // Add a switch paired with a value. - assertFalse(cl.hasSwitch(CL_ADDED_SWITCH_2)); - assertNull(cl.getSwitchValue(CL_ADDED_SWITCH_2)); + Assert.assertFalse(cl.hasSwitch(CL_ADDED_SWITCH_2)); + Assert.assertNull(cl.getSwitchValue(CL_ADDED_SWITCH_2)); cl.appendSwitchWithValue(CL_ADDED_SWITCH_2, CL_ADDED_VALUE_2); - assertTrue(CL_ADDED_VALUE_2.equals(cl.getSwitchValue(CL_ADDED_SWITCH_2))); + Assert.assertTrue(CL_ADDED_VALUE_2.equals(cl.getSwitchValue(CL_ADDED_SWITCH_2))); // Append a few new things. final String switchesAndArgs[] = { "dummy", "--superfast", "--speed=turbo" }; - assertFalse(cl.hasSwitch("dummy")); - assertFalse(cl.hasSwitch("superfast")); - assertNull(cl.getSwitchValue("speed")); + Assert.assertFalse(cl.hasSwitch("dummy")); + Assert.assertFalse(cl.hasSwitch("superfast")); + Assert.assertNull(cl.getSwitchValue("speed")); cl.appendSwitchesAndArguments(switchesAndArgs); - assertFalse(cl.hasSwitch("dummy")); - assertFalse(cl.hasSwitch("command")); - assertTrue(cl.hasSwitch("superfast")); - assertTrue("turbo".equals(cl.getSwitchValue("speed"))); + Assert.assertFalse(cl.hasSwitch("dummy")); + Assert.assertFalse(cl.hasSwitch("command")); + Assert.assertTrue(cl.hasSwitch("superfast")); + Assert.assertTrue("turbo".equals(cl.getSwitchValue("speed"))); } void checkTokenizer(String[] expected, String toParse) { String[] actual = CommandLine.tokenizeQuotedAruments(toParse.toCharArray()); - assertEquals(expected.length, actual.length); + Assert.assertEquals(expected.length, actual.length); for (int i = 0; i < expected.length; ++i) { - assertEquals("comparing element " + i, expected[i], actual[i]); + Assert.assertEquals("comparing element " + i, expected[i], actual[i]); } } + @Test @SmallTest @Feature({"Android-AppBase"}) public void testJavaInitialization() { @@ -89,6 +96,7 @@ public class CommandLineTest extends InstrumentationTestCase { checkSettingThenGetting(); } + @Test @SmallTest @Feature({"Android-AppBase"}) public void testBufferInitialization() { @@ -97,6 +105,7 @@ public class CommandLineTest extends InstrumentationTestCase { checkSettingThenGetting(); } + @Test @SmallTest @Feature({"Android-AppBase"}) public void testArgumentTokenizer() { diff --git a/base/android/javatests/src/org/chromium/base/ObserverListTest.java b/base/android/javatests/src/org/chromium/base/ObserverListTest.java index 94a9c71..1899f45 100644 --- a/base/android/javatests/src/org/chromium/base/ObserverListTest.java +++ b/base/android/javatests/src/org/chromium/base/ObserverListTest.java @@ -1,12 +1,16 @@ -// Copyright 2013 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. package org.chromium.base; -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.SmallTest; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.BaseJUnit4ClassRunner; import org.chromium.base.test.util.Feature; import java.util.Collection; @@ -16,7 +20,8 @@ import java.util.NoSuchElementException; /** * Tests for (@link ObserverList}. */ -public class ObserverListTest extends InstrumentationTestCase { +@RunWith(BaseJUnit4ClassRunner.class) +public class ObserverListTest { interface Observer { void observe(int x); } @@ -79,6 +84,7 @@ public class ObserverListTest extends InstrumentationTestCase { return num; } + @Test @SmallTest @Feature({"Android-AppBase"}) public void testRemoveWhileIteration() { @@ -105,17 +111,18 @@ public class ObserverListTest extends InstrumentationTestCase { for (Observer obs : observerList) obs.observe(10); // observe should be called twice on a. - assertEquals(20, a.mTotal); + Assert.assertEquals(20, a.mTotal); // observe should be called twice on b. - assertEquals(-20, b.mTotal); + Assert.assertEquals(-20, b.mTotal); // evil removed c from the observerList before it got any callbacks. - assertEquals(0, c.mTotal); + Assert.assertEquals(0, c.mTotal); // observe should be called once on d. - assertEquals(-10, d.mTotal); + Assert.assertEquals(-10, d.mTotal); // e was never added to the list, observe should not be called. - assertEquals(0, e.mTotal); + Assert.assertEquals(0, e.mTotal); } + @Test @SmallTest @Feature({"Android-AppBase"}) public void testAddWhileIteration() { @@ -131,12 +138,13 @@ public class ObserverListTest extends InstrumentationTestCase { for (Observer obs : observerList) obs.observe(10); - assertTrue(observerList.hasObserver(c)); - assertEquals(10, a.mTotal); - assertEquals(-10, b.mTotal); - assertEquals(0, c.mTotal); + Assert.assertTrue(observerList.hasObserver(c)); + Assert.assertEquals(10, a.mTotal); + Assert.assertEquals(-10, b.mTotal); + Assert.assertEquals(0, c.mTotal); } + @Test @SmallTest @Feature({"Android-AppBase"}) public void testIterator() { @@ -144,38 +152,39 @@ public class ObserverListTest extends InstrumentationTestCase { observerList.addObserver(5); observerList.addObserver(10); observerList.addObserver(15); - assertEquals(3, getSizeOfIterable(observerList)); + Assert.assertEquals(3, getSizeOfIterable(observerList)); observerList.removeObserver(10); - assertEquals(2, getSizeOfIterable(observerList)); + Assert.assertEquals(2, getSizeOfIterable(observerList)); Iterator<Integer> it = observerList.iterator(); - assertTrue(it.hasNext()); - assertTrue(5 == it.next()); - assertTrue(it.hasNext()); - assertTrue(15 == it.next()); - assertFalse(it.hasNext()); + Assert.assertTrue(it.hasNext()); + Assert.assertTrue(5 == it.next()); + Assert.assertTrue(it.hasNext()); + Assert.assertTrue(15 == it.next()); + Assert.assertFalse(it.hasNext()); boolean removeExceptionThrown = false; try { it.remove(); - fail("Expecting UnsupportedOperationException to be thrown here."); + Assert.fail("Expecting UnsupportedOperationException to be thrown here."); } catch (UnsupportedOperationException e) { removeExceptionThrown = true; } - assertTrue(removeExceptionThrown); - assertEquals(2, getSizeOfIterable(observerList)); + Assert.assertTrue(removeExceptionThrown); + Assert.assertEquals(2, getSizeOfIterable(observerList)); boolean noElementExceptionThrown = false; try { it.next(); - fail("Expecting NoSuchElementException to be thrown here."); + Assert.fail("Expecting NoSuchElementException to be thrown here."); } catch (NoSuchElementException e) { noElementExceptionThrown = true; } - assertTrue(noElementExceptionThrown); + Assert.assertTrue(noElementExceptionThrown); } + @Test @SmallTest @Feature({"Android-AppBase"}) public void testRewindableIterator() { @@ -183,52 +192,54 @@ public class ObserverListTest extends InstrumentationTestCase { observerList.addObserver(5); observerList.addObserver(10); observerList.addObserver(15); - assertEquals(3, getSizeOfIterable(observerList)); + Assert.assertEquals(3, getSizeOfIterable(observerList)); ObserverList.RewindableIterator<Integer> it = observerList.rewindableIterator(); - assertTrue(it.hasNext()); - assertTrue(5 == it.next()); - assertTrue(it.hasNext()); - assertTrue(10 == it.next()); - assertTrue(it.hasNext()); - assertTrue(15 == it.next()); - assertFalse(it.hasNext()); + Assert.assertTrue(it.hasNext()); + Assert.assertTrue(5 == it.next()); + Assert.assertTrue(it.hasNext()); + Assert.assertTrue(10 == it.next()); + Assert.assertTrue(it.hasNext()); + Assert.assertTrue(15 == it.next()); + Assert.assertFalse(it.hasNext()); it.rewind(); - assertTrue(it.hasNext()); - assertTrue(5 == it.next()); - assertTrue(it.hasNext()); - assertTrue(10 == it.next()); - assertTrue(it.hasNext()); - assertTrue(15 == it.next()); - assertEquals(5, (int) observerList.mObservers.get(0)); + Assert.assertTrue(it.hasNext()); + Assert.assertTrue(5 == it.next()); + Assert.assertTrue(it.hasNext()); + Assert.assertTrue(10 == it.next()); + Assert.assertTrue(it.hasNext()); + Assert.assertTrue(15 == it.next()); + Assert.assertEquals(5, (int) observerList.mObservers.get(0)); observerList.removeObserver(5); - assertEquals(null, observerList.mObservers.get(0)); + Assert.assertEquals(null, observerList.mObservers.get(0)); it.rewind(); - assertEquals(10, (int) observerList.mObservers.get(0)); - assertTrue(it.hasNext()); - assertTrue(10 == it.next()); - assertTrue(it.hasNext()); - assertTrue(15 == it.next()); + Assert.assertEquals(10, (int) observerList.mObservers.get(0)); + Assert.assertTrue(it.hasNext()); + Assert.assertTrue(10 == it.next()); + Assert.assertTrue(it.hasNext()); + Assert.assertTrue(15 == it.next()); } + @Test @SmallTest @Feature({"Android-AppBase"}) public void testAddObserverReturnValue() { ObserverList<Object> observerList = new ObserverList<Object>(); Object a = new Object(); - assertTrue(observerList.addObserver(a)); - assertFalse(observerList.addObserver(a)); + Assert.assertTrue(observerList.addObserver(a)); + Assert.assertFalse(observerList.addObserver(a)); Object b = new Object(); - assertTrue(observerList.addObserver(b)); - assertFalse(observerList.addObserver(null)); + Assert.assertTrue(observerList.addObserver(b)); + Assert.assertFalse(observerList.addObserver(null)); } + @Test @SmallTest @Feature({"Android-AppBase"}) public void testRemoveObserverReturnValue() { @@ -239,91 +250,92 @@ public class ObserverListTest extends InstrumentationTestCase { observerList.addObserver(a); observerList.addObserver(b); - assertTrue(observerList.removeObserver(a)); - assertFalse(observerList.removeObserver(a)); - assertFalse(observerList.removeObserver(new Object())); - assertTrue(observerList.removeObserver(b)); - assertFalse(observerList.removeObserver(null)); + Assert.assertTrue(observerList.removeObserver(a)); + Assert.assertFalse(observerList.removeObserver(a)); + Assert.assertFalse(observerList.removeObserver(new Object())); + Assert.assertTrue(observerList.removeObserver(b)); + Assert.assertFalse(observerList.removeObserver(null)); // If we remove an object while iterating, it will be replaced by 'null'. observerList.addObserver(a); - assertTrue(observerList.removeObserver(a)); - assertFalse(observerList.removeObserver(null)); + Assert.assertTrue(observerList.removeObserver(a)); + Assert.assertFalse(observerList.removeObserver(null)); } + @Test @SmallTest @Feature({"Android-AppBase"}) public void testSize() { ObserverList<Object> observerList = new ObserverList<Object>(); - assertEquals(0, observerList.size()); - assertTrue(observerList.isEmpty()); + Assert.assertEquals(0, observerList.size()); + Assert.assertTrue(observerList.isEmpty()); observerList.addObserver(null); - assertEquals(0, observerList.size()); - assertTrue(observerList.isEmpty()); + Assert.assertEquals(0, observerList.size()); + Assert.assertTrue(observerList.isEmpty()); Object a = new Object(); observerList.addObserver(a); - assertEquals(1, observerList.size()); - assertFalse(observerList.isEmpty()); + Assert.assertEquals(1, observerList.size()); + Assert.assertFalse(observerList.isEmpty()); observerList.addObserver(a); - assertEquals(1, observerList.size()); - assertFalse(observerList.isEmpty()); + Assert.assertEquals(1, observerList.size()); + Assert.assertFalse(observerList.isEmpty()); observerList.addObserver(null); - assertEquals(1, observerList.size()); - assertFalse(observerList.isEmpty()); + Assert.assertEquals(1, observerList.size()); + Assert.assertFalse(observerList.isEmpty()); Object b = new Object(); observerList.addObserver(b); - assertEquals(2, observerList.size()); - assertFalse(observerList.isEmpty()); + Assert.assertEquals(2, observerList.size()); + Assert.assertFalse(observerList.isEmpty()); observerList.removeObserver(null); - assertEquals(2, observerList.size()); - assertFalse(observerList.isEmpty()); + Assert.assertEquals(2, observerList.size()); + Assert.assertFalse(observerList.isEmpty()); observerList.removeObserver(new Object()); - assertEquals(2, observerList.size()); - assertFalse(observerList.isEmpty()); + Assert.assertEquals(2, observerList.size()); + Assert.assertFalse(observerList.isEmpty()); observerList.removeObserver(b); - assertEquals(1, observerList.size()); - assertFalse(observerList.isEmpty()); + Assert.assertEquals(1, observerList.size()); + Assert.assertFalse(observerList.isEmpty()); observerList.removeObserver(b); - assertEquals(1, observerList.size()); - assertFalse(observerList.isEmpty()); + Assert.assertEquals(1, observerList.size()); + Assert.assertFalse(observerList.isEmpty()); observerList.removeObserver(a); - assertEquals(0, observerList.size()); - assertTrue(observerList.isEmpty()); + Assert.assertEquals(0, observerList.size()); + Assert.assertTrue(observerList.isEmpty()); observerList.removeObserver(a); observerList.removeObserver(b); observerList.removeObserver(null); observerList.removeObserver(new Object()); - assertEquals(0, observerList.size()); - assertTrue(observerList.isEmpty()); + Assert.assertEquals(0, observerList.size()); + Assert.assertTrue(observerList.isEmpty()); observerList.addObserver(new Object()); observerList.addObserver(new Object()); observerList.addObserver(new Object()); observerList.addObserver(a); - assertEquals(4, observerList.size()); - assertFalse(observerList.isEmpty()); + Assert.assertEquals(4, observerList.size()); + Assert.assertFalse(observerList.isEmpty()); observerList.clear(); - assertEquals(0, observerList.size()); - assertTrue(observerList.isEmpty()); + Assert.assertEquals(0, observerList.size()); + Assert.assertTrue(observerList.isEmpty()); observerList.removeObserver(a); observerList.removeObserver(b); observerList.removeObserver(null); observerList.removeObserver(new Object()); - assertEquals(0, observerList.size()); - assertTrue(observerList.isEmpty()); + Assert.assertEquals(0, observerList.size()); + Assert.assertTrue(observerList.isEmpty()); } } diff --git a/base/android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java b/base/android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java index 6b1888c..daf639b 100644 --- a/base/android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java +++ b/base/android/javatests/src/org/chromium/base/metrics/RecordHistogramTest.java @@ -1,14 +1,19 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium.base.metrics; -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import org.chromium.base.library_loader.LibraryLoader; import org.chromium.base.library_loader.LibraryProcessType; +import org.chromium.base.test.BaseJUnit4ClassRunner; import org.chromium.base.test.util.MetricsUtils.HistogramDelta; import java.util.concurrent.TimeUnit; @@ -16,42 +21,43 @@ import java.util.concurrent.TimeUnit; /** * Tests for the Java API for recording UMA histograms. */ -public class RecordHistogramTest extends InstrumentationTestCase { - @Override - protected void setUp() throws Exception { - super.setUp(); - LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER) - .ensureInitialized(getInstrumentation().getTargetContext()); +@RunWith(BaseJUnit4ClassRunner.class) +public class RecordHistogramTest { + @Before + public void setUp() throws Exception { + LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER).ensureInitialized(); RecordHistogram.initialize(); } /** * Tests recording of boolean histograms. */ + @Test @SmallTest public void testRecordBooleanHistogram() { String histogram = "HelloWorld.BooleanMetric"; HistogramDelta falseCount = new HistogramDelta(histogram, 0); HistogramDelta trueCount = new HistogramDelta(histogram, 1); - assertEquals(0, trueCount.getDelta()); - assertEquals(0, falseCount.getDelta()); + Assert.assertEquals(0, trueCount.getDelta()); + Assert.assertEquals(0, falseCount.getDelta()); RecordHistogram.recordBooleanHistogram(histogram, true); - assertEquals(1, trueCount.getDelta()); - assertEquals(0, falseCount.getDelta()); + Assert.assertEquals(1, trueCount.getDelta()); + Assert.assertEquals(0, falseCount.getDelta()); RecordHistogram.recordBooleanHistogram(histogram, true); - assertEquals(2, trueCount.getDelta()); - assertEquals(0, falseCount.getDelta()); + Assert.assertEquals(2, trueCount.getDelta()); + Assert.assertEquals(0, falseCount.getDelta()); RecordHistogram.recordBooleanHistogram(histogram, false); - assertEquals(2, trueCount.getDelta()); - assertEquals(1, falseCount.getDelta()); + Assert.assertEquals(2, trueCount.getDelta()); + Assert.assertEquals(1, falseCount.getDelta()); } /** * Tests recording of enumerated histograms. */ + @Test @SmallTest public void testRecordEnumeratedHistogram() { String histogram = "HelloWorld.EnumeratedMetric"; @@ -60,29 +66,30 @@ public class RecordHistogramTest extends InstrumentationTestCase { HistogramDelta twoCount = new HistogramDelta(histogram, 2); final int boundary = 3; - assertEquals(0, zeroCount.getDelta()); - assertEquals(0, oneCount.getDelta()); - assertEquals(0, twoCount.getDelta()); + Assert.assertEquals(0, zeroCount.getDelta()); + Assert.assertEquals(0, oneCount.getDelta()); + Assert.assertEquals(0, twoCount.getDelta()); RecordHistogram.recordEnumeratedHistogram(histogram, 0, boundary); - assertEquals(1, zeroCount.getDelta()); - assertEquals(0, oneCount.getDelta()); - assertEquals(0, twoCount.getDelta()); + Assert.assertEquals(1, zeroCount.getDelta()); + Assert.assertEquals(0, oneCount.getDelta()); + Assert.assertEquals(0, twoCount.getDelta()); RecordHistogram.recordEnumeratedHistogram(histogram, 0, boundary); - assertEquals(2, zeroCount.getDelta()); - assertEquals(0, oneCount.getDelta()); - assertEquals(0, twoCount.getDelta()); + Assert.assertEquals(2, zeroCount.getDelta()); + Assert.assertEquals(0, oneCount.getDelta()); + Assert.assertEquals(0, twoCount.getDelta()); RecordHistogram.recordEnumeratedHistogram(histogram, 2, boundary); - assertEquals(2, zeroCount.getDelta()); - assertEquals(0, oneCount.getDelta()); - assertEquals(1, twoCount.getDelta()); + Assert.assertEquals(2, zeroCount.getDelta()); + Assert.assertEquals(0, oneCount.getDelta()); + Assert.assertEquals(1, twoCount.getDelta()); } /** * Tests recording of count histograms. */ + @Test @SmallTest public void testRecordCountHistogram() { String histogram = "HelloWorld.CountMetric"; @@ -91,39 +98,40 @@ public class RecordHistogramTest extends InstrumentationTestCase { HistogramDelta twoCount = new HistogramDelta(histogram, 2); HistogramDelta eightThousandCount = new HistogramDelta(histogram, 8000); - assertEquals(0, zeroCount.getDelta()); - assertEquals(0, oneCount.getDelta()); - assertEquals(0, twoCount.getDelta()); - assertEquals(0, eightThousandCount.getDelta()); + Assert.assertEquals(0, zeroCount.getDelta()); + Assert.assertEquals(0, oneCount.getDelta()); + Assert.assertEquals(0, twoCount.getDelta()); + Assert.assertEquals(0, eightThousandCount.getDelta()); RecordHistogram.recordCountHistogram(histogram, 0); - assertEquals(1, zeroCount.getDelta()); - assertEquals(0, oneCount.getDelta()); - assertEquals(0, twoCount.getDelta()); - assertEquals(0, eightThousandCount.getDelta()); + Assert.assertEquals(1, zeroCount.getDelta()); + Assert.assertEquals(0, oneCount.getDelta()); + Assert.assertEquals(0, twoCount.getDelta()); + Assert.assertEquals(0, eightThousandCount.getDelta()); RecordHistogram.recordCountHistogram(histogram, 0); - assertEquals(2, zeroCount.getDelta()); - assertEquals(0, oneCount.getDelta()); - assertEquals(0, twoCount.getDelta()); - assertEquals(0, eightThousandCount.getDelta()); + Assert.assertEquals(2, zeroCount.getDelta()); + Assert.assertEquals(0, oneCount.getDelta()); + Assert.assertEquals(0, twoCount.getDelta()); + Assert.assertEquals(0, eightThousandCount.getDelta()); RecordHistogram.recordCountHistogram(histogram, 2); - assertEquals(2, zeroCount.getDelta()); - assertEquals(0, oneCount.getDelta()); - assertEquals(1, twoCount.getDelta()); - assertEquals(0, eightThousandCount.getDelta()); + Assert.assertEquals(2, zeroCount.getDelta()); + Assert.assertEquals(0, oneCount.getDelta()); + Assert.assertEquals(1, twoCount.getDelta()); + Assert.assertEquals(0, eightThousandCount.getDelta()); RecordHistogram.recordCountHistogram(histogram, 8000); - assertEquals(2, zeroCount.getDelta()); - assertEquals(0, oneCount.getDelta()); - assertEquals(1, twoCount.getDelta()); - assertEquals(1, eightThousandCount.getDelta()); + Assert.assertEquals(2, zeroCount.getDelta()); + Assert.assertEquals(0, oneCount.getDelta()); + Assert.assertEquals(1, twoCount.getDelta()); + Assert.assertEquals(1, eightThousandCount.getDelta()); } /** * Tests recording of custom times histograms. */ + @Test @SmallTest public void testRecordCustomTimesHistogram() { String histogram = "HelloWorld.CustomTimesMetric"; @@ -131,36 +139,37 @@ public class RecordHistogramTest extends InstrumentationTestCase { HistogramDelta oneCount = new HistogramDelta(histogram, 1); HistogramDelta twoCount = new HistogramDelta(histogram, 100); - assertEquals(0, zeroCount.getDelta()); - assertEquals(0, oneCount.getDelta()); - assertEquals(0, twoCount.getDelta()); + Assert.assertEquals(0, zeroCount.getDelta()); + Assert.assertEquals(0, oneCount.getDelta()); + Assert.assertEquals(0, twoCount.getDelta()); TimeUnit milli = TimeUnit.MILLISECONDS; RecordHistogram.recordCustomTimesHistogram(histogram, 0, 1, 100, milli, 3); - assertEquals(1, zeroCount.getDelta()); - assertEquals(0, oneCount.getDelta()); - assertEquals(0, twoCount.getDelta()); + Assert.assertEquals(1, zeroCount.getDelta()); + Assert.assertEquals(0, oneCount.getDelta()); + Assert.assertEquals(0, twoCount.getDelta()); RecordHistogram.recordCustomTimesHistogram(histogram, 0, 1, 100, milli, 3); - assertEquals(2, zeroCount.getDelta()); - assertEquals(0, oneCount.getDelta()); - assertEquals(0, twoCount.getDelta()); + Assert.assertEquals(2, zeroCount.getDelta()); + Assert.assertEquals(0, oneCount.getDelta()); + Assert.assertEquals(0, twoCount.getDelta()); RecordHistogram.recordCustomTimesHistogram(histogram, 95, 1, 100, milli, 3); - assertEquals(2, zeroCount.getDelta()); - assertEquals(1, oneCount.getDelta()); - assertEquals(0, twoCount.getDelta()); + Assert.assertEquals(2, zeroCount.getDelta()); + Assert.assertEquals(1, oneCount.getDelta()); + Assert.assertEquals(0, twoCount.getDelta()); RecordHistogram.recordCustomTimesHistogram(histogram, 200, 1, 100, milli, 3); - assertEquals(2, zeroCount.getDelta()); - assertEquals(1, oneCount.getDelta()); - assertEquals(1, twoCount.getDelta()); + Assert.assertEquals(2, zeroCount.getDelta()); + Assert.assertEquals(1, oneCount.getDelta()); + Assert.assertEquals(1, twoCount.getDelta()); } /** * Tests recording of linear count histograms. */ + @Test @SmallTest public void testRecordLinearCountHistogram() { String histogram = "HelloWorld.LinearCountMetric"; @@ -171,23 +180,23 @@ public class RecordHistogramTest extends InstrumentationTestCase { final int max = 3; final int numBuckets = 4; - assertEquals(0, zeroCount.getDelta()); - assertEquals(0, oneCount.getDelta()); - assertEquals(0, twoCount.getDelta()); + Assert.assertEquals(0, zeroCount.getDelta()); + Assert.assertEquals(0, oneCount.getDelta()); + Assert.assertEquals(0, twoCount.getDelta()); RecordHistogram.recordLinearCountHistogram(histogram, 0, min, max, numBuckets); - assertEquals(1, zeroCount.getDelta()); - assertEquals(0, oneCount.getDelta()); - assertEquals(0, twoCount.getDelta()); + Assert.assertEquals(1, zeroCount.getDelta()); + Assert.assertEquals(0, oneCount.getDelta()); + Assert.assertEquals(0, twoCount.getDelta()); RecordHistogram.recordLinearCountHistogram(histogram, 0, min, max, numBuckets); - assertEquals(2, zeroCount.getDelta()); - assertEquals(0, oneCount.getDelta()); - assertEquals(0, twoCount.getDelta()); + Assert.assertEquals(2, zeroCount.getDelta()); + Assert.assertEquals(0, oneCount.getDelta()); + Assert.assertEquals(0, twoCount.getDelta()); RecordHistogram.recordLinearCountHistogram(histogram, 2, min, max, numBuckets); - assertEquals(2, zeroCount.getDelta()); - assertEquals(0, oneCount.getDelta()); - assertEquals(1, twoCount.getDelta()); + Assert.assertEquals(2, zeroCount.getDelta()); + Assert.assertEquals(0, oneCount.getDelta()); + Assert.assertEquals(1, twoCount.getDelta()); } } diff --git a/base/android/jni_android.cc b/base/android/jni_android.cc index 3b53624..56dc5c2 100644 --- a/base/android/jni_android.cc +++ b/base/android/jni_android.cc @@ -11,33 +11,40 @@ #include "base/android/build_info.h" #include "base/android/jni_string.h" #include "base/android/jni_utils.h" +#include "base/debug/debugging_flags.h" #include "base/lazy_instance.h" #include "base/logging.h" +#include "base/threading/thread_local.h" namespace { using base::android::GetClass; using base::android::MethodID; using base::android::ScopedJavaLocalRef; -bool g_disable_manual_jni_registration = false; +base::android::JniRegistrationType g_jni_registration_type = + base::android::ALL_JNI_REGISTRATION; JavaVM* g_jvm = NULL; -base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >::Leaky +base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject>>::Leaky g_class_loader = LAZY_INSTANCE_INITIALIZER; jmethodID g_class_loader_load_class_method_id = 0; +#if BUILDFLAG(ENABLE_PROFILING) && HAVE_TRACE_STACK_FRAME_POINTERS +base::LazyInstance<base::ThreadLocalPointer<void>>::Leaky + g_stack_frame_pointer = LAZY_INSTANCE_INITIALIZER; +#endif + } // namespace namespace base { namespace android { -bool IsManualJniRegistrationDisabled() { - return g_disable_manual_jni_registration; +JniRegistrationType GetJniRegistrationType() { + return g_jni_registration_type; } -void DisableManualJniRegistration() { - DCHECK(!g_disable_manual_jni_registration); - g_disable_manual_jni_registration = true; +void SetJniRegistrationType(JniRegistrationType jni_registration_type) { + g_jni_registration_type = jni_registration_type; } JNIEnv* AttachCurrentThread() { @@ -233,16 +240,7 @@ void CheckException(JNIEnv* env) { } // Now, feel good about it and die. - // TODO(lhchavez): Remove this hack. See b/28814913 for details. - // We're using BuildInfo's java_exception_info() instead of storing the - // exception info a few lines above to avoid extra copies. It will be - // truncated to 1024 bytes anyways. - const char* exception_string = - base::android::BuildInfo::GetInstance()->java_exception_info(); - if (exception_string) - LOG(FATAL) << exception_string; - else - LOG(FATAL) << "Unhandled exception"; + LOG(FATAL) << "Please include Java exception stack in crash report"; } std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) { @@ -291,6 +289,22 @@ std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) { return ConvertJavaStringToUTF8(exception_string); } +#if BUILDFLAG(ENABLE_PROFILING) && HAVE_TRACE_STACK_FRAME_POINTERS + +JNIStackFrameSaver::JNIStackFrameSaver(void* current_fp) { + previous_fp_ = g_stack_frame_pointer.Pointer()->Get(); + g_stack_frame_pointer.Pointer()->Set(current_fp); +} + +JNIStackFrameSaver::~JNIStackFrameSaver() { + g_stack_frame_pointer.Pointer()->Set(previous_fp_); +} + +void* JNIStackFrameSaver::SavedFrame() { + return g_stack_frame_pointer.Pointer()->Get(); +} + +#endif // ENABLE_PROFILING && HAVE_TRACE_STACK_FRAME_POINTERS } // namespace android } // namespace base diff --git a/base/android/jni_android.h b/base/android/jni_android.h index 909e3da..de53c10 100644 --- a/base/android/jni_android.h +++ b/base/android/jni_android.h @@ -14,6 +14,39 @@ #include "base/atomicops.h" #include "base/base_export.h" #include "base/compiler_specific.h" +#include "base/debug/stack_trace.h" +#include "base/macros.h" + +#if HAVE_TRACE_STACK_FRAME_POINTERS + +// When profiling is enabled (enable_profiling=true) this macro is added to +// all generated JNI stubs so that it becomes the last thing that runs before +// control goes into Java. +// +// This macro saves stack frame pointer of the current function. Saved value +// used later by JNI_LINK_SAVED_FRAME_POINTER. +#define JNI_SAVE_FRAME_POINTER \ + base::android::JNIStackFrameSaver jni_frame_saver(__builtin_frame_address(0)) + +// When profiling is enabled (enable_profiling=true) this macro is added to +// all generated JNI callbacks so that it becomes the first thing that runs +// after control returns from Java. +// +// This macro links stack frame of the current function to the stack frame +// saved by JNI_SAVE_FRAME_POINTER, allowing frame-based unwinding +// (used by the heap profiler) to produce complete traces. +#define JNI_LINK_SAVED_FRAME_POINTER \ + base::debug::ScopedStackFrameLinker jni_frame_linker( \ + __builtin_frame_address(0), \ + base::android::JNIStackFrameSaver::SavedFrame()) + +#else + +// Frame-based stack unwinding is not supported, do nothing. +#define JNI_SAVE_FRAME_POINTER +#define JNI_LINK_SAVED_FRAME_POINTER + +#endif // HAVE_TRACE_STACK_FRAME_POINTERS namespace base { namespace android { @@ -21,12 +54,23 @@ namespace android { // Used to mark symbols to be exported in a shared library's symbol table. #define JNI_EXPORT __attribute__ ((visibility("default"))) -// Used to disable manual JNI registration in binaries that prefer to use native -// JNI exports for startup performance. This is not compatible with the crazy -// linker and so defaults to off. Call DisableManualJniRegistration at the very -// beginning of JNI_OnLoad to use this. -BASE_EXPORT bool IsManualJniRegistrationDisabled(); -BASE_EXPORT void DisableManualJniRegistration(); +// The level of JNI registration required for the current process. +enum JniRegistrationType { + // Register all native methods. + ALL_JNI_REGISTRATION, + // Register some native methods, as controlled by the jni_generator. + SELECTIVE_JNI_REGISTRATION, + // Do not register any native methods. + NO_JNI_REGISTRATION, +}; + +BASE_EXPORT JniRegistrationType GetJniRegistrationType(); + +// Set the JniRegistrationType for this process (defaults to +// ALL_JNI_REGISTRATION). This should be called in the JNI_OnLoad function +// which is called when the native library is first loaded. +BASE_EXPORT void SetJniRegistrationType( + JniRegistrationType jni_registration_type); // Contains the registration method information for initializing JNI bindings. struct RegistrationMethod { @@ -122,6 +166,24 @@ BASE_EXPORT void CheckException(JNIEnv* env); BASE_EXPORT std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable); +#if HAVE_TRACE_STACK_FRAME_POINTERS + +// Saves caller's PC and stack frame in a thread-local variable. +// Implemented only when profiling is enabled (enable_profiling=true). +class BASE_EXPORT JNIStackFrameSaver { + public: + JNIStackFrameSaver(void* current_fp); + ~JNIStackFrameSaver(); + static void* SavedFrame(); + + private: + void* previous_fp_; + + DISALLOW_COPY_AND_ASSIGN(JNIStackFrameSaver); +}; + +#endif // HAVE_TRACE_STACK_FRAME_POINTERS + } // namespace android } // namespace base diff --git a/base/android/jni_generator/golden_sample_for_tests_jni.h b/base/android/jni_generator/golden_sample_for_tests_jni.h deleted file mode 100644 index e982609..0000000 --- a/base/android/jni_generator/golden_sample_for_tests_jni.h +++ /dev/null @@ -1,481 +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. - -// This file is autogenerated by -// base/android/jni_generator/jni_generator.py -// For -// org/chromium/example/jni_generator/SampleForTests - -#ifndef org_chromium_example_jni_generator_SampleForTests_JNI -#define org_chromium_example_jni_generator_SampleForTests_JNI - -#include <jni.h> - -#include "base/android/jni_generator/jni_generator_helper.h" - -#include "base/android/jni_int_wrapper.h" - -// Step 1: forward declarations. -namespace { -const char kInnerStructAClassPath[] = - "org/chromium/example/jni_generator/SampleForTests$InnerStructA"; -const char kSampleForTestsClassPath[] = - "org/chromium/example/jni_generator/SampleForTests"; -const char kInnerStructBClassPath[] = - "org/chromium/example/jni_generator/SampleForTests$InnerStructB"; -// Leaking this jclass as we cannot use LazyInstance from some threads. -jclass g_InnerStructA_clazz = NULL; -#define InnerStructA_clazz(env) g_InnerStructA_clazz -// Leaking this jclass as we cannot use LazyInstance from some threads. -jclass g_SampleForTests_clazz = NULL; -#define SampleForTests_clazz(env) g_SampleForTests_clazz -// Leaking this jclass as we cannot use LazyInstance from some threads. -jclass g_InnerStructB_clazz = NULL; -#define InnerStructB_clazz(env) g_InnerStructB_clazz - -} // namespace - -namespace base { -namespace android { - -// Step 2: method stubs. - -static jlong Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller, - const JavaParamRef<jstring>& param); - -static jlong - Java_org_chromium_example_jni_1generator_SampleForTests_nativeInit(JNIEnv* - env, jobject jcaller, - jstring param) { - return Init(env, JavaParamRef<jobject>(env, jcaller), - JavaParamRef<jstring>(env, param)); -} - -static void - Java_org_chromium_example_jni_1generator_SampleForTests_nativeDestroy(JNIEnv* - env, - jobject jcaller, - jlong nativeCPPClass) { - CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass); - CHECK_NATIVE_PTR(env, jcaller, native, "Destroy"); - return native->Destroy(env, JavaParamRef<jobject>(env, jcaller)); -} - -static jdouble GetDoubleFunction(JNIEnv* env, const JavaParamRef<jobject>& - jcaller); - -static jdouble - Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetDoubleFunction(JNIEnv* - env, jobject jcaller) { - return GetDoubleFunction(env, JavaParamRef<jobject>(env, jcaller)); -} - -static jfloat GetFloatFunction(JNIEnv* env, const JavaParamRef<jclass>& - jcaller); - -static jfloat - Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetFloatFunction(JNIEnv* - env, jclass jcaller) { - return GetFloatFunction(env, JavaParamRef<jclass>(env, jcaller)); -} - -static void SetNonPODDatatype(JNIEnv* env, const JavaParamRef<jobject>& jcaller, - const JavaParamRef<jobject>& rect); - -static void - Java_org_chromium_example_jni_1generator_SampleForTests_nativeSetNonPODDatatype(JNIEnv* - env, jobject jcaller, - jobject rect) { - return SetNonPODDatatype(env, JavaParamRef<jobject>(env, jcaller), - JavaParamRef<jobject>(env, rect)); -} - -static ScopedJavaLocalRef<jobject> GetNonPODDatatype(JNIEnv* env, const - JavaParamRef<jobject>& jcaller); - -static jobject - Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetNonPODDatatype(JNIEnv* - env, jobject jcaller) { - return GetNonPODDatatype(env, JavaParamRef<jobject>(env, jcaller)).Release(); -} - -static jint - Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethod(JNIEnv* - env, - jobject jcaller, - jlong nativeCPPClass) { - CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass); - CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0); - return native->Method(env, JavaParamRef<jobject>(env, jcaller)); -} - -static jdouble - Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethodOtherP0(JNIEnv* - env, - jobject jcaller, - jlong nativePtr) { - CPPClass::InnerClass* native = - reinterpret_cast<CPPClass::InnerClass*>(nativePtr); - CHECK_NATIVE_PTR(env, jcaller, native, "MethodOtherP0", 0); - return native->MethodOtherP0(env, JavaParamRef<jobject>(env, jcaller)); -} - -static void - Java_org_chromium_example_jni_1generator_SampleForTests_nativeAddStructB(JNIEnv* - env, - jobject jcaller, - jlong nativeCPPClass, - jobject b) { - CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass); - CHECK_NATIVE_PTR(env, jcaller, native, "AddStructB"); - return native->AddStructB(env, JavaParamRef<jobject>(env, jcaller), - JavaParamRef<jobject>(env, b)); -} - -static void - Java_org_chromium_example_jni_1generator_SampleForTests_nativeIterateAndDoSomethingWithStructB(JNIEnv* - env, - jobject jcaller, - jlong nativeCPPClass) { - CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass); - CHECK_NATIVE_PTR(env, jcaller, native, "IterateAndDoSomethingWithStructB"); - return native->IterateAndDoSomethingWithStructB(env, - JavaParamRef<jobject>(env, jcaller)); -} - -static jstring - Java_org_chromium_example_jni_1generator_SampleForTests_nativeReturnAString(JNIEnv* - env, - jobject jcaller, - jlong nativeCPPClass) { - CPPClass* native = reinterpret_cast<CPPClass*>(nativeCPPClass); - CHECK_NATIVE_PTR(env, jcaller, native, "ReturnAString", NULL); - return native->ReturnAString(env, JavaParamRef<jobject>(env, - jcaller)).Release(); -} - -static base::subtle::AtomicWord g_SampleForTests_javaMethod = 0; -static jint Java_SampleForTests_javaMethod(JNIEnv* env, jobject obj, - JniIntWrapper foo, - JniIntWrapper bar) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, - SampleForTests_clazz(env), 0); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, SampleForTests_clazz(env), - "javaMethod", - -"(" -"I" -"I" -")" -"I", - &g_SampleForTests_javaMethod); - - jint ret = - env->CallIntMethod(obj, - method_id, as_jint(foo), as_jint(bar)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_SampleForTests_staticJavaMethod = 0; -static jboolean Java_SampleForTests_staticJavaMethod(JNIEnv* env) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, SampleForTests_clazz(env), - SampleForTests_clazz(env), false); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, SampleForTests_clazz(env), - "staticJavaMethod", - -"(" -")" -"Z", - &g_SampleForTests_staticJavaMethod); - - jboolean ret = - env->CallStaticBooleanMethod(SampleForTests_clazz(env), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_SampleForTests_packagePrivateJavaMethod = 0; -static void Java_SampleForTests_packagePrivateJavaMethod(JNIEnv* env, jobject - obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, - SampleForTests_clazz(env)); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, SampleForTests_clazz(env), - "packagePrivateJavaMethod", - -"(" -")" -"V", - &g_SampleForTests_packagePrivateJavaMethod); - - env->CallVoidMethod(obj, - method_id); - jni_generator::CheckException(env); - -} - -static base::subtle::AtomicWord g_SampleForTests_methodThatThrowsException = 0; -static void Java_SampleForTests_methodThatThrowsException(JNIEnv* env, jobject - obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, - SampleForTests_clazz(env)); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, SampleForTests_clazz(env), - "methodThatThrowsException", - -"(" -")" -"V", - &g_SampleForTests_methodThatThrowsException); - - env->CallVoidMethod(obj, - method_id); - -} - -static base::subtle::AtomicWord g_InnerStructA_create = 0; -static ScopedJavaLocalRef<jobject> Java_InnerStructA_create(JNIEnv* env, jlong - l, - JniIntWrapper i, - jstring s) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, InnerStructA_clazz(env), - InnerStructA_clazz(env), NULL); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, InnerStructA_clazz(env), - "create", - -"(" -"J" -"I" -"Ljava/lang/String;" -")" -"Lorg/chromium/example/jni_generator/SampleForTests$InnerStructA;", - &g_InnerStructA_create); - - jobject ret = - env->CallStaticObjectMethod(InnerStructA_clazz(env), - method_id, l, as_jint(i), s); - jni_generator::CheckException(env); - return ScopedJavaLocalRef<jobject>(env, ret); -} - -static base::subtle::AtomicWord g_SampleForTests_addStructA = 0; -static void Java_SampleForTests_addStructA(JNIEnv* env, jobject obj, jobject a) - { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, - SampleForTests_clazz(env)); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, SampleForTests_clazz(env), - "addStructA", - -"(" -"Lorg/chromium/example/jni_generator/SampleForTests$InnerStructA;" -")" -"V", - &g_SampleForTests_addStructA); - - env->CallVoidMethod(obj, - method_id, a); - jni_generator::CheckException(env); - -} - -static base::subtle::AtomicWord g_SampleForTests_iterateAndDoSomething = 0; -static void Java_SampleForTests_iterateAndDoSomething(JNIEnv* env, jobject obj) - { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, - SampleForTests_clazz(env)); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, SampleForTests_clazz(env), - "iterateAndDoSomething", - -"(" -")" -"V", - &g_SampleForTests_iterateAndDoSomething); - - env->CallVoidMethod(obj, - method_id); - jni_generator::CheckException(env); - -} - -static base::subtle::AtomicWord g_InnerStructB_getKey = 0; -static jlong Java_InnerStructB_getKey(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, - InnerStructB_clazz(env), 0); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, InnerStructB_clazz(env), - "getKey", - -"(" -")" -"J", - &g_InnerStructB_getKey); - - jlong ret = - env->CallLongMethod(obj, - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_InnerStructB_getValue = 0; -static ScopedJavaLocalRef<jstring> Java_InnerStructB_getValue(JNIEnv* env, - jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, - InnerStructB_clazz(env), NULL); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, InnerStructB_clazz(env), - "getValue", - -"(" -")" -"Ljava/lang/String;", - &g_InnerStructB_getValue); - - jstring ret = - static_cast<jstring>(env->CallObjectMethod(obj, - method_id)); - jni_generator::CheckException(env); - return ScopedJavaLocalRef<jstring>(env, ret); -} - -// Step 3: RegisterNatives. - -static const JNINativeMethod kMethodsSampleForTests[] = { - { "nativeInit", -"(" -"Ljava/lang/String;" -")" -"J", - reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeInit) - }, - { "nativeDestroy", -"(" -"J" -")" -"V", - reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeDestroy) - }, - { "nativeGetDoubleFunction", -"(" -")" -"D", - reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetDoubleFunction) - }, - { "nativeGetFloatFunction", -"(" -")" -"F", - reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetFloatFunction) - }, - { "nativeSetNonPODDatatype", -"(" -"Landroid/graphics/Rect;" -")" -"V", - reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeSetNonPODDatatype) - }, - { "nativeGetNonPODDatatype", -"(" -")" -"Ljava/lang/Object;", - reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeGetNonPODDatatype) - }, - { "nativeMethod", -"(" -"J" -")" -"I", - reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethod) - }, - { "nativeMethodOtherP0", -"(" -"J" -")" -"D", - reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethodOtherP0) - }, - { "nativeAddStructB", -"(" -"J" -"Lorg/chromium/example/jni_generator/SampleForTests$InnerStructB;" -")" -"V", - reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeAddStructB) - }, - { "nativeIterateAndDoSomethingWithStructB", -"(" -"J" -")" -"V", - reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeIterateAndDoSomethingWithStructB) - }, - { "nativeReturnAString", -"(" -"J" -")" -"Ljava/lang/String;", - reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeReturnAString) - }, -}; - -static bool RegisterNativesImpl(JNIEnv* env) { - - g_InnerStructA_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( - base::android::GetClass(env, kInnerStructAClassPath).obj())); - g_SampleForTests_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( - base::android::GetClass(env, kSampleForTestsClassPath).obj())); - g_InnerStructB_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( - base::android::GetClass(env, kInnerStructBClassPath).obj())); - - const int kMethodsSampleForTestsSize = arraysize(kMethodsSampleForTests); - - if (env->RegisterNatives(SampleForTests_clazz(env), - kMethodsSampleForTests, - kMethodsSampleForTestsSize) < 0) { - jni_generator::HandleRegistrationError( - env, SampleForTests_clazz(env), __FILE__); - return false; - } - - return true; -} - -} // namespace android -} // namespace base - -#endif // org_chromium_example_jni_generator_SampleForTests_JNI diff --git a/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java b/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java index 4958f04..42d8e56 100644 --- a/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java +++ b/base/android/jni_generator/java/src/org/chromium/example/jni_generator/SampleForTests.java @@ -10,6 +10,7 @@ import org.chromium.base.annotations.AccessedByNative; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.CalledByNativeUnchecked; import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeCall; import org.chromium.base.annotations.NativeClassQualifiedName; import java.util.ArrayList; @@ -164,6 +165,13 @@ class SampleForTests { // String constants that look like comments don't confuse the generator: private String mArrgh = "*/*"; + private @interface SomeAnnotation {} + + // The generator is not confused by @Annotated parameters. + @CalledByNative + void javaMethodWithAnnotatedParam(@SomeAnnotation int foo) { + } + // --------------------------------------------------------------------------------------------- // Java fields which are accessed from C++ code only must be annotated with @AccessedByNative to // prevent them being eliminated when unreferenced code is stripped. @@ -301,4 +309,10 @@ class SampleForTests { native void nativeAddStructB(long nativeCPPClass, InnerStructB b); native void nativeIterateAndDoSomethingWithStructB(long nativeCPPClass); native String nativeReturnAString(long nativeCPPClass); + + // This inner class shows how to annotate native methods on inner classes. + static class InnerClass { + @NativeCall("InnerClass") + private static native int nativeGetInnerIntFunction(); + } } diff --git a/base/android/jni_generator/jni_generator.gyp b/base/android/jni_generator/jni_generator.gyp deleted file mode 100644 index ce936a0..0000000 --- a/base/android/jni_generator/jni_generator.gyp +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -{ - 'targets': [ - # GYP: //base/android/jni_generator:jni_generator_tests - { - 'target_name': 'jni_generator_py_tests', - 'type': 'none', - 'variables': { - 'stamp': '<(INTERMEDIATE_DIR)/jni_generator_py_tests.stamp', - }, - 'actions': [ - { - 'action_name': 'run_jni_generator_py_tests', - 'inputs': [ - 'jni_generator.py', - 'jni_generator_tests.py', - 'java/src/org/chromium/example/jni_generator/SampleForTests.java', - 'golden_sample_for_tests_jni.h', - ], - 'outputs': [ - '<(stamp)', - ], - 'action': [ - 'python', 'jni_generator_tests.py', - '--stamp=<(stamp)', - ], - }, - ], - }, - # GYP: //base/android/jni_generator:jni_sample_header - { - 'target_name': 'jni_sample_header', - 'type': 'none', - 'sources': [ - 'java/src/org/chromium/example/jni_generator/SampleForTests.java', - ], - 'variables': { - 'jni_gen_package': 'example', - }, - 'includes': [ '../../../build/jni_generator.gypi' ], - }, - # GYP: //base/android/jni_generator:jni_sample_java - { - 'target_name': 'jni_sample_java', - 'type': 'none', - 'variables': { - 'java_in_dir': '../../../base/android/jni_generator/java', - }, - 'dependencies': [ - '<(DEPTH)/base/base.gyp:base_java', - ], - 'includes': [ '../../../build/java.gypi' ], - }, - # GYP: //base/android/jni_generator:jni_generator_tests - { - 'target_name': 'jni_generator_tests', - 'type': 'executable', - 'dependencies': [ - '../../base.gyp:test_support_base', - 'jni_generator_py_tests', - 'jni_sample_header', - 'jni_sample_java', - ], - 'sources': [ - 'sample_for_tests.cc', - ], - }, - ], -} diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py index b0134d6..99d8b42 100755 --- a/base/android/jni_generator/jni_generator.py +++ b/base/android/jni_generator/jni_generator.py @@ -138,7 +138,7 @@ def JavaDataTypeToC(java_type): def WrapCTypeForDeclaration(c_type): """Wrap the C datatype in a JavaRef if required.""" if re.match(RE_SCOPED_JNI_TYPES, c_type): - return 'const JavaParamRef<' + c_type + '>&' + return 'const base::android::JavaParamRef<' + c_type + '>&' else: return c_type @@ -153,7 +153,11 @@ def JavaDataTypeToCForCalledByNativeParam(java_type): if java_type == 'int': return 'JniIntWrapper' else: - return JavaDataTypeToC(java_type) + c_type = JavaDataTypeToC(java_type) + if re.match(RE_SCOPED_JNI_TYPES, c_type): + return 'const base::android::JavaRefOrBare<' + c_type + '>&' + else: + return c_type def JavaReturnValueToC(java_type): @@ -352,8 +356,14 @@ class JniParams(object): ret = [] for p in [p.strip() for p in params.split(',')]: items = p.split(' ') + + # Remove @Annotations from parameters. + while items[0].startswith('@'): + del items[0] + if 'final' in items: items.remove('final') + param = Param( datatype=items[0], name=(items[1] if len(items) > 1 else 'p%s' % len(ret)), @@ -402,6 +412,19 @@ def ExtractNatives(contents, ptr_type): return natives +def IsMainDexJavaClass(contents): + """Returns "true" if the class is annotated with "@MainDex", "false" if not. + + JNI registration doesn't always need to be completed for non-browser processes + since most Java code is only used by the browser process. Classes that are + needed by non-browser processes must explicitly be annotated with @MainDex + to force JNI registration. + """ + re_maindex = re.compile(r'@MainDex[\s\S]*class\s+\w+\s*{') + found = re.search(re_maindex, contents) + return 'true' if found else 'false' + + def GetStaticCastForReturnType(return_type): type_map = { 'String' : 'jstring', 'java/lang/String' : 'jstring', @@ -508,11 +531,17 @@ RE_SCOPED_JNI_TYPES = re.compile('jobject|jclass|jstring|jthrowable|.*Array') RE_CALLED_BY_NATIVE = re.compile( '@CalledByNative(?P<Unchecked>(Unchecked)*?)(?:\("(?P<annotation>.*)"\))?' '\s+(?P<prefix>[\w ]*?)' + '(:?\s*@\w+)?' # Ignore annotations in return types. '\s*(?P<return_type>\S+?)' '\s+(?P<name>\w+)' '\s*\((?P<params>[^\)]*)\)') +# Removes empty lines that are indented (i.e. start with 2x spaces). +def RemoveIndentedEmptyLines(string): + return re.sub('^(?: {2})+$\n', '', string, flags=re.MULTILINE) + + def ExtractCalledByNatives(contents): """Parses all methods annotated with @CalledByNative. @@ -618,8 +647,8 @@ class JNIFromJavaP(object): value=value.group('value'))) self.inl_header_file_generator = InlHeaderFileGenerator( - self.namespace, self.fully_qualified_class, [], - self.called_by_natives, self.constant_fields, options) + self.namespace, self.fully_qualified_class, [], self.called_by_natives, + self.constant_fields, options) def GetContent(self): return self.inl_header_file_generator.GetContent() @@ -653,12 +682,13 @@ class JNIFromJavaSource(object): jni_namespace = ExtractJNINamespace(contents) or options.namespace natives = ExtractNatives(contents, options.ptr_type) called_by_natives = ExtractCalledByNatives(contents) + maindex = IsMainDexJavaClass(contents) if len(natives) == 0 and len(called_by_natives) == 0: raise SyntaxError('Unable to find any JNI methods for %s.' % fully_qualified_class) inl_header_file_generator = InlHeaderFileGenerator( - jni_namespace, fully_qualified_class, natives, called_by_natives, - [], options) + jni_namespace, fully_qualified_class, natives, called_by_natives, [], + options, maindex) self.content = inl_header_file_generator.GetContent() @classmethod @@ -694,7 +724,7 @@ class InlHeaderFileGenerator(object): """Generates an inline header file for JNI integration.""" def __init__(self, namespace, fully_qualified_class, natives, - called_by_natives, constant_fields, options): + called_by_natives, constant_fields, options, maindex='false'): self.namespace = namespace self.fully_qualified_class = fully_qualified_class self.class_name = self.fully_qualified_class.split('/')[-1] @@ -702,6 +732,7 @@ class InlHeaderFileGenerator(object): self.called_by_natives = called_by_natives self.header_guard = fully_qualified_class.replace('/', '_') + '_JNI' self.constant_fields = constant_fields + self.maindex = maindex self.options = options @@ -760,6 +791,8 @@ $CLOSE_NAMESPACE 'HEADER_GUARD': self.header_guard, 'INCLUDES': self.GetIncludesString(), } + assert ((values['JNI_NATIVE_METHODS'] == '') == + (values['REGISTER_NATIVES'] == '')) return WrapOutput(template.substitute(values)) def GetClassPathDefinitionsString(self): @@ -818,7 +851,7 @@ $CLOSE_NAMESPACE def GetJNINativeMethodsString(self): """Returns the implementation of the array of native methods.""" - if self.options.native_exports and not self.options.native_exports_optional: + if not self.options.native_exports_optional: return '' template = Template("""\ static const JNINativeMethod kMethods${JAVA_CLASS}[] = { @@ -829,10 +862,13 @@ ${KMETHODS} def GetRegisterNativesString(self): """Returns the code for RegisterNatives.""" + natives = self.GetRegisterNativesImplString() + if not natives: + return '' + template = Template("""\ ${REGISTER_NATIVES_SIGNATURE} { ${EARLY_EXIT} -${CLASSES} ${NATIVES} return true; } @@ -841,20 +877,20 @@ ${NATIVES} early_exit = '' if self.options.native_exports_optional: early_exit = """\ - if (base::android::IsManualJniRegistrationDisabled()) return true; -""" + if (jni_generator::ShouldSkipJniRegistration(%s)) + return true; +""" % self.maindex - natives = self.GetRegisterNativesImplString() values = {'REGISTER_NATIVES_SIGNATURE': signature, 'EARLY_EXIT': early_exit, - 'CLASSES': self.GetFindClasses(), 'NATIVES': natives, } + return template.substitute(values) def GetRegisterNativesImplString(self): """Returns the shared implementation for RegisterNatives.""" - if self.options.native_exports and not self.options.native_exports_optional: + if not self.options.native_exports_optional: return '' template = Template("""\ @@ -954,7 +990,8 @@ ${NATIVES} return template.substitute(values) def GetJavaParamRefForCall(self, c_type, name): - return Template('JavaParamRef<${TYPE}>(env, ${NAME})').substitute({ + return Template( + 'base::android::JavaParamRef<${TYPE}>(env, ${NAME})').substitute({ 'TYPE': c_type, 'NAME': name, }) @@ -979,15 +1016,15 @@ ${NATIVES} params_in_call.append(p.name) params_in_call = ', '.join(params_in_call) - if self.options.native_exports: - stub_visibility = 'extern "C" __attribute__((visibility("default")))\n' - else: - stub_visibility = 'static ' return_type = return_declaration = JavaDataTypeToC(native.return_type) post_call = '' if re.match(RE_SCOPED_JNI_TYPES, return_type): post_call = '.Release()' - return_declaration = 'ScopedJavaLocalRef<' + return_type + '>' + return_declaration = ('base::android::ScopedJavaLocalRef<' + return_type + + '>') + profiling_entered_native = '' + if self.options.enable_profiling: + profiling_entered_native = 'JNI_LINK_SAVED_FRAME_POINTER;' values = { 'RETURN': return_type, 'RETURN_DECLARATION': return_declaration, @@ -997,7 +1034,7 @@ ${NATIVES} 'PARAMS_IN_CALL': params_in_call, 'POST_CALL': post_call, 'STUB_NAME': self.GetStubName(native), - 'STUB_VISIBILITY': stub_visibility, + 'PROFILING_ENTERED_NATIVE': profiling_entered_native, } if is_method: @@ -1010,8 +1047,8 @@ ${NATIVES} 'P0_TYPE': native.p0_type, }) template = Template("""\ -${STUB_VISIBILITY}${RETURN} ${STUB_NAME}(JNIEnv* env, - ${PARAMS_IN_STUB}) { +JNI_GENERATOR_EXPORT ${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_STUB}) { + ${PROFILING_ENTERED_NATIVE} ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME}); CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}"${OPTIONAL_ERROR_RETURN}); return native->${NAME}(${PARAMS_IN_CALL})${POST_CALL}; @@ -1021,16 +1058,21 @@ ${STUB_VISIBILITY}${RETURN} ${STUB_NAME}(JNIEnv* env, template = Template(""" static ${RETURN_DECLARATION} ${NAME}(JNIEnv* env, ${PARAMS}); -${STUB_VISIBILITY}${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_STUB}) { +JNI_GENERATOR_EXPORT ${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_STUB}) { + ${PROFILING_ENTERED_NATIVE} return ${NAME}(${PARAMS_IN_CALL})${POST_CALL}; } """) - return template.substitute(values) + return RemoveIndentedEmptyLines(template.substitute(values)) def GetArgument(self, param): - return ('as_jint(' + param.name + ')' - if param.datatype == 'int' else param.name) + if param.datatype == 'int': + return 'as_jint(' + param.name + ')' + elif re.match(RE_SCOPED_JNI_TYPES, JavaDataTypeToC(param.datatype)): + return param.name + '.obj()' + else: + return param.name def GetArgumentsInCall(self, params): """Return a string of arguments to call from native into Java""" @@ -1043,8 +1085,9 @@ ${STUB_VISIBILITY}${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_STUB}) { first_param_in_declaration = '' first_param_in_call = ('%s_clazz(env)' % java_class) else: - first_param_in_declaration = ', jobject obj' - first_param_in_call = 'obj' + first_param_in_declaration = ( + ', const base::android::JavaRefOrBare<jobject>& obj') + first_param_in_call = 'obj.obj()' params_in_declaration = self.GetCalledByNativeParamsInDeclaration( called_by_native) if params_in_declaration: @@ -1070,10 +1113,13 @@ ${STUB_VISIBILITY}${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_STUB}) { pre_call = ' ' + pre_call return_declaration = return_type + ' ret =' if re.match(RE_SCOPED_JNI_TYPES, return_type): - return_type = 'ScopedJavaLocalRef<' + return_type + '>' + return_type = 'base::android::ScopedJavaLocalRef<' + return_type + '>' return_clause = 'return ' + return_type + '(env, ret);' else: return_clause = 'return ret;' + profiling_leaving_native = '' + if self.options.enable_profiling: + profiling_leaving_native = 'JNI_SAVE_FRAME_POINTER;' return { 'JAVA_CLASS': java_class, 'RETURN_TYPE': return_type, @@ -1089,7 +1135,8 @@ ${STUB_VISIBILITY}${RETURN} ${STUB_NAME}(JNIEnv* env, ${PARAMS_IN_STUB}) { 'PARAMS_IN_CALL': params_in_call, 'METHOD_ID_VAR_NAME': called_by_native.method_id_var_name, 'CHECK_EXCEPTION': check_exception, - 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native) + 'GET_METHOD_ID_IMPL': self.GetMethodIDImpl(called_by_native), + 'PROFILING_LEAVING_NATIVE': profiling_leaving_native, } @@ -1106,11 +1153,11 @@ ${FUNCTION_SIGNATURE} {""") template = Template(""" static base::subtle::AtomicWord g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME} = 0; ${FUNCTION_HEADER} - /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, ${FIRST_PARAM_IN_CALL}, ${JAVA_CLASS}_clazz(env)${OPTIONAL_ERROR_RETURN}); jmethodID method_id = ${GET_METHOD_ID_IMPL} + ${PROFILING_LEAVING_NATIVE} ${RETURN_DECLARATION} ${PRE_CALL}env->${ENV_CALL}(${FIRST_PARAM_IN_CALL}, method_id${PARAMS_IN_CALL})${POST_CALL}; @@ -1125,7 +1172,7 @@ ${FUNCTION_HEADER} function_header_with_unused_template.substitute(values)) else: values['FUNCTION_HEADER'] = function_header_template.substitute(values) - return template.substitute(values) + return RemoveIndentedEmptyLines(template.substitute(values)) def GetKMethodArrayEntry(self, native): template = Template(' { "native${NAME}", ${JNI_SIGNATURE}, ' + @@ -1153,13 +1200,9 @@ ${FUNCTION_HEADER} ret = [] template = Template("""\ const char k${JAVA_CLASS}ClassPath[] = "${JNI_CLASS_PATH}";""") - native_classes = self.GetUniqueClasses(self.natives) - called_by_native_classes = self.GetUniqueClasses(self.called_by_natives) - if self.options.native_exports: - all_classes = called_by_native_classes - else: - all_classes = native_classes - all_classes.update(called_by_native_classes) + all_classes = self.GetUniqueClasses(self.called_by_natives) + if self.options.native_exports_optional: + all_classes.update(self.GetUniqueClasses(self.natives)) for clazz in all_classes: values = { @@ -1169,21 +1212,14 @@ const char k${JAVA_CLASS}ClassPath[] = "${JNI_CLASS_PATH}";""") ret += [template.substitute(values)] ret += '' - class_getter_methods = [] - if self.options.native_exports: - template = Template("""\ + template = Template("""\ // Leaking this jclass as we cannot use LazyInstance from some threads. base::subtle::AtomicWord g_${JAVA_CLASS}_clazz __attribute__((unused)) = 0; #define ${JAVA_CLASS}_clazz(env) \ base::android::LazyGetClass(env, k${JAVA_CLASS}ClassPath, \ &g_${JAVA_CLASS}_clazz)""") - else: - template = Template("""\ -// Leaking this jclass as we cannot use LazyInstance from some threads. -jclass g_${JAVA_CLASS}_clazz = NULL; -#define ${JAVA_CLASS}_clazz(env) g_${JAVA_CLASS}_clazz""") - for clazz in called_by_native_classes: + for clazz in all_classes: values = { 'JAVA_CLASS': clazz, } @@ -1191,19 +1227,6 @@ jclass g_${JAVA_CLASS}_clazz = NULL; return '\n'.join(ret) - def GetFindClasses(self): - """Returns the imlementation of FindClass for all known classes.""" - if self.options.native_exports: - return '\n' - template = Template("""\ - g_${JAVA_CLASS}_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( - base::android::GetClass(env, k${JAVA_CLASS}ClassPath).obj()));""") - ret = [] - for clazz in self.GetUniqueClasses(self.called_by_natives): - values = {'JAVA_CLASS': clazz} - ret += [template.substitute(values)] - return '\n'.join(ret) - def GetMethodIDImpl(self, called_by_native): """Returns the implementation of GetMethodID.""" template = Template("""\ @@ -1366,15 +1389,12 @@ See SampleForTests.java for more details. help='The path to cpp command.') option_parser.add_option('--javap', default='javap', help='The path to javap command.') - option_parser.add_option('--native_exports', action='store_true', - help='Native method registration through .so ' - 'exports.') option_parser.add_option('--native_exports_optional', action='store_true', help='Support both explicit and native method' 'registration.') + option_parser.add_option('--enable_profiling', action='store_true', + help='Add additional profiling instrumentation.') options, args = option_parser.parse_args(argv) - if options.native_exports_optional: - options.native_exports = True if options.jar_file: input_file = ExtractJarInputFile(options.jar_file, options.input_file, options.output_dir) @@ -1391,9 +1411,7 @@ See SampleForTests.java for more details. GenerateJNIHeader(input_file, output_file, options) if options.depfile: - build_utils.WriteDepfile( - options.depfile, - build_utils.GetPythonDependencies()) + build_utils.WriteDepfile(options.depfile, output_file) if __name__ == '__main__': diff --git a/base/android/jni_generator/jni_generator_helper.h b/base/android/jni_generator/jni_generator_helper.h index 9075d3c..3062806 100644 --- a/base/android/jni_generator/jni_generator_helper.h +++ b/base/android/jni_generator/jni_generator_helper.h @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - #ifndef BASE_ANDROID_JNI_GENERATOR_JNI_GENERATOR_HELPER_H_ #define BASE_ANDROID_JNI_GENERATOR_JNI_GENERATOR_HELPER_H_ @@ -11,30 +10,53 @@ #include "base/android/jni_android.h" #include "base/android/scoped_java_ref.h" #include "base/logging.h" +#include "build/build_config.h" // Project-specific macros used by the header files generated by // jni_generator.py. Different projects can then specify their own // implementation for this file. #define CHECK_NATIVE_PTR(env, jcaller, native_ptr, method_name, ...) \ - DCHECK(native_ptr) << method_name; + DCHECK(native_ptr) << method_name; -#define CHECK_CLAZZ(env, jcaller, clazz, ...) \ - DCHECK(clazz); +#define CHECK_CLAZZ(env, jcaller, clazz, ...) DCHECK(clazz); -namespace jni_generator { +#if defined(ARCH_CPU_X86) +// Dalvik JIT generated code doesn't guarantee 16-byte stack alignment on +// x86 - use force_align_arg_pointer to realign the stack at the JNI +// boundary. crbug.com/655248 +#define JNI_GENERATOR_EXPORT \ + extern "C" __attribute__((visibility("default"), force_align_arg_pointer)) +#else +#define JNI_GENERATOR_EXPORT extern "C" __attribute__((visibility("default"))) +#endif - inline void HandleRegistrationError(JNIEnv* env, jclass clazz, - const char* filename) { - LOG(ERROR) << "RegisterNatives failed in " << filename; - } +namespace jni_generator { - inline void CheckException(JNIEnv* env) { - base::android::CheckException(env); +inline void HandleRegistrationError(JNIEnv* env, + jclass clazz, + const char* filename) { + LOG(ERROR) << "RegisterNatives failed in " << filename; +} + +inline void CheckException(JNIEnv* env) { + base::android::CheckException(env); +} + +inline bool ShouldSkipJniRegistration(bool is_maindex_class) { + switch (base::android::GetJniRegistrationType()) { + case base::android::ALL_JNI_REGISTRATION: + return false; + case base::android::NO_JNI_REGISTRATION: + // TODO(estevenson): Change this to a DCHECK. + return true; + case base::android::SELECTIVE_JNI_REGISTRATION: + return !is_maindex_class; + default: + NOTREACHED(); + return false; } +} } // namespace jni_generator -using base::android::ScopedJavaLocalRef; -using base::android::JavaParamRef; - #endif // BASE_ANDROID_JNI_GENERATOR_JNI_GENERATOR_HELPER_H_ diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py index 5126896..c0c8238 100755 --- a/base/android/jni_generator/jni_generator_tests.py +++ b/base/android/jni_generator/jni_generator_tests.py @@ -40,8 +40,8 @@ class TestOptions(object): self.ptr_type = 'long' self.cpp = 'cpp' self.javap = 'javap' - self.native_exports = False - self.native_exports_optional = False + self.native_exports_optional = True + self.enable_profiling = False class TestGenerator(unittest.TestCase): def assertObjEquals(self, first, second): @@ -403,6 +403,10 @@ class TestGenerator(unittest.TestCase): return } } + @CalledByNative + public static @Status int updateStatus(@Status int status) { + return getAndUpdateStatus(status); + } @CalledByNativeUnchecked private void uncheckedCall(int iParam); @@ -525,6 +529,17 @@ class TestGenerator(unittest.TestCase): unchecked=False, ), CalledByNative( + return_type='int', + system_class=False, + static=True, + name='updateStatus', + method_id_var_name='updateStatus', + java_class_name='', + params=[Param(datatype='int', name='status')], + env_call=('Integer', ''), + unchecked=False, + ), + CalledByNative( return_type='void', system_class=False, static=False, @@ -819,7 +834,7 @@ public class java.util.HashSet { content = file(os.path.join(script_dir, 'java/src/org/chromium/example/jni_generator/SampleForTests.java') ).read() - golden_file = os.path.join(script_dir, 'golden_sample_for_tests_jni.h') + golden_file = os.path.join(script_dir, 'SampleForTests_jni.golden') golden_content = file(golden_file).read() jni_from_java = jni_generator.JNIFromJavaSource( content, 'org/chromium/example/jni_generator/SampleForTests', @@ -935,7 +950,34 @@ class Foo { natives, [], [], test_options) self.assertGoldenTextEquals(h.GetContent()) - def runNativeExportsOption(self, optional): + def testMainDexFile(self): + test_data = """ + package org.chromium.example.jni_generator; + + @MainDex + class Test { + private static native int nativeStaticMethod(long nativeTest, int arg1); + } + """ + options = TestOptions() + jni_from_java = jni_generator.JNIFromJavaSource( + test_data, 'org/chromium/foo/Bar', options) + self.assertGoldenTextEquals(jni_from_java.GetContent()) + + def testNonMainDexFile(self): + test_data = """ + package org.chromium.example.jni_generator; + + class Test { + private static native int nativeStaticMethod(long nativeTest, int arg1); + } + """ + options = TestOptions() + jni_from_java = jni_generator.JNIFromJavaSource( + test_data, 'org/chromium/foo/Bar', options) + self.assertGoldenTextEquals(jni_from_java.GetContent()) + + def testNativeExportsOnlyOption(self): test_data = """ package org.chromium.example.jni_generator; @@ -967,19 +1009,10 @@ class Foo { } """ options = TestOptions() - options.native_exports = True - options.native_exports_optional = optional + options.native_exports_optional = False jni_from_java = jni_generator.JNIFromJavaSource( test_data, 'org/chromium/example/jni_generator/SampleForTests', options) - return jni_from_java.GetContent() - - def testNativeExportsOption(self): - content = self.runNativeExportsOption(False) - self.assertGoldenTextEquals(content) - - def testNativeExportsOptionalOption(self): - content = self.runNativeExportsOption(True) - self.assertGoldenTextEquals(content) + self.assertGoldenTextEquals(jni_from_java.GetContent()) def testOuterInnerRaises(self): test_data = """ @@ -1041,7 +1074,7 @@ class Foo { def TouchStamp(stamp_path): dir_name = os.path.dirname(stamp_path) if not os.path.isdir(dir_name): - os.makedirs() + os.makedirs(dir_name) with open(stamp_path, 'a'): os.utime(stamp_path, None) diff --git a/base/android/jni_generator/sample_for_tests.cc b/base/android/jni_generator/sample_for_tests.cc index 08e5ac9..42b2143 100644 --- a/base/android/jni_generator/sample_for_tests.cc +++ b/base/android/jni_generator/sample_for_tests.cc @@ -51,8 +51,8 @@ void CPPClass::AddStructB(JNIEnv* env, const JavaParamRef<jobject>& caller, const JavaParamRef<jobject>& structb) { long key = Java_InnerStructB_getKey(env, structb); - std::string value = ConvertJavaStringToUTF8( - env, Java_InnerStructB_getValue(env, structb).obj()); + std::string value = + ConvertJavaStringToUTF8(env, Java_InnerStructB_getValue(env, structb)); map_[key] = value; } @@ -100,6 +100,10 @@ static ScopedJavaLocalRef<jobject> GetNonPODDatatype( return ScopedJavaLocalRef<jobject>(); } +static jint GetInnerIntFunction(JNIEnv*, const JavaParamRef<jclass>&) { + return 0; +} + } // namespace android } // namespace base @@ -113,7 +117,7 @@ int main() { // This is how you call a java method from C++. Note that you must have // obtained the jobject somehow. - jobject my_java_object = NULL; + ScopedJavaLocalRef<jobject> my_java_object; int bar = base::android::Java_SampleForTests_javaMethod( env, my_java_object, 1, 2); @@ -123,14 +127,16 @@ int main() { // Creates a "struct" that will then be used by the java side. ScopedJavaLocalRef<jobject> struct_a = base::android::Java_InnerStructA_create( - env, 0, 1, ConvertUTF8ToJavaString(env, "test").obj()); - base::android::Java_SampleForTests_addStructA( - env, my_java_object, struct_a.obj()); + env, 0, 1, ConvertUTF8ToJavaString(env, "test")); + base::android::Java_SampleForTests_addStructA(env, my_java_object, + struct_a); } base::android::Java_SampleForTests_iterateAndDoSomething(env, my_java_object); base::android::Java_SampleForTests_packagePrivateJavaMethod(env, my_java_object); base::android::Java_SampleForTests_methodThatThrowsException(env, my_java_object); + base::android::Java_SampleForTests_javaMethodWithAnnotatedParam( + env, my_java_object, 42); return 0; } diff --git a/base/android/jni_generator/testCalledByNatives.golden b/base/android/jni_generator/testCalledByNatives.golden index 3bc586c..ac86b2e 100644 --- a/base/android/jni_generator/testCalledByNatives.golden +++ b/base/android/jni_generator/testCalledByNatives.golden @@ -21,32 +21,31 @@ namespace { const char kTestJniClassPath[] = "org/chromium/TestJni"; const char kInfoBarClassPath[] = "org/chromium/TestJni$InfoBar"; // Leaking this jclass as we cannot use LazyInstance from some threads. -jclass g_TestJni_clazz = NULL; -#define TestJni_clazz(env) g_TestJni_clazz +base::subtle::AtomicWord g_TestJni_clazz __attribute__((unused)) = 0; +#define TestJni_clazz(env) base::android::LazyGetClass(env, kTestJniClassPath, &g_TestJni_clazz) // Leaking this jclass as we cannot use LazyInstance from some threads. -jclass g_InfoBar_clazz = NULL; -#define InfoBar_clazz(env) g_InfoBar_clazz +base::subtle::AtomicWord g_InfoBar_clazz __attribute__((unused)) = 0; +#define InfoBar_clazz(env) base::android::LazyGetClass(env, kInfoBarClassPath, &g_InfoBar_clazz) } // namespace // Step 2: method stubs. static base::subtle::AtomicWord g_TestJni_showConfirmInfoBar = 0; -static ScopedJavaLocalRef<jobject> Java_TestJni_showConfirmInfoBar(JNIEnv* env, - jobject obj, JniIntWrapper nativeInfoBar, - jstring buttonOk, - jstring buttonCancel, - jstring title, - jobject icon) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static base::android::ScopedJavaLocalRef<jobject> + Java_TestJni_showConfirmInfoBar(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper nativeInfoBar, + const base::android::JavaRefOrBare<jstring>& buttonOk, + const base::android::JavaRefOrBare<jstring>& buttonCancel, + const base::android::JavaRefOrBare<jstring>& title, + const base::android::JavaRefOrBare<jobject>& icon) { + CHECK_CLAZZ(env, obj.obj(), TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( env, TestJni_clazz(env), "showConfirmInfoBar", - "(" "I" "Ljava/lang/String;" @@ -58,28 +57,27 @@ static ScopedJavaLocalRef<jobject> Java_TestJni_showConfirmInfoBar(JNIEnv* env, &g_TestJni_showConfirmInfoBar); jobject ret = - env->CallObjectMethod(obj, - method_id, as_jint(nativeInfoBar), buttonOk, buttonCancel, title, - icon); + env->CallObjectMethod(obj.obj(), + method_id, as_jint(nativeInfoBar), buttonOk.obj(), buttonCancel.obj(), + title.obj(), icon.obj()); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jobject>(env, ret); + return base::android::ScopedJavaLocalRef<jobject>(env, ret); } static base::subtle::AtomicWord g_TestJni_showAutoLoginInfoBar = 0; -static ScopedJavaLocalRef<jobject> Java_TestJni_showAutoLoginInfoBar(JNIEnv* - env, jobject obj, JniIntWrapper nativeInfoBar, - jstring realm, - jstring account, - jstring args) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static base::android::ScopedJavaLocalRef<jobject> + Java_TestJni_showAutoLoginInfoBar(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper nativeInfoBar, + const base::android::JavaRefOrBare<jstring>& realm, + const base::android::JavaRefOrBare<jstring>& account, + const base::android::JavaRefOrBare<jstring>& args) { + CHECK_CLAZZ(env, obj.obj(), TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( env, TestJni_clazz(env), "showAutoLoginInfoBar", - "(" "I" "Ljava/lang/String;" @@ -90,40 +88,39 @@ static ScopedJavaLocalRef<jobject> Java_TestJni_showAutoLoginInfoBar(JNIEnv* &g_TestJni_showAutoLoginInfoBar); jobject ret = - env->CallObjectMethod(obj, - method_id, as_jint(nativeInfoBar), realm, account, args); + env->CallObjectMethod(obj.obj(), + method_id, as_jint(nativeInfoBar), realm.obj(), account.obj(), + args.obj()); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jobject>(env, ret); + return base::android::ScopedJavaLocalRef<jobject>(env, ret); } static base::subtle::AtomicWord g_InfoBar_dismiss = 0; -static void Java_InfoBar_dismiss(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static void Java_InfoBar_dismiss(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), InfoBar_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( env, InfoBar_clazz(env), "dismiss", - "(" ")" "V", &g_InfoBar_dismiss); - env->CallVoidMethod(obj, + env->CallVoidMethod(obj.obj(), method_id); jni_generator::CheckException(env); - } static base::subtle::AtomicWord g_TestJni_shouldShowAutoLogin = 0; -static jboolean Java_TestJni_shouldShowAutoLogin(JNIEnv* env, jobject view, - jstring realm, - jstring account, - jstring args) { - /* Must call RegisterNativesImpl() */ +static jboolean Java_TestJni_shouldShowAutoLogin(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& view, + const base::android::JavaRefOrBare<jstring>& realm, + const base::android::JavaRefOrBare<jstring>& account, + const base::android::JavaRefOrBare<jstring>& args) { CHECK_CLAZZ(env, TestJni_clazz(env), TestJni_clazz(env), false); jmethodID method_id = @@ -131,7 +128,6 @@ static jboolean Java_TestJni_shouldShowAutoLogin(JNIEnv* env, jobject view, base::android::MethodID::TYPE_STATIC>( env, TestJni_clazz(env), "shouldShowAutoLogin", - "(" "Landroid/view/View;" "Ljava/lang/String;" @@ -143,15 +139,14 @@ static jboolean Java_TestJni_shouldShowAutoLogin(JNIEnv* env, jobject view, jboolean ret = env->CallStaticBooleanMethod(TestJni_clazz(env), - method_id, view, realm, account, args); + method_id, view.obj(), realm.obj(), account.obj(), args.obj()); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_TestJni_openUrl = 0; -static ScopedJavaLocalRef<jobject> Java_TestJni_openUrl(JNIEnv* env, jstring - url) { - /* Must call RegisterNativesImpl() */ +static base::android::ScopedJavaLocalRef<jobject> Java_TestJni_openUrl(JNIEnv* + env, const base::android::JavaRefOrBare<jstring>& url) { CHECK_CLAZZ(env, TestJni_clazz(env), TestJni_clazz(env), NULL); jmethodID method_id = @@ -159,7 +154,6 @@ static ScopedJavaLocalRef<jobject> Java_TestJni_openUrl(JNIEnv* env, jstring base::android::MethodID::TYPE_STATIC>( env, TestJni_clazz(env), "openUrl", - "(" "Ljava/lang/String;" ")" @@ -168,27 +162,25 @@ static ScopedJavaLocalRef<jobject> Java_TestJni_openUrl(JNIEnv* env, jstring jobject ret = env->CallStaticObjectMethod(TestJni_clazz(env), - method_id, url); + method_id, url.obj()); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jobject>(env, ret); + return base::android::ScopedJavaLocalRef<jobject>(env, ret); } static base::subtle::AtomicWord g_TestJni_activateHardwareAcceleration = 0; -static void Java_TestJni_activateHardwareAcceleration(JNIEnv* env, jobject obj, - jboolean activated, +static void Java_TestJni_activateHardwareAcceleration(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, jboolean activated, JniIntWrapper iPid, JniIntWrapper iType, JniIntWrapper iPrimaryID, JniIntWrapper iSecondaryID) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), TestJni_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( env, TestJni_clazz(env), "activateHardwareAcceleration", - "(" "Z" "I" @@ -199,310 +191,307 @@ static void Java_TestJni_activateHardwareAcceleration(JNIEnv* env, jobject obj, "V", &g_TestJni_activateHardwareAcceleration); - env->CallVoidMethod(obj, + env->CallVoidMethod(obj.obj(), method_id, activated, as_jint(iPid), as_jint(iType), as_jint(iPrimaryID), as_jint(iSecondaryID)); jni_generator::CheckException(env); +} + +static base::subtle::AtomicWord g_TestJni_updateStatus = 0; +static jint Java_TestJni_updateStatus(JNIEnv* env, JniIntWrapper status) { + CHECK_CLAZZ(env, TestJni_clazz(env), + TestJni_clazz(env), 0); + jmethodID method_id = + base::android::MethodID::LazyGet< + base::android::MethodID::TYPE_STATIC>( + env, TestJni_clazz(env), + "updateStatus", +"(" +"I" +")" +"I", + &g_TestJni_updateStatus); + jint ret = + env->CallStaticIntMethod(TestJni_clazz(env), + method_id, as_jint(status)); + jni_generator::CheckException(env); + return ret; } static base::subtle::AtomicWord g_TestJni_uncheckedCall = 0; -static void Java_TestJni_uncheckedCall(JNIEnv* env, jobject obj, JniIntWrapper - iParam) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static void Java_TestJni_uncheckedCall(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper iParam) { + CHECK_CLAZZ(env, obj.obj(), TestJni_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( env, TestJni_clazz(env), "uncheckedCall", - "(" "I" ")" "V", &g_TestJni_uncheckedCall); - env->CallVoidMethod(obj, + env->CallVoidMethod(obj.obj(), method_id, as_jint(iParam)); - } static base::subtle::AtomicWord g_TestJni_returnByteArray = 0; -static ScopedJavaLocalRef<jbyteArray> Java_TestJni_returnByteArray(JNIEnv* env, - jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static base::android::ScopedJavaLocalRef<jbyteArray> + Java_TestJni_returnByteArray(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( env, TestJni_clazz(env), "returnByteArray", - "(" ")" "[B", &g_TestJni_returnByteArray); jbyteArray ret = - static_cast<jbyteArray>(env->CallObjectMethod(obj, + static_cast<jbyteArray>(env->CallObjectMethod(obj.obj(), method_id)); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jbyteArray>(env, ret); + return base::android::ScopedJavaLocalRef<jbyteArray>(env, ret); } static base::subtle::AtomicWord g_TestJni_returnBooleanArray = 0; -static ScopedJavaLocalRef<jbooleanArray> Java_TestJni_returnBooleanArray(JNIEnv* - env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static base::android::ScopedJavaLocalRef<jbooleanArray> + Java_TestJni_returnBooleanArray(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( env, TestJni_clazz(env), "returnBooleanArray", - "(" ")" "[Z", &g_TestJni_returnBooleanArray); jbooleanArray ret = - static_cast<jbooleanArray>(env->CallObjectMethod(obj, + static_cast<jbooleanArray>(env->CallObjectMethod(obj.obj(), method_id)); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jbooleanArray>(env, ret); + return base::android::ScopedJavaLocalRef<jbooleanArray>(env, ret); } static base::subtle::AtomicWord g_TestJni_returnCharArray = 0; -static ScopedJavaLocalRef<jcharArray> Java_TestJni_returnCharArray(JNIEnv* env, - jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static base::android::ScopedJavaLocalRef<jcharArray> + Java_TestJni_returnCharArray(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( env, TestJni_clazz(env), "returnCharArray", - "(" ")" "[C", &g_TestJni_returnCharArray); jcharArray ret = - static_cast<jcharArray>(env->CallObjectMethod(obj, + static_cast<jcharArray>(env->CallObjectMethod(obj.obj(), method_id)); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jcharArray>(env, ret); + return base::android::ScopedJavaLocalRef<jcharArray>(env, ret); } static base::subtle::AtomicWord g_TestJni_returnShortArray = 0; -static ScopedJavaLocalRef<jshortArray> Java_TestJni_returnShortArray(JNIEnv* - env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static base::android::ScopedJavaLocalRef<jshortArray> + Java_TestJni_returnShortArray(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( env, TestJni_clazz(env), "returnShortArray", - "(" ")" "[S", &g_TestJni_returnShortArray); jshortArray ret = - static_cast<jshortArray>(env->CallObjectMethod(obj, + static_cast<jshortArray>(env->CallObjectMethod(obj.obj(), method_id)); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jshortArray>(env, ret); + return base::android::ScopedJavaLocalRef<jshortArray>(env, ret); } static base::subtle::AtomicWord g_TestJni_returnIntArray = 0; -static ScopedJavaLocalRef<jintArray> Java_TestJni_returnIntArray(JNIEnv* env, - jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static base::android::ScopedJavaLocalRef<jintArray> + Java_TestJni_returnIntArray(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( env, TestJni_clazz(env), "returnIntArray", - "(" ")" "[I", &g_TestJni_returnIntArray); jintArray ret = - static_cast<jintArray>(env->CallObjectMethod(obj, + static_cast<jintArray>(env->CallObjectMethod(obj.obj(), method_id)); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jintArray>(env, ret); + return base::android::ScopedJavaLocalRef<jintArray>(env, ret); } static base::subtle::AtomicWord g_TestJni_returnLongArray = 0; -static ScopedJavaLocalRef<jlongArray> Java_TestJni_returnLongArray(JNIEnv* env, - jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static base::android::ScopedJavaLocalRef<jlongArray> + Java_TestJni_returnLongArray(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( env, TestJni_clazz(env), "returnLongArray", - "(" ")" "[J", &g_TestJni_returnLongArray); jlongArray ret = - static_cast<jlongArray>(env->CallObjectMethod(obj, + static_cast<jlongArray>(env->CallObjectMethod(obj.obj(), method_id)); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jlongArray>(env, ret); + return base::android::ScopedJavaLocalRef<jlongArray>(env, ret); } static base::subtle::AtomicWord g_TestJni_returnDoubleArray = 0; -static ScopedJavaLocalRef<jdoubleArray> Java_TestJni_returnDoubleArray(JNIEnv* - env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static base::android::ScopedJavaLocalRef<jdoubleArray> + Java_TestJni_returnDoubleArray(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( env, TestJni_clazz(env), "returnDoubleArray", - "(" ")" "[D", &g_TestJni_returnDoubleArray); jdoubleArray ret = - static_cast<jdoubleArray>(env->CallObjectMethod(obj, + static_cast<jdoubleArray>(env->CallObjectMethod(obj.obj(), method_id)); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jdoubleArray>(env, ret); + return base::android::ScopedJavaLocalRef<jdoubleArray>(env, ret); } static base::subtle::AtomicWord g_TestJni_returnObjectArray = 0; -static ScopedJavaLocalRef<jobjectArray> Java_TestJni_returnObjectArray(JNIEnv* - env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static base::android::ScopedJavaLocalRef<jobjectArray> + Java_TestJni_returnObjectArray(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( env, TestJni_clazz(env), "returnObjectArray", - "(" ")" "[Ljava/lang/Object;", &g_TestJni_returnObjectArray); jobjectArray ret = - static_cast<jobjectArray>(env->CallObjectMethod(obj, + static_cast<jobjectArray>(env->CallObjectMethod(obj.obj(), method_id)); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jobjectArray>(env, ret); + return base::android::ScopedJavaLocalRef<jobjectArray>(env, ret); } static base::subtle::AtomicWord g_TestJni_returnArrayOfByteArray = 0; -static ScopedJavaLocalRef<jobjectArray> - Java_TestJni_returnArrayOfByteArray(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static base::android::ScopedJavaLocalRef<jobjectArray> + Java_TestJni_returnArrayOfByteArray(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( env, TestJni_clazz(env), "returnArrayOfByteArray", - "(" ")" "[[B", &g_TestJni_returnArrayOfByteArray); jobjectArray ret = - static_cast<jobjectArray>(env->CallObjectMethod(obj, + static_cast<jobjectArray>(env->CallObjectMethod(obj.obj(), method_id)); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jobjectArray>(env, ret); + return base::android::ScopedJavaLocalRef<jobjectArray>(env, ret); } static base::subtle::AtomicWord g_TestJni_getCompressFormat = 0; -static ScopedJavaLocalRef<jobject> Java_TestJni_getCompressFormat(JNIEnv* env, - jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static base::android::ScopedJavaLocalRef<jobject> + Java_TestJni_getCompressFormat(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( env, TestJni_clazz(env), "getCompressFormat", - "(" ")" "Landroid/graphics/Bitmap$CompressFormat;", &g_TestJni_getCompressFormat); jobject ret = - env->CallObjectMethod(obj, + env->CallObjectMethod(obj.obj(), method_id); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jobject>(env, ret); + return base::android::ScopedJavaLocalRef<jobject>(env, ret); } static base::subtle::AtomicWord g_TestJni_getCompressFormatList = 0; -static ScopedJavaLocalRef<jobject> Java_TestJni_getCompressFormatList(JNIEnv* - env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static base::android::ScopedJavaLocalRef<jobject> + Java_TestJni_getCompressFormatList(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( env, TestJni_clazz(env), "getCompressFormatList", - "(" ")" "Ljava/util/List;", &g_TestJni_getCompressFormatList); jobject ret = - env->CallObjectMethod(obj, + env->CallObjectMethod(obj.obj(), method_id); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jobject>(env, ret); + return base::android::ScopedJavaLocalRef<jobject>(env, ret); } // Step 3: RegisterNatives. -static bool RegisterNativesImpl(JNIEnv* env) { - - g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( - base::android::GetClass(env, kTestJniClassPath).obj())); - g_InfoBar_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( - base::android::GetClass(env, kInfoBarClassPath).obj())); - - return true; -} - #endif // org_chromium_TestJni_JNI diff --git a/base/android/jni_generator/testConstantsFromJavaP.golden b/base/android/jni_generator/testConstantsFromJavaP.golden index 97c00f9..b16956f 100644 --- a/base/android/jni_generator/testConstantsFromJavaP.golden +++ b/base/android/jni_generator/testConstantsFromJavaP.golden @@ -20,8 +20,8 @@ namespace { const char kMotionEventClassPath[] = "android/view/MotionEvent"; // Leaking this jclass as we cannot use LazyInstance from some threads. -jclass g_MotionEvent_clazz = NULL; -#define MotionEvent_clazz(env) g_MotionEvent_clazz +base::subtle::AtomicWord g_MotionEvent_clazz __attribute__((unused)) = 0; +#define MotionEvent_clazz(env) base::android::LazyGetClass(env, kMotionEventClassPath, &g_MotionEvent_clazz) } // namespace @@ -113,11 +113,11 @@ enum Java_MotionEvent_constant_fields { // Step 2: method stubs. static base::subtle::AtomicWord g_MotionEvent_finalize = 0; -static void Java_MotionEvent_finalize(JNIEnv* env, jobject obj) __attribute__ - ((unused)); -static void Java_MotionEvent_finalize(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static void Java_MotionEvent_finalize(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static void Java_MotionEvent_finalize(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -127,22 +127,21 @@ static void Java_MotionEvent_finalize(JNIEnv* env, jobject obj) { "()V", &g_MotionEvent_finalize); - env->CallVoidMethod(obj, + env->CallVoidMethod(obj.obj(), method_id); jni_generator::CheckException(env); - } static base::subtle::AtomicWord g_MotionEvent_obtainAVME_J_J_I_I_LAVMEPP_LAVMEPC_I_I_F_F_I_I_I_I = 0; -static ScopedJavaLocalRef<jobject> +static base::android::ScopedJavaLocalRef<jobject> Java_MotionEvent_obtainAVME_J_J_I_I_LAVMEPP_LAVMEPC_I_I_F_F_I_I_I_I(JNIEnv* env, jlong p0, jlong p1, JniIntWrapper p2, JniIntWrapper p3, - jobjectArray p4, - jobjectArray p5, + const base::android::JavaRefOrBare<jobjectArray>& p4, + const base::android::JavaRefOrBare<jobjectArray>& p5, JniIntWrapper p6, JniIntWrapper p7, jfloat p8, @@ -151,14 +150,14 @@ static ScopedJavaLocalRef<jobject> JniIntWrapper p11, JniIntWrapper p12, JniIntWrapper p13) __attribute__ ((unused)); -static ScopedJavaLocalRef<jobject> +static base::android::ScopedJavaLocalRef<jobject> Java_MotionEvent_obtainAVME_J_J_I_I_LAVMEPP_LAVMEPC_I_I_F_F_I_I_I_I(JNIEnv* env, jlong p0, jlong p1, JniIntWrapper p2, JniIntWrapper p3, - jobjectArray p4, - jobjectArray p5, + const base::android::JavaRefOrBare<jobjectArray>& p4, + const base::android::JavaRefOrBare<jobjectArray>& p5, JniIntWrapper p6, JniIntWrapper p7, jfloat p8, @@ -167,7 +166,6 @@ static ScopedJavaLocalRef<jobject> JniIntWrapper p11, JniIntWrapper p12, JniIntWrapper p13) { - /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, MotionEvent_clazz(env), MotionEvent_clazz(env), NULL); jmethodID method_id = @@ -180,23 +178,23 @@ static ScopedJavaLocalRef<jobject> jobject ret = env->CallStaticObjectMethod(MotionEvent_clazz(env), - method_id, p0, p1, as_jint(p2), as_jint(p3), p4, p5, as_jint(p6), - as_jint(p7), p8, p9, as_jint(p10), as_jint(p11), as_jint(p12), - as_jint(p13)); + method_id, p0, p1, as_jint(p2), as_jint(p3), p4.obj(), p5.obj(), + as_jint(p6), as_jint(p7), p8, p9, as_jint(p10), as_jint(p11), + as_jint(p12), as_jint(p13)); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jobject>(env, ret); + return base::android::ScopedJavaLocalRef<jobject>(env, ret); } static base::subtle::AtomicWord g_MotionEvent_obtainAVME_J_J_I_I_AI_LAVMEPC_I_F_F_I_I_I_I = 0; -static ScopedJavaLocalRef<jobject> +static base::android::ScopedJavaLocalRef<jobject> Java_MotionEvent_obtainAVME_J_J_I_I_AI_LAVMEPC_I_F_F_I_I_I_I(JNIEnv* env, jlong p0, jlong p1, JniIntWrapper p2, JniIntWrapper p3, - jintArray p4, - jobjectArray p5, + const base::android::JavaRefOrBare<jintArray>& p4, + const base::android::JavaRefOrBare<jobjectArray>& p5, JniIntWrapper p6, jfloat p7, jfloat p8, @@ -204,14 +202,14 @@ static ScopedJavaLocalRef<jobject> JniIntWrapper p10, JniIntWrapper p11, JniIntWrapper p12) __attribute__ ((unused)); -static ScopedJavaLocalRef<jobject> +static base::android::ScopedJavaLocalRef<jobject> Java_MotionEvent_obtainAVME_J_J_I_I_AI_LAVMEPC_I_F_F_I_I_I_I(JNIEnv* env, jlong p0, jlong p1, JniIntWrapper p2, JniIntWrapper p3, - jintArray p4, - jobjectArray p5, + const base::android::JavaRefOrBare<jintArray>& p4, + const base::android::JavaRefOrBare<jobjectArray>& p5, JniIntWrapper p6, jfloat p7, jfloat p8, @@ -219,7 +217,6 @@ static ScopedJavaLocalRef<jobject> JniIntWrapper p10, JniIntWrapper p11, JniIntWrapper p12) { - /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, MotionEvent_clazz(env), MotionEvent_clazz(env), NULL); jmethodID method_id = @@ -232,15 +229,16 @@ static ScopedJavaLocalRef<jobject> jobject ret = env->CallStaticObjectMethod(MotionEvent_clazz(env), - method_id, p0, p1, as_jint(p2), as_jint(p3), p4, p5, as_jint(p6), p7, - p8, as_jint(p9), as_jint(p10), as_jint(p11), as_jint(p12)); + method_id, p0, p1, as_jint(p2), as_jint(p3), p4.obj(), p5.obj(), + as_jint(p6), p7, p8, as_jint(p9), as_jint(p10), as_jint(p11), + as_jint(p12)); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jobject>(env, ret); + return base::android::ScopedJavaLocalRef<jobject>(env, ret); } static base::subtle::AtomicWord g_MotionEvent_obtainAVME_J_J_I_F_F_F_F_I_F_F_I_I = 0; -static ScopedJavaLocalRef<jobject> +static base::android::ScopedJavaLocalRef<jobject> Java_MotionEvent_obtainAVME_J_J_I_F_F_F_F_I_F_F_I_I(JNIEnv* env, jlong p0, jlong p1, JniIntWrapper p2, @@ -253,7 +251,7 @@ static ScopedJavaLocalRef<jobject> jfloat p9, JniIntWrapper p10, JniIntWrapper p11) __attribute__ ((unused)); -static ScopedJavaLocalRef<jobject> +static base::android::ScopedJavaLocalRef<jobject> Java_MotionEvent_obtainAVME_J_J_I_F_F_F_F_I_F_F_I_I(JNIEnv* env, jlong p0, jlong p1, JniIntWrapper p2, @@ -266,7 +264,6 @@ static ScopedJavaLocalRef<jobject> jfloat p9, JniIntWrapper p10, JniIntWrapper p11) { - /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, MotionEvent_clazz(env), MotionEvent_clazz(env), NULL); jmethodID method_id = @@ -282,12 +279,12 @@ static ScopedJavaLocalRef<jobject> method_id, p0, p1, as_jint(p2), p3, p4, p5, p6, as_jint(p7), p8, p9, as_jint(p10), as_jint(p11)); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jobject>(env, ret); + return base::android::ScopedJavaLocalRef<jobject>(env, ret); } static base::subtle::AtomicWord g_MotionEvent_obtainAVME_J_J_I_I_F_F_F_F_I_F_F_I_I = 0; -static ScopedJavaLocalRef<jobject> +static base::android::ScopedJavaLocalRef<jobject> Java_MotionEvent_obtainAVME_J_J_I_I_F_F_F_F_I_F_F_I_I(JNIEnv* env, jlong p0, jlong p1, JniIntWrapper p2, @@ -301,7 +298,7 @@ static ScopedJavaLocalRef<jobject> jfloat p10, JniIntWrapper p11, JniIntWrapper p12) __attribute__ ((unused)); -static ScopedJavaLocalRef<jobject> +static base::android::ScopedJavaLocalRef<jobject> Java_MotionEvent_obtainAVME_J_J_I_I_F_F_F_F_I_F_F_I_I(JNIEnv* env, jlong p0, jlong p1, JniIntWrapper p2, @@ -315,7 +312,6 @@ static ScopedJavaLocalRef<jobject> jfloat p10, JniIntWrapper p11, JniIntWrapper p12) { - /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, MotionEvent_clazz(env), MotionEvent_clazz(env), NULL); jmethodID method_id = @@ -331,25 +327,24 @@ static ScopedJavaLocalRef<jobject> method_id, p0, p1, as_jint(p2), as_jint(p3), p4, p5, p6, p7, as_jint(p8), p9, p10, as_jint(p11), as_jint(p12)); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jobject>(env, ret); + return base::android::ScopedJavaLocalRef<jobject>(env, ret); } static base::subtle::AtomicWord g_MotionEvent_obtainAVME_J_J_I_F_F_I = 0; -static ScopedJavaLocalRef<jobject> +static base::android::ScopedJavaLocalRef<jobject> Java_MotionEvent_obtainAVME_J_J_I_F_F_I(JNIEnv* env, jlong p0, jlong p1, JniIntWrapper p2, jfloat p3, jfloat p4, JniIntWrapper p5) __attribute__ ((unused)); -static ScopedJavaLocalRef<jobject> +static base::android::ScopedJavaLocalRef<jobject> Java_MotionEvent_obtainAVME_J_J_I_F_F_I(JNIEnv* env, jlong p0, jlong p1, JniIntWrapper p2, jfloat p3, jfloat p4, JniIntWrapper p5) { - /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, MotionEvent_clazz(env), MotionEvent_clazz(env), NULL); jmethodID method_id = @@ -364,15 +359,16 @@ static ScopedJavaLocalRef<jobject> env->CallStaticObjectMethod(MotionEvent_clazz(env), method_id, p0, p1, as_jint(p2), p3, p4, as_jint(p5)); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jobject>(env, ret); + return base::android::ScopedJavaLocalRef<jobject>(env, ret); } static base::subtle::AtomicWord g_MotionEvent_obtainAVME_AVME = 0; -static ScopedJavaLocalRef<jobject> Java_MotionEvent_obtainAVME_AVME(JNIEnv* env, - jobject p0) __attribute__ ((unused)); -static ScopedJavaLocalRef<jobject> Java_MotionEvent_obtainAVME_AVME(JNIEnv* env, - jobject p0) { - /* Must call RegisterNativesImpl() */ +static base::android::ScopedJavaLocalRef<jobject> + Java_MotionEvent_obtainAVME_AVME(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& p0) __attribute__ ((unused)); +static base::android::ScopedJavaLocalRef<jobject> + Java_MotionEvent_obtainAVME_AVME(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& p0) { CHECK_CLAZZ(env, MotionEvent_clazz(env), MotionEvent_clazz(env), NULL); jmethodID method_id = @@ -385,17 +381,18 @@ static ScopedJavaLocalRef<jobject> Java_MotionEvent_obtainAVME_AVME(JNIEnv* env, jobject ret = env->CallStaticObjectMethod(MotionEvent_clazz(env), - method_id, p0); + method_id, p0.obj()); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jobject>(env, ret); + return base::android::ScopedJavaLocalRef<jobject>(env, ret); } static base::subtle::AtomicWord g_MotionEvent_obtainNoHistory = 0; -static ScopedJavaLocalRef<jobject> Java_MotionEvent_obtainNoHistory(JNIEnv* env, - jobject p0) __attribute__ ((unused)); -static ScopedJavaLocalRef<jobject> Java_MotionEvent_obtainNoHistory(JNIEnv* env, - jobject p0) { - /* Must call RegisterNativesImpl() */ +static base::android::ScopedJavaLocalRef<jobject> + Java_MotionEvent_obtainNoHistory(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& p0) __attribute__ ((unused)); +static base::android::ScopedJavaLocalRef<jobject> + Java_MotionEvent_obtainNoHistory(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& p0) { CHECK_CLAZZ(env, MotionEvent_clazz(env), MotionEvent_clazz(env), NULL); jmethodID method_id = @@ -408,17 +405,17 @@ static ScopedJavaLocalRef<jobject> Java_MotionEvent_obtainNoHistory(JNIEnv* env, jobject ret = env->CallStaticObjectMethod(MotionEvent_clazz(env), - method_id, p0); + method_id, p0.obj()); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jobject>(env, ret); + return base::android::ScopedJavaLocalRef<jobject>(env, ret); } static base::subtle::AtomicWord g_MotionEvent_recycle = 0; -static void Java_MotionEvent_recycle(JNIEnv* env, jobject obj) __attribute__ - ((unused)); -static void Java_MotionEvent_recycle(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static void Java_MotionEvent_recycle(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static void Java_MotionEvent_recycle(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -428,18 +425,17 @@ static void Java_MotionEvent_recycle(JNIEnv* env, jobject obj) { "()V", &g_MotionEvent_recycle); - env->CallVoidMethod(obj, + env->CallVoidMethod(obj.obj(), method_id); jni_generator::CheckException(env); - } static base::subtle::AtomicWord g_MotionEvent_getDeviceId = 0; -static jint Java_MotionEvent_getDeviceId(JNIEnv* env, jobject obj) __attribute__ - ((unused)); -static jint Java_MotionEvent_getDeviceId(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jint Java_MotionEvent_getDeviceId(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jint Java_MotionEvent_getDeviceId(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -450,18 +446,18 @@ static jint Java_MotionEvent_getDeviceId(JNIEnv* env, jobject obj) { &g_MotionEvent_getDeviceId); jint ret = - env->CallIntMethod(obj, + env->CallIntMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getSource = 0; -static jint Java_MotionEvent_getSource(JNIEnv* env, jobject obj) __attribute__ - ((unused)); -static jint Java_MotionEvent_getSource(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jint Java_MotionEvent_getSource(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jint Java_MotionEvent_getSource(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -472,19 +468,19 @@ static jint Java_MotionEvent_getSource(JNIEnv* env, jobject obj) { &g_MotionEvent_getSource); jint ret = - env->CallIntMethod(obj, + env->CallIntMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_setSource = 0; -static void Java_MotionEvent_setSource(JNIEnv* env, jobject obj, JniIntWrapper - p0) __attribute__ ((unused)); -static void Java_MotionEvent_setSource(JNIEnv* env, jobject obj, JniIntWrapper - p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static void Java_MotionEvent_setSource(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static void Java_MotionEvent_setSource(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -494,18 +490,17 @@ static void Java_MotionEvent_setSource(JNIEnv* env, jobject obj, JniIntWrapper "(I)V", &g_MotionEvent_setSource); - env->CallVoidMethod(obj, + env->CallVoidMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); - } static base::subtle::AtomicWord g_MotionEvent_getAction = 0; -static jint Java_MotionEvent_getAction(JNIEnv* env, jobject obj) __attribute__ - ((unused)); -static jint Java_MotionEvent_getAction(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jint Java_MotionEvent_getAction(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jint Java_MotionEvent_getAction(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -516,18 +511,18 @@ static jint Java_MotionEvent_getAction(JNIEnv* env, jobject obj) { &g_MotionEvent_getAction); jint ret = - env->CallIntMethod(obj, + env->CallIntMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getActionMasked = 0; -static jint Java_MotionEvent_getActionMasked(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jint Java_MotionEvent_getActionMasked(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jint Java_MotionEvent_getActionMasked(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jint Java_MotionEvent_getActionMasked(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -538,18 +533,18 @@ static jint Java_MotionEvent_getActionMasked(JNIEnv* env, jobject obj) { &g_MotionEvent_getActionMasked); jint ret = - env->CallIntMethod(obj, + env->CallIntMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getActionIndex = 0; -static jint Java_MotionEvent_getActionIndex(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jint Java_MotionEvent_getActionIndex(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jint Java_MotionEvent_getActionIndex(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jint Java_MotionEvent_getActionIndex(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -560,18 +555,18 @@ static jint Java_MotionEvent_getActionIndex(JNIEnv* env, jobject obj) { &g_MotionEvent_getActionIndex); jint ret = - env->CallIntMethod(obj, + env->CallIntMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getFlags = 0; -static jint Java_MotionEvent_getFlags(JNIEnv* env, jobject obj) __attribute__ - ((unused)); -static jint Java_MotionEvent_getFlags(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jint Java_MotionEvent_getFlags(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jint Java_MotionEvent_getFlags(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -582,18 +577,18 @@ static jint Java_MotionEvent_getFlags(JNIEnv* env, jobject obj) { &g_MotionEvent_getFlags); jint ret = - env->CallIntMethod(obj, + env->CallIntMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getDownTime = 0; -static jlong Java_MotionEvent_getDownTime(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jlong Java_MotionEvent_getDownTime(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jlong Java_MotionEvent_getDownTime(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jlong Java_MotionEvent_getDownTime(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -604,18 +599,18 @@ static jlong Java_MotionEvent_getDownTime(JNIEnv* env, jobject obj) { &g_MotionEvent_getDownTime); jlong ret = - env->CallLongMethod(obj, + env->CallLongMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getEventTime = 0; -static jlong Java_MotionEvent_getEventTime(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jlong Java_MotionEvent_getEventTime(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jlong Java_MotionEvent_getEventTime(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jlong Java_MotionEvent_getEventTime(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -626,18 +621,18 @@ static jlong Java_MotionEvent_getEventTime(JNIEnv* env, jobject obj) { &g_MotionEvent_getEventTime); jlong ret = - env->CallLongMethod(obj, + env->CallLongMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getXF = 0; -static jfloat Java_MotionEvent_getXF(JNIEnv* env, jobject obj) __attribute__ - ((unused)); -static jfloat Java_MotionEvent_getXF(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getXF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jfloat Java_MotionEvent_getXF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -648,18 +643,18 @@ static jfloat Java_MotionEvent_getXF(JNIEnv* env, jobject obj) { &g_MotionEvent_getXF); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getYF = 0; -static jfloat Java_MotionEvent_getYF(JNIEnv* env, jobject obj) __attribute__ - ((unused)); -static jfloat Java_MotionEvent_getYF(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getYF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jfloat Java_MotionEvent_getYF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -670,18 +665,18 @@ static jfloat Java_MotionEvent_getYF(JNIEnv* env, jobject obj) { &g_MotionEvent_getYF); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getPressureF = 0; -static jfloat Java_MotionEvent_getPressureF(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jfloat Java_MotionEvent_getPressureF(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getPressureF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jfloat Java_MotionEvent_getPressureF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -692,18 +687,18 @@ static jfloat Java_MotionEvent_getPressureF(JNIEnv* env, jobject obj) { &g_MotionEvent_getPressureF); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getSizeF = 0; -static jfloat Java_MotionEvent_getSizeF(JNIEnv* env, jobject obj) __attribute__ - ((unused)); -static jfloat Java_MotionEvent_getSizeF(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getSizeF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jfloat Java_MotionEvent_getSizeF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -714,18 +709,18 @@ static jfloat Java_MotionEvent_getSizeF(JNIEnv* env, jobject obj) { &g_MotionEvent_getSizeF); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getTouchMajorF = 0; -static jfloat Java_MotionEvent_getTouchMajorF(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jfloat Java_MotionEvent_getTouchMajorF(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getTouchMajorF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jfloat Java_MotionEvent_getTouchMajorF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -736,18 +731,18 @@ static jfloat Java_MotionEvent_getTouchMajorF(JNIEnv* env, jobject obj) { &g_MotionEvent_getTouchMajorF); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getTouchMinorF = 0; -static jfloat Java_MotionEvent_getTouchMinorF(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jfloat Java_MotionEvent_getTouchMinorF(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getTouchMinorF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jfloat Java_MotionEvent_getTouchMinorF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -758,18 +753,18 @@ static jfloat Java_MotionEvent_getTouchMinorF(JNIEnv* env, jobject obj) { &g_MotionEvent_getTouchMinorF); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getToolMajorF = 0; -static jfloat Java_MotionEvent_getToolMajorF(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jfloat Java_MotionEvent_getToolMajorF(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getToolMajorF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jfloat Java_MotionEvent_getToolMajorF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -780,18 +775,18 @@ static jfloat Java_MotionEvent_getToolMajorF(JNIEnv* env, jobject obj) { &g_MotionEvent_getToolMajorF); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getToolMinorF = 0; -static jfloat Java_MotionEvent_getToolMinorF(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jfloat Java_MotionEvent_getToolMinorF(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getToolMinorF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jfloat Java_MotionEvent_getToolMinorF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -802,18 +797,18 @@ static jfloat Java_MotionEvent_getToolMinorF(JNIEnv* env, jobject obj) { &g_MotionEvent_getToolMinorF); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getOrientationF = 0; -static jfloat Java_MotionEvent_getOrientationF(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jfloat Java_MotionEvent_getOrientationF(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getOrientationF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jfloat Java_MotionEvent_getOrientationF(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -824,19 +819,19 @@ static jfloat Java_MotionEvent_getOrientationF(JNIEnv* env, jobject obj) { &g_MotionEvent_getOrientationF); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getAxisValueF_I = 0; -static jfloat Java_MotionEvent_getAxisValueF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getAxisValueF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getAxisValueF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getAxisValueF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -847,18 +842,18 @@ static jfloat Java_MotionEvent_getAxisValueF_I(JNIEnv* env, jobject obj, &g_MotionEvent_getAxisValueF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getPointerCount = 0; -static jint Java_MotionEvent_getPointerCount(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jint Java_MotionEvent_getPointerCount(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jint Java_MotionEvent_getPointerCount(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jint Java_MotionEvent_getPointerCount(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -869,19 +864,19 @@ static jint Java_MotionEvent_getPointerCount(JNIEnv* env, jobject obj) { &g_MotionEvent_getPointerCount); jint ret = - env->CallIntMethod(obj, + env->CallIntMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getPointerId = 0; -static jint Java_MotionEvent_getPointerId(JNIEnv* env, jobject obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jint Java_MotionEvent_getPointerId(JNIEnv* env, jobject obj, - JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jint Java_MotionEvent_getPointerId(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jint Java_MotionEvent_getPointerId(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -892,19 +887,19 @@ static jint Java_MotionEvent_getPointerId(JNIEnv* env, jobject obj, &g_MotionEvent_getPointerId); jint ret = - env->CallIntMethod(obj, + env->CallIntMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getToolType = 0; -static jint Java_MotionEvent_getToolType(JNIEnv* env, jobject obj, JniIntWrapper - p0) __attribute__ ((unused)); -static jint Java_MotionEvent_getToolType(JNIEnv* env, jobject obj, JniIntWrapper - p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jint Java_MotionEvent_getToolType(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jint Java_MotionEvent_getToolType(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -915,19 +910,19 @@ static jint Java_MotionEvent_getToolType(JNIEnv* env, jobject obj, JniIntWrapper &g_MotionEvent_getToolType); jint ret = - env->CallIntMethod(obj, + env->CallIntMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_findPointerIndex = 0; -static jint Java_MotionEvent_findPointerIndex(JNIEnv* env, jobject obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jint Java_MotionEvent_findPointerIndex(JNIEnv* env, jobject obj, - JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jint Java_MotionEvent_findPointerIndex(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jint Java_MotionEvent_findPointerIndex(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -938,19 +933,19 @@ static jint Java_MotionEvent_findPointerIndex(JNIEnv* env, jobject obj, &g_MotionEvent_findPointerIndex); jint ret = - env->CallIntMethod(obj, + env->CallIntMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getXF_I = 0; -static jfloat Java_MotionEvent_getXF_I(JNIEnv* env, jobject obj, JniIntWrapper - p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getXF_I(JNIEnv* env, jobject obj, JniIntWrapper - p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getXF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getXF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -961,19 +956,19 @@ static jfloat Java_MotionEvent_getXF_I(JNIEnv* env, jobject obj, JniIntWrapper &g_MotionEvent_getXF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getYF_I = 0; -static jfloat Java_MotionEvent_getYF_I(JNIEnv* env, jobject obj, JniIntWrapper - p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getYF_I(JNIEnv* env, jobject obj, JniIntWrapper - p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getYF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getYF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -984,19 +979,19 @@ static jfloat Java_MotionEvent_getYF_I(JNIEnv* env, jobject obj, JniIntWrapper &g_MotionEvent_getYF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getPressureF_I = 0; -static jfloat Java_MotionEvent_getPressureF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getPressureF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getPressureF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getPressureF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1007,19 +1002,19 @@ static jfloat Java_MotionEvent_getPressureF_I(JNIEnv* env, jobject obj, &g_MotionEvent_getPressureF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getSizeF_I = 0; -static jfloat Java_MotionEvent_getSizeF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getSizeF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getSizeF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getSizeF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1030,19 +1025,19 @@ static jfloat Java_MotionEvent_getSizeF_I(JNIEnv* env, jobject obj, &g_MotionEvent_getSizeF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getTouchMajorF_I = 0; -static jfloat Java_MotionEvent_getTouchMajorF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getTouchMajorF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getTouchMajorF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getTouchMajorF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1053,19 +1048,19 @@ static jfloat Java_MotionEvent_getTouchMajorF_I(JNIEnv* env, jobject obj, &g_MotionEvent_getTouchMajorF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getTouchMinorF_I = 0; -static jfloat Java_MotionEvent_getTouchMinorF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getTouchMinorF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getTouchMinorF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getTouchMinorF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1076,19 +1071,19 @@ static jfloat Java_MotionEvent_getTouchMinorF_I(JNIEnv* env, jobject obj, &g_MotionEvent_getTouchMinorF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getToolMajorF_I = 0; -static jfloat Java_MotionEvent_getToolMajorF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getToolMajorF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getToolMajorF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getToolMajorF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1099,19 +1094,19 @@ static jfloat Java_MotionEvent_getToolMajorF_I(JNIEnv* env, jobject obj, &g_MotionEvent_getToolMajorF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getToolMinorF_I = 0; -static jfloat Java_MotionEvent_getToolMinorF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getToolMinorF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getToolMinorF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getToolMinorF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1122,19 +1117,19 @@ static jfloat Java_MotionEvent_getToolMinorF_I(JNIEnv* env, jobject obj, &g_MotionEvent_getToolMinorF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getOrientationF_I = 0; -static jfloat Java_MotionEvent_getOrientationF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getOrientationF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getOrientationF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getOrientationF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1145,21 +1140,20 @@ static jfloat Java_MotionEvent_getOrientationF_I(JNIEnv* env, jobject obj, &g_MotionEvent_getOrientationF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getAxisValueF_I_I = 0; -static jfloat Java_MotionEvent_getAxisValueF_I_I(JNIEnv* env, jobject obj, - JniIntWrapper p0, +static jfloat Java_MotionEvent_getAxisValueF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getAxisValueF_I_I(JNIEnv* env, jobject obj, - JniIntWrapper p0, +static jfloat Java_MotionEvent_getAxisValueF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1170,21 +1164,20 @@ static jfloat Java_MotionEvent_getAxisValueF_I_I(JNIEnv* env, jobject obj, &g_MotionEvent_getAxisValueF_I_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0), as_jint(p1)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getPointerCoords = 0; -static void Java_MotionEvent_getPointerCoords(JNIEnv* env, jobject obj, - JniIntWrapper p0, - jobject p1) __attribute__ ((unused)); -static void Java_MotionEvent_getPointerCoords(JNIEnv* env, jobject obj, - JniIntWrapper p0, - jobject p1) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static void Java_MotionEvent_getPointerCoords(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, + const base::android::JavaRefOrBare<jobject>& p1) __attribute__ ((unused)); +static void Java_MotionEvent_getPointerCoords(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, + const base::android::JavaRefOrBare<jobject>& p1) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1194,21 +1187,19 @@ static void Java_MotionEvent_getPointerCoords(JNIEnv* env, jobject obj, "(ILandroid/view/MotionEvent$PointerCoords;)V", &g_MotionEvent_getPointerCoords); - env->CallVoidMethod(obj, - method_id, as_jint(p0), p1); + env->CallVoidMethod(obj.obj(), + method_id, as_jint(p0), p1.obj()); jni_generator::CheckException(env); - } static base::subtle::AtomicWord g_MotionEvent_getPointerProperties = 0; -static void Java_MotionEvent_getPointerProperties(JNIEnv* env, jobject obj, - JniIntWrapper p0, - jobject p1) __attribute__ ((unused)); -static void Java_MotionEvent_getPointerProperties(JNIEnv* env, jobject obj, - JniIntWrapper p0, - jobject p1) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static void Java_MotionEvent_getPointerProperties(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, + const base::android::JavaRefOrBare<jobject>& p1) __attribute__ ((unused)); +static void Java_MotionEvent_getPointerProperties(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, + const base::android::JavaRefOrBare<jobject>& p1) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1218,18 +1209,17 @@ static void Java_MotionEvent_getPointerProperties(JNIEnv* env, jobject obj, "(ILandroid/view/MotionEvent$PointerProperties;)V", &g_MotionEvent_getPointerProperties); - env->CallVoidMethod(obj, - method_id, as_jint(p0), p1); + env->CallVoidMethod(obj.obj(), + method_id, as_jint(p0), p1.obj()); jni_generator::CheckException(env); - } static base::subtle::AtomicWord g_MotionEvent_getMetaState = 0; -static jint Java_MotionEvent_getMetaState(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jint Java_MotionEvent_getMetaState(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jint Java_MotionEvent_getMetaState(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jint Java_MotionEvent_getMetaState(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1240,18 +1230,18 @@ static jint Java_MotionEvent_getMetaState(JNIEnv* env, jobject obj) { &g_MotionEvent_getMetaState); jint ret = - env->CallIntMethod(obj, + env->CallIntMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getButtonState = 0; -static jint Java_MotionEvent_getButtonState(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jint Java_MotionEvent_getButtonState(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jint Java_MotionEvent_getButtonState(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jint Java_MotionEvent_getButtonState(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1262,18 +1252,18 @@ static jint Java_MotionEvent_getButtonState(JNIEnv* env, jobject obj) { &g_MotionEvent_getButtonState); jint ret = - env->CallIntMethod(obj, + env->CallIntMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getRawX = 0; -static jfloat Java_MotionEvent_getRawX(JNIEnv* env, jobject obj) __attribute__ - ((unused)); -static jfloat Java_MotionEvent_getRawX(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getRawX(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jfloat Java_MotionEvent_getRawX(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1284,18 +1274,18 @@ static jfloat Java_MotionEvent_getRawX(JNIEnv* env, jobject obj) { &g_MotionEvent_getRawX); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getRawY = 0; -static jfloat Java_MotionEvent_getRawY(JNIEnv* env, jobject obj) __attribute__ - ((unused)); -static jfloat Java_MotionEvent_getRawY(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getRawY(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jfloat Java_MotionEvent_getRawY(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1306,18 +1296,18 @@ static jfloat Java_MotionEvent_getRawY(JNIEnv* env, jobject obj) { &g_MotionEvent_getRawY); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getXPrecision = 0; -static jfloat Java_MotionEvent_getXPrecision(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jfloat Java_MotionEvent_getXPrecision(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getXPrecision(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jfloat Java_MotionEvent_getXPrecision(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1328,18 +1318,18 @@ static jfloat Java_MotionEvent_getXPrecision(JNIEnv* env, jobject obj) { &g_MotionEvent_getXPrecision); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getYPrecision = 0; -static jfloat Java_MotionEvent_getYPrecision(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jfloat Java_MotionEvent_getYPrecision(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getYPrecision(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jfloat Java_MotionEvent_getYPrecision(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1350,18 +1340,18 @@ static jfloat Java_MotionEvent_getYPrecision(JNIEnv* env, jobject obj) { &g_MotionEvent_getYPrecision); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistorySize = 0; -static jint Java_MotionEvent_getHistorySize(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jint Java_MotionEvent_getHistorySize(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jint Java_MotionEvent_getHistorySize(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jint Java_MotionEvent_getHistorySize(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1372,19 +1362,19 @@ static jint Java_MotionEvent_getHistorySize(JNIEnv* env, jobject obj) { &g_MotionEvent_getHistorySize); jint ret = - env->CallIntMethod(obj, + env->CallIntMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalEventTime = 0; -static jlong Java_MotionEvent_getHistoricalEventTime(JNIEnv* env, jobject obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jlong Java_MotionEvent_getHistoricalEventTime(JNIEnv* env, jobject obj, - JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jlong Java_MotionEvent_getHistoricalEventTime(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jlong Java_MotionEvent_getHistoricalEventTime(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1395,19 +1385,19 @@ static jlong Java_MotionEvent_getHistoricalEventTime(JNIEnv* env, jobject obj, &g_MotionEvent_getHistoricalEventTime); jlong ret = - env->CallLongMethod(obj, + env->CallLongMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalXF_I = 0; -static jfloat Java_MotionEvent_getHistoricalXF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalXF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getHistoricalXF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getHistoricalXF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1418,19 +1408,19 @@ static jfloat Java_MotionEvent_getHistoricalXF_I(JNIEnv* env, jobject obj, &g_MotionEvent_getHistoricalXF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalYF_I = 0; -static jfloat Java_MotionEvent_getHistoricalYF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalYF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getHistoricalYF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getHistoricalYF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1441,19 +1431,19 @@ static jfloat Java_MotionEvent_getHistoricalYF_I(JNIEnv* env, jobject obj, &g_MotionEvent_getHistoricalYF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalPressureF_I = 0; -static jfloat Java_MotionEvent_getHistoricalPressureF_I(JNIEnv* env, jobject - obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalPressureF_I(JNIEnv* env, jobject - obj, JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getHistoricalPressureF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getHistoricalPressureF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1464,19 +1454,19 @@ static jfloat Java_MotionEvent_getHistoricalPressureF_I(JNIEnv* env, jobject &g_MotionEvent_getHistoricalPressureF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalSizeF_I = 0; -static jfloat Java_MotionEvent_getHistoricalSizeF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalSizeF_I(JNIEnv* env, jobject obj, - JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getHistoricalSizeF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getHistoricalSizeF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1487,19 +1477,19 @@ static jfloat Java_MotionEvent_getHistoricalSizeF_I(JNIEnv* env, jobject obj, &g_MotionEvent_getHistoricalSizeF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalTouchMajorF_I = 0; -static jfloat Java_MotionEvent_getHistoricalTouchMajorF_I(JNIEnv* env, jobject - obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalTouchMajorF_I(JNIEnv* env, jobject - obj, JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getHistoricalTouchMajorF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getHistoricalTouchMajorF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1510,19 +1500,19 @@ static jfloat Java_MotionEvent_getHistoricalTouchMajorF_I(JNIEnv* env, jobject &g_MotionEvent_getHistoricalTouchMajorF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalTouchMinorF_I = 0; -static jfloat Java_MotionEvent_getHistoricalTouchMinorF_I(JNIEnv* env, jobject - obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalTouchMinorF_I(JNIEnv* env, jobject - obj, JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getHistoricalTouchMinorF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getHistoricalTouchMinorF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1533,19 +1523,19 @@ static jfloat Java_MotionEvent_getHistoricalTouchMinorF_I(JNIEnv* env, jobject &g_MotionEvent_getHistoricalTouchMinorF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalToolMajorF_I = 0; -static jfloat Java_MotionEvent_getHistoricalToolMajorF_I(JNIEnv* env, jobject - obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalToolMajorF_I(JNIEnv* env, jobject - obj, JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getHistoricalToolMajorF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getHistoricalToolMajorF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1556,19 +1546,19 @@ static jfloat Java_MotionEvent_getHistoricalToolMajorF_I(JNIEnv* env, jobject &g_MotionEvent_getHistoricalToolMajorF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalToolMinorF_I = 0; -static jfloat Java_MotionEvent_getHistoricalToolMinorF_I(JNIEnv* env, jobject - obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalToolMinorF_I(JNIEnv* env, jobject - obj, JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getHistoricalToolMinorF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getHistoricalToolMinorF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1579,19 +1569,19 @@ static jfloat Java_MotionEvent_getHistoricalToolMinorF_I(JNIEnv* env, jobject &g_MotionEvent_getHistoricalToolMinorF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalOrientationF_I = 0; -static jfloat Java_MotionEvent_getHistoricalOrientationF_I(JNIEnv* env, jobject - obj, JniIntWrapper p0) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalOrientationF_I(JNIEnv* env, jobject - obj, JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jfloat Java_MotionEvent_getHistoricalOrientationF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static jfloat Java_MotionEvent_getHistoricalOrientationF_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1602,21 +1592,20 @@ static jfloat Java_MotionEvent_getHistoricalOrientationF_I(JNIEnv* env, jobject &g_MotionEvent_getHistoricalOrientationF_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalAxisValueF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalAxisValueF_I_I(JNIEnv* env, jobject - obj, JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalAxisValueF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalAxisValueF_I_I(JNIEnv* env, jobject - obj, JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalAxisValueF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1627,21 +1616,20 @@ static jfloat Java_MotionEvent_getHistoricalAxisValueF_I_I(JNIEnv* env, jobject &g_MotionEvent_getHistoricalAxisValueF_I_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0), as_jint(p1)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalXF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalXF_I_I(JNIEnv* env, jobject obj, - JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalXF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalXF_I_I(JNIEnv* env, jobject obj, - JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalXF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1652,21 +1640,20 @@ static jfloat Java_MotionEvent_getHistoricalXF_I_I(JNIEnv* env, jobject obj, &g_MotionEvent_getHistoricalXF_I_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0), as_jint(p1)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalYF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalYF_I_I(JNIEnv* env, jobject obj, - JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalYF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalYF_I_I(JNIEnv* env, jobject obj, - JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalYF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1677,21 +1664,20 @@ static jfloat Java_MotionEvent_getHistoricalYF_I_I(JNIEnv* env, jobject obj, &g_MotionEvent_getHistoricalYF_I_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0), as_jint(p1)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalPressureF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalPressureF_I_I(JNIEnv* env, jobject - obj, JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalPressureF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalPressureF_I_I(JNIEnv* env, jobject - obj, JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalPressureF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1702,21 +1688,20 @@ static jfloat Java_MotionEvent_getHistoricalPressureF_I_I(JNIEnv* env, jobject &g_MotionEvent_getHistoricalPressureF_I_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0), as_jint(p1)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalSizeF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalSizeF_I_I(JNIEnv* env, jobject obj, - JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalSizeF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalSizeF_I_I(JNIEnv* env, jobject obj, - JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalSizeF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1727,21 +1712,20 @@ static jfloat Java_MotionEvent_getHistoricalSizeF_I_I(JNIEnv* env, jobject obj, &g_MotionEvent_getHistoricalSizeF_I_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0), as_jint(p1)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalTouchMajorF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalTouchMajorF_I_I(JNIEnv* env, jobject - obj, JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalTouchMajorF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalTouchMajorF_I_I(JNIEnv* env, jobject - obj, JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalTouchMajorF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1752,21 +1736,20 @@ static jfloat Java_MotionEvent_getHistoricalTouchMajorF_I_I(JNIEnv* env, jobject &g_MotionEvent_getHistoricalTouchMajorF_I_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0), as_jint(p1)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalTouchMinorF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalTouchMinorF_I_I(JNIEnv* env, jobject - obj, JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalTouchMinorF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalTouchMinorF_I_I(JNIEnv* env, jobject - obj, JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalTouchMinorF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1777,21 +1760,20 @@ static jfloat Java_MotionEvent_getHistoricalTouchMinorF_I_I(JNIEnv* env, jobject &g_MotionEvent_getHistoricalTouchMinorF_I_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0), as_jint(p1)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalToolMajorF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalToolMajorF_I_I(JNIEnv* env, jobject - obj, JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalToolMajorF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalToolMajorF_I_I(JNIEnv* env, jobject - obj, JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalToolMajorF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1802,21 +1784,20 @@ static jfloat Java_MotionEvent_getHistoricalToolMajorF_I_I(JNIEnv* env, jobject &g_MotionEvent_getHistoricalToolMajorF_I_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0), as_jint(p1)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalToolMinorF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalToolMinorF_I_I(JNIEnv* env, jobject - obj, JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalToolMinorF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalToolMinorF_I_I(JNIEnv* env, jobject - obj, JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalToolMinorF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1827,21 +1808,20 @@ static jfloat Java_MotionEvent_getHistoricalToolMinorF_I_I(JNIEnv* env, jobject &g_MotionEvent_getHistoricalToolMinorF_I_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0), as_jint(p1)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalOrientationF_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalOrientationF_I_I(JNIEnv* env, - jobject obj, JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalOrientationF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalOrientationF_I_I(JNIEnv* env, - jobject obj, JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalOrientationF_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1852,23 +1832,22 @@ static jfloat Java_MotionEvent_getHistoricalOrientationF_I_I(JNIEnv* env, &g_MotionEvent_getHistoricalOrientationF_I_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0), as_jint(p1)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalAxisValueF_I_I_I = 0; -static jfloat Java_MotionEvent_getHistoricalAxisValueF_I_I_I(JNIEnv* env, - jobject obj, JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalAxisValueF_I_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1, JniIntWrapper p2) __attribute__ ((unused)); -static jfloat Java_MotionEvent_getHistoricalAxisValueF_I_I_I(JNIEnv* env, - jobject obj, JniIntWrapper p0, +static jfloat Java_MotionEvent_getHistoricalAxisValueF_I_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1, JniIntWrapper p2) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1879,23 +1858,22 @@ static jfloat Java_MotionEvent_getHistoricalAxisValueF_I_I_I(JNIEnv* env, &g_MotionEvent_getHistoricalAxisValueF_I_I_I); jfloat ret = - env->CallFloatMethod(obj, + env->CallFloatMethod(obj.obj(), method_id, as_jint(p0), as_jint(p1), as_jint(p2)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_getHistoricalPointerCoords = 0; -static void Java_MotionEvent_getHistoricalPointerCoords(JNIEnv* env, jobject - obj, JniIntWrapper p0, +static void Java_MotionEvent_getHistoricalPointerCoords(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1, - jobject p2) __attribute__ ((unused)); -static void Java_MotionEvent_getHistoricalPointerCoords(JNIEnv* env, jobject - obj, JniIntWrapper p0, + const base::android::JavaRefOrBare<jobject>& p2) __attribute__ ((unused)); +static void Java_MotionEvent_getHistoricalPointerCoords(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0, JniIntWrapper p1, - jobject p2) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + const base::android::JavaRefOrBare<jobject>& p2) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1905,18 +1883,17 @@ static void Java_MotionEvent_getHistoricalPointerCoords(JNIEnv* env, jobject "(IILandroid/view/MotionEvent$PointerCoords;)V", &g_MotionEvent_getHistoricalPointerCoords); - env->CallVoidMethod(obj, - method_id, as_jint(p0), as_jint(p1), p2); + env->CallVoidMethod(obj.obj(), + method_id, as_jint(p0), as_jint(p1), p2.obj()); jni_generator::CheckException(env); - } static base::subtle::AtomicWord g_MotionEvent_getEdgeFlags = 0; -static jint Java_MotionEvent_getEdgeFlags(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jint Java_MotionEvent_getEdgeFlags(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jint Java_MotionEvent_getEdgeFlags(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jint Java_MotionEvent_getEdgeFlags(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1927,19 +1904,19 @@ static jint Java_MotionEvent_getEdgeFlags(JNIEnv* env, jobject obj) { &g_MotionEvent_getEdgeFlags); jint ret = - env->CallIntMethod(obj, + env->CallIntMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_setEdgeFlags = 0; -static void Java_MotionEvent_setEdgeFlags(JNIEnv* env, jobject obj, - JniIntWrapper p0) __attribute__ ((unused)); -static void Java_MotionEvent_setEdgeFlags(JNIEnv* env, jobject obj, - JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static void Java_MotionEvent_setEdgeFlags(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static void Java_MotionEvent_setEdgeFlags(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1949,19 +1926,18 @@ static void Java_MotionEvent_setEdgeFlags(JNIEnv* env, jobject obj, "(I)V", &g_MotionEvent_setEdgeFlags); - env->CallVoidMethod(obj, + env->CallVoidMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); - } static base::subtle::AtomicWord g_MotionEvent_setAction = 0; -static void Java_MotionEvent_setAction(JNIEnv* env, jobject obj, JniIntWrapper - p0) __attribute__ ((unused)); -static void Java_MotionEvent_setAction(JNIEnv* env, jobject obj, JniIntWrapper - p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static void Java_MotionEvent_setAction(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static void Java_MotionEvent_setAction(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1971,19 +1947,19 @@ static void Java_MotionEvent_setAction(JNIEnv* env, jobject obj, JniIntWrapper "(I)V", &g_MotionEvent_setAction); - env->CallVoidMethod(obj, + env->CallVoidMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); - } static base::subtle::AtomicWord g_MotionEvent_offsetLocation = 0; -static void Java_MotionEvent_offsetLocation(JNIEnv* env, jobject obj, jfloat p0, +static void Java_MotionEvent_offsetLocation(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, jfloat p0, jfloat p1) __attribute__ ((unused)); -static void Java_MotionEvent_offsetLocation(JNIEnv* env, jobject obj, jfloat p0, +static void Java_MotionEvent_offsetLocation(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, jfloat p0, jfloat p1) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -1993,19 +1969,19 @@ static void Java_MotionEvent_offsetLocation(JNIEnv* env, jobject obj, jfloat p0, "(FF)V", &g_MotionEvent_offsetLocation); - env->CallVoidMethod(obj, + env->CallVoidMethod(obj.obj(), method_id, p0, p1); jni_generator::CheckException(env); - } static base::subtle::AtomicWord g_MotionEvent_setLocation = 0; -static void Java_MotionEvent_setLocation(JNIEnv* env, jobject obj, jfloat p0, +static void Java_MotionEvent_setLocation(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, jfloat p0, jfloat p1) __attribute__ ((unused)); -static void Java_MotionEvent_setLocation(JNIEnv* env, jobject obj, jfloat p0, +static void Java_MotionEvent_setLocation(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, jfloat p0, jfloat p1) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -2015,18 +1991,19 @@ static void Java_MotionEvent_setLocation(JNIEnv* env, jobject obj, jfloat p0, "(FF)V", &g_MotionEvent_setLocation); - env->CallVoidMethod(obj, + env->CallVoidMethod(obj.obj(), method_id, p0, p1); jni_generator::CheckException(env); - } static base::subtle::AtomicWord g_MotionEvent_transform = 0; -static void Java_MotionEvent_transform(JNIEnv* env, jobject obj, jobject p0) - __attribute__ ((unused)); -static void Java_MotionEvent_transform(JNIEnv* env, jobject obj, jobject p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static void Java_MotionEvent_transform(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, const + base::android::JavaRefOrBare<jobject>& p0) __attribute__ ((unused)); +static void Java_MotionEvent_transform(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, const + base::android::JavaRefOrBare<jobject>& p0) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -2036,29 +2013,27 @@ static void Java_MotionEvent_transform(JNIEnv* env, jobject obj, jobject p0) { "(Landroid/graphics/Matrix;)V", &g_MotionEvent_transform); - env->CallVoidMethod(obj, - method_id, p0); + env->CallVoidMethod(obj.obj(), + method_id, p0.obj()); jni_generator::CheckException(env); - } static base::subtle::AtomicWord g_MotionEvent_addBatchV_J_F_F_F_F_I = 0; -static void Java_MotionEvent_addBatchV_J_F_F_F_F_I(JNIEnv* env, jobject obj, - jlong p0, +static void Java_MotionEvent_addBatchV_J_F_F_F_F_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, jlong p0, jfloat p1, jfloat p2, jfloat p3, jfloat p4, JniIntWrapper p5) __attribute__ ((unused)); -static void Java_MotionEvent_addBatchV_J_F_F_F_F_I(JNIEnv* env, jobject obj, - jlong p0, +static void Java_MotionEvent_addBatchV_J_F_F_F_F_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, jlong p0, jfloat p1, jfloat p2, jfloat p3, jfloat p4, JniIntWrapper p5) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -2068,23 +2043,21 @@ static void Java_MotionEvent_addBatchV_J_F_F_F_F_I(JNIEnv* env, jobject obj, "(JFFFFI)V", &g_MotionEvent_addBatchV_J_F_F_F_F_I); - env->CallVoidMethod(obj, + env->CallVoidMethod(obj.obj(), method_id, p0, p1, p2, p3, p4, as_jint(p5)); jni_generator::CheckException(env); - } static base::subtle::AtomicWord g_MotionEvent_addBatchV_J_LAVMEPC_I = 0; -static void Java_MotionEvent_addBatchV_J_LAVMEPC_I(JNIEnv* env, jobject obj, - jlong p0, - jobjectArray p1, +static void Java_MotionEvent_addBatchV_J_LAVMEPC_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, jlong p0, + const base::android::JavaRefOrBare<jobjectArray>& p1, JniIntWrapper p2) __attribute__ ((unused)); -static void Java_MotionEvent_addBatchV_J_LAVMEPC_I(JNIEnv* env, jobject obj, - jlong p0, - jobjectArray p1, +static void Java_MotionEvent_addBatchV_J_LAVMEPC_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, jlong p0, + const base::android::JavaRefOrBare<jobjectArray>& p1, JniIntWrapper p2) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -2094,19 +2067,19 @@ static void Java_MotionEvent_addBatchV_J_LAVMEPC_I(JNIEnv* env, jobject obj, "(J[Landroid/view/MotionEvent$PointerCoords;I)V", &g_MotionEvent_addBatchV_J_LAVMEPC_I); - env->CallVoidMethod(obj, - method_id, p0, p1, as_jint(p2)); + env->CallVoidMethod(obj.obj(), + method_id, p0, p1.obj(), as_jint(p2)); jni_generator::CheckException(env); - } static base::subtle::AtomicWord g_MotionEvent_toString = 0; -static ScopedJavaLocalRef<jstring> Java_MotionEvent_toString(JNIEnv* env, - jobject obj) __attribute__ ((unused)); -static ScopedJavaLocalRef<jstring> Java_MotionEvent_toString(JNIEnv* env, - jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static base::android::ScopedJavaLocalRef<jstring> + Java_MotionEvent_toString(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static base::android::ScopedJavaLocalRef<jstring> + Java_MotionEvent_toString(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< @@ -2117,18 +2090,18 @@ static ScopedJavaLocalRef<jstring> Java_MotionEvent_toString(JNIEnv* env, &g_MotionEvent_toString); jstring ret = - static_cast<jstring>(env->CallObjectMethod(obj, + static_cast<jstring>(env->CallObjectMethod(obj.obj(), method_id)); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jstring>(env, ret); + return base::android::ScopedJavaLocalRef<jstring>(env, ret); } static base::subtle::AtomicWord g_MotionEvent_actionToString = 0; -static ScopedJavaLocalRef<jstring> Java_MotionEvent_actionToString(JNIEnv* env, - JniIntWrapper p0) __attribute__ ((unused)); -static ScopedJavaLocalRef<jstring> Java_MotionEvent_actionToString(JNIEnv* env, - JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ +static base::android::ScopedJavaLocalRef<jstring> + Java_MotionEvent_actionToString(JNIEnv* env, JniIntWrapper p0) __attribute__ + ((unused)); +static base::android::ScopedJavaLocalRef<jstring> + Java_MotionEvent_actionToString(JNIEnv* env, JniIntWrapper p0) { CHECK_CLAZZ(env, MotionEvent_clazz(env), MotionEvent_clazz(env), NULL); jmethodID method_id = @@ -2143,15 +2116,15 @@ static ScopedJavaLocalRef<jstring> Java_MotionEvent_actionToString(JNIEnv* env, static_cast<jstring>(env->CallStaticObjectMethod(MotionEvent_clazz(env), method_id, as_jint(p0))); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jstring>(env, ret); + return base::android::ScopedJavaLocalRef<jstring>(env, ret); } static base::subtle::AtomicWord g_MotionEvent_axisToString = 0; -static ScopedJavaLocalRef<jstring> Java_MotionEvent_axisToString(JNIEnv* env, - JniIntWrapper p0) __attribute__ ((unused)); -static ScopedJavaLocalRef<jstring> Java_MotionEvent_axisToString(JNIEnv* env, - JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ +static base::android::ScopedJavaLocalRef<jstring> + Java_MotionEvent_axisToString(JNIEnv* env, JniIntWrapper p0) __attribute__ + ((unused)); +static base::android::ScopedJavaLocalRef<jstring> + Java_MotionEvent_axisToString(JNIEnv* env, JniIntWrapper p0) { CHECK_CLAZZ(env, MotionEvent_clazz(env), MotionEvent_clazz(env), NULL); jmethodID method_id = @@ -2166,14 +2139,14 @@ static ScopedJavaLocalRef<jstring> Java_MotionEvent_axisToString(JNIEnv* env, static_cast<jstring>(env->CallStaticObjectMethod(MotionEvent_clazz(env), method_id, as_jint(p0))); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jstring>(env, ret); + return base::android::ScopedJavaLocalRef<jstring>(env, ret); } static base::subtle::AtomicWord g_MotionEvent_axisFromString = 0; -static jint Java_MotionEvent_axisFromString(JNIEnv* env, jstring p0) - __attribute__ ((unused)); -static jint Java_MotionEvent_axisFromString(JNIEnv* env, jstring p0) { - /* Must call RegisterNativesImpl() */ +static jint Java_MotionEvent_axisFromString(JNIEnv* env, const + base::android::JavaRefOrBare<jstring>& p0) __attribute__ ((unused)); +static jint Java_MotionEvent_axisFromString(JNIEnv* env, const + base::android::JavaRefOrBare<jstring>& p0) { CHECK_CLAZZ(env, MotionEvent_clazz(env), MotionEvent_clazz(env), 0); jmethodID method_id = @@ -2186,18 +2159,21 @@ static jint Java_MotionEvent_axisFromString(JNIEnv* env, jstring p0) { jint ret = env->CallStaticIntMethod(MotionEvent_clazz(env), - method_id, p0); + method_id, p0.obj()); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_MotionEvent_writeToParcel = 0; -static void Java_MotionEvent_writeToParcel(JNIEnv* env, jobject obj, jobject p0, +static void Java_MotionEvent_writeToParcel(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, const + base::android::JavaRefOrBare<jobject>& p0, JniIntWrapper p1) __attribute__ ((unused)); -static void Java_MotionEvent_writeToParcel(JNIEnv* env, jobject obj, jobject p0, +static void Java_MotionEvent_writeToParcel(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, const + base::android::JavaRefOrBare<jobject>& p0, JniIntWrapper p1) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -2207,22 +2183,13 @@ static void Java_MotionEvent_writeToParcel(JNIEnv* env, jobject obj, jobject p0, "(Landroid/os/Parcel;I)V", &g_MotionEvent_writeToParcel); - env->CallVoidMethod(obj, - method_id, p0, as_jint(p1)); + env->CallVoidMethod(obj.obj(), + method_id, p0.obj(), as_jint(p1)); jni_generator::CheckException(env); - } // Step 3: RegisterNatives. -static bool RegisterNativesImpl(JNIEnv* env) { - - g_MotionEvent_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( - base::android::GetClass(env, kMotionEventClassPath).obj())); - - return true; -} - } // namespace JNI_MotionEvent #endif // android_view_MotionEvent_JNI diff --git a/base/android/jni_generator/testFromJavaP.golden b/base/android/jni_generator/testFromJavaP.golden index f32666c..18a9430 100644 --- a/base/android/jni_generator/testFromJavaP.golden +++ b/base/android/jni_generator/testFromJavaP.golden @@ -20,8 +20,8 @@ namespace { const char kInputStreamClassPath[] = "java/io/InputStream"; // Leaking this jclass as we cannot use LazyInstance from some threads. -jclass g_InputStream_clazz = NULL; -#define InputStream_clazz(env) g_InputStream_clazz +base::subtle::AtomicWord g_InputStream_clazz __attribute__((unused)) = 0; +#define InputStream_clazz(env) base::android::LazyGetClass(env, kInputStreamClassPath, &g_InputStream_clazz) } // namespace @@ -30,11 +30,11 @@ namespace JNI_InputStream { // Step 2: method stubs. static base::subtle::AtomicWord g_InputStream_available = 0; -static jint Java_InputStream_available(JNIEnv* env, jobject obj) __attribute__ - ((unused)); -static jint Java_InputStream_available(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jint Java_InputStream_available(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jint Java_InputStream_available(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), InputStream_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -45,18 +45,18 @@ static jint Java_InputStream_available(JNIEnv* env, jobject obj) { &g_InputStream_available); jint ret = - env->CallIntMethod(obj, + env->CallIntMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_InputStream_close = 0; -static void Java_InputStream_close(JNIEnv* env, jobject obj) __attribute__ - ((unused)); -static void Java_InputStream_close(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static void Java_InputStream_close(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static void Java_InputStream_close(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), InputStream_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -66,18 +66,18 @@ static void Java_InputStream_close(JNIEnv* env, jobject obj) { "()V", &g_InputStream_close); - env->CallVoidMethod(obj, + env->CallVoidMethod(obj.obj(), method_id); jni_generator::CheckException(env); - } static base::subtle::AtomicWord g_InputStream_mark = 0; -static void Java_InputStream_mark(JNIEnv* env, jobject obj, JniIntWrapper p0) - __attribute__ ((unused)); -static void Java_InputStream_mark(JNIEnv* env, jobject obj, JniIntWrapper p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static void Java_InputStream_mark(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) __attribute__ + ((unused)); +static void Java_InputStream_mark(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, JniIntWrapper p0) { + CHECK_CLAZZ(env, obj.obj(), InputStream_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -87,18 +87,17 @@ static void Java_InputStream_mark(JNIEnv* env, jobject obj, JniIntWrapper p0) { "(I)V", &g_InputStream_mark); - env->CallVoidMethod(obj, + env->CallVoidMethod(obj.obj(), method_id, as_jint(p0)); jni_generator::CheckException(env); - } static base::subtle::AtomicWord g_InputStream_markSupported = 0; -static jboolean Java_InputStream_markSupported(JNIEnv* env, jobject obj) - __attribute__ ((unused)); -static jboolean Java_InputStream_markSupported(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jboolean Java_InputStream_markSupported(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jboolean Java_InputStream_markSupported(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), InputStream_clazz(env), false); jmethodID method_id = base::android::MethodID::LazyGet< @@ -109,18 +108,18 @@ static jboolean Java_InputStream_markSupported(JNIEnv* env, jobject obj) { &g_InputStream_markSupported); jboolean ret = - env->CallBooleanMethod(obj, + env->CallBooleanMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_InputStream_readI = 0; -static jint Java_InputStream_readI(JNIEnv* env, jobject obj) __attribute__ - ((unused)); -static jint Java_InputStream_readI(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jint Java_InputStream_readI(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static jint Java_InputStream_readI(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), InputStream_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -131,18 +130,20 @@ static jint Java_InputStream_readI(JNIEnv* env, jobject obj) { &g_InputStream_readI); jint ret = - env->CallIntMethod(obj, + env->CallIntMethod(obj.obj(), method_id); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_InputStream_readI_AB = 0; -static jint Java_InputStream_readI_AB(JNIEnv* env, jobject obj, jbyteArray p0) - __attribute__ ((unused)); -static jint Java_InputStream_readI_AB(JNIEnv* env, jobject obj, jbyteArray p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jint Java_InputStream_readI_AB(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, const + base::android::JavaRefOrBare<jbyteArray>& p0) __attribute__ ((unused)); +static jint Java_InputStream_readI_AB(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, const + base::android::JavaRefOrBare<jbyteArray>& p0) { + CHECK_CLAZZ(env, obj.obj(), InputStream_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -153,23 +154,24 @@ static jint Java_InputStream_readI_AB(JNIEnv* env, jobject obj, jbyteArray p0) { &g_InputStream_readI_AB); jint ret = - env->CallIntMethod(obj, - method_id, p0); + env->CallIntMethod(obj.obj(), + method_id, p0.obj()); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_InputStream_readI_AB_I_I = 0; -static jint Java_InputStream_readI_AB_I_I(JNIEnv* env, jobject obj, jbyteArray - p0, +static jint Java_InputStream_readI_AB_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, const + base::android::JavaRefOrBare<jbyteArray>& p0, JniIntWrapper p1, JniIntWrapper p2) __attribute__ ((unused)); -static jint Java_InputStream_readI_AB_I_I(JNIEnv* env, jobject obj, jbyteArray - p0, +static jint Java_InputStream_readI_AB_I_I(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, const + base::android::JavaRefOrBare<jbyteArray>& p0, JniIntWrapper p1, JniIntWrapper p2) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, + CHECK_CLAZZ(env, obj.obj(), InputStream_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -180,18 +182,18 @@ static jint Java_InputStream_readI_AB_I_I(JNIEnv* env, jobject obj, jbyteArray &g_InputStream_readI_AB_I_I); jint ret = - env->CallIntMethod(obj, - method_id, p0, as_jint(p1), as_jint(p2)); + env->CallIntMethod(obj.obj(), + method_id, p0.obj(), as_jint(p1), as_jint(p2)); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_InputStream_reset = 0; -static void Java_InputStream_reset(JNIEnv* env, jobject obj) __attribute__ - ((unused)); -static void Java_InputStream_reset(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static void Java_InputStream_reset(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static void Java_InputStream_reset(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), InputStream_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -201,18 +203,18 @@ static void Java_InputStream_reset(JNIEnv* env, jobject obj) { "()V", &g_InputStream_reset); - env->CallVoidMethod(obj, + env->CallVoidMethod(obj.obj(), method_id); jni_generator::CheckException(env); - } static base::subtle::AtomicWord g_InputStream_skip = 0; -static jlong Java_InputStream_skip(JNIEnv* env, jobject obj, jlong p0) - __attribute__ ((unused)); -static jlong Java_InputStream_skip(JNIEnv* env, jobject obj, jlong p0) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static jlong Java_InputStream_skip(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, jlong p0) __attribute__ + ((unused)); +static jlong Java_InputStream_skip(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj, jlong p0) { + CHECK_CLAZZ(env, obj.obj(), InputStream_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< @@ -223,17 +225,17 @@ static jlong Java_InputStream_skip(JNIEnv* env, jobject obj, jlong p0) { &g_InputStream_skip); jlong ret = - env->CallLongMethod(obj, + env->CallLongMethod(obj.obj(), method_id, p0); jni_generator::CheckException(env); return ret; } static base::subtle::AtomicWord g_InputStream_Constructor = 0; -static ScopedJavaLocalRef<jobject> Java_InputStream_Constructor(JNIEnv* env) - __attribute__ ((unused)); -static ScopedJavaLocalRef<jobject> Java_InputStream_Constructor(JNIEnv* env) { - /* Must call RegisterNativesImpl() */ +static base::android::ScopedJavaLocalRef<jobject> + Java_InputStream_Constructor(JNIEnv* env) __attribute__ ((unused)); +static base::android::ScopedJavaLocalRef<jobject> + Java_InputStream_Constructor(JNIEnv* env) { CHECK_CLAZZ(env, InputStream_clazz(env), InputStream_clazz(env), NULL); jmethodID method_id = @@ -248,19 +250,11 @@ static ScopedJavaLocalRef<jobject> Java_InputStream_Constructor(JNIEnv* env) { env->NewObject(InputStream_clazz(env), method_id); jni_generator::CheckException(env); - return ScopedJavaLocalRef<jobject>(env, ret); + return base::android::ScopedJavaLocalRef<jobject>(env, ret); } // Step 3: RegisterNatives. -static bool RegisterNativesImpl(JNIEnv* env) { - - g_InputStream_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( - base::android::GetClass(env, kInputStreamClassPath).obj())); - - return true; -} - } // namespace JNI_InputStream #endif // java_io_InputStream_JNI diff --git a/base/android/jni_generator/testFromJavaPGenerics.golden b/base/android/jni_generator/testFromJavaPGenerics.golden index 489872c..c076c39 100644 --- a/base/android/jni_generator/testFromJavaPGenerics.golden +++ b/base/android/jni_generator/testFromJavaPGenerics.golden @@ -20,8 +20,8 @@ namespace { const char kHashSetClassPath[] = "java/util/HashSet"; // Leaking this jclass as we cannot use LazyInstance from some threads. -jclass g_HashSet_clazz = NULL; -#define HashSet_clazz(env) g_HashSet_clazz +base::subtle::AtomicWord g_HashSet_clazz __attribute__((unused)) = 0; +#define HashSet_clazz(env) base::android::LazyGetClass(env, kHashSetClassPath, &g_HashSet_clazz) } // namespace @@ -30,11 +30,11 @@ namespace JNI_HashSet { // Step 2: method stubs. static base::subtle::AtomicWord g_HashSet_dummy = 0; -static void Java_HashSet_dummy(JNIEnv* env, jobject obj) __attribute__ - ((unused)); -static void Java_HashSet_dummy(JNIEnv* env, jobject obj) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, +static void Java_HashSet_dummy(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) __attribute__ ((unused)); +static void Java_HashSet_dummy(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& obj) { + CHECK_CLAZZ(env, obj.obj(), HashSet_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< @@ -44,22 +44,13 @@ static void Java_HashSet_dummy(JNIEnv* env, jobject obj) { "()V", &g_HashSet_dummy); - env->CallVoidMethod(obj, + env->CallVoidMethod(obj.obj(), method_id); jni_generator::CheckException(env); - } // Step 3: RegisterNatives. -static bool RegisterNativesImpl(JNIEnv* env) { - - g_HashSet_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( - base::android::GetClass(env, kHashSetClassPath).obj())); - - return true; -} - } // namespace JNI_HashSet #endif // java_util_HashSet_JNI diff --git a/base/android/jni_generator/testInnerClassNatives.golden b/base/android/jni_generator/testInnerClassNatives.golden index ad140e2..20b8830 100644 --- a/base/android/jni_generator/testInnerClassNatives.golden +++ b/base/android/jni_generator/testInnerClassNatives.golden @@ -21,18 +21,23 @@ namespace { const char kTestJniClassPath[] = "org/chromium/TestJni"; const char kMyInnerClassClassPath[] = "org/chromium/TestJni$MyInnerClass"; // Leaking this jclass as we cannot use LazyInstance from some threads. -jclass g_TestJni_clazz = NULL; -#define TestJni_clazz(env) g_TestJni_clazz +base::subtle::AtomicWord g_TestJni_clazz __attribute__((unused)) = 0; +#define TestJni_clazz(env) base::android::LazyGetClass(env, kTestJniClassPath, &g_TestJni_clazz) +// Leaking this jclass as we cannot use LazyInstance from some threads. +base::subtle::AtomicWord g_MyInnerClass_clazz __attribute__((unused)) = 0; +#define MyInnerClass_clazz(env) base::android::LazyGetClass(env, kMyInnerClassClassPath, &g_MyInnerClass_clazz) } // namespace // Step 2: method stubs. -static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); +static jint Init(JNIEnv* env, const base::android::JavaParamRef<jobject>& + jcaller); -static jint Java_org_chromium_TestJni_00024MyInnerClass_nativeInit(JNIEnv* env, - jobject jcaller) { - return Init(env, JavaParamRef<jobject>(env, jcaller)); +JNI_GENERATOR_EXPORT jint + Java_org_chromium_TestJni_00024MyInnerClass_nativeInit(JNIEnv* env, jobject + jcaller) { + return Init(env, base::android::JavaParamRef<jobject>(env, jcaller)); } // Step 3: RegisterNatives. @@ -47,9 +52,8 @@ static const JNINativeMethod kMethodsMyInnerClass[] = { }; static bool RegisterNativesImpl(JNIEnv* env) { - - g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( - base::android::GetClass(env, kTestJniClassPath).obj())); + if (jni_generator::ShouldSkipJniRegistration(false)) + return true; const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass); diff --git a/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden b/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden index 0a890e7..67352e7 100644 --- a/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden +++ b/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden @@ -22,24 +22,31 @@ const char kMyOtherInnerClassClassPath[] = "org/chromium/TestJni$MyOtherInnerClass"; const char kTestJniClassPath[] = "org/chromium/TestJni"; // Leaking this jclass as we cannot use LazyInstance from some threads. -jclass g_TestJni_clazz = NULL; -#define TestJni_clazz(env) g_TestJni_clazz +base::subtle::AtomicWord g_MyOtherInnerClass_clazz __attribute__((unused)) = 0; +#define MyOtherInnerClass_clazz(env) base::android::LazyGetClass(env, kMyOtherInnerClassClassPath, &g_MyOtherInnerClass_clazz) +// Leaking this jclass as we cannot use LazyInstance from some threads. +base::subtle::AtomicWord g_TestJni_clazz __attribute__((unused)) = 0; +#define TestJni_clazz(env) base::android::LazyGetClass(env, kTestJniClassPath, &g_TestJni_clazz) } // namespace // Step 2: method stubs. -static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); +static jint Init(JNIEnv* env, const base::android::JavaParamRef<jobject>& + jcaller); -static jint Java_org_chromium_TestJni_nativeInit(JNIEnv* env, jobject jcaller) { - return Init(env, JavaParamRef<jobject>(env, jcaller)); +JNI_GENERATOR_EXPORT jint Java_org_chromium_TestJni_nativeInit(JNIEnv* env, + jobject jcaller) { + return Init(env, base::android::JavaParamRef<jobject>(env, jcaller)); } -static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); +static jint Init(JNIEnv* env, const base::android::JavaParamRef<jobject>& + jcaller); -static jint Java_org_chromium_TestJni_00024MyOtherInnerClass_nativeInit(JNIEnv* - env, jobject jcaller) { - return Init(env, JavaParamRef<jobject>(env, jcaller)); +JNI_GENERATOR_EXPORT jint + Java_org_chromium_TestJni_00024MyOtherInnerClass_nativeInit(JNIEnv* env, + jobject jcaller) { + return Init(env, base::android::JavaParamRef<jobject>(env, jcaller)); } // Step 3: RegisterNatives. @@ -61,9 +68,8 @@ static const JNINativeMethod kMethodsTestJni[] = { }; static bool RegisterNativesImpl(JNIEnv* env) { - - g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( - base::android::GetClass(env, kTestJniClassPath).obj())); + if (jni_generator::ShouldSkipJniRegistration(false)) + return true; const int kMethodsMyOtherInnerClassSize = arraysize(kMethodsMyOtherInnerClass); diff --git a/base/android/jni_generator/testInnerClassNativesMultiple.golden b/base/android/jni_generator/testInnerClassNativesMultiple.golden index 268f794..7807efa 100644 --- a/base/android/jni_generator/testInnerClassNativesMultiple.golden +++ b/base/android/jni_generator/testInnerClassNativesMultiple.golden @@ -23,25 +23,35 @@ const char kMyOtherInnerClassClassPath[] = const char kTestJniClassPath[] = "org/chromium/TestJni"; const char kMyInnerClassClassPath[] = "org/chromium/TestJni$MyInnerClass"; // Leaking this jclass as we cannot use LazyInstance from some threads. -jclass g_TestJni_clazz = NULL; -#define TestJni_clazz(env) g_TestJni_clazz +base::subtle::AtomicWord g_MyOtherInnerClass_clazz __attribute__((unused)) = 0; +#define MyOtherInnerClass_clazz(env) base::android::LazyGetClass(env, kMyOtherInnerClassClassPath, &g_MyOtherInnerClass_clazz) +// Leaking this jclass as we cannot use LazyInstance from some threads. +base::subtle::AtomicWord g_TestJni_clazz __attribute__((unused)) = 0; +#define TestJni_clazz(env) base::android::LazyGetClass(env, kTestJniClassPath, &g_TestJni_clazz) +// Leaking this jclass as we cannot use LazyInstance from some threads. +base::subtle::AtomicWord g_MyInnerClass_clazz __attribute__((unused)) = 0; +#define MyInnerClass_clazz(env) base::android::LazyGetClass(env, kMyInnerClassClassPath, &g_MyInnerClass_clazz) } // namespace // Step 2: method stubs. -static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); +static jint Init(JNIEnv* env, const base::android::JavaParamRef<jobject>& + jcaller); -static jint Java_org_chromium_TestJni_00024MyInnerClass_nativeInit(JNIEnv* env, - jobject jcaller) { - return Init(env, JavaParamRef<jobject>(env, jcaller)); +JNI_GENERATOR_EXPORT jint + Java_org_chromium_TestJni_00024MyInnerClass_nativeInit(JNIEnv* env, jobject + jcaller) { + return Init(env, base::android::JavaParamRef<jobject>(env, jcaller)); } -static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); +static jint Init(JNIEnv* env, const base::android::JavaParamRef<jobject>& + jcaller); -static jint Java_org_chromium_TestJni_00024MyOtherInnerClass_nativeInit(JNIEnv* - env, jobject jcaller) { - return Init(env, JavaParamRef<jobject>(env, jcaller)); +JNI_GENERATOR_EXPORT jint + Java_org_chromium_TestJni_00024MyOtherInnerClass_nativeInit(JNIEnv* env, + jobject jcaller) { + return Init(env, base::android::JavaParamRef<jobject>(env, jcaller)); } // Step 3: RegisterNatives. @@ -65,9 +75,8 @@ static const JNINativeMethod kMethodsMyInnerClass[] = { }; static bool RegisterNativesImpl(JNIEnv* env) { - - g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( - base::android::GetClass(env, kTestJniClassPath).obj())); + if (jni_generator::ShouldSkipJniRegistration(false)) + return true; const int kMethodsMyOtherInnerClassSize = arraysize(kMethodsMyOtherInnerClass); diff --git a/base/android/jni_generator/testMultipleJNIAdditionalImport.golden b/base/android/jni_generator/testMultipleJNIAdditionalImport.golden index 6de8c21..0eecb5a 100644 --- a/base/android/jni_generator/testMultipleJNIAdditionalImport.golden +++ b/base/android/jni_generator/testMultipleJNIAdditionalImport.golden @@ -20,30 +20,31 @@ namespace { const char kFooClassPath[] = "org/chromium/foo/Foo"; // Leaking this jclass as we cannot use LazyInstance from some threads. -jclass g_Foo_clazz = NULL; -#define Foo_clazz(env) g_Foo_clazz +base::subtle::AtomicWord g_Foo_clazz __attribute__((unused)) = 0; +#define Foo_clazz(env) base::android::LazyGetClass(env, kFooClassPath, &g_Foo_clazz) } // namespace // Step 2: method stubs. -static void DoSomething(JNIEnv* env, const JavaParamRef<jclass>& jcaller, - const JavaParamRef<jobject>& callback1, - const JavaParamRef<jobject>& callback2); - -static void Java_org_chromium_foo_Foo_nativeDoSomething(JNIEnv* env, jclass +static void DoSomething(JNIEnv* env, const base::android::JavaParamRef<jclass>& jcaller, + const base::android::JavaParamRef<jobject>& callback1, + const base::android::JavaParamRef<jobject>& callback2); + +JNI_GENERATOR_EXPORT void Java_org_chromium_foo_Foo_nativeDoSomething(JNIEnv* + env, jclass jcaller, jobject callback1, jobject callback2) { - return DoSomething(env, JavaParamRef<jclass>(env, jcaller), - JavaParamRef<jobject>(env, callback1), JavaParamRef<jobject>(env, - callback2)); + return DoSomething(env, base::android::JavaParamRef<jclass>(env, jcaller), + base::android::JavaParamRef<jobject>(env, callback1), + base::android::JavaParamRef<jobject>(env, callback2)); } static base::subtle::AtomicWord g_Foo_calledByNative = 0; -static void Java_Foo_calledByNative(JNIEnv* env, jobject callback1, - jobject callback2) { - /* Must call RegisterNativesImpl() */ +static void Java_Foo_calledByNative(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& callback1, + const base::android::JavaRefOrBare<jobject>& callback2) { CHECK_CLAZZ(env, Foo_clazz(env), Foo_clazz(env)); jmethodID method_id = @@ -51,7 +52,6 @@ static void Java_Foo_calledByNative(JNIEnv* env, jobject callback1, base::android::MethodID::TYPE_STATIC>( env, Foo_clazz(env), "calledByNative", - "(" "Lorg/chromium/foo/Bar1$Callback;" "Lorg/chromium/foo/Bar2$Callback;" @@ -60,9 +60,8 @@ static void Java_Foo_calledByNative(JNIEnv* env, jobject callback1, &g_Foo_calledByNative); env->CallStaticVoidMethod(Foo_clazz(env), - method_id, callback1, callback2); + method_id, callback1.obj(), callback2.obj()); jni_generator::CheckException(env); - } // Step 3: RegisterNatives. @@ -77,9 +76,8 @@ static const JNINativeMethod kMethodsFoo[] = { }; static bool RegisterNativesImpl(JNIEnv* env) { - - g_Foo_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( - base::android::GetClass(env, kFooClassPath).obj())); + if (jni_generator::ShouldSkipJniRegistration(false)) + return true; const int kMethodsFooSize = arraysize(kMethodsFoo); diff --git a/base/android/jni_generator/testNativeExportsOption.golden b/base/android/jni_generator/testNativeExportsOption.golden deleted file mode 100644 index 2c89e3b..0000000 --- a/base/android/jni_generator/testNativeExportsOption.golden +++ /dev/null @@ -1,203 +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. - -// This file is autogenerated by -// base/android/jni_generator/jni_generator.py -// For -// org/chromium/example/jni_generator/SampleForTests - -#ifndef org_chromium_example_jni_generator_SampleForTests_JNI -#define org_chromium_example_jni_generator_SampleForTests_JNI - -#include <jni.h> - -#include "base/android/jni_generator/jni_generator_helper.h" - -#include "base/android/jni_int_wrapper.h" - -// Step 1: forward declarations. -namespace { -const char kSampleForTestsClassPath[] = - "org/chromium/example/jni_generator/SampleForTests"; -// Leaking this jclass as we cannot use LazyInstance from some threads. -base::subtle::AtomicWord g_SampleForTests_clazz __attribute__((unused)) = 0; -#define SampleForTests_clazz(env) base::android::LazyGetClass(env, kSampleForTestsClassPath, &g_SampleForTests_clazz) - -} // namespace - -// Step 2: method stubs. -extern "C" __attribute__((visibility("default"))) -jint - Java_org_chromium_example_jni_1generator_SampleForTests_nativeStaticMethod(JNIEnv* - env, - jobject jcaller, - jlong nativeTest, - jint arg1) { - Test* native = reinterpret_cast<Test*>(nativeTest); - CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0); - return native->StaticMethod(env, JavaParamRef<jobject>(env, jcaller), arg1); -} - -extern "C" __attribute__((visibility("default"))) -jint - Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethod(JNIEnv* - env, - jobject jcaller, - jlong nativeTest, - jint arg1) { - Test* native = reinterpret_cast<Test*>(nativeTest); - CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0); - return native->Method(env, JavaParamRef<jobject>(env, jcaller), arg1); -} - -static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); - -extern "C" __attribute__((visibility("default"))) -jint - Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit(JNIEnv* - env, jobject jcaller) { - return Init(env, JavaParamRef<jobject>(env, jcaller)); -} - -static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); - -extern "C" __attribute__((visibility("default"))) -jint - Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit(JNIEnv* - env, jobject jcaller) { - return Init(env, JavaParamRef<jobject>(env, jcaller)); -} - -static base::subtle::AtomicWord g_SampleForTests_testMethodWithParam = 0; -static void Java_SampleForTests_testMethodWithParam(JNIEnv* env, jobject obj, - JniIntWrapper iParam) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, - SampleForTests_clazz(env)); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, SampleForTests_clazz(env), - "testMethodWithParam", - -"(" -"I" -")" -"V", - &g_SampleForTests_testMethodWithParam); - - env->CallVoidMethod(obj, - method_id, as_jint(iParam)); - jni_generator::CheckException(env); - -} - -static base::subtle::AtomicWord g_SampleForTests_testMethodWithParamAndReturn = - 0; -static ScopedJavaLocalRef<jstring> - Java_SampleForTests_testMethodWithParamAndReturn(JNIEnv* env, jobject obj, - JniIntWrapper iParam) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, - SampleForTests_clazz(env), NULL); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, SampleForTests_clazz(env), - "testMethodWithParamAndReturn", - -"(" -"I" -")" -"Ljava/lang/String;", - &g_SampleForTests_testMethodWithParamAndReturn); - - jstring ret = - static_cast<jstring>(env->CallObjectMethod(obj, - method_id, as_jint(iParam))); - jni_generator::CheckException(env); - return ScopedJavaLocalRef<jstring>(env, ret); -} - -static base::subtle::AtomicWord g_SampleForTests_testStaticMethodWithParam = 0; -static jint Java_SampleForTests_testStaticMethodWithParam(JNIEnv* env, - JniIntWrapper iParam) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, SampleForTests_clazz(env), - SampleForTests_clazz(env), 0); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, SampleForTests_clazz(env), - "testStaticMethodWithParam", - -"(" -"I" -")" -"I", - &g_SampleForTests_testStaticMethodWithParam); - - jint ret = - env->CallStaticIntMethod(SampleForTests_clazz(env), - method_id, as_jint(iParam)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_SampleForTests_testMethodWithNoParam = 0; -static jdouble Java_SampleForTests_testMethodWithNoParam(JNIEnv* env) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, SampleForTests_clazz(env), - SampleForTests_clazz(env), 0); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, SampleForTests_clazz(env), - "testMethodWithNoParam", - -"(" -")" -"D", - &g_SampleForTests_testMethodWithNoParam); - - jdouble ret = - env->CallStaticDoubleMethod(SampleForTests_clazz(env), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_SampleForTests_testStaticMethodWithNoParam = - 0; -static ScopedJavaLocalRef<jstring> - Java_SampleForTests_testStaticMethodWithNoParam(JNIEnv* env) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, SampleForTests_clazz(env), - SampleForTests_clazz(env), NULL); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, SampleForTests_clazz(env), - "testStaticMethodWithNoParam", - -"(" -")" -"Ljava/lang/String;", - &g_SampleForTests_testStaticMethodWithNoParam); - - jstring ret = -static_cast<jstring>(env->CallStaticObjectMethod(SampleForTests_clazz(env), - method_id)); - jni_generator::CheckException(env); - return ScopedJavaLocalRef<jstring>(env, ret); -} - -// Step 3: RegisterNatives. - -static bool RegisterNativesImpl(JNIEnv* env) { - - return true; -} - -#endif // org_chromium_example_jni_generator_SampleForTests_JNI diff --git a/base/android/jni_generator/testNativeExportsOptionalOption.golden b/base/android/jni_generator/testNativeExportsOptionalOption.golden deleted file mode 100644 index 03484c9..0000000 --- a/base/android/jni_generator/testNativeExportsOptionalOption.golden +++ /dev/null @@ -1,272 +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. - -// This file is autogenerated by -// base/android/jni_generator/jni_generator.py -// For -// org/chromium/example/jni_generator/SampleForTests - -#ifndef org_chromium_example_jni_generator_SampleForTests_JNI -#define org_chromium_example_jni_generator_SampleForTests_JNI - -#include <jni.h> - -#include "base/android/jni_generator/jni_generator_helper.h" - -#include "base/android/jni_int_wrapper.h" - -// Step 1: forward declarations. -namespace { -const char kSampleForTestsClassPath[] = - "org/chromium/example/jni_generator/SampleForTests"; -// Leaking this jclass as we cannot use LazyInstance from some threads. -base::subtle::AtomicWord g_SampleForTests_clazz __attribute__((unused)) = 0; -#define SampleForTests_clazz(env) base::android::LazyGetClass(env, kSampleForTestsClassPath, &g_SampleForTests_clazz) - -} // namespace - -// Step 2: method stubs. -extern "C" __attribute__((visibility("default"))) -jint - Java_org_chromium_example_jni_1generator_SampleForTests_nativeStaticMethod(JNIEnv* - env, - jobject jcaller, - jlong nativeTest, - jint arg1) { - Test* native = reinterpret_cast<Test*>(nativeTest); - CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0); - return native->StaticMethod(env, JavaParamRef<jobject>(env, jcaller), arg1); -} - -extern "C" __attribute__((visibility("default"))) -jint - Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethod(JNIEnv* - env, - jobject jcaller, - jlong nativeTest, - jint arg1) { - Test* native = reinterpret_cast<Test*>(nativeTest); - CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0); - return native->Method(env, JavaParamRef<jobject>(env, jcaller), arg1); -} - -static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); - -extern "C" __attribute__((visibility("default"))) -jint - Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit(JNIEnv* - env, jobject jcaller) { - return Init(env, JavaParamRef<jobject>(env, jcaller)); -} - -static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); - -extern "C" __attribute__((visibility("default"))) -jint - Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit(JNIEnv* - env, jobject jcaller) { - return Init(env, JavaParamRef<jobject>(env, jcaller)); -} - -static base::subtle::AtomicWord g_SampleForTests_testMethodWithParam = 0; -static void Java_SampleForTests_testMethodWithParam(JNIEnv* env, jobject obj, - JniIntWrapper iParam) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, - SampleForTests_clazz(env)); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, SampleForTests_clazz(env), - "testMethodWithParam", - -"(" -"I" -")" -"V", - &g_SampleForTests_testMethodWithParam); - - env->CallVoidMethod(obj, - method_id, as_jint(iParam)); - jni_generator::CheckException(env); - -} - -static base::subtle::AtomicWord g_SampleForTests_testMethodWithParamAndReturn = - 0; -static ScopedJavaLocalRef<jstring> - Java_SampleForTests_testMethodWithParamAndReturn(JNIEnv* env, jobject obj, - JniIntWrapper iParam) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, obj, - SampleForTests_clazz(env), NULL); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_INSTANCE>( - env, SampleForTests_clazz(env), - "testMethodWithParamAndReturn", - -"(" -"I" -")" -"Ljava/lang/String;", - &g_SampleForTests_testMethodWithParamAndReturn); - - jstring ret = - static_cast<jstring>(env->CallObjectMethod(obj, - method_id, as_jint(iParam))); - jni_generator::CheckException(env); - return ScopedJavaLocalRef<jstring>(env, ret); -} - -static base::subtle::AtomicWord g_SampleForTests_testStaticMethodWithParam = 0; -static jint Java_SampleForTests_testStaticMethodWithParam(JNIEnv* env, - JniIntWrapper iParam) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, SampleForTests_clazz(env), - SampleForTests_clazz(env), 0); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, SampleForTests_clazz(env), - "testStaticMethodWithParam", - -"(" -"I" -")" -"I", - &g_SampleForTests_testStaticMethodWithParam); - - jint ret = - env->CallStaticIntMethod(SampleForTests_clazz(env), - method_id, as_jint(iParam)); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_SampleForTests_testMethodWithNoParam = 0; -static jdouble Java_SampleForTests_testMethodWithNoParam(JNIEnv* env) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, SampleForTests_clazz(env), - SampleForTests_clazz(env), 0); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, SampleForTests_clazz(env), - "testMethodWithNoParam", - -"(" -")" -"D", - &g_SampleForTests_testMethodWithNoParam); - - jdouble ret = - env->CallStaticDoubleMethod(SampleForTests_clazz(env), - method_id); - jni_generator::CheckException(env); - return ret; -} - -static base::subtle::AtomicWord g_SampleForTests_testStaticMethodWithNoParam = - 0; -static ScopedJavaLocalRef<jstring> - Java_SampleForTests_testStaticMethodWithNoParam(JNIEnv* env) { - /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, SampleForTests_clazz(env), - SampleForTests_clazz(env), NULL); - jmethodID method_id = - base::android::MethodID::LazyGet< - base::android::MethodID::TYPE_STATIC>( - env, SampleForTests_clazz(env), - "testStaticMethodWithNoParam", - -"(" -")" -"Ljava/lang/String;", - &g_SampleForTests_testStaticMethodWithNoParam); - - jstring ret = -static_cast<jstring>(env->CallStaticObjectMethod(SampleForTests_clazz(env), - method_id)); - jni_generator::CheckException(env); - return ScopedJavaLocalRef<jstring>(env, ret); -} - -// Step 3: RegisterNatives. - -static const JNINativeMethod kMethodsMyOtherInnerClass[] = { - { "nativeInit", -"(" -")" -"I", - reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit) - }, -}; - -static const JNINativeMethod kMethodsMyInnerClass[] = { - { "nativeInit", -"(" -")" -"I", - reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit) - }, -}; - -static const JNINativeMethod kMethodsSampleForTests[] = { - { "nativeStaticMethod", -"(" -"J" -"I" -")" -"I", - reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeStaticMethod) - }, - { "nativeMethod", -"(" -"J" -"I" -")" -"I", - reinterpret_cast<void*>(Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethod) - }, -}; - -static bool RegisterNativesImpl(JNIEnv* env) { - if (base::android::IsManualJniRegistrationDisabled()) return true; - - const int kMethodsMyOtherInnerClassSize = - arraysize(kMethodsMyOtherInnerClass); - - if (env->RegisterNatives(MyOtherInnerClass_clazz(env), - kMethodsMyOtherInnerClass, - kMethodsMyOtherInnerClassSize) < 0) { - jni_generator::HandleRegistrationError( - env, MyOtherInnerClass_clazz(env), __FILE__); - return false; - } - - const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass); - - if (env->RegisterNatives(MyInnerClass_clazz(env), - kMethodsMyInnerClass, - kMethodsMyInnerClassSize) < 0) { - jni_generator::HandleRegistrationError( - env, MyInnerClass_clazz(env), __FILE__); - return false; - } - - const int kMethodsSampleForTestsSize = arraysize(kMethodsSampleForTests); - - if (env->RegisterNatives(SampleForTests_clazz(env), - kMethodsSampleForTests, - kMethodsSampleForTestsSize) < 0) { - jni_generator::HandleRegistrationError( - env, SampleForTests_clazz(env), __FILE__); - return false; - } - - return true; -} - -#endif // org_chromium_example_jni_generator_SampleForTests_JNI diff --git a/base/android/jni_generator/testNatives.golden b/base/android/jni_generator/testNatives.golden index f9538a3..3362c92 100644 --- a/base/android/jni_generator/testNatives.golden +++ b/base/android/jni_generator/testNatives.golden @@ -20,30 +20,33 @@ namespace { const char kTestJniClassPath[] = "org/chromium/TestJni"; // Leaking this jclass as we cannot use LazyInstance from some threads. -jclass g_TestJni_clazz = NULL; -#define TestJni_clazz(env) g_TestJni_clazz +base::subtle::AtomicWord g_TestJni_clazz __attribute__((unused)) = 0; +#define TestJni_clazz(env) base::android::LazyGetClass(env, kTestJniClassPath, &g_TestJni_clazz) } // namespace // Step 2: method stubs. -static jint Init(JNIEnv* env, const JavaParamRef<jobject>& jcaller); +static jint Init(JNIEnv* env, const base::android::JavaParamRef<jobject>& + jcaller); -static jint Java_org_chromium_TestJni_nativeInit(JNIEnv* env, jobject jcaller) { - return Init(env, JavaParamRef<jobject>(env, jcaller)); +JNI_GENERATOR_EXPORT jint Java_org_chromium_TestJni_nativeInit(JNIEnv* env, + jobject jcaller) { + return Init(env, base::android::JavaParamRef<jobject>(env, jcaller)); } -static void Java_org_chromium_TestJni_nativeDestroy(JNIEnv* env, +JNI_GENERATOR_EXPORT void Java_org_chromium_TestJni_nativeDestroy(JNIEnv* env, jobject jcaller, jint nativeChromeBrowserProvider) { ChromeBrowserProvider* native = reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider); CHECK_NATIVE_PTR(env, jcaller, native, "Destroy"); - return native->Destroy(env, JavaParamRef<jobject>(env, jcaller)); + return native->Destroy(env, base::android::JavaParamRef<jobject>(env, + jcaller)); } -static jlong Java_org_chromium_TestJni_nativeAddBookmark(JNIEnv* env, - jobject jcaller, +JNI_GENERATOR_EXPORT jlong Java_org_chromium_TestJni_nativeAddBookmark(JNIEnv* + env, jobject jcaller, jint nativeChromeBrowserProvider, jstring url, jstring title, @@ -52,71 +55,79 @@ static jlong Java_org_chromium_TestJni_nativeAddBookmark(JNIEnv* env, ChromeBrowserProvider* native = reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider); CHECK_NATIVE_PTR(env, jcaller, native, "AddBookmark", 0); - return native->AddBookmark(env, JavaParamRef<jobject>(env, jcaller), - JavaParamRef<jstring>(env, url), JavaParamRef<jstring>(env, title), - isFolder, parentId); + return native->AddBookmark(env, base::android::JavaParamRef<jobject>(env, + jcaller), base::android::JavaParamRef<jstring>(env, url), + base::android::JavaParamRef<jstring>(env, title), isFolder, parentId); } -static ScopedJavaLocalRef<jstring> GetDomainAndRegistry(JNIEnv* env, const - JavaParamRef<jclass>& jcaller, - const JavaParamRef<jstring>& url); +static base::android::ScopedJavaLocalRef<jstring> GetDomainAndRegistry(JNIEnv* + env, const base::android::JavaParamRef<jclass>& jcaller, + const base::android::JavaParamRef<jstring>& url); -static jstring Java_org_chromium_TestJni_nativeGetDomainAndRegistry(JNIEnv* env, - jclass jcaller, +JNI_GENERATOR_EXPORT jstring + Java_org_chromium_TestJni_nativeGetDomainAndRegistry(JNIEnv* env, jclass + jcaller, jstring url) { - return GetDomainAndRegistry(env, JavaParamRef<jclass>(env, jcaller), - JavaParamRef<jstring>(env, url)).Release(); + return GetDomainAndRegistry(env, base::android::JavaParamRef<jclass>(env, + jcaller), base::android::JavaParamRef<jstring>(env, url)).Release(); } static void CreateHistoricalTabFromState(JNIEnv* env, const - JavaParamRef<jclass>& jcaller, - const JavaParamRef<jbyteArray>& state, + base::android::JavaParamRef<jclass>& jcaller, + const base::android::JavaParamRef<jbyteArray>& state, jint tab_index); -static void Java_org_chromium_TestJni_nativeCreateHistoricalTabFromState(JNIEnv* - env, jclass jcaller, +JNI_GENERATOR_EXPORT void + Java_org_chromium_TestJni_nativeCreateHistoricalTabFromState(JNIEnv* env, + jclass jcaller, jbyteArray state, jint tab_index) { - return CreateHistoricalTabFromState(env, JavaParamRef<jclass>(env, jcaller), - JavaParamRef<jbyteArray>(env, state), tab_index); + return CreateHistoricalTabFromState(env, + base::android::JavaParamRef<jclass>(env, jcaller), + base::android::JavaParamRef<jbyteArray>(env, state), tab_index); } -static ScopedJavaLocalRef<jbyteArray> GetStateAsByteArray(JNIEnv* env, const - JavaParamRef<jobject>& jcaller, - const JavaParamRef<jobject>& view); +static base::android::ScopedJavaLocalRef<jbyteArray> GetStateAsByteArray(JNIEnv* + env, const base::android::JavaParamRef<jobject>& jcaller, + const base::android::JavaParamRef<jobject>& view); -static jbyteArray Java_org_chromium_TestJni_nativeGetStateAsByteArray(JNIEnv* - env, jobject jcaller, +JNI_GENERATOR_EXPORT jbyteArray + Java_org_chromium_TestJni_nativeGetStateAsByteArray(JNIEnv* env, jobject + jcaller, jobject view) { - return GetStateAsByteArray(env, JavaParamRef<jobject>(env, jcaller), - JavaParamRef<jobject>(env, view)).Release(); + return GetStateAsByteArray(env, base::android::JavaParamRef<jobject>(env, + jcaller), base::android::JavaParamRef<jobject>(env, view)).Release(); } -static ScopedJavaLocalRef<jobjectArray> GetAutofillProfileGUIDs(JNIEnv* env, - const JavaParamRef<jclass>& jcaller); +static base::android::ScopedJavaLocalRef<jobjectArray> + GetAutofillProfileGUIDs(JNIEnv* env, const + base::android::JavaParamRef<jclass>& jcaller); -static jobjectArray +JNI_GENERATOR_EXPORT jobjectArray Java_org_chromium_TestJni_nativeGetAutofillProfileGUIDs(JNIEnv* env, jclass jcaller) { - return GetAutofillProfileGUIDs(env, JavaParamRef<jclass>(env, + return GetAutofillProfileGUIDs(env, base::android::JavaParamRef<jclass>(env, jcaller)).Release(); } -static void SetRecognitionResults(JNIEnv* env, const JavaParamRef<jobject>& - jcaller, +static void SetRecognitionResults(JNIEnv* env, const + base::android::JavaParamRef<jobject>& jcaller, jint sessionId, - const JavaParamRef<jobjectArray>& results); + const base::android::JavaParamRef<jobjectArray>& results); -static void Java_org_chromium_TestJni_nativeSetRecognitionResults(JNIEnv* env, - jobject jcaller, +JNI_GENERATOR_EXPORT void + Java_org_chromium_TestJni_nativeSetRecognitionResults(JNIEnv* env, jobject + jcaller, jint sessionId, jobjectArray results) { - return SetRecognitionResults(env, JavaParamRef<jobject>(env, jcaller), - sessionId, JavaParamRef<jobjectArray>(env, results)); + return SetRecognitionResults(env, base::android::JavaParamRef<jobject>(env, + jcaller), sessionId, base::android::JavaParamRef<jobjectArray>(env, + results)); } -static jlong Java_org_chromium_TestJni_nativeAddBookmarkFromAPI(JNIEnv* env, - jobject jcaller, +JNI_GENERATOR_EXPORT jlong + Java_org_chromium_TestJni_nativeAddBookmarkFromAPI(JNIEnv* env, jobject + jcaller, jint nativeChromeBrowserProvider, jstring url, jobject created, @@ -128,33 +139,39 @@ static jlong Java_org_chromium_TestJni_nativeAddBookmarkFromAPI(JNIEnv* env, ChromeBrowserProvider* native = reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider); CHECK_NATIVE_PTR(env, jcaller, native, "AddBookmarkFromAPI", 0); - return native->AddBookmarkFromAPI(env, JavaParamRef<jobject>(env, jcaller), - JavaParamRef<jstring>(env, url), JavaParamRef<jobject>(env, created), - JavaParamRef<jobject>(env, isBookmark), JavaParamRef<jobject>(env, date), - JavaParamRef<jbyteArray>(env, favicon), JavaParamRef<jstring>(env, title), - JavaParamRef<jobject>(env, visits)); + return native->AddBookmarkFromAPI(env, + base::android::JavaParamRef<jobject>(env, jcaller), + base::android::JavaParamRef<jstring>(env, url), + base::android::JavaParamRef<jobject>(env, created), + base::android::JavaParamRef<jobject>(env, isBookmark), + base::android::JavaParamRef<jobject>(env, date), + base::android::JavaParamRef<jbyteArray>(env, favicon), + base::android::JavaParamRef<jstring>(env, title), + base::android::JavaParamRef<jobject>(env, visits)); } -static jint FindAll(JNIEnv* env, const JavaParamRef<jobject>& jcaller, - const JavaParamRef<jstring>& find); - -static jint Java_org_chromium_TestJni_nativeFindAll(JNIEnv* env, jobject +static jint FindAll(JNIEnv* env, const base::android::JavaParamRef<jobject>& jcaller, + const base::android::JavaParamRef<jstring>& find); + +JNI_GENERATOR_EXPORT jint Java_org_chromium_TestJni_nativeFindAll(JNIEnv* env, + jobject jcaller, jstring find) { - return FindAll(env, JavaParamRef<jobject>(env, jcaller), - JavaParamRef<jstring>(env, find)); + return FindAll(env, base::android::JavaParamRef<jobject>(env, jcaller), + base::android::JavaParamRef<jstring>(env, find)); } -static ScopedJavaLocalRef<jobject> GetInnerClass(JNIEnv* env, const - JavaParamRef<jclass>& jcaller); +static base::android::ScopedJavaLocalRef<jobject> GetInnerClass(JNIEnv* env, + const base::android::JavaParamRef<jclass>& jcaller); -static jobject Java_org_chromium_TestJni_nativeGetInnerClass(JNIEnv* env, jclass - jcaller) { - return GetInnerClass(env, JavaParamRef<jclass>(env, jcaller)).Release(); +JNI_GENERATOR_EXPORT jobject + Java_org_chromium_TestJni_nativeGetInnerClass(JNIEnv* env, jclass jcaller) { + return GetInnerClass(env, base::android::JavaParamRef<jclass>(env, + jcaller)).Release(); } -static jobject Java_org_chromium_TestJni_nativeQueryBitmap(JNIEnv* env, - jobject jcaller, +JNI_GENERATOR_EXPORT jobject Java_org_chromium_TestJni_nativeQueryBitmap(JNIEnv* + env, jobject jcaller, jint nativeChromeBrowserProvider, jobjectArray projection, jstring selection, @@ -163,14 +180,15 @@ static jobject Java_org_chromium_TestJni_nativeQueryBitmap(JNIEnv* env, ChromeBrowserProvider* native = reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider); CHECK_NATIVE_PTR(env, jcaller, native, "QueryBitmap", NULL); - return native->QueryBitmap(env, JavaParamRef<jobject>(env, jcaller), - JavaParamRef<jobjectArray>(env, projection), JavaParamRef<jstring>(env, - selection), JavaParamRef<jobjectArray>(env, selectionArgs), - JavaParamRef<jstring>(env, sortOrder)).Release(); + return native->QueryBitmap(env, base::android::JavaParamRef<jobject>(env, + jcaller), base::android::JavaParamRef<jobjectArray>(env, projection), + base::android::JavaParamRef<jstring>(env, selection), + base::android::JavaParamRef<jobjectArray>(env, selectionArgs), + base::android::JavaParamRef<jstring>(env, sortOrder)).Release(); } -static void Java_org_chromium_TestJni_nativeGotOrientation(JNIEnv* env, - jobject jcaller, +JNI_GENERATOR_EXPORT void Java_org_chromium_TestJni_nativeGotOrientation(JNIEnv* + env, jobject jcaller, jint nativeDataFetcherImplAndroid, jdouble alpha, jdouble beta, @@ -178,19 +196,21 @@ static void Java_org_chromium_TestJni_nativeGotOrientation(JNIEnv* env, DataFetcherImplAndroid* native = reinterpret_cast<DataFetcherImplAndroid*>(nativeDataFetcherImplAndroid); CHECK_NATIVE_PTR(env, jcaller, native, "GotOrientation"); - return native->GotOrientation(env, JavaParamRef<jobject>(env, jcaller), alpha, - beta, gamma); + return native->GotOrientation(env, base::android::JavaParamRef<jobject>(env, + jcaller), alpha, beta, gamma); } -static ScopedJavaLocalRef<jthrowable> MessWithJavaException(JNIEnv* env, const - JavaParamRef<jclass>& jcaller, - const JavaParamRef<jthrowable>& e); +static base::android::ScopedJavaLocalRef<jthrowable> + MessWithJavaException(JNIEnv* env, const + base::android::JavaParamRef<jclass>& jcaller, + const base::android::JavaParamRef<jthrowable>& e); -static jthrowable Java_org_chromium_TestJni_nativeMessWithJavaException(JNIEnv* - env, jclass jcaller, +JNI_GENERATOR_EXPORT jthrowable + Java_org_chromium_TestJni_nativeMessWithJavaException(JNIEnv* env, jclass + jcaller, jthrowable e) { - return MessWithJavaException(env, JavaParamRef<jclass>(env, jcaller), - JavaParamRef<jthrowable>(env, e)).Release(); + return MessWithJavaException(env, base::android::JavaParamRef<jclass>(env, + jcaller), base::android::JavaParamRef<jthrowable>(env, e)).Release(); } // Step 3: RegisterNatives. @@ -301,9 +321,8 @@ static const JNINativeMethod kMethodsTestJni[] = { }; static bool RegisterNativesImpl(JNIEnv* env) { - - g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( - base::android::GetClass(env, kTestJniClassPath).obj())); + if (jni_generator::ShouldSkipJniRegistration(false)) + return true; const int kMethodsTestJniSize = arraysize(kMethodsTestJni); diff --git a/base/android/jni_generator/testNativesLong.golden b/base/android/jni_generator/testNativesLong.golden index d5b67ba..ec029ce 100644 --- a/base/android/jni_generator/testNativesLong.golden +++ b/base/android/jni_generator/testNativesLong.golden @@ -20,19 +20,20 @@ namespace { const char kTestJniClassPath[] = "org/chromium/TestJni"; // Leaking this jclass as we cannot use LazyInstance from some threads. -jclass g_TestJni_clazz = NULL; -#define TestJni_clazz(env) g_TestJni_clazz +base::subtle::AtomicWord g_TestJni_clazz __attribute__((unused)) = 0; +#define TestJni_clazz(env) base::android::LazyGetClass(env, kTestJniClassPath, &g_TestJni_clazz) } // namespace // Step 2: method stubs. -static void Java_org_chromium_TestJni_nativeDestroy(JNIEnv* env, +JNI_GENERATOR_EXPORT void Java_org_chromium_TestJni_nativeDestroy(JNIEnv* env, jobject jcaller, jlong nativeChromeBrowserProvider) { ChromeBrowserProvider* native = reinterpret_cast<ChromeBrowserProvider*>(nativeChromeBrowserProvider); CHECK_NATIVE_PTR(env, jcaller, native, "Destroy"); - return native->Destroy(env, JavaParamRef<jobject>(env, jcaller)); + return native->Destroy(env, base::android::JavaParamRef<jobject>(env, + jcaller)); } // Step 3: RegisterNatives. @@ -46,9 +47,8 @@ static const JNINativeMethod kMethodsTestJni[] = { }; static bool RegisterNativesImpl(JNIEnv* env) { - - g_TestJni_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( - base::android::GetClass(env, kTestJniClassPath).obj())); + if (jni_generator::ShouldSkipJniRegistration(false)) + return true; const int kMethodsTestJniSize = arraysize(kMethodsTestJni); diff --git a/base/android/jni_generator/testSingleJNIAdditionalImport.golden b/base/android/jni_generator/testSingleJNIAdditionalImport.golden index 1b2895e..ef618da 100644 --- a/base/android/jni_generator/testSingleJNIAdditionalImport.golden +++ b/base/android/jni_generator/testSingleJNIAdditionalImport.golden @@ -20,26 +20,27 @@ namespace { const char kFooClassPath[] = "org/chromium/foo/Foo"; // Leaking this jclass as we cannot use LazyInstance from some threads. -jclass g_Foo_clazz = NULL; -#define Foo_clazz(env) g_Foo_clazz +base::subtle::AtomicWord g_Foo_clazz __attribute__((unused)) = 0; +#define Foo_clazz(env) base::android::LazyGetClass(env, kFooClassPath, &g_Foo_clazz) } // namespace // Step 2: method stubs. -static void DoSomething(JNIEnv* env, const JavaParamRef<jclass>& jcaller, - const JavaParamRef<jobject>& callback); - -static void Java_org_chromium_foo_Foo_nativeDoSomething(JNIEnv* env, jclass +static void DoSomething(JNIEnv* env, const base::android::JavaParamRef<jclass>& jcaller, + const base::android::JavaParamRef<jobject>& callback); + +JNI_GENERATOR_EXPORT void Java_org_chromium_foo_Foo_nativeDoSomething(JNIEnv* + env, jclass jcaller, jobject callback) { - return DoSomething(env, JavaParamRef<jclass>(env, jcaller), - JavaParamRef<jobject>(env, callback)); + return DoSomething(env, base::android::JavaParamRef<jclass>(env, jcaller), + base::android::JavaParamRef<jobject>(env, callback)); } static base::subtle::AtomicWord g_Foo_calledByNative = 0; -static void Java_Foo_calledByNative(JNIEnv* env, jobject callback) { - /* Must call RegisterNativesImpl() */ +static void Java_Foo_calledByNative(JNIEnv* env, const + base::android::JavaRefOrBare<jobject>& callback) { CHECK_CLAZZ(env, Foo_clazz(env), Foo_clazz(env)); jmethodID method_id = @@ -47,7 +48,6 @@ static void Java_Foo_calledByNative(JNIEnv* env, jobject callback) { base::android::MethodID::TYPE_STATIC>( env, Foo_clazz(env), "calledByNative", - "(" "Lorg/chromium/foo/Bar$Callback;" ")" @@ -55,9 +55,8 @@ static void Java_Foo_calledByNative(JNIEnv* env, jobject callback) { &g_Foo_calledByNative); env->CallStaticVoidMethod(Foo_clazz(env), - method_id, callback); + method_id, callback.obj()); jni_generator::CheckException(env); - } // Step 3: RegisterNatives. @@ -71,9 +70,8 @@ static const JNINativeMethod kMethodsFoo[] = { }; static bool RegisterNativesImpl(JNIEnv* env) { - - g_Foo_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( - base::android::GetClass(env, kFooClassPath).obj())); + if (jni_generator::ShouldSkipJniRegistration(false)) + return true; const int kMethodsFooSize = arraysize(kMethodsFoo); diff --git a/base/android/jni_utils.cc b/base/android/jni_utils.cc index b4d682b..848dfd7 100644 --- a/base/android/jni_utils.cc +++ b/base/android/jni_utils.cc @@ -4,7 +4,6 @@ #include "base/android/jni_utils.h" -#include "base/android/jni_android.h" #include "base/android/scoped_java_ref.h" #include "jni/JNIUtils_jni.h" @@ -16,8 +15,8 @@ ScopedJavaLocalRef<jobject> GetClassLoader(JNIEnv* env) { return Java_JNIUtils_getClassLoader(env); } -bool RegisterJNIUtils(JNIEnv* env) { - return RegisterNativesImpl(env); +bool isSelectiveJniRegistrationEnabled(JNIEnv* env) { + return Java_JNIUtils_isSelectiveJniRegistrationEnabled(env); } } // namespace android diff --git a/base/android/jni_utils.h b/base/android/jni_utils.h index b793aed..ef645c2 100644 --- a/base/android/jni_utils.h +++ b/base/android/jni_utils.h @@ -18,7 +18,8 @@ namespace android { // via JNI from Java. BASE_EXPORT ScopedJavaLocalRef<jobject> GetClassLoader(JNIEnv* env); -bool RegisterJNIUtils(JNIEnv* env); +// Returns true if the current process permits selective JNI registration. +BASE_EXPORT bool isSelectiveJniRegistrationEnabled(JNIEnv* env); } // namespace android } // namespace base diff --git a/base/android/jni_weak_ref.cc b/base/android/jni_weak_ref.cc index 55244f2..fe7ea2e 100644 --- a/base/android/jni_weak_ref.cc +++ b/base/android/jni_weak_ref.cc @@ -4,24 +4,34 @@ #include "base/android/jni_weak_ref.h" +#include <utility> + #include "base/android/jni_android.h" #include "base/logging.h" using base::android::AttachCurrentThread; -JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef() - : obj_(NULL) { -} +JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef() : obj_(nullptr) {} JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef( const JavaObjectWeakGlobalRef& orig) - : obj_(NULL) { + : obj_(nullptr) { Assign(orig); } +JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef(JavaObjectWeakGlobalRef&& orig) + : obj_(orig.obj_) { + orig.obj_ = nullptr; +} + JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef(JNIEnv* env, jobject obj) : obj_(env->NewWeakGlobalRef(obj)) { - DCHECK(obj_); +} + +JavaObjectWeakGlobalRef::JavaObjectWeakGlobalRef( + JNIEnv* env, + const base::android::JavaRef<jobject>& obj) + : obj_(env->NewWeakGlobalRef(obj.obj())) { } JavaObjectWeakGlobalRef::~JavaObjectWeakGlobalRef() { @@ -32,10 +42,14 @@ void JavaObjectWeakGlobalRef::operator=(const JavaObjectWeakGlobalRef& rhs) { Assign(rhs); } +void JavaObjectWeakGlobalRef::operator=(JavaObjectWeakGlobalRef&& rhs) { + std::swap(obj_, rhs.obj_); +} + void JavaObjectWeakGlobalRef::reset() { if (obj_) { AttachCurrentThread()->DeleteWeakGlobalRef(obj_); - obj_ = NULL; + obj_ = nullptr; } } @@ -46,7 +60,7 @@ base::android::ScopedJavaLocalRef<jobject> base::android::ScopedJavaLocalRef<jobject> GetRealObject( JNIEnv* env, jweak obj) { - jobject real = NULL; + jobject real = nullptr; if (obj) real = env->NewLocalRef(obj); return base::android::ScopedJavaLocalRef<jobject>(env, real); @@ -60,5 +74,5 @@ void JavaObjectWeakGlobalRef::Assign(const JavaObjectWeakGlobalRef& other) { if (obj_) env->DeleteWeakGlobalRef(obj_); - obj_ = other.obj_ ? env->NewWeakGlobalRef(other.obj_) : NULL; + obj_ = other.obj_ ? env->NewWeakGlobalRef(other.obj_) : nullptr; } diff --git a/base/android/jni_weak_ref.h b/base/android/jni_weak_ref.h index c851046..223c47b 100644 --- a/base/android/jni_weak_ref.h +++ b/base/android/jni_weak_ref.h @@ -18,14 +18,22 @@ class BASE_EXPORT JavaObjectWeakGlobalRef { public: JavaObjectWeakGlobalRef(); JavaObjectWeakGlobalRef(const JavaObjectWeakGlobalRef& orig); + JavaObjectWeakGlobalRef(JavaObjectWeakGlobalRef&& orig); JavaObjectWeakGlobalRef(JNIEnv* env, jobject obj); + JavaObjectWeakGlobalRef(JNIEnv* env, + const base::android::JavaRef<jobject>& obj); virtual ~JavaObjectWeakGlobalRef(); void operator=(const JavaObjectWeakGlobalRef& rhs); + void operator=(JavaObjectWeakGlobalRef&& rhs); base::android::ScopedJavaLocalRef<jobject> get(JNIEnv* env) const; - bool is_empty() const { return obj_ == NULL; } + // Returns true if the weak reference has not been initialized to point at + // an object (or ḣas had reset() called). + // Do not call this to test if the object referred to still exists! The weak + // reference remains initialized even if the target object has been collected. + bool is_uninitialized() const { return obj_ == nullptr; } void reset(); diff --git a/base/android/junit/src/org/chromium/base/BaseChromiumApplicationTest.java b/base/android/junit/src/org/chromium/base/BaseChromiumApplicationTest.java index bfcd7aa..269f84b 100644 --- a/base/android/junit/src/org/chromium/base/BaseChromiumApplicationTest.java +++ b/base/android/junit/src/org/chromium/base/BaseChromiumApplicationTest.java @@ -10,18 +10,19 @@ import static org.mockito.Mockito.verify; import android.app.Activity; import android.view.KeyEvent; -import junit.framework.Assert; - import org.chromium.base.BaseChromiumApplication.WindowFocusChangedListener; -import org.chromium.base.test.shadows.ShadowMultiDex; import org.chromium.testing.local.LocalRobolectricTestRunner; +import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.shadows.ShadowActivity; +import org.robolectric.shadows.multidex.ShadowMultiDex; import org.robolectric.util.ActivityController; /** Unit tests for {@link BaseChromiumApplication}. */ @@ -52,7 +53,7 @@ public class BaseChromiumApplicationTest { @Test public void testWindowsFocusChanged() throws Exception { - BaseChromiumApplication app = (BaseChromiumApplication) Robolectric.application; + BaseChromiumApplication app = (BaseChromiumApplication) RuntimeEnvironment.application; WindowFocusChangedListener mock = mock(WindowFocusChangedListener.class); app.registerWindowFocusChangedListener(mock); @@ -60,7 +61,7 @@ public class BaseChromiumApplicationTest { ActivityController<Activity> controller = Robolectric.buildActivity(Activity.class).create().start().visible(); TrackingShadowActivity shadow = - (TrackingShadowActivity) Robolectric.shadowOf(controller.get()); + (TrackingShadowActivity) Shadows.shadowOf(controller.get()); controller.get().getWindow().getCallback().onWindowFocusChanged(true); // Assert that listeners were notified. diff --git a/base/android/library_loader/library_loader_hooks.cc b/base/android/library_loader/library_loader_hooks.cc index 025075e..621575c 100644 --- a/base/android/library_loader/library_loader_hooks.cc +++ b/base/android/library_loader/library_loader_hooks.cc @@ -10,7 +10,7 @@ #include "base/android/library_loader/library_prefetcher.h" #include "base/at_exit.h" #include "base/metrics/histogram.h" -#include "base/metrics/sparse_histogram.h" +#include "base/metrics/histogram_macros.h" #include "jni/LibraryLoader_jni.h" namespace base { @@ -21,6 +21,7 @@ namespace { base::AtExitManager* g_at_exit_manager = NULL; const char* g_library_version_number = ""; LibraryLoadedHook* g_registration_callback = NULL; +NativeInitializationHook* g_native_initialization_hook = NULL; enum RendererHistogramCode { // Renderer load at fixed address success, fail, or not attempted. @@ -149,6 +150,11 @@ static void RegisterLibraryPreloaderRendererHistogram( g_library_preloader_renderer_histogram_code_registered = true; } +void SetNativeInitializationHook( + NativeInitializationHook native_initialization_hook) { + g_native_initialization_hook = native_initialization_hook; +} + void RecordLibraryLoaderRendererHistograms() { RecordChromiumAndroidLinkerRendererHistogram(); RecordLibraryPreloaderRendereHistogram(); @@ -167,6 +173,9 @@ static void InitCommandLine( static jboolean LibraryLoaded(JNIEnv* env, const JavaParamRef<jobject>& jcaller) { + if (g_native_initialization_hook && !g_native_initialization_hook()) { + return false; + } if (g_registration_callback == NULL) { return true; } diff --git a/base/android/library_loader/library_loader_hooks.h b/base/android/library_loader/library_loader_hooks.h index 3e8969b..5c37e6e 100644 --- a/base/android/library_loader/library_loader_hooks.h +++ b/base/android/library_loader/library_loader_hooks.h @@ -8,6 +8,7 @@ #include <jni.h> #include "base/base_export.h" +#include "base/callback.h" namespace base { namespace android { @@ -27,6 +28,11 @@ enum LibraryProcessType { PROCESS_WEBVIEW_CHILD = 4, }; +typedef bool NativeInitializationHook(); + +BASE_EXPORT void SetNativeInitializationHook( + NativeInitializationHook native_initialization_hook); + // Record any pending renderer histogram value as histograms. Pending values // are set by RegisterChromiumAndroidLinkerRendererHistogram and // RegisterLibraryPreloaderRendererHistogram. diff --git a/base/android/library_loader/library_prefetcher.cc b/base/android/library_loader/library_prefetcher.cc index c7ec990..a946c82 100644 --- a/base/android/library_loader/library_prefetcher.cc +++ b/base/android/library_loader/library_prefetcher.cc @@ -16,6 +16,7 @@ #include "base/macros.h" #include "base/posix/eintr_wrapper.h" #include "base/strings/string_util.h" +#include "build/build_config.h" namespace base { namespace android { @@ -50,6 +51,13 @@ bool PathMatchesSuffix(const std::string& path) { // Heap allocations, syscalls and library functions are not allowed in this // function. // Returns true for success. +#if defined(ADDRESS_SANITIZER) +// Disable AddressSanitizer instrumentation for this function. It is touching +// memory that hasn't been allocated by the app, though the addresses are +// valid. Furthermore, this takes place in a child process. See crbug.com/653372 +// for the context. +__attribute__((no_sanitize_address)) +#endif bool Prefetch(const std::vector<std::pair<uintptr_t, uintptr_t>>& ranges) { for (const auto& range : ranges) { const uintptr_t page_mask = kPageSize - 1; diff --git a/base/android/linker/BUILD.gn b/base/android/linker/BUILD.gn index 3724b88..c2bfb11 100644 --- a/base/android/linker/BUILD.gn +++ b/base/android/linker/BUILD.gn @@ -6,7 +6,6 @@ import("//build/config/android/config.gni") assert(is_android) -# GYP: //base/base.gyp:chromium_android_linker shared_library("chromium_android_linker") { sources = [ "android_dlext.h", diff --git a/base/android/linker/config.gni b/base/android/linker/config.gni index 174c1ab..27793ff 100644 --- a/base/android/linker/config.gni +++ b/base/android/linker/config.gni @@ -6,7 +6,8 @@ import("//build/config/android/config.gni") import("//build/config/compiler/compiler.gni") import("//build/config/sanitizers/sanitizers.gni") -# Chromium linker crashes on component builds on Android 4.4. See b/11379966 +# Chromium linker doesn't reliably support loading multiple libraries; +# disable for component builds, see crbug.com/657093. # Chromium linker causes instrumentation to return incorrect results. chromium_linker_supported = !is_component_build && !enable_profiling && !use_order_profiling && !is_asan diff --git a/base/android/locale_utils.cc b/base/android/locale_utils.cc index af46f89..b3a2346 100644 --- a/base/android/locale_utils.cc +++ b/base/android/locale_utils.cc @@ -16,16 +16,12 @@ std::string GetDefaultCountryCode() { return ConvertJavaStringToUTF8(Java_LocaleUtils_getDefaultCountryCode(env)); } -std::string GetDefaultLocale() { +std::string GetDefaultLocaleString() { JNIEnv* env = base::android::AttachCurrentThread(); - ScopedJavaLocalRef<jstring> locale = Java_LocaleUtils_getDefaultLocale( - env); + ScopedJavaLocalRef<jstring> locale = + Java_LocaleUtils_getDefaultLocaleString(env); return ConvertJavaStringToUTF8(locale); } -bool RegisterLocaleUtils(JNIEnv* env) { - return RegisterNativesImpl(env); -} - } // namespace android } // namespace base diff --git a/base/android/locale_utils.h b/base/android/locale_utils.h index 9e03b83..be68890 100644 --- a/base/android/locale_utils.h +++ b/base/android/locale_utils.h @@ -16,10 +16,8 @@ namespace android { BASE_EXPORT std::string GetDefaultCountryCode(); -// Return the current default locale of the device. -BASE_EXPORT std::string GetDefaultLocale(); - -BASE_EXPORT bool RegisterLocaleUtils(JNIEnv* env); +// Return the current default locale of the device as string. +BASE_EXPORT std::string GetDefaultLocaleString(); } // namespace android } // namespace base diff --git a/base/android/memory_pressure_listener_android.cc b/base/android/memory_pressure_listener_android.cc index 5975b94..32e0871 100644 --- a/base/android/memory_pressure_listener_android.cc +++ b/base/android/memory_pressure_listener_android.cc @@ -8,6 +8,8 @@ #include "base/memory/memory_pressure_listener.h" #include "jni/MemoryPressureListener_jni.h" +using base::android::JavaParamRef; + // Defined and called by JNI. static void OnMemoryPressure(JNIEnv* env, const JavaParamRef<jclass>& clazz, diff --git a/base/android/path_utils.cc b/base/android/path_utils.cc index b765449..89ab833 100644 --- a/base/android/path_utils.cc +++ b/base/android/path_utils.cc @@ -17,8 +17,7 @@ namespace android { bool GetDataDirectory(FilePath* result) { JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jstring> path = - Java_PathUtils_getDataDirectory(env, GetApplicationContext()); + ScopedJavaLocalRef<jstring> path = Java_PathUtils_getDataDirectory(env); FilePath data_path(ConvertJavaStringToUTF8(path)); *result = data_path; return true; @@ -26,8 +25,7 @@ bool GetDataDirectory(FilePath* result) { bool GetDatabaseDirectory(FilePath* result) { JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jstring> path = - Java_PathUtils_getDatabaseDirectory(env, GetApplicationContext()); + ScopedJavaLocalRef<jstring> path = Java_PathUtils_getDatabaseDirectory(env); FilePath data_path(ConvertJavaStringToUTF8(path)); *result = data_path; return true; @@ -35,8 +33,7 @@ bool GetDatabaseDirectory(FilePath* result) { bool GetCacheDirectory(FilePath* result) { JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jstring> path = - Java_PathUtils_getCacheDirectory(env, GetApplicationContext()); + ScopedJavaLocalRef<jstring> path = Java_PathUtils_getCacheDirectory(env); FilePath cache_path(ConvertJavaStringToUTF8(path)); *result = cache_path; return true; @@ -45,7 +42,7 @@ bool GetCacheDirectory(FilePath* result) { bool GetThumbnailCacheDirectory(FilePath* result) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jstring> path = - Java_PathUtils_getThumbnailCacheDirectory(env, GetApplicationContext()); + Java_PathUtils_getThumbnailCacheDirectory(env); FilePath thumbnail_cache_path(ConvertJavaStringToUTF8(path)); *result = thumbnail_cache_path; return true; @@ -53,8 +50,7 @@ bool GetThumbnailCacheDirectory(FilePath* result) { bool GetDownloadsDirectory(FilePath* result) { JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jstring> path = - Java_PathUtils_getDownloadsDirectory(env, GetApplicationContext()); + ScopedJavaLocalRef<jstring> path = Java_PathUtils_getDownloadsDirectory(env); FilePath downloads_path(ConvertJavaStringToUTF8(path)); *result = downloads_path; return true; @@ -63,7 +59,7 @@ bool GetDownloadsDirectory(FilePath* result) { bool GetNativeLibraryDirectory(FilePath* result) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef<jstring> path = - Java_PathUtils_getNativeLibraryDirectory(env, GetApplicationContext()); + Java_PathUtils_getNativeLibraryDirectory(env); FilePath library_path(ConvertJavaStringToUTF8(path)); *result = library_path; return true; @@ -78,9 +74,5 @@ bool GetExternalStorageDirectory(FilePath* result) { return true; } -bool RegisterPathUtils(JNIEnv* env) { - return RegisterNativesImpl(env); -} - } // namespace android } // namespace base diff --git a/base/android/path_utils.h b/base/android/path_utils.h index 6501f1b..7402644 100644 --- a/base/android/path_utils.h +++ b/base/android/path_utils.h @@ -48,8 +48,6 @@ BASE_EXPORT bool GetNativeLibraryDirectory(FilePath* result); // is placed in the FilePath pointed to by 'result'. BASE_EXPORT bool GetExternalStorageDirectory(FilePath* result); -bool RegisterPathUtils(JNIEnv* env); - } // namespace android } // namespace base diff --git a/base/android/record_histogram.cc b/base/android/record_histogram.cc index 3c51fe2..1a207a1 100644 --- a/base/android/record_histogram.cc +++ b/base/android/record_histogram.cc @@ -53,12 +53,16 @@ class HistogramCache { jstring j_histogram_name, int32_t expected_min, int32_t expected_max, - int32_t expected_bucket_count, + uint32_t expected_bucket_count, HistogramBase* histogram) { + std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); + bool valid_arguments = Histogram::InspectConstructionArguments( + histogram_name, &expected_min, &expected_max, &expected_bucket_count); + DCHECK(valid_arguments); DCHECK(histogram->HasConstructionArguments(expected_min, expected_max, expected_bucket_count)) - << ConvertJavaStringToUTF8(env, j_histogram_name) << "/" << expected_min - << "/" << expected_max << "/" << expected_bucket_count << " vs. " + << histogram_name << "/" << expected_min << "/" << expected_max << "/" + << expected_bucket_count << " vs. " << HistogramConstructionParamsToString(histogram); } @@ -113,6 +117,8 @@ class HistogramCache { return histogram; } + DCHECK_GE(min, 1) << "The min expected sample must be >= 1"; + std::string histogram_name = ConvertJavaStringToUTF8(env, j_histogram_name); histogram = Histogram::FactoryGet(histogram_name, min, max, num_buckets, diff --git a/base/android/scoped_java_ref.cc b/base/android/scoped_java_ref.cc index 4d4ef6d..2876ba4 100644 --- a/base/android/scoped_java_ref.cc +++ b/base/android/scoped_java_ref.cc @@ -26,7 +26,9 @@ ScopedJavaLocalFrame::ScopedJavaLocalFrame(JNIEnv* env, int capacity) DCHECK(!failed); } -ScopedJavaLocalFrame::~ScopedJavaLocalFrame() { env_->PopLocalFrame(NULL); } +ScopedJavaLocalFrame::~ScopedJavaLocalFrame() { + env_->PopLocalFrame(nullptr); +} #if DCHECK_IS_ON() // This constructor is inlined when DCHECKs are disabled; don't add anything @@ -69,14 +71,14 @@ void JavaRef<jobject>::ResetLocalRef(JNIEnv* env) { if (obj_) { DCHECK_EQ(env, AttachCurrentThread()); // Is |env| on correct thread. env->DeleteLocalRef(obj_); - obj_ = NULL; + obj_ = nullptr; } } void JavaRef<jobject>::ResetGlobalRef() { if (obj_) { AttachCurrentThread()->DeleteGlobalRef(obj_); - obj_ = NULL; + obj_ = nullptr; } } diff --git a/base/android/scoped_java_ref.h b/base/android/scoped_java_ref.h index a1b4b13..6d728e9 100644 --- a/base/android/scoped_java_ref.h +++ b/base/android/scoped_java_ref.h @@ -9,6 +9,7 @@ #include <stddef.h> #include <type_traits> +#include <utility> #include "base/base_export.h" #include "base/logging.h" @@ -43,23 +44,23 @@ template<typename T> class JavaRef; template<> class BASE_EXPORT JavaRef<jobject> { public: + // Initializes a null reference. Don't add anything else here; it's inlined. + JavaRef() : obj_(nullptr) {} + // Allow nullptr to be converted to JavaRef. This avoids having to declare an - // empty ScopedJavaLocalRef just to pass null to a function with a JavaRef - // parameter, and makes C++ "nullptr" and Java "null" equivalent. + // empty JavaRef just to pass null to a function, and makes C++ "nullptr" and + // Java "null" equivalent. JavaRef(std::nullptr_t) : JavaRef() {} - // Public to allow destruction of temporary JavaRef objects created by the - // nullptr conversion. Don't add anything else here; it's inlined. + // Public to allow destruction of null JavaRef objects. + // Don't add anything else here; it's inlined. ~JavaRef() {} jobject obj() const { return obj_; } - bool is_null() const { return obj_ == NULL; } + bool is_null() const { return obj_ == nullptr; } protected: - // Initializes a NULL reference. Don't add anything else here; it's inlined. - JavaRef() : obj_(NULL) {} - // Takes ownership of the |obj| reference passed; requires it to be a local // reference type. #if DCHECK_IS_ON() @@ -70,6 +71,8 @@ class BASE_EXPORT JavaRef<jobject> { JavaRef(JNIEnv* env, jobject obj) : obj_(obj) {} #endif + void swap(JavaRef& other) { std::swap(obj_, other.obj_); } + // The following are implementation detail convenience methods, for // use by the sub-classes. JNIEnv* SetNewLocalRef(JNIEnv* env, jobject obj); @@ -90,14 +93,13 @@ class BASE_EXPORT JavaRef<jobject> { template<typename T> class JavaRef : public JavaRef<jobject> { public: + JavaRef() {} JavaRef(std::nullptr_t) : JavaRef<jobject>(nullptr) {} ~JavaRef() {} T obj() const { return static_cast<T>(JavaRef<jobject>::obj()); } protected: - JavaRef() {} - JavaRef(JNIEnv* env, T obj) : JavaRef<jobject>(env, obj) {} private: @@ -144,7 +146,8 @@ class JavaParamRef : public JavaRef<T> { template<typename T> class ScopedJavaLocalRef : public JavaRef<T> { public: - ScopedJavaLocalRef() : env_(NULL) {} + ScopedJavaLocalRef() : env_(nullptr) {} + ScopedJavaLocalRef(std::nullptr_t) : env_(nullptr) {} // Non-explicit copy constructor, to allow ScopedJavaLocalRef to be returned // by value as this is the normal usage pattern. @@ -153,14 +156,18 @@ class ScopedJavaLocalRef : public JavaRef<T> { this->SetNewLocalRef(env_, other.obj()); } - template<typename U> - explicit ScopedJavaLocalRef(const U& other) - : env_(NULL) { + ScopedJavaLocalRef(ScopedJavaLocalRef<T>&& other) : env_(other.env_) { + this->swap(other); + } + + explicit ScopedJavaLocalRef(const JavaRef<T>& other) : env_(nullptr) { this->Reset(other); } // Assumes that |obj| is a local reference to a Java object and takes // ownership of this local reference. + // TODO(torne): this shouldn't be used outside of JNI helper functions but + // there are currently some cases where there aren't helpers for things. ScopedJavaLocalRef(JNIEnv* env, T obj) : JavaRef<T>(env, obj), env_(env) {} ~ScopedJavaLocalRef() { @@ -173,31 +180,32 @@ class ScopedJavaLocalRef : public JavaRef<T> { this->Reset(other); } + void operator=(ScopedJavaLocalRef<T>&& other) { + env_ = other.env_; + this->swap(other); + } + void Reset() { this->ResetLocalRef(env_); } - template<typename U> - void Reset(const ScopedJavaLocalRef<U>& other) { + void Reset(const ScopedJavaLocalRef<T>& other) { // We can copy over env_ here as |other| instance must be from the same // thread as |this| local ref. (See class comment for multi-threading // limitations, and alternatives). this->Reset(other.env_, other.obj()); } - template<typename U> - void Reset(const U& other) { - // If |env_| was not yet set (is still NULL) it will be attached to the + void Reset(const JavaRef<T>& other) { + // If |env_| was not yet set (is still null) it will be attached to the // current thread in SetNewLocalRef(). this->Reset(env_, other.obj()); } - template<typename U> - void Reset(JNIEnv* env, U obj) { - static_assert(std::is_convertible<U, T>::value, - "U must be convertible to T"); - env_ = this->SetNewLocalRef(env, obj); - } + // Creates a new local reference to the Java object, unlike the constructor + // with the same parameters that takes ownership of the existing reference. + // TODO(torne): these should match as this is confusing. + void Reset(JNIEnv* env, T obj) { env_ = this->SetNewLocalRef(env, obj); } // Releases the local reference to the caller. The caller *must* delete the // local reference when it is done with it. Note that calling a Java method @@ -227,17 +235,17 @@ template<typename T> class ScopedJavaGlobalRef : public JavaRef<T> { public: ScopedJavaGlobalRef() {} + ScopedJavaGlobalRef(std::nullptr_t) {} ScopedJavaGlobalRef(const ScopedJavaGlobalRef<T>& other) { this->Reset(other); } + ScopedJavaGlobalRef(ScopedJavaGlobalRef<T>&& other) { this->swap(other); } + ScopedJavaGlobalRef(JNIEnv* env, T obj) { this->Reset(env, obj); } - template<typename U> - explicit ScopedJavaGlobalRef(const U& other) { - this->Reset(other); - } + explicit ScopedJavaGlobalRef(const JavaRef<T>& other) { this->Reset(other); } ~ScopedJavaGlobalRef() { this->Reset(); @@ -249,26 +257,19 @@ class ScopedJavaGlobalRef : public JavaRef<T> { this->Reset(other); } + void operator=(ScopedJavaGlobalRef<T>&& other) { this->swap(other); } + void Reset() { this->ResetGlobalRef(); } - template<typename U> - void Reset(const U& other) { - this->Reset(NULL, other.obj()); - } + void Reset(const JavaRef<T>& other) { this->Reset(nullptr, other.obj()); } - template<typename U> - void Reset(JNIEnv* env, const JavaParamRef<U>& other) { + void Reset(JNIEnv* env, const JavaParamRef<T>& other) { this->Reset(env, other.obj()); } - template<typename U> - void Reset(JNIEnv* env, U obj) { - static_assert(std::is_convertible<U, T>::value, - "U must be convertible to T"); - this->SetNewGlobalRef(env, obj); - } + void Reset(JNIEnv* env, T obj) { this->SetNewGlobalRef(env, obj); } // Releases the global reference to the caller. The caller *must* delete the // global reference when it is done with it. Note that calling a Java method @@ -278,6 +279,22 @@ class ScopedJavaGlobalRef : public JavaRef<T> { } }; +// Temporary type for parameters to Java functions, to allow incremental +// migration from bare jobject to JavaRef. Don't use outside JNI generator. +template <typename T> +class JavaRefOrBare { + public: + JavaRefOrBare(std::nullptr_t) : obj_(nullptr) {} + JavaRefOrBare(const JavaRef<T>& ref) : obj_(ref.obj()) {} + JavaRefOrBare(T obj) : obj_(obj) {} + T obj() const { return obj_; } + + private: + T obj_; + + DISALLOW_COPY_AND_ASSIGN(JavaRefOrBare); +}; + } // namespace android } // namespace base diff --git a/base/android/scoped_java_ref_unittest.cc b/base/android/scoped_java_ref_unittest.cc index 3f4419a..99d035b 100644 --- a/base/android/scoped_java_ref_unittest.cc +++ b/base/android/scoped_java_ref_unittest.cc @@ -106,6 +106,17 @@ TEST_F(ScopedJavaRefTest, RefCounts) { EXPECT_EQ(2, g_local_refs); } EXPECT_EQ(1, g_local_refs); + { + ScopedJavaLocalRef<jstring> str4((ScopedJavaLocalRef<jstring>(str2))); + EXPECT_EQ(2, g_local_refs); + } + EXPECT_EQ(1, g_local_refs); + { + ScopedJavaLocalRef<jstring> str5; + str5 = ScopedJavaLocalRef<jstring>(str2); + EXPECT_EQ(2, g_local_refs); + } + EXPECT_EQ(1, g_local_refs); str2.Reset(); EXPECT_EQ(0, g_local_refs); global_str.Reset(); diff --git a/base/android/sys_utils.cc b/base/android/sys_utils.cc index e89c1b3..a576644 100644 --- a/base/android/sys_utils.cc +++ b/base/android/sys_utils.cc @@ -11,10 +11,6 @@ namespace base { namespace android { -bool SysUtils::Register(JNIEnv* env) { - return RegisterNativesImpl(env); -} - bool SysUtils::IsLowEndDeviceFromJni() { JNIEnv* env = AttachCurrentThread(); return Java_SysUtils_isLowEndDevice(env); @@ -22,4 +18,4 @@ bool SysUtils::IsLowEndDeviceFromJni() { } // namespace android -} // namespace base
\ No newline at end of file +} // namespace base diff --git a/base/android/sys_utils.h b/base/android/sys_utils.h index 85dc035..2edbdd5 100644 --- a/base/android/sys_utils.h +++ b/base/android/sys_utils.h @@ -12,8 +12,6 @@ namespace android { class BASE_EXPORT SysUtils { public: - static bool Register(JNIEnv* env); - // Returns true iff this is a low-end device. static bool IsLowEndDeviceFromJni(); }; diff --git a/base/android/thread_utils.h b/base/android/thread_utils.h deleted file mode 100644 index cbe65f0..0000000 --- a/base/android/thread_utils.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ANDROID_THREAD_UTILS_H_ -#define BASE_ANDROID_THREAD_UTILS_H_ - -#include "base/android/jni_android.h" - -namespace base { - -bool RegisterThreadUtils(JNIEnv* env); - -} // namespace base - -#endif // BASE_ANDROID_THREAD_UTILS_H_ diff --git a/base/debug/stack_trace_android.cc b/base/debug/stack_trace_android.cc index 1e80385..329204c 100644 --- a/base/debug/stack_trace_android.cc +++ b/base/debug/stack_trace_android.cc @@ -7,6 +7,8 @@ #include <android/log.h> #include <stddef.h> #include <unwind.h> + +#include <algorithm> #include <ostream> #include "base/debug/proc_maps_linux.h" @@ -67,8 +69,10 @@ bool EnableInProcessStackDumping() { return (sigaction(SIGPIPE, &action, NULL) == 0); } -StackTrace::StackTrace() { - StackCrawlState state(reinterpret_cast<uintptr_t*>(trace_), kMaxTraces); +StackTrace::StackTrace(size_t count) { + count = std::min(arraysize(trace_), count); + + StackCrawlState state(reinterpret_cast<uintptr_t*>(trace_), count); _Unwind_Backtrace(&TraceStackFrame, &state); count_ = state.frame_count; } diff --git a/base/i18n/base_i18n_export.h b/base/i18n/base_i18n_export.h new file mode 100644 index 0000000..e8a2add --- /dev/null +++ b/base/i18n/base_i18n_export.h @@ -0,0 +1,29 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_I18N_BASE_I18N_EXPORT_H_ +#define BASE_I18N_BASE_I18N_EXPORT_H_ + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(BASE_I18N_IMPLEMENTATION) +#define BASE_I18N_EXPORT __declspec(dllexport) +#else +#define BASE_I18N_EXPORT __declspec(dllimport) +#endif // defined(BASE_I18N_IMPLEMENTATION) + +#else // defined(WIN32) +#if defined(BASE_I18N_IMPLEMENTATION) +#define BASE_I18N_EXPORT __attribute__((visibility("default"))) +#else +#define BASE_I18N_EXPORT +#endif +#endif + +#else // defined(COMPONENT_BUILD) +#define BASE_I18N_EXPORT +#endif + +#endif // BASE_I18N_BASE_I18N_EXPORT_H_ diff --git a/base/i18n/rtl.h b/base/i18n/rtl.h new file mode 100644 index 0000000..df15cd0 --- /dev/null +++ b/base/i18n/rtl.h @@ -0,0 +1,155 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_I18N_RTL_H_ +#define BASE_I18N_RTL_H_ + +#include <string> + +#include "base/compiler_specific.h" +#include "base/i18n/base_i18n_export.h" +#include "base/strings/string16.h" +#include "build/build_config.h" + +namespace base { + +class FilePath; + +namespace i18n { + +const char16 kRightToLeftMark = 0x200F; +const char16 kLeftToRightMark = 0x200E; +const char16 kLeftToRightEmbeddingMark = 0x202A; +const char16 kRightToLeftEmbeddingMark = 0x202B; +const char16 kPopDirectionalFormatting = 0x202C; +const char16 kLeftToRightOverride = 0x202D; +const char16 kRightToLeftOverride = 0x202E; + +// Locale.java mirrored this enum TextDirection. Please keep in sync. +enum TextDirection { + UNKNOWN_DIRECTION = 0, + RIGHT_TO_LEFT = 1, + LEFT_TO_RIGHT = 2, + TEXT_DIRECTION_MAX = LEFT_TO_RIGHT, +}; + +// Get the locale that the currently running process has been configured to use. +// The return value is of the form language[-country] (e.g., en-US) where the +// language is the 2 or 3 letter code from ISO-639. +BASE_I18N_EXPORT std::string GetConfiguredLocale(); + +// Canonicalize a string (eg. a POSIX locale string) to a Chrome locale name. +BASE_I18N_EXPORT std::string GetCanonicalLocale(const std::string& locale); + +// Sets the default locale of ICU. +// Once the application locale of Chrome in GetApplicationLocale is determined, +// the default locale of ICU need to be changed to match the application locale +// so that ICU functions work correctly in a locale-dependent manner. +// This is handy in that we don't have to call GetApplicationLocale() +// everytime we call locale-dependent ICU APIs as long as we make sure +// that this is called before any locale-dependent API is called. +BASE_I18N_EXPORT void SetICUDefaultLocale(const std::string& locale_string); + +// Returns true if the application text direction is right-to-left. +BASE_I18N_EXPORT bool IsRTL(); + +// Returns whether the text direction for the default ICU locale is RTL. This +// assumes that SetICUDefaultLocale has been called to set the default locale to +// the UI locale of Chrome. +// NOTE: Generally, you should call IsRTL() instead of this. +BASE_I18N_EXPORT bool ICUIsRTL(); + +// Returns the text direction for |locale_name|. +// As a startup optimization, this method checks the locale against a list of +// Chrome-supported RTL locales. +BASE_I18N_EXPORT TextDirection +GetTextDirectionForLocaleInStartUp(const char* locale_name); + +// Returns the text direction for |locale_name|. +BASE_I18N_EXPORT TextDirection GetTextDirectionForLocale( + const char* locale_name); + +// Given the string in |text|, returns the directionality of the first or last +// character with strong directionality in the string. If no character in the +// text has strong directionality, LEFT_TO_RIGHT is returned. The Bidi +// character types L, LRE, LRO, R, AL, RLE, and RLO are considered as strong +// directionality characters. Please refer to http://unicode.org/reports/tr9/ +// for more information. +BASE_I18N_EXPORT TextDirection GetFirstStrongCharacterDirection( + const string16& text); +BASE_I18N_EXPORT TextDirection GetLastStrongCharacterDirection( + const string16& text); + +// Given the string in |text|, returns LEFT_TO_RIGHT or RIGHT_TO_LEFT if all the +// strong directionality characters in the string are of the same +// directionality. It returns UNKNOWN_DIRECTION if the string contains a mix of +// LTR and RTL strong directionality characters. Defaults to LEFT_TO_RIGHT if +// the string does not contain directionality characters. Please refer to +// http://unicode.org/reports/tr9/ for more information. +BASE_I18N_EXPORT TextDirection GetStringDirection(const string16& text); + +// Given the string in |text|, this function modifies the string in place with +// the appropriate Unicode formatting marks that mark the string direction +// (either left-to-right or right-to-left). The function checks both the current +// locale and the contents of the string in order to determine the direction of +// the returned string. The function returns true if the string in |text| was +// properly adjusted. +// +// Certain LTR strings are not rendered correctly when the context is RTL. For +// example, the string "Foo!" will appear as "!Foo" if it is rendered as is in +// an RTL context. Calling this function will make sure the returned localized +// string is always treated as a right-to-left string. This is done by +// inserting certain Unicode formatting marks into the returned string. +// +// ** Notes about the Windows version of this function: +// TODO(idana) bug 6806: this function adjusts the string in question only +// if the current locale is right-to-left. The function does not take care of +// the opposite case (an RTL string displayed in an LTR context) since +// adjusting the string involves inserting Unicode formatting characters that +// Windows does not handle well unless right-to-left language support is +// installed. Since the English version of Windows doesn't have right-to-left +// language support installed by default, inserting the direction Unicode mark +// results in Windows displaying squares. +BASE_I18N_EXPORT bool AdjustStringForLocaleDirection(string16* text); + +// Undoes the actions of the above function (AdjustStringForLocaleDirection). +BASE_I18N_EXPORT bool UnadjustStringForLocaleDirection(string16* text); + +// Returns true if the string contains at least one character with strong right +// to left directionality; that is, a character with either R or AL Unicode +// BiDi character type. +BASE_I18N_EXPORT bool StringContainsStrongRTLChars(const string16& text); + +// Wraps a string with an LRE-PDF pair which essentialy marks the string as a +// Left-To-Right string. Doing this is useful in order to make sure LTR +// strings are rendered properly in an RTL context. +BASE_I18N_EXPORT void WrapStringWithLTRFormatting(string16* text); + +// Wraps a string with an RLE-PDF pair which essentialy marks the string as a +// Right-To-Left string. Doing this is useful in order to make sure RTL +// strings are rendered properly in an LTR context. +BASE_I18N_EXPORT void WrapStringWithRTLFormatting(string16* text); + +// Wraps file path to get it to display correctly in RTL UI. All filepaths +// should be passed through this function before display in UI for RTL locales. +BASE_I18N_EXPORT void WrapPathWithLTRFormatting(const FilePath& path, + string16* rtl_safe_path); + +// Return the string in |text| wrapped with LRE (Left-To-Right Embedding) and +// PDF (Pop Directional Formatting) marks, if needed for UI display purposes. +BASE_I18N_EXPORT string16 GetDisplayStringInLTRDirectionality( + const string16& text) WARN_UNUSED_RESULT; + +// Strip the beginning (U+202A..U+202B, U+202D..U+202E) and/or ending (U+202C) +// explicit bidi control characters from |text|, if there are any. Otherwise, +// return the text itself. Explicit bidi control characters display and have +// semantic effect. They can be deleted so they might not always appear in a +// pair. +BASE_I18N_EXPORT string16 StripWrappingBidiControlCharacters( + const string16& text) WARN_UNUSED_RESULT; + +} // namespace i18n +} // namespace base + +#endif // BASE_I18N_RTL_H_ diff --git a/base/message_loop/message_pump_android.cc b/base/message_loop/message_pump_android.cc index a0eee12..bedb205 100644 --- a/base/message_loop/message_pump_android.cc +++ b/base/message_loop/message_pump_android.cc @@ -6,14 +6,17 @@ #include <jni.h> +#include "base/android/java_message_handler_factory.h" #include "base/android/jni_android.h" #include "base/android/scoped_java_ref.h" #include "base/lazy_instance.h" #include "base/logging.h" +#include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/time/time.h" #include "jni/SystemMessageHandler_jni.h" +using base::android::JavaParamRef; using base::android::ScopedJavaLocalRef; // ---------------------------------------------------------------------------- @@ -24,10 +27,14 @@ using base::android::ScopedJavaLocalRef; static void DoRunLoopOnce(JNIEnv* env, const JavaParamRef<jobject>& obj, jlong native_delegate, + jlong native_message_pump, jlong delayed_scheduled_time_ticks) { base::MessagePump::Delegate* delegate = reinterpret_cast<base::MessagePump::Delegate*>(native_delegate); DCHECK(delegate); + base::MessagePumpForUI* pump = + reinterpret_cast<base::MessagePumpForUI*>(native_message_pump); + DCHECK(pump); // This is based on MessagePumpForUI::DoRunLoop() from desktop. // Note however that our system queue is handled in the java side. // In desktop we inspect and process a single system message and then @@ -35,6 +42,11 @@ static void DoRunLoopOnce(JNIEnv* env, // On Android, the java message queue may contain messages for other handlers // that will be processed before calling here again. bool did_work = delegate->DoWork(); + if (pump->ShouldAbort()) { + // There is a pending JNI exception, return to Java so that the exception is + // thrown correctly. + return; + } // In the java side, |SystemMessageHandler| keeps a single "delayed" message. // It's an expensive operation to |removeMessage| there, so this is optimized @@ -60,6 +72,11 @@ static void DoRunLoopOnce(JNIEnv* env, // avoid comparisons with TimeDelta / Now() (expensive). base::TimeTicks next_delayed_work_time; did_work |= delegate->DoDelayedWork(&next_delayed_work_time); + if (pump->ShouldAbort()) { + // There is a pending JNI exception, return to Java so that the exception is + // thrown correctly + return; + } if (!next_delayed_work_time.is_null()) { // Schedule a new message if there's nothing already scheduled or there's a @@ -81,13 +98,16 @@ static void DoRunLoopOnce(JNIEnv* env, return; delegate->DoIdleWork(); + // Note that we do not check whether we should abort here since we are + // returning to the JVM anyway. If, in the future, we add any more code after + // the call to DoIdleWork() here, we should add an abort-check and return + // immediately if the check passes. } namespace base { MessagePumpForUI::MessagePumpForUI() - : run_loop_(NULL) { -} + : run_loop_(nullptr), should_abort_(false) {} MessagePumpForUI::~MessagePumpForUI() { } @@ -97,7 +117,7 @@ void MessagePumpForUI::Run(Delegate* delegate) { " test_stub_android.h"; } -void MessagePumpForUI::Start(Delegate* delegate) { +JNIEnv* MessagePumpForUI::StartInternal() { run_loop_ = new RunLoop(); // Since the RunLoop was just created above, BeforeRun should be guaranteed to // return true (it only returns false if the RunLoop has been Quit already). @@ -108,10 +128,23 @@ void MessagePumpForUI::Start(Delegate* delegate) { JNIEnv* env = base::android::AttachCurrentThread(); DCHECK(env); + return env; +} + +void MessagePumpForUI::Start(Delegate* delegate) { + JNIEnv* env = StartInternal(); + system_message_handler_obj_.Reset(Java_SystemMessageHandler_create( + env, reinterpret_cast<intptr_t>(delegate), + reinterpret_cast<intptr_t>(this))); +} +void MessagePumpForUI::StartForUnitTest( + Delegate* delegate, + base::android::JavaMessageHandlerFactory* factory, + WaitableEvent* test_done_event) { + JNIEnv* env = StartInternal(); system_message_handler_obj_.Reset( - Java_SystemMessageHandler_create( - env, reinterpret_cast<intptr_t>(delegate))); + factory->CreateMessageHandler(env, delegate, this, test_done_event)); } void MessagePumpForUI::Quit() { @@ -119,8 +152,8 @@ void MessagePumpForUI::Quit() { JNIEnv* env = base::android::AttachCurrentThread(); DCHECK(env); - Java_SystemMessageHandler_removeAllPendingMessages(env, - system_message_handler_obj_.obj()); + Java_SystemMessageHandler_removeAllPendingMessages( + env, system_message_handler_obj_); system_message_handler_obj_.Reset(); } @@ -137,8 +170,7 @@ void MessagePumpForUI::ScheduleWork() { JNIEnv* env = base::android::AttachCurrentThread(); DCHECK(env); - Java_SystemMessageHandler_scheduleWork(env, - system_message_handler_obj_.obj()); + Java_SystemMessageHandler_scheduleWork(env, system_message_handler_obj_); } void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { @@ -151,9 +183,9 @@ void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { (delayed_work_time - TimeTicks::Now()).InMillisecondsRoundedUp(); // Note that we're truncating to milliseconds as required by the java side, // even though delayed_work_time is microseconds resolution. - Java_SystemMessageHandler_scheduleDelayedWork(env, - system_message_handler_obj_.obj(), - delayed_work_time.ToInternalValue(), millis); + Java_SystemMessageHandler_scheduleDelayedWork( + env, system_message_handler_obj_, delayed_work_time.ToInternalValue(), + millis); } // static diff --git a/base/message_loop/message_pump_android.h b/base/message_loop/message_pump_android.h index 795bd5e..e4adaf6 100644 --- a/base/message_loop/message_pump_android.h +++ b/base/message_loop/message_pump_android.h @@ -15,8 +15,13 @@ namespace base { +namespace android { +class JavaMessageHandlerFactory; +} + class RunLoop; class TimeTicks; +class WaitableEvent; // This class implements a MessagePump needed for TYPE_UI MessageLoops on // OS_ANDROID platform. @@ -31,12 +36,25 @@ class BASE_EXPORT MessagePumpForUI : public MessagePump { void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; virtual void Start(Delegate* delegate); + void StartForUnitTest(Delegate* delegate, + base::android::JavaMessageHandlerFactory* factory, + WaitableEvent* test_done_event); + + // We call Abort when there is a pending JNI exception, meaning that the + // current thread will crash when we return to Java. + // We can't call any JNI-methods before returning to Java as we would then + // cause a native crash (instead of the original Java crash). + void Abort() { should_abort_ = true; } + bool ShouldAbort() const { return should_abort_; } static bool RegisterBindings(JNIEnv* env); private: + JNIEnv* StartInternal(); + RunLoop* run_loop_; base::android::ScopedJavaGlobalRef<jobject> system_message_handler_obj_; + bool should_abort_; DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI); }; diff --git a/base/path_service.cc b/base/path_service.cc index 3f954d7..1b9d394 100644 --- a/base/path_service.cc +++ b/base/path_service.cc @@ -13,7 +13,6 @@ #include "base/containers/hash_tables.h" #include "base/files/file_path.h" #include "base/files/file_util.h" -#include "base/lazy_instance.h" #include "base/logging.h" #include "base/synchronization/lock.h" #include "build/build_config.h" @@ -129,10 +128,9 @@ struct PathData { } }; -static LazyInstance<PathData>::Leaky g_path_data = LAZY_INSTANCE_INITIALIZER; - static PathData* GetPathData() { - return g_path_data.Pointer(); + static auto* path_data = new PathData(); + return path_data; } // Tries to find |key| in the cache. |path_data| should be locked by the caller! diff --git a/base/threading/thread_local_android.cc b/base/threading/thread_local_android.cc deleted file mode 100644 index 813dd78..0000000 --- a/base/threading/thread_local_android.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/threading/thread_local.h" - -namespace base { -namespace internal { - -// static -void ThreadLocalPlatform::AllocateSlot(SlotType* slot) { - slot->Initialize(nullptr); -} - -// static -void ThreadLocalPlatform::FreeSlot(SlotType slot) { - slot.Free(); -} - -// static -void* ThreadLocalPlatform::GetValueFromSlot(SlotType slot) { - return slot.Get(); -} - -// static -void ThreadLocalPlatform::SetValueInSlot(SlotType slot, void* value) { - slot.Set(value); -} - -} // namespace internal -} // namespace base diff --git a/base/trace_event/trace_event_android.cc b/base/trace_event/trace_event_android.cc index a28c54a..0a4e6ea 100644 --- a/base/trace_event/trace_event_android.cc +++ b/base/trace_event/trace_event_android.cc @@ -13,6 +13,7 @@ #include "base/posix/eintr_wrapper.h" #include "base/strings/stringprintf.h" #include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" #include "base/trace_event/trace_event.h" namespace base { diff --git a/base/unguessable_token.cc b/base/unguessable_token.cc new file mode 100644 index 0000000..cd9830e --- /dev/null +++ b/base/unguessable_token.cc @@ -0,0 +1,41 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/unguessable_token.h" + +#include "base/format_macros.h" +#include "base/rand_util.h" +#include "base/strings/stringprintf.h" + +namespace base { + +UnguessableToken::UnguessableToken(uint64_t high, uint64_t low) + : high_(high), low_(low) {} + +std::string UnguessableToken::ToString() const { + return base::StringPrintf("(%08" PRIX64 "%08" PRIX64 ")", high_, low_); +} + +// static +UnguessableToken UnguessableToken::Create() { + UnguessableToken token; + // Use base::RandBytes instead of crypto::RandBytes, because crypto calls the + // base version directly, and to prevent the dependency from base/ to crypto/. + base::RandBytes(&token, sizeof(token)); + return token; +} + +// static +UnguessableToken UnguessableToken::Deserialize(uint64_t high, uint64_t low) { + // Receiving a zeroed out UnguessableToken from another process means that it + // was never initialized via Create(). Treat this case as a security issue. + DCHECK(!(high == 0 && low == 0)); + return UnguessableToken(high, low); +} + +std::ostream& operator<<(std::ostream& out, const UnguessableToken& token) { + return out << token.ToString(); +} + +} // namespace base diff --git a/base/unguessable_token.h b/base/unguessable_token.h new file mode 100644 index 0000000..9f38783 --- /dev/null +++ b/base/unguessable_token.h @@ -0,0 +1,103 @@ +// 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 BASE_UNGUESSABLE_TOKEN_H_ +#define BASE_UNGUESSABLE_TOKEN_H_ + +#include <stdint.h> +#include <string.h> +#include <iosfwd> +#include <tuple> + +#include "base/base_export.h" +#include "base/hash.h" +#include "base/logging.h" + +namespace base { + +struct UnguessableTokenHash; + +// A UnguessableToken is an 128-bit token generated from a cryptographically +// strong random source. +// +// UnguessableToken should be used when a sensitive ID needs to be unguessable, +// and is shared across processes. It can be used as part of a larger aggregate +// type, or as an ID in and of itself. +// +// Use Create() for creating new UnguessableTokens. +// +// NOTE: It is illegal to send empty UnguessableTokens across processes, and +// sending/receiving empty tokens should be treated as a security issue. +// If there is a valid scenario for sending "no token" across processes, +// base::Optional should be used instead of an empty token. +class BASE_EXPORT UnguessableToken { + public: + // Create a unique UnguessableToken. + static UnguessableToken Create(); + + // Return a UnguessableToken built from the high/low bytes provided. + // It should only be used in deserialization scenarios. + // + // NOTE: If the deserialized token is empty, it means that it was never + // initialized via Create(). This is a security issue, and should be handled. + static UnguessableToken Deserialize(uint64_t high, uint64_t low); + + // Creates an empty UnguessableToken. + // Assign to it with Create() before using it. + constexpr UnguessableToken() = default; + + // NOTE: Serializing an empty UnguessableToken is an illegal operation. + uint64_t GetHighForSerialization() const { + DCHECK(!is_empty()); + return high_; + }; + + // NOTE: Serializing an empty UnguessableToken is an illegal operation. + uint64_t GetLowForSerialization() const { + DCHECK(!is_empty()); + return low_; + } + + bool is_empty() const { return high_ == 0 && low_ == 0; } + + std::string ToString() const; + + explicit operator bool() const { return !is_empty(); } + + bool operator<(const UnguessableToken& other) const { + return std::tie(high_, low_) < std::tie(other.high_, other.low_); + } + + bool operator==(const UnguessableToken& other) const { + return high_ == other.high_ && low_ == other.low_; + } + + bool operator!=(const UnguessableToken& other) const { + return !(*this == other); + } + + private: + friend struct UnguessableTokenHash; + UnguessableToken(uint64_t high, uint64_t low); + + // Note: Two uint64_t are used instead of uint8_t[16], in order to have a + // simpler ToString() and is_empty(). + uint64_t high_ = 0; + uint64_t low_ = 0; +}; + +BASE_EXPORT std::ostream& operator<<(std::ostream& out, + const UnguessableToken& token); + +// For use in std::unordered_map. +struct UnguessableTokenHash { + size_t operator()(const base::UnguessableToken& token) const { + DCHECK(token); + return base::HashInts64(token.high_, token.low_); + } +}; + +} // namespace base + +#endif // BASE_UNGUESSABLE_TOKEN_H_ diff --git a/build/android/gyp/util/build_utils.py b/build/android/gyp/util/build_utils.py index 371ad90..7b2f48d 100644 --- a/build/android/gyp/util/build_utils.py +++ b/build/android/gyp/util/build_utils.py @@ -23,12 +23,16 @@ import md5_check # pylint: disable=relative-import sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)) from pylib.constants import host_paths +sys.path.append(os.path.join(os.path.dirname(__file__), + os.pardir, os.pardir, os.pardir)) +import gn_helpers + COLORAMA_ROOT = os.path.join(host_paths.DIR_SOURCE_ROOT, 'third_party', 'colorama', 'src') # aapt should ignore OWNERS files in addition the default ignore pattern. AAPT_IGNORE_PATTERN = ('!OWNERS:!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:' + '!CVS:!thumbs.db:!picasa.ini:!*~:!*.d.stamp') -_HERMETIC_TIMESTAMP = (2001, 1, 1, 0, 0, 0) +HERMETIC_TIMESTAMP = (2001, 1, 1, 0, 0, 0) _HERMETIC_FILE_ATTR = (0644 << 16L) @@ -78,31 +82,23 @@ def FindInDirectories(directories, filename_filter): def ParseGnList(gn_string): - # TODO(brettw) bug 573132: This doesn't handle GN escaping properly, so any - # weird characters like $ or \ in the strings will be corrupted. - # - # The code should import build/gn_helpers.py and then do: - # parser = gn_helpers.GNValueParser(gn_string) - # return return parser.ParseList() - # As of this writing, though, there is a CastShell build script that sends - # JSON through this function, and using correct GN parsing corrupts that. - # - # We need to be consistent about passing either JSON or GN lists through - # this function. - return ast.literal_eval(gn_string) - - -def ParseGypList(gyp_string): - # The ninja generator doesn't support $ in strings, so use ## to - # represent $. - # TODO(cjhopman): Remove when - # https://code.google.com/p/gyp/issues/detail?id=327 - # is addressed. - gyp_string = gyp_string.replace('##', '$') - - if gyp_string.startswith('['): - return ParseGnList(gyp_string) - return shlex.split(gyp_string) + """Converts a command-line parameter into a list. + + If the input starts with a '[' it is assumed to be a GN-formatted list and + it will be parsed accordingly. When empty an empty list will be returned. + Otherwise, the parameter will be treated as a single raw string (not + GN-formatted in that it's not assumed to have literal quotes that must be + removed) and a list will be returned containing that string. + + The common use for this behavior is in the Android build where things can + take lists of @FileArg references that are expanded via ExpandFileArgs. + """ + if gn_string.startswith('['): + parser = gn_helpers.GNValueParser(gn_string) + return parser.ParseList() + if len(gn_string): + return [ gn_string ] + return [] def CheckOptions(options, parser, required=None): @@ -228,6 +224,7 @@ def ExtractAll(zip_path, path=None, no_clobber=True, pattern=None, if not zipfile.is_zipfile(zip_path): raise Exception('Invalid zip file: %s' % zip_path) + extracted = [] with zipfile.ZipFile(zip_path) as z: for name in z.namelist(): if name.endswith('/'): @@ -248,8 +245,12 @@ def ExtractAll(zip_path, path=None, no_clobber=True, pattern=None, dest = os.path.join(path, name) MakeDirectory(os.path.dirname(dest)) os.symlink(z.read(name), dest) + extracted.append(dest) else: z.extract(name, path) + extracted.append(os.path.join(path, name)) + + return extracted def AddToZipHermetic(zip_file, zip_path, src_path=None, data=None, @@ -267,7 +268,7 @@ def AddToZipHermetic(zip_file, zip_path, src_path=None, data=None, assert (src_path is None) != (data is None), ( '|src_path| and |data| are mutually exclusive.') CheckZipPath(zip_path) - zipinfo = zipfile.ZipInfo(filename=zip_path, date_time=_HERMETIC_TIMESTAMP) + zipinfo = zipfile.ZipInfo(filename=zip_path, date_time=HERMETIC_TIMESTAMP) zipinfo.external_attr = _HERMETIC_FILE_ATTR if src_path and os.path.islink(src_path): @@ -332,7 +333,14 @@ def MergeZips(output, inputs, exclude_patterns=None, path_transform=None): path_transform = path_transform or (lambda p, z: p) added_names = set() - with zipfile.ZipFile(output, 'w') as out_zip: + output_is_already_open = not isinstance(output, basestring) + if output_is_already_open: + assert isinstance(output, zipfile.ZipFile) + out_zip = output + else: + out_zip = zipfile.ZipFile(output, 'w') + + try: for in_file in inputs: with zipfile.ZipFile(in_file, 'r') as in_zip: in_zip._expected_crc = None @@ -343,8 +351,12 @@ def MergeZips(output, inputs, exclude_patterns=None, path_transform=None): dst_name = path_transform(info.filename, in_file) already_added = dst_name in added_names if not already_added and not MatchesGlob(dst_name, exclude_patterns): - AddToZipHermetic(out_zip, dst_name, data=in_zip.read(info)) + AddToZipHermetic(out_zip, dst_name, data=in_zip.read(info), + compress=info.compress_type != zipfile.ZIP_STORED) added_names.add(dst_name) + finally: + if not output_is_already_open: + out_zip.close() def PrintWarning(message): @@ -399,8 +411,7 @@ def GetPythonDependencies(): A path is assumed to be a "system" import if it is outside of chromium's src/. The paths will be relative to the current directory. """ - module_paths = (m.__file__ for m in sys.modules.itervalues() - if m is not None and hasattr(m, '__file__')) + module_paths = GetModulePaths() abs_module_paths = map(os.path.abspath, module_paths) @@ -417,6 +428,30 @@ def GetPythonDependencies(): return sorted(set(non_system_module_paths)) +def GetModulePaths(): + """Returns the paths to all of the modules in sys.modules.""" + ForceLazyModulesToLoad() + return (m.__file__ for m in sys.modules.itervalues() + if m is not None and hasattr(m, '__file__')) + + +def ForceLazyModulesToLoad(): + """Forces any lazily imported modules to fully load themselves. + + Inspecting the modules' __file__ attribute causes lazily imported modules + (e.g. from email) to get fully imported and update sys.modules. Iterate + over the values until sys.modules stabilizes so that no modules are missed. + """ + while True: + num_modules_before = len(sys.modules.keys()) + for m in sys.modules.values(): + if m is not None and hasattr(m, '__file__'): + _ = m.__file__ + num_modules_after = len(sys.modules.keys()) + if num_modules_before == num_modules_after: + break + + def AddDepfileOption(parser): # TODO(agrieve): Get rid of this once we've moved to argparse. if hasattr(parser, 'add_option'): @@ -424,14 +459,20 @@ def AddDepfileOption(parser): else: func = parser.add_argument func('--depfile', - help='Path to depfile. Must be specified as the action\'s first output.') - - -def WriteDepfile(path, dependencies): - with open(path, 'w') as depfile: - depfile.write(path) + help='Path to depfile (refer to `gn help depfile`)') + + +def WriteDepfile(depfile_path, first_gn_output, inputs=None, add_pydeps=True): + assert depfile_path != first_gn_output # http://crbug.com/646165 + inputs = inputs or [] + if add_pydeps: + inputs = GetPythonDependencies() + inputs + MakeDirectory(os.path.dirname(depfile_path)) + # Ninja does not support multiple outputs in depfiles. + with open(depfile_path, 'w') as depfile: + depfile.write(first_gn_output.replace(' ', '\\ ')) depfile.write(': ') - depfile.write(' '.join(dependencies)) + depfile.write(' '.join(i.replace(' ', '\\ ') for i in inputs)) depfile.write('\n') @@ -469,11 +510,25 @@ def ExpandFileArgs(args): for k in lookup_path[1:]: expansion = expansion[k] - new_args[i] = arg[:match.start()] + str(expansion) + # This should match ParseGNList. The output is either a GN-formatted list + # or a literal (with no quotes). + if isinstance(expansion, list): + new_args[i] = arg[:match.start()] + gn_helpers.ToGNString(expansion) + else: + new_args[i] = arg[:match.start()] + str(expansion) return new_args +def ReadSourcesList(sources_list_file_name): + """Reads a GN-written file containing list of file names and returns a list. + + Note that this function should not be used to parse response files. + """ + with open(sources_list_file_name) as f: + return [file_name.strip() for file_name in f] + + def CallAndWriteDepfileIfStale(function, options, record_path=None, input_paths=None, input_strings=None, output_paths=None, force=False, @@ -513,7 +568,8 @@ def CallAndWriteDepfileIfStale(function, options, record_path=None, all_depfile_deps = list(python_deps) if depfile_deps: all_depfile_deps.extend(depfile_deps) - WriteDepfile(options.depfile, all_depfile_deps) + WriteDepfile(options.depfile, output_paths[0], all_depfile_deps, + add_pydeps=False) if stamp_file: Touch(stamp_file) diff --git a/build/android/pylib/__init__.py b/build/android/pylib/__init__.py index 16ee312..b93eb4f 100644 --- a/build/android/pylib/__init__.py +++ b/build/android/pylib/__init__.py @@ -5,9 +5,27 @@ import os import sys -_DEVIL_PATH = os.path.abspath(os.path.join( - os.path.dirname(__file__), '..', '..', '..', 'third_party', 'catapult', - 'devil')) + +_CATAPULT_PATH = os.path.abspath(os.path.join( + os.path.dirname(__file__), '..', '..', '..', 'third_party', 'catapult')) + +_DEVIL_PATH = os.path.join(_CATAPULT_PATH, 'devil') + +_PYTRACE_PATH = os.path.join(_CATAPULT_PATH, 'common', 'py_trace_event') + +_PY_UTILS_PATH = os.path.join(_CATAPULT_PATH, 'common', 'py_utils') + +_TRACE2HTML_PATH = os.path.join(_CATAPULT_PATH, 'tracing') + if _DEVIL_PATH not in sys.path: sys.path.append(_DEVIL_PATH) + +if _PYTRACE_PATH not in sys.path: + sys.path.append(_PYTRACE_PATH) + +if _PY_UTILS_PATH not in sys.path: + sys.path.append(_PY_UTILS_PATH) + +if _TRACE2HTML_PATH not in sys.path: + sys.path.append(_TRACE2HTML_PATH) diff --git a/build/android/pylib/constants/__init__.py b/build/android/pylib/constants/__init__.py index 9b25dcd..916ee27 100644 --- a/build/android/pylib/constants/__init__.py +++ b/build/android/pylib/constants/__init__.py @@ -37,28 +37,28 @@ PACKAGE_INFO.update({ 'chromecast_shell': chrome.PackageInfo( 'com.google.android.apps.mediashell', 'com.google.android.apps.mediashell.MediaShellActivity', - '/data/local/tmp/castshell-command-line', + 'castshell-command-line', None), 'android_webview_shell': chrome.PackageInfo( 'org.chromium.android_webview.shell', 'org.chromium.android_webview.shell.AwShellActivity', - '/data/local/tmp/android-webview-command-line', + 'android-webview-command-line', None), 'gtest': chrome.PackageInfo( 'org.chromium.native_test', 'org.chromium.native_test.NativeUnitTestActivity', - '/data/local/tmp/chrome-native-tests-command-line', + 'chrome-native-tests-command-line', None), 'components_browsertests': chrome.PackageInfo( 'org.chromium.components_browsertests_apk', ('org.chromium.components_browsertests_apk' + '.ComponentsBrowserTestsActivity'), - '/data/local/tmp/chrome-native-tests-command-line', + 'chrome-native-tests-command-line', None), 'content_browsertests': chrome.PackageInfo( 'org.chromium.content_browsertests_apk', 'org.chromium.content_browsertests_apk.ContentBrowserTestsActivity', - '/data/local/tmp/chrome-native-tests-command-line', + 'chrome-native-tests-command-line', None), 'chromedriver_webview_shell': chrome.PackageInfo( 'org.chromium.chromedriver_webview_shell', @@ -96,7 +96,7 @@ DEVICE_PERF_OUTPUT_DIR = ( SCREENSHOTS_DIR = os.path.join(DIR_SOURCE_ROOT, 'out_screenshots') ANDROID_SDK_VERSION = version_codes.MARSHMALLOW -ANDROID_SDK_BUILD_TOOLS_VERSION = '23.0.1' +ANDROID_SDK_BUILD_TOOLS_VERSION = '24.0.2' ANDROID_SDK_ROOT = os.path.join(DIR_SOURCE_ROOT, 'third_party', 'android_tools', 'sdk') ANDROID_SDK_TOOLS = os.path.join(ANDROID_SDK_ROOT, @@ -140,9 +140,9 @@ PYTHON_UNIT_TEST_SUITES = { } LOCAL_MACHINE_TESTS = ['junit', 'python'] -VALID_ENVIRONMENTS = ['local', 'remote_device'] +VALID_ENVIRONMENTS = ['local'] VALID_TEST_TYPES = ['gtest', 'instrumentation', 'junit', 'linker', 'monkey', - 'perf', 'python', 'uirobot'] + 'perf', 'python'] VALID_DEVICE_TYPES = ['Android', 'iOS'] diff --git a/build/build_config.h b/build/build_config.h index c3d82d0..fd5489f 100644 --- a/build/build_config.h +++ b/build/build_config.h @@ -6,6 +6,7 @@ // Operating System: // OS_WIN / OS_MACOSX / OS_LINUX / OS_POSIX (MACOSX or LINUX) / // OS_NACL (NACL_SFI or NACL_NONSFI) / OS_NACL_SFI / OS_NACL_NONSFI +// OS_CHROMEOS is set by the build system // Compiler: // COMPILER_MSVC / COMPILER_GCC // Processor: @@ -48,9 +49,10 @@ #endif #elif defined(_WIN32) #define OS_WIN 1 -#define TOOLKIT_VIEWS 1 #elif defined(__FreeBSD__) #define OS_FREEBSD 1 +#elif defined(__NetBSD__) +#define OS_NETBSD 1 #elif defined(__OpenBSD__) #define OS_OPENBSD 1 #elif defined(__sun) @@ -67,15 +69,16 @@ // For access to standard BSD features, use OS_BSD instead of a // more specific macro. -#if defined(OS_FREEBSD) || defined(OS_OPENBSD) +#if defined(OS_FREEBSD) || defined(OS_NETBSD) || defined(OS_OPENBSD) #define OS_BSD 1 #endif // For access to standard POSIXish features, use OS_POSIX instead of a // more specific macro. #if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_FREEBSD) || \ - defined(OS_OPENBSD) || defined(OS_SOLARIS) || defined(OS_ANDROID) || \ - defined(OS_NACL) || defined(OS_QNX) + defined(OS_NETBSD) || defined(OS_OPENBSD) || defined(OS_SOLARIS) || \ + defined(OS_ANDROID) || defined(OS_OPENBSD) || defined(OS_SOLARIS) || \ + defined(OS_ANDROID) || defined(OS_NACL) || defined(OS_QNX) #define OS_POSIX 1 #endif @@ -108,6 +111,31 @@ #define ARCH_CPU_X86 1 #define ARCH_CPU_32_BITS 1 #define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__s390x__) +#define ARCH_CPU_S390_FAMILY 1 +#define ARCH_CPU_S390X 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#elif defined(__s390__) +#define ARCH_CPU_S390_FAMILY 1 +#define ARCH_CPU_S390 1 +#define ARCH_CPU_31_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#elif defined(__PPC64__) && defined(__BIG_ENDIAN__) +#define ARCH_CPU_PPC64_FAMILY 1 +#define ARCH_CPU_PPC64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#elif defined(__PPC64__) && defined(__LITTLE_ENDIAN__) +#define ARCH_CPU_PPC64_FAMILY 1 +#define ARCH_CPU_PPC64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__PPC__) +#define ARCH_CPU_PPC_FAMILY 1 +#define ARCH_CPU_PPC 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 #elif defined(__ARMEL__) #define ARCH_CPU_ARM_FAMILY 1 #define ARCH_CPU_ARMEL 1 diff --git a/build/gn_helpers.py b/build/gn_helpers.py new file mode 100644 index 0000000..33cc578 --- /dev/null +++ b/build/gn_helpers.py @@ -0,0 +1,351 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Helper functions useful when writing scripts that integrate with GN. + +The main functions are ToGNString and FromGNString which convert between +serialized GN veriables and Python variables. + +To use in a random python file in the build: + + import os + import sys + + sys.path.append(os.path.join(os.path.dirname(__file__), + os.pardir, os.pardir, "build")) + import gn_helpers + +Where the sequence of parameters to join is the relative path from your source +file to the build directory.""" + +class GNException(Exception): + pass + + +def ToGNString(value, allow_dicts = True): + """Returns a stringified GN equivalent of the Python value. + + allow_dicts indicates if this function will allow converting dictionaries + to GN scopes. This is only possible at the top level, you can't nest a + GN scope in a list, so this should be set to False for recursive calls.""" + if isinstance(value, basestring): + if value.find('\n') >= 0: + raise GNException("Trying to print a string with a newline in it.") + return '"' + \ + value.replace('\\', '\\\\').replace('"', '\\"').replace('$', '\\$') + \ + '"' + + if isinstance(value, unicode): + return ToGNString(value.encode('utf-8')) + + if isinstance(value, bool): + if value: + return "true" + return "false" + + if isinstance(value, list): + return '[ %s ]' % ', '.join(ToGNString(v) for v in value) + + if isinstance(value, dict): + if not allow_dicts: + raise GNException("Attempting to recursively print a dictionary.") + result = "" + for key in sorted(value): + if not isinstance(key, basestring): + raise GNException("Dictionary key is not a string.") + result += "%s = %s\n" % (key, ToGNString(value[key], False)) + return result + + if isinstance(value, int): + return str(value) + + raise GNException("Unsupported type when printing to GN.") + + +def FromGNString(input_string): + """Converts the input string from a GN serialized value to Python values. + + For details on supported types see GNValueParser.Parse() below. + + If your GN script did: + something = [ "file1", "file2" ] + args = [ "--values=$something" ] + The command line would look something like: + --values="[ \"file1\", \"file2\" ]" + Which when interpreted as a command line gives the value: + [ "file1", "file2" ] + + You can parse this into a Python list using GN rules with: + input_values = FromGNValues(options.values) + Although the Python 'ast' module will parse many forms of such input, it + will not handle GN escaping properly, nor GN booleans. You should use this + function instead. + + + A NOTE ON STRING HANDLING: + + If you just pass a string on the command line to your Python script, or use + string interpolation on a string variable, the strings will not be quoted: + str = "asdf" + args = [ str, "--value=$str" ] + Will yield the command line: + asdf --value=asdf + The unquoted asdf string will not be valid input to this function, which + accepts only quoted strings like GN scripts. In such cases, you can just use + the Python string literal directly. + + The main use cases for this is for other types, in particular lists. When + using string interpolation on a list (as in the top example) the embedded + strings will be quoted and escaped according to GN rules so the list can be + re-parsed to get the same result.""" + parser = GNValueParser(input_string) + return parser.Parse() + + +def FromGNArgs(input_string): + """Converts a string with a bunch of gn arg assignments into a Python dict. + + Given a whitespace-separated list of + + <ident> = (integer | string | boolean | <list of the former>) + + gn assignments, this returns a Python dict, i.e.: + + FromGNArgs("foo=true\nbar=1\n") -> { 'foo': True, 'bar': 1 }. + + Only simple types and lists supported; variables, structs, calls + and other, more complicated things are not. + + This routine is meant to handle only the simple sorts of values that + arise in parsing --args. + """ + parser = GNValueParser(input_string) + return parser.ParseArgs() + + +def UnescapeGNString(value): + """Given a string with GN escaping, returns the unescaped string. + + Be careful not to feed with input from a Python parsing function like + 'ast' because it will do Python unescaping, which will be incorrect when + fed into the GN unescaper.""" + result = '' + i = 0 + while i < len(value): + if value[i] == '\\': + if i < len(value) - 1: + next_char = value[i + 1] + if next_char in ('$', '"', '\\'): + # These are the escaped characters GN supports. + result += next_char + i += 1 + else: + # Any other backslash is a literal. + result += '\\' + else: + result += value[i] + i += 1 + return result + + +def _IsDigitOrMinus(char): + return char in "-0123456789" + + +class GNValueParser(object): + """Duplicates GN parsing of values and converts to Python types. + + Normally you would use the wrapper function FromGNValue() below. + + If you expect input as a specific type, you can also call one of the Parse* + functions directly. All functions throw GNException on invalid input. """ + def __init__(self, string): + self.input = string + self.cur = 0 + + def IsDone(self): + return self.cur == len(self.input) + + def ConsumeWhitespace(self): + while not self.IsDone() and self.input[self.cur] in ' \t\n': + self.cur += 1 + + def Parse(self): + """Converts a string representing a printed GN value to the Python type. + + See additional usage notes on FromGNString above. + + - GN booleans ('true', 'false') will be converted to Python booleans. + + - GN numbers ('123') will be converted to Python numbers. + + - GN strings (double-quoted as in '"asdf"') will be converted to Python + strings with GN escaping rules. GN string interpolation (embedded + variables preceeded by $) are not supported and will be returned as + literals. + + - GN lists ('[1, "asdf", 3]') will be converted to Python lists. + + - GN scopes ('{ ... }') are not supported.""" + result = self._ParseAllowTrailing() + self.ConsumeWhitespace() + if not self.IsDone(): + raise GNException("Trailing input after parsing:\n " + + self.input[self.cur:]) + return result + + def ParseArgs(self): + """Converts a whitespace-separated list of ident=literals to a dict. + + See additional usage notes on FromGNArgs, above. + """ + d = {} + + self.ConsumeWhitespace() + while not self.IsDone(): + ident = self._ParseIdent() + self.ConsumeWhitespace() + if self.input[self.cur] != '=': + raise GNException("Unexpected token: " + self.input[self.cur:]) + self.cur += 1 + self.ConsumeWhitespace() + val = self._ParseAllowTrailing() + self.ConsumeWhitespace() + d[ident] = val + + return d + + def _ParseAllowTrailing(self): + """Internal version of Parse that doesn't check for trailing stuff.""" + self.ConsumeWhitespace() + if self.IsDone(): + raise GNException("Expected input to parse.") + + next_char = self.input[self.cur] + if next_char == '[': + return self.ParseList() + elif _IsDigitOrMinus(next_char): + return self.ParseNumber() + elif next_char == '"': + return self.ParseString() + elif self._ConstantFollows('true'): + return True + elif self._ConstantFollows('false'): + return False + else: + raise GNException("Unexpected token: " + self.input[self.cur:]) + + def _ParseIdent(self): + ident = '' + + next_char = self.input[self.cur] + if not next_char.isalpha() and not next_char=='_': + raise GNException("Expected an identifier: " + self.input[self.cur:]) + + ident += next_char + self.cur += 1 + + next_char = self.input[self.cur] + while next_char.isalpha() or next_char.isdigit() or next_char=='_': + ident += next_char + self.cur += 1 + next_char = self.input[self.cur] + + return ident + + def ParseNumber(self): + self.ConsumeWhitespace() + if self.IsDone(): + raise GNException('Expected number but got nothing.') + + begin = self.cur + + # The first character can include a negative sign. + if not self.IsDone() and _IsDigitOrMinus(self.input[self.cur]): + self.cur += 1 + while not self.IsDone() and self.input[self.cur].isdigit(): + self.cur += 1 + + number_string = self.input[begin:self.cur] + if not len(number_string) or number_string == '-': + raise GNException("Not a valid number.") + return int(number_string) + + def ParseString(self): + self.ConsumeWhitespace() + if self.IsDone(): + raise GNException('Expected string but got nothing.') + + if self.input[self.cur] != '"': + raise GNException('Expected string beginning in a " but got:\n ' + + self.input[self.cur:]) + self.cur += 1 # Skip over quote. + + begin = self.cur + while not self.IsDone() and self.input[self.cur] != '"': + if self.input[self.cur] == '\\': + self.cur += 1 # Skip over the backslash. + if self.IsDone(): + raise GNException("String ends in a backslash in:\n " + + self.input) + self.cur += 1 + + if self.IsDone(): + raise GNException('Unterminated string:\n ' + self.input[begin:]) + + end = self.cur + self.cur += 1 # Consume trailing ". + + return UnescapeGNString(self.input[begin:end]) + + def ParseList(self): + self.ConsumeWhitespace() + if self.IsDone(): + raise GNException('Expected list but got nothing.') + + # Skip over opening '['. + if self.input[self.cur] != '[': + raise GNException("Expected [ for list but got:\n " + + self.input[self.cur:]) + self.cur += 1 + self.ConsumeWhitespace() + if self.IsDone(): + raise GNException("Unterminated list:\n " + self.input) + + list_result = [] + previous_had_trailing_comma = True + while not self.IsDone(): + if self.input[self.cur] == ']': + self.cur += 1 # Skip over ']'. + return list_result + + if not previous_had_trailing_comma: + raise GNException("List items not separated by comma.") + + list_result += [ self._ParseAllowTrailing() ] + self.ConsumeWhitespace() + if self.IsDone(): + break + + # Consume comma if there is one. + previous_had_trailing_comma = self.input[self.cur] == ',' + if previous_had_trailing_comma: + # Consume comma. + self.cur += 1 + self.ConsumeWhitespace() + + raise GNException("Unterminated list:\n " + self.input) + + def _ConstantFollows(self, constant): + """Returns true if the given constant follows immediately at the current + location in the input. If it does, the text is consumed and the function + returns true. Otherwise, returns false and the current position is + unchanged.""" + end = self.cur + len(constant) + if end > len(self.input): + return False # Not enough room. + if self.input[self.cur:end] == constant: + self.cur = end + return True + return False diff --git a/build_mojom.mk b/build_mojom.mk index 31f252e..2af9e49 100644 --- a/build_mojom.mk +++ b/build_mojom.mk @@ -19,10 +19,11 @@ mojom_file := $(1) local_path := $(LOCAL_PATH) target_path := $(generated_sources_dir) gen_cc := $$(target_path)/$$(mojom_file).cc +gen_shared_cc := $$(target_path)/$$(mojom_file)-shared.cc gen_h := $$(target_path)/$$(mojom_file).h gen_internal_h := $$(target_path)/$$(mojom_file)-internal.h gen_srcjar := $$(target_path)/$$(mojom_file).srcjar -gen_src := $$(gen_cc) $$(gen_h) $$(gen_internal_h) $$(gen_srcjar) +gen_src := $$(gen_cc) $$(gen_shared_cc) $$(gen_h) $$(gen_internal_h) $$(gen_srcjar) mojom_bindings_generator_flags := $$(LOCAL_MOJOM_BINDINGS_GENERATOR_FLAGS) # TODO(lhchavez): Generate these files instead of expecting them to be there. mojom_type_mappings := @@ -31,6 +32,7 @@ ifneq ($$(LOCAL_MOJOM_TYPE_MAPPINGS),) mojom_bindings_generator_flags += --typemap $$(abspath $$(mojom_type_mappings)) endif + $$(gen_cc) : PRIVATE_PATH := $$(local_path) $$(gen_cc) : PRIVATE_MOJO_ROOT := $$(LOCAL_MOJO_ROOT) $$(gen_cc) : PRIVATE_TARGET := $$(target_path) @@ -49,10 +51,30 @@ $$(gen_cc) : $$(local_path)/$$(mojom_file) $$(mojom_type_mappings) \ $$(MOJOM_TEMPLATE_TOOLS) $$(generated_templates_dir)/.stamp $$(transform-generated-source) -# Make the other generated files depend on the .cc file. Unfortunately, the +$$(gen_shared_cc) : PRIVATE_PATH := $$(local_path) +$$(gen_shared_cc) : PRIVATE_MOJO_ROOT := $$(LOCAL_MOJO_ROOT) +$$(gen_shared_cc) : PRIVATE_TARGET := $$(target_path) +$$(gen_shared_cc) : PRIVATE_FLAGS := $$(mojom_bindings_generator_flags) +$$(gen_shared_cc) : PRIVATE_CUSTOM_TOOL = \ + (cd $$(PRIVATE_PATH) && \ + python $$(abspath $$(MOJOM_BINDINGS_GENERATOR)) \ + --use_bundled_pylibs generate \ + $$(subst $$(PRIVATE_PATH)/,,$$<) \ + -I $$(abspath $$(PRIVATE_MOJO_ROOT)):$$(abspath $$(PRIVATE_MOJO_ROOT)) \ + -o $$(abspath $$(PRIVATE_TARGET)) \ + --bytecode_path $$(abspath $$(generated_templates_dir)) \ + --generate_non_variant_code \ + -g c++,java \ + $$(PRIVATE_FLAGS)) +$$(gen_shared_cc) : $$(local_path)/$$(mojom_file) $$(mojom_type_mappings) \ + $$(MOJOM_TEMPLATE_TOOLS) $$(generated_templates_dir)/.stamp + $$(transform-generated-source) + + +# Make the other generated files depend on the .cc files. Unfortunately, the # Make->ninja translation would generate one individual rule for each generated # file, resulting in the files being (racily) generated multiple times. -$$(gen_internal_h): $$(gen_cc) +$$(gen_internal_h): $$(gen_cc) $$(gen_shared_cc) $$(hide) touch $$@ $$(gen_h): $$(gen_cc) diff --git a/common.mk b/common.mk new file mode 100644 index 0000000..aed7d3e --- /dev/null +++ b/common.mk @@ -0,0 +1,934 @@ +# Copyright (c) 2012 The Chromium OS 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 this file is part of another source distribution, it's license may be +# stored in LICENSE.makefile or LICENSE.common.mk. +# +# NOTE NOTE NOTE +# The authoritative common.mk is located in: +# https://chromium.googlesource.com/chromiumos/platform2/+/master/common-mk +# Please make all changes there, then copy into place in other repos. +# NOTE NOTE NOTE +# +# This file provides a common architecture for building C/C++ source trees. +# It uses recursive makefile inclusion to create a single make process which +# can be built in the source tree or with the build artifacts placed elsewhere. +# +# It is fully parallelizable for all targets, including static archives. +# +# To use: +# 1. Place common.mk in your top source level +# 2. In your top-level Makefile, place "include common.mk" at the top +# 3. In all subdirectories, create a 'module.mk' file that starts with: +# include common.mk +# And then contains the remainder of your targets. +# 4. All build targets should look like: +# relative/path/target: relative/path/obj.o +# +# See existing makefiles for rule examples. +# +# Exported macros: +# - cc_binary, cxx_binary provide standard compilation steps for binaries +# - cxx_library, cc_library provide standard compilation steps for +# shared objects. +# All of the above optionally take an argument for extra flags. +# - update_archive creates/updates a given .a target +# +# Instead of using the build macros, most users can just use wrapped targets: +# - CXX_BINARY, CC_BINARY, CC_STATIC_BINARY, CXX_STATIC_BINARY +# - CXX_LIBRARY, CC_LIBRARY, CC_STATIC_LIBRARY, CXX_STATIC_LIBRARY +# - E.g., CXX_BINARY(mahbinary): foo.o +# - object.depends targets may be used when a prerequisite is required for an +# object file. Because object files result in multiple build artifacts to +# handle PIC and PIE weirdness. E.g. +# foo.o.depends: generated/dbus.h +# - TEST(binary) or TEST(CXX_BINARY(binary)) may be used as a prerequisite +# for the tests target to trigger an automated test run. +# - CLEAN(file_or_dir) dependency can be added to 'clean'. +# +# If source code is being generated, rules will need to be registered for +# compiling the objects. This can be done by adding one of the following +# to the Makefile: +# - For C source files +# $(eval $(call add_object_rules,sub/dir/gen_a.o sub/dir/b.o,CC,c,CFLAGS)) +# - For C++ source files +# $(eval $(call add_object_rules,sub/dir/gen_a.o sub/dir/b.o,CXX,cc,CXXFLAGS)) +# +# Exported targets meant to have prerequisites added to: +# - all - Your desired targets should be given +# - tests - Any TEST(test_binary) targets should be given +# - FORCE - force the given target to run regardless of changes +# In most cases, using .PHONY is preferred. +# +# Possible command line variables: +# - COLOR=[0|1] to set ANSI color output (default: 1) +# - VERBOSE=[0|1] to hide/show commands (default: 0) +# - MODE=[opt|dbg|profiling] (default: opt) +# opt - Enable optimizations for release builds +# dbg - Turn down optimization for debugging +# profiling - Turn off optimization and turn on profiling/coverage +# support. +# - ARCH=[x86|arm|supported qemu name] (default: from portage or uname -m) +# - SPLITDEBUG=[0|1] splits debug info in target.debug (default: 0) +# If NOSTRIP=1, SPLITDEBUG will never strip the final emitted objects. +# - NOSTRIP=[0|1] determines if binaries are stripped. (default: 1) +# NOSTRIP=0 and MODE=opt will also drop -g from the CFLAGS. +# - VALGRIND=[0|1] runs tests under valgrind (default: 0) +# - OUT=/path/to/builddir puts all output in given path (default: $PWD) +# - VALGRIND_ARGS="" supplies extra memcheck arguments +# +# Per-target(-ish) variable: +# - NEEDS_ROOT=[0|1] allows a TEST() target to run with root. +# Default is 0 unless it is running under QEmu. +# - NEEDS_MOUNTS=[0|1] allows a TEST() target running on QEmu to get +# setup mounts in the $(SYSROOT) +# +# Caveats: +# - Directories or files with spaces in them DO NOT get along with GNU Make. +# If you need them, all uses of dir/notdir/etc will need to have magic +# wrappers. Proceed at risk to your own sanity. +# - External CXXFLAGS and CFLAGS should be passed via the environment since +# this file does not use 'override' to control them. +# - Our version of GNU Make doesn't seem to support the 'private' variable +# annotation, so you can't tag a variable private on a wrapping target. + +# Behavior configuration variables +SPLITDEBUG ?= 0 +NOSTRIP ?= 1 +VALGRIND ?= 0 +COLOR ?= 1 +VERBOSE ?= 0 +MODE ?= opt +CXXEXCEPTIONS ?= 0 +ARCH ?= $(shell uname -m) + +# Put objects in a separate tree based on makefile locations +# This means you can build a tree without touching it: +# make -C $SRCDIR # will create ./build-$(MODE) +# Or +# make -C $SRCDIR OUT=$PWD +# This variable is extended on subdir calls and doesn't need to be re-called. +OUT ?= $(PWD)/ + +# Make OUT now so we can use realpath. +$(shell mkdir -p "$(OUT)") + +# TODO(wad) Relative paths are resolved against SRC and not the calling dir. +# Ensure a command-line supplied OUT has a slash +override OUT := $(realpath $(OUT))/ + +# SRC is not meant to be set by the end user, but during make call relocation. +# $(PWD) != $(CURDIR) all the time. +export SRC ?= $(CURDIR) + +# Re-start in the $(OUT) directory if we're not there. +# We may be invoked using -C or bare and we need to ensure behavior +# is consistent so we check both PWD vs OUT and PWD vs CURDIR. +override RELOCATE_BUILD := 0 +ifneq (${PWD}/,${OUT}) +override RELOCATE_BUILD := 1 +endif +# Make sure we're running with no builtin targets. They cause +# leakage and mayhem! +ifneq (${PWD},${CURDIR}) +override RELOCATE_BUILD := 1 +# If we're run from the build dir, don't let it get cleaned up later. +ifeq (${PWD}/,${OUT}) +$(shell touch "$(PWD)/.dont_delete_on_clean") +endif +endif # ifneq (${PWD},${CURDIR} + +# "Relocate" if we need to restart without implicit rules. +ifeq ($(subst r,,$(MAKEFLAGS)),$(MAKEFLAGS)) +override RELOCATE_BUILD := 1 +endif + +ifeq (${RELOCATE_BUILD},1) +# By default, silence build output. Reused below as well. +QUIET = @ +ifeq ($(VERBOSE),1) + QUIET= +endif + +# This target will override all targets, including prerequisites. To avoid +# calling $(MAKE) once per prereq on the given CMDGOAL, we guard it with a local +# variable. +RUN_ONCE := 0 +MAKECMDGOALS ?= all +# Keep the rules split as newer make does not allow them to be declared +# on the same line. But the way :: rules work, the _all here will also +# invoke the %:: rule while retaining "_all" as the default. +_all:: +%:: + $(if $(filter 0,$(RUN_ONCE)), \ + cd "$(OUT)" && \ + $(MAKE) -r -I "$(SRC)" -f "$(CURDIR)/Makefile" \ + SRC="$(CURDIR)" OUT="$(OUT)" $(foreach g,$(MAKECMDGOALS),"$(g)"),) + $(eval RUN_ONCE := 1) +pass-to-subcall := 1 +endif + +ifeq ($(pass-to-subcall),) + +# Only call MODULE if we're in a submodule +MODULES_LIST := $(filter-out Makefile %.d,$(MAKEFILE_LIST)) +ifeq ($(words $(filter-out Makefile common.mk %.d $(SRC)/Makefile \ + $(SRC)/common.mk,$(MAKEFILE_LIST))),0) + +# All the top-level defines outside of module.mk. + +# +# Helper macros +# + +# Create the directory if it doesn't yet exist. +define auto_mkdir + $(if $(wildcard $(dir $1)),$2,$(QUIET)mkdir -p "$(dir $1)") +endef + +# Creates the actual archive with an index. +# The target $@ must end with .pic.a or .pie.a. +define update_archive + $(call auto_mkdir,$(TARGET_OR_MEMBER)) + $(QUIET)# Create the archive in one step to avoid parallel use accessing it + $(QUIET)# before all the symbols are present. + @$(ECHO) "AR $(subst \ +$(SRC)/,,$(^:.o=$(suffix $(basename $(TARGET_OR_MEMBER))).o)) \ +-> $(subst $(SRC)/,,$(TARGET_OR_MEMBER))" + $(QUIET)$(AR) rcs $(TARGET_OR_MEMBER) \ + $(subst $(SRC)/,,$(^:.o=$(suffix $(basename $(TARGET_OR_MEMBER))).o)) +endef + +# Default compile from objects using pre-requisites but filters out +# subdirs and .d files. +define cc_binary + $(call COMPILE_BINARY_implementation,CC,$(CFLAGS) $(1),$(EXTRA_FLAGS)) +endef + +define cxx_binary + $(call COMPILE_BINARY_implementation,CXX,$(CXXFLAGS) $(1),$(EXTRA_FLAGS)) +endef + +# Default compile from objects using pre-requisites but filters out +# subdirs and .d files. +define cc_library + $(call COMPILE_LIBRARY_implementation,CC,$(CFLAGS) $(1),$(EXTRA_FLAGS)) +endef +define cxx_library + $(call COMPILE_LIBRARY_implementation,CXX,$(CXXFLAGS) $(1),$(EXTRA_FLAGS)) +endef + +# Deletes files silently if they exist. Meant for use in any local +# clean targets. +define silent_rm + $(if $(wildcard $(1)), + $(QUIET)($(ECHO) -n '$(COLOR_RED)CLEANFILE$(COLOR_RESET) ' && \ + $(ECHO) '$(subst $(OUT)/,,$(wildcard $(1)))' && \ + $(RM) $(1) 2>/dev/null) || true,) +endef +define silent_rmdir + $(if $(wildcard $(1)), + $(if $(wildcard $(1)/*), + $(QUIET)# $(1) not empty [$(wildcard $(1)/*)]. Not deleting., + $(QUIET)($(ECHO) -n '$(COLOR_RED)CLEANDIR$(COLOR_RESET) ' && \ + $(ECHO) '$(subst $(OUT)/,,$(wildcard $(1)))' && \ + $(RMDIR) $(1) 2>/dev/null) || true),) +endef + +# +# Default variable values +# + +# Only override toolchain vars if they are from make. +CROSS_COMPILE ?= +define override_var +ifneq ($(filter undefined default,$(origin $1)),) +$1 = $(CROSS_COMPILE)$2 +endif +endef +$(eval $(call override_var,AR,ar)) +$(eval $(call override_var,CC,gcc)) +$(eval $(call override_var,CXX,g++)) +$(eval $(call override_var,OBJCOPY,objcopy)) +$(eval $(call override_var,PKG_CONFIG,pkg-config)) +$(eval $(call override_var,RANLIB,ranlib)) +$(eval $(call override_var,STRIP,strip)) + +RMDIR ?= rmdir +ECHO = /bin/echo -e + +ifeq ($(lastword $(subst /, ,$(CC))),clang) +CDRIVER = clang +else +CDRIVER = gcc +endif + +ifeq ($(lastword $(subst /, ,$(CXX))),clang++) +CXXDRIVER = clang +else +CXXDRIVER = gcc +endif + +# Internal macro to support check_XXX macros below. +# Usage: $(call check_compile, [code], [compiler], [code_type], [c_flags], +# [extra_c_flags], [library_flags], [success_ret], [fail_ret]) +# Return: [success_ret] if compile succeeded, otherwise [fail_ret] +check_compile = $(shell printf '%b\n' $(1) | \ + $($(2)) $($(4)) -x $(3) $(LDFLAGS) $(5) - $(6) -o /dev/null > /dev/null 2>&1 \ + && echo "$(7)" || echo "$(8)") + +# Helper macro to check whether a test program will compile with the specified +# compiler flags. +# Usage: $(call check_compile_cc, [code], [flags], [alternate_flags]) +# Return: [flags] if compile succeeded, otherwise [alternate_flags] +check_compile_cc = $(call check_compile,$(1),CC,c,CFLAGS,$(2),,$(2),$(3)) +check_compile_cxx = $(call check_compile,$(1),CXX,c++,CXXFLAGS,$(2),,$(2),$(3)) + +# Helper macro to check whether a test program will compile with the specified +# libraries. +# Usage: $(call check_compile_cc, [code], [library_flags], [alternate_flags]) +# Return: [library_flags] if compile succeeded, otherwise [alternate_flags] +check_libs_cc = $(call check_compile,$(1),CC,c,CFLAGS,,$(2),$(2),$(3)) +check_libs_cxx = $(call check_compile,$(1),CXX,c++,CXXFLAGS,,$(2),$(2),$(3)) + +# Helper macro to check whether the compiler accepts the specified flags. +# Usage: $(call check_compile_cc, [flags], [alternate_flags]) +# Return: [flags] if compile succeeded, otherwise [alternate_flags] +check_cc = $(call check_compile_cc,'int main() { return 0; }',$(1),$(2)) +check_cxx = $(call check_compile_cxx,'int main() { return 0; }',$(1),$(2)) + +# Choose the stack protector flags based on whats supported by the compiler. +SSP_CFLAGS := $(call check_cc,-fstack-protector-strong) +ifeq ($(SSP_CFLAGS),) + SSP_CFLAGS := $(call check_cc,-fstack-protector-all) +endif + +# To update these from an including Makefile: +# CXXFLAGS += -mahflag # Append to the list +# CXXFLAGS := -mahflag $(CXXFLAGS) # Prepend to the list +# CXXFLAGS := $(filter-out badflag,$(CXXFLAGS)) # Filter out a value +# The same goes for CFLAGS. +COMMON_CFLAGS-gcc := -fvisibility=internal -ggdb3 -Wa,--noexecstack +COMMON_CFLAGS-clang := -fvisibility=hidden -ggdb +COMMON_CFLAGS := -Wall -Werror -fno-strict-aliasing $(SSP_CFLAGS) -O1 -Wformat=2 +CXXFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CXXDRIVER)) +CFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CDRIVER)) +CPPFLAGS += -D_FORTIFY_SOURCE=2 + +# Enable large file support. +CPPFLAGS += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE + +# Disable exceptions based on the CXXEXCEPTIONS setting. +ifeq ($(CXXEXCEPTIONS),0) + CXXFLAGS := $(CXXFLAGS) -fno-exceptions -fno-unwind-tables \ + -fno-asynchronous-unwind-tables +endif + +ifeq ($(MODE),opt) + # Up the optimizations. + CFLAGS := $(filter-out -O1,$(CFLAGS)) -O2 + CXXFLAGS := $(filter-out -O1,$(CXXFLAGS)) -O2 + # Only drop -g* if symbols aren't desired. + ifeq ($(NOSTRIP),0) + # TODO: do we want -fomit-frame-pointer on x86? + CFLAGS := $(filter-out -ggdb3,$(CFLAGS)) + CXXFLAGS := $(filter-out -ggdb3,$(CXXFLAGS)) + endif +endif + +ifeq ($(MODE),profiling) + CFLAGS := $(CFLAGS) -O0 -g --coverage + CXXFLAGS := $(CXXFLAGS) -O0 -g --coverage + LDFLAGS := $(LDFLAGS) --coverage +endif + +LDFLAGS := $(LDFLAGS) -Wl,-z,relro -Wl,-z,noexecstack -Wl,-z,now + +# Fancy helpers for color if a prompt is defined +ifeq ($(COLOR),1) +COLOR_RESET = \x1b[0m +COLOR_GREEN = \x1b[32;01m +COLOR_RED = \x1b[31;01m +COLOR_YELLOW = \x1b[33;01m +endif + +# By default, silence build output. +QUIET = @ +ifeq ($(VERBOSE),1) + QUIET= +endif + +# +# Implementation macros for compile helpers above +# + +# Useful for dealing with pie-broken toolchains. +# Call make with PIE=0 to disable default PIE use. +OBJ_PIE_FLAG = -fPIE +COMPILE_PIE_FLAG = -pie +ifeq ($(PIE),0) + OBJ_PIE_FLAG = + COMPILE_PIE_FLAG = +endif + +# Favor member targets first for CXX_BINARY(%) magic. +# And strip out nested members if possible. +LP := ( +RP := ) +TARGET_OR_MEMBER = $(lastword $(subst $(LP), ,$(subst $(RP),,$(or $%,$@)))) + +# Default compile from objects using pre-requisites but filters out +# all non-.o files. +define COMPILE_BINARY_implementation + @$(ECHO) "LD$(1) $(subst $(PWD)/,,$(TARGET_OR_MEMBER))" + $(call auto_mkdir,$(TARGET_OR_MEMBER)) + $(QUIET)$($(1)) $(COMPILE_PIE_FLAGS) -o $(TARGET_OR_MEMBER) \ + $(2) $(LDFLAGS) \ + $(filter %.o %.a,$(^:.o=.pie.o)) \ + $(foreach so,$(filter %.so,$^),-L$(dir $(so)) \ + -l$(patsubst lib%,%,$(basename $(notdir $(so))))) \ + $(LDLIBS) + $(call conditional_strip) + @$(ECHO) -n "BIN " + @$(ECHO) "$(COLOR_GREEN)$(subst $(PWD)/,,$(TARGET_OR_MEMBER))$(COLOR_RESET)" + @$(ECHO) " $(COLOR_YELLOW)-----$(COLOR_RESET)" +endef + +# TODO: add version support extracted from PV environment variable +#ifeq ($(PV),9999) +#$(warning PV=$(PV). If shared object versions matter, please force PV=.) +#endif +# Then add -Wl,-soname,$@.$(PV) ? + +# Default compile from objects using pre-requisites but filters out +# all non-.o values. (Remember to add -L$(OUT) -llib) +COMMA := , +define COMPILE_LIBRARY_implementation + @$(ECHO) "SHARED$(1) $(subst $(PWD)/,,$(TARGET_OR_MEMBER))" + $(call auto_mkdir,$(TARGET_OR_MEMBER)) + $(QUIET)$($(1)) -shared -Wl,-E -o $(TARGET_OR_MEMBER) \ + $(2) $(LDFLAGS) \ + $(if $(filter %.a,$^),-Wl$(COMMA)--whole-archive,) \ + $(filter %.o ,$(^:.o=.pic.o)) \ + $(foreach a,$(filter %.a,$^),-L$(dir $(a)) \ + -l$(patsubst lib%,%,$(basename $(notdir $(a))))) \ + $(foreach so,$(filter %.so,$^),-L$(dir $(so)) \ + -l$(patsubst lib%,%,$(basename $(notdir $(so))))) \ + $(LDLIBS) + $(call conditional_strip) + @$(ECHO) -n "LIB $(COLOR_GREEN)" + @$(ECHO) "$(subst $(PWD)/,,$(TARGET_OR_MEMBER))$(COLOR_RESET)" + @$(ECHO) " $(COLOR_YELLOW)-----$(COLOR_RESET)" +endef + +define conditional_strip + $(if $(filter 0,$(NOSTRIP)),$(call strip_artifact)) +endef + +define strip_artifact + @$(ECHO) "STRIP $(subst $(OUT)/,,$(TARGET_OR_MEMBER))" + $(if $(filter 1,$(SPLITDEBUG)), @$(ECHO) -n "DEBUG "; \ + $(ECHO) "$(COLOR_YELLOW)\ +$(subst $(OUT)/,,$(TARGET_OR_MEMBER)).debug$(COLOR_RESET)") + $(if $(filter 1,$(SPLITDEBUG)), \ + $(QUIET)$(OBJCOPY) --only-keep-debug "$(TARGET_OR_MEMBER)" \ + "$(TARGET_OR_MEMBER).debug") + $(if $(filter-out dbg,$(MODE)),$(QUIET)$(STRIP) --strip-unneeded \ + "$(TARGET_OR_MEMBER)",) +endef + +# +# Global pattern rules +# + +# Below, the archive member syntax is abused to create fancier +# syntactic sugar for recipe authors that avoids needed to know +# subcall options. The downside is that make attempts to look +# into the phony archives for timestamps. This will cause the final +# target to be rebuilt/linked on _every_ call to make even when nothing +# has changed. Until a better way presents itself, we have helpers that +# do the stat check on make's behalf. Dodgy but simple. +define old_or_no_timestamp + $(if $(realpath $%),,$(1)) + $(if $(shell find $^ -cnewer "$%" 2>/dev/null),$(1)) +endef + +define check_deps + $(if $(filter 0,$(words $^)),\ + $(error Missing dependencies or declaration of $@($%)),) +endef + +# Build a cxx target magically +CXX_BINARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cxx_binary)) +clean: CLEAN(CXX_BINARY*) + +CC_BINARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cc_binary)) +clean: CLEAN(CC_BINARY*) + +CXX_STATIC_BINARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cxx_binary,-static)) +clean: CLEAN(CXX_STATIC_BINARY*) + +CC_STATIC_BINARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cc_binary,-static)) +clean: CLEAN(CC_STATIC_BINARY*) + +CXX_LIBRARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cxx_library)) +clean: CLEAN(CXX_LIBRARY*) + +CXX_LIBARY(%): + $(error Typo alert! LIBARY != LIBRARY) + +CC_LIBRARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call cc_library)) +clean: CLEAN(CC_LIBRARY*) + +CC_LIBARY(%): + $(error Typo alert! LIBARY != LIBRARY) + +CXX_STATIC_LIBRARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call update_archive)) +clean: CLEAN(CXX_STATIC_LIBRARY*) + +CXX_STATIC_LIBARY(%): + $(error Typo alert! LIBARY != LIBRARY) + +CC_STATIC_LIBRARY(%): + $(call check_deps) + $(call old_or_no_timestamp,$(call update_archive)) +clean: CLEAN(CC_STATIC_LIBRARY*) + +CC_STATIC_LIBARY(%): + $(error Typo alert! LIBARY != LIBRARY) + + +TEST(%): % qemu_chroot_install + $(call TEST_implementation) +.PHONY: TEST + +# multiple targets with a wildcard need to share an directory. +# Don't use this directly it just makes sure the directory is removed _after_ +# the files are. +CLEANFILE(%): + $(call silent_rm,$(TARGET_OR_MEMBER)) +.PHONY: CLEANFILE + +CLEAN(%): CLEANFILE(%) + $(QUIET)# CLEAN($%) meta-target called + $(if $(filter-out $(PWD)/,$(dir $(abspath $(TARGET_OR_MEMBER)))), \ + $(call silent_rmdir,$(dir $(abspath $(TARGET_OR_MEMBER)))),\ + $(QUIET)# Not deleting $(dir $(abspath $(TARGET_OR_MEMBER))) yet.) +.PHONY: CLEAN + +# +# Top-level objects and pattern rules +# + +# All objects for .c files at the top level +C_OBJECTS = $(patsubst $(SRC)/%.c,%.o,$(wildcard $(SRC)/*.c)) + + +# All objects for .cxx files at the top level +CXX_OBJECTS = $(patsubst $(SRC)/%.cc,%.o,$(wildcard $(SRC)/*.cc)) + +# Note, the catch-all pattern rules don't work in subdirectories because +# we're building from the $(OUT) directory. At the top-level (here) they will +# work, but we go ahead and match using the module form. Then we can place a +# generic pattern rule to capture leakage from the main Makefile. (Later in the +# file.) +# +# The reason target specific pattern rules work well for modules, +# MODULE_C_OBJECTS, is because it scopes the behavior to the given target which +# ensures we get a relative directory offset from $(OUT) which otherwise would +# not match without further magic on a per-subdirectory basis. + +# Creates object file rules. Call with eval. +# $(1) list of .o files +# $(2) source type (CC or CXX) +# $(3) source suffix (cc or c) +# $(4) compiler flag name (CFLAGS or CXXFLAGS) +# $(5) source dir: _only_ if $(SRC). Leave blank for obj tree. +define add_object_rules +$(patsubst %.o,%.pie.o,$(1)): %.pie.o: $(5)%.$(3) %.o.depends + $$(call auto_mkdir,$$@) + $$(call OBJECT_PATTERN_implementation,$(2),\ + $$(basename $$@),$$($(4)) $$(CPPFLAGS) $$(OBJ_PIE_FLAG)) + +$(patsubst %.o,%.pic.o,$(1)): %.pic.o: $(5)%.$(3) %.o.depends + $$(call auto_mkdir,$$@) + $$(call OBJECT_PATTERN_implementation,$(2),\ + $$(basename $$@),$$($(4)) $$(CPPFLAGS) -fPIC) + +# Placeholder for depends +$(patsubst %.o,%.o.depends,$(1)): + $$(call auto_mkdir,$$@) + $$(QUIET)touch "$$@" + +$(1): %.o: %.pic.o %.pie.o + $$(call auto_mkdir,$$@) + $$(QUIET)touch "$$@" +endef + +define OBJECT_PATTERN_implementation + @$(ECHO) "$(1) $(subst $(SRC)/,,$<) -> $(2).o" + $(call auto_mkdir,$@) + $(QUIET)$($(1)) -c -MD -MF $(2).d $(3) -o $(2).o $< + $(QUIET)# Wrap all the deps in $$(wildcard) so a missing header + $(QUIET)# won't cause weirdness. First we remove newlines and \, + $(QUIET)# then wrap it. + $(QUIET)sed -i -e :j -e '$$!N;s|\\\s*\n| |;tj' \ + -e 's|^\(.*\s*:\s*\)\(.*\)$$|\1 $$\(wildcard \2\)|' $(2).d +endef + +# Now actually register handlers for C(XX)_OBJECTS. +$(eval $(call add_object_rules,$(C_OBJECTS),CC,c,CFLAGS,$(SRC)/)) +$(eval $(call add_object_rules,$(CXX_OBJECTS),CXX,cc,CXXFLAGS,$(SRC)/)) + +# Disable default pattern rules to help avoid leakage. +# These may already be handled by '-r', but let's keep it to be safe. +%: %.o ; +%.a: %.o ; +%.o: %.c ; +%.o: %.cc ; + +# NOTE: A specific rule for archive objects is avoided because parallel +# update of the archive causes build flakiness. +# Instead, just make the objects the prerequisites and use update_archive +# To use the foo.a(obj.o) functionality, targets would need to specify the +# explicit object they expect on the prerequisite line. + +# +# Architecture detection and QEMU wrapping +# + +HOST_ARCH ?= $(shell uname -m) +override ARCH := $(strip $(ARCH)) +override HOST_ARCH := $(strip $(HOST_ARCH)) +# emake will supply "x86" or "arm" for ARCH, but +# if uname -m runs and you get x86_64, then this subst +# will break. +ifeq ($(subst x86,i386,$(ARCH)),i386) + QEMU_ARCH := $(subst x86,i386,$(ARCH)) # x86 -> i386 +else ifeq ($(subst amd64,x86_64,$(ARCH)),x86_64) + QEMU_ARCH := $(subst amd64,x86_64,$(ARCH)) # amd64 -> x86_64 +else + QEMU_ARCH = $(ARCH) +endif +override QEMU_ARCH := $(strip $(QEMU_ARCH)) + +# If we're cross-compiling, try to use qemu for running the tests. +ifneq ($(QEMU_ARCH),$(HOST_ARCH)) + ifeq ($(SYSROOT),) + $(info SYSROOT not defined. qemu-based testing disabled) + else + # A SYSROOT is assumed for QEmu use. + USE_QEMU ?= 1 + + # Allow 64-bit hosts to run 32-bit without qemu. + ifeq ($(HOST_ARCH),x86_64) + ifeq ($(QEMU_ARCH),i386) + USE_QEMU = 0 + endif + endif + endif +else + USE_QEMU ?= 0 +endif + +# Normally we don't need to run as root or do bind mounts, so only +# enable it by default when we're using QEMU. +NEEDS_ROOT ?= $(USE_QEMU) +NEEDS_MOUNTS ?= $(USE_QEMU) + +SYSROOT_OUT = $(OUT) +ifneq ($(SYSROOT),) + SYSROOT_OUT = $(subst $(SYSROOT),,$(OUT)) +else + # Default to / when all the empty-sysroot logic is done. + SYSROOT = / +endif + +QEMU_NAME = qemu-$(QEMU_ARCH) +QEMU_PATH = /build/bin/$(QEMU_NAME) +QEMU_SYSROOT_PATH = $(SYSROOT)$(QEMU_PATH) +QEMU_SRC_PATH = /usr/bin/$(QEMU_NAME) +QEMU_BINFMT_PATH = /proc/sys/fs/binfmt_misc/$(QEMU_NAME) +QEMU_REGISTER_PATH = /proc/sys/fs/binfmt_misc/register + +QEMU_MAGIC_arm = ":$(QEMU_NAME):M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/build/bin/qemu-arm:" + + +# +# Output full configuration at top level +# + +# Don't show on clean +ifneq ($(MAKECMDGOALS),clean) + $(info build configuration:) + $(info - OUT=$(OUT)) + $(info - SRC=$(SRC)) + $(info - MODE=$(MODE)) + $(info - SPLITDEBUG=$(SPLITDEBUG)) + $(info - NOSTRIP=$(NOSTRIP)) + $(info - VALGRIND=$(VALGRIND)) + $(info - COLOR=$(COLOR)) + $(info - CXXEXCEPTIONS=$(CXXEXCEPTIONS)) + $(info - ARCH=$(ARCH)) + $(info - QEMU_ARCH=$(QEMU_ARCH)) + $(info - USE_QEMU=$(USE_QEMU)) + $(info - NEEDS_ROOT=$(NEEDS_ROOT)) + $(info - NEEDS_MOUNTS=$(NEEDS_MOUNTS)) + $(info - SYSROOT=$(SYSROOT)) + $(info ) +endif + +# +# Standard targets with detection for when they are improperly configured. +# + +# all does not include tests by default +all: + $(QUIET)(test -z "$^" && \ + $(ECHO) "You must add your targets as 'all' prerequisites") || true + $(QUIET)test -n "$^" + +# Builds and runs tests for the target arch +# Run them in parallel +# After the test have completed, if profiling, run coverage analysis +tests: +ifeq ($(MODE),profiling) + @$(ECHO) "COVERAGE [$(COLOR_YELLOW)STARTED$(COLOR_RESET)]" + $(QUIET)FILES=""; \ + for GCNO in `find . -name "*.gcno"`; do \ + GCDA="$${GCNO%.gcno}.gcda"; \ + if [ -e $${GCDA} ]; then \ + FILES="$${FILES} $${GCDA}"; \ + fi \ + done; \ + if [ -n "$${FILES}" ]; then \ + gcov -l $${FILES}; \ + lcov --capture --directory . \ + --output-file=lcov-coverage.info; \ + genhtml lcov-coverage.info \ + --output-directory lcov-html; \ + fi + @$(ECHO) "COVERAGE [$(COLOR_YELLOW)FINISHED$(COLOR_RESET)]" +endif +.PHONY: tests + +qemu_chroot_install: +ifeq ($(USE_QEMU),1) + $(QUIET)$(ECHO) "QEMU Preparing $(QEMU_NAME)" + @# Copying strategy + @# Compare /usr/bin/qemu inode to /build/$board/build/bin/qemu, if different + @# hard link to a temporary file, then rename temp to target. This should + @# ensure that once $QEMU_SYSROOT_PATH exists it will always exist, regardless + @# of simultaneous test setups. + $(QUIET)if [[ ! -e $(QEMU_SYSROOT_PATH) || \ + `stat -c %i $(QEMU_SRC_PATH)` != `stat -c %i $(QEMU_SYSROOT_PATH)` \ + ]]; then \ + $(ROOT_CMD) ln -Tf $(QEMU_SRC_PATH) $(QEMU_SYSROOT_PATH).$$$$; \ + $(ROOT_CMD) mv -Tf $(QEMU_SYSROOT_PATH).$$$$ $(QEMU_SYSROOT_PATH); \ + fi + + @# Prep the binfmt handler. First mount if needed, then unregister any bad + @# mappings and then register our mapping. + @# There may still be some race conditions here where one script de-registers + @# and another script starts executing before it gets re-registered, however + @# it should be rare. + -$(QUIET)[[ -e $(QEMU_REGISTER_PATH) ]] || \ + $(ROOT_CMD) mount binfmt_misc -t binfmt_misc \ + /proc/sys/fs/binfmt_misc + + -$(QUIET)if [[ -e $(QEMU_BINFMT_PATH) && \ + `awk '$$1 == "interpreter" {print $$NF}' $(QEMU_BINFMT_PATH)` != \ + "$(QEMU_PATH)" ]]; then \ + echo -1 | $(ROOT_CMD) tee $(QEMU_BINFMT_PATH) >/dev/null; \ + fi + + -$(if $(QEMU_MAGIC_$(ARCH)),$(QUIET)[[ -e $(QEMU_BINFMT_PATH) ]] || \ + echo $(QEMU_MAGIC_$(ARCH)) | $(ROOT_CMD) tee $(QEMU_REGISTER_PATH) \ + >/dev/null) +endif +.PHONY: qemu_clean qemu_chroot_install + +# TODO(wad) Move to -L $(SYSROOT) and fakechroot when qemu-user +# doesn't hang traversing /proc from SYSROOT. +SUDO_CMD = sudo +UNSHARE_CMD = unshare +QEMU_CMD = +ROOT_CMD = $(if $(filter 1,$(NEEDS_ROOT)),$(SUDO_CMD) , ) +MOUNT_CMD = $(if $(filter 1,$(NEEDS_MOUNTS)),$(ROOT_CMD) mount, \#) +UMOUNT_CMD = $(if $(filter 1,$(NEEDS_MOUNTS)),$(ROOT_CMD) umount, \#) +QEMU_LDPATH = $(SYSROOT_LDPATH):/lib64:/lib:/usr/lib64:/usr/lib +ROOT_CMD_LDPATH = $(SYSROOT_LDPATH):$(SYSROOT)/lib64: +ROOT_CMD_LDPATH := $(ROOT_CMD_LDPATH):$(SYSROOT)/lib:$(SYSROOT)/usr/lib64: +ROOT_CMD_LDPATH := $(ROOT_CMD_LDPATH):$(SYSROOT)/usr/lib +ifeq ($(USE_QEMU),1) + export QEMU_CMD = \ + $(SUDO_CMD) chroot $(SYSROOT) $(QEMU_PATH) \ + -drop-ld-preload \ + -E LD_LIBRARY_PATH="$(QEMU_LDPATH):$(patsubst $(OUT),,$(LD_DIRS))" \ + -E HOME="$(HOME)" -E SRC="$(SRC)" -- + # USE_QEMU conditional function + define if_qemu + $(1) + endef +else + ROOT_CMD = $(if $(filter 1,$(NEEDS_ROOT)),sudo, ) \ + LD_LIBRARY_PATH="$(ROOT_CMD_LDPATH):$(LD_DIRS)" + define if_qemu + $(2) + endef +endif + +VALGRIND_CMD = +ifeq ($(VALGRIND),1) + VALGRIND_CMD = /usr/bin/valgrind --tool=memcheck $(VALGRIND_ARGS) -- +endif + +define TEST_implementation + $(QUIET)$(call TEST_setup) + $(QUIET)$(call TEST_run) + $(QUIET)$(call TEST_teardown) + $(QUIET)exit $$(cat $(OUT)$(TARGET_OR_MEMBER).status.test) +endef + +define TEST_setup + @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " + @$(ECHO) "[$(COLOR_YELLOW)SETUP$(COLOR_RESET)]" + $(QUIET)# Setup a target-specific results file + $(QUIET)(echo > $(OUT)$(TARGET_OR_MEMBER).setup.test) + $(QUIET)(echo 1 > $(OUT)$(TARGET_OR_MEMBER).status.test) + $(QUIET)(echo > $(OUT)$(TARGET_OR_MEMBER).cleanup.test) + $(QUIET)# No setup if we are not using QEMU + $(QUIET)# TODO(wad) this is racy until we use a vfs namespace + $(call if_qemu,\ + $(QUIET)(echo "mkdir -p '$(SYSROOT)/proc' '$(SYSROOT)/dev' \ + '$(SYSROOT)/mnt/host/source'" \ + >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")) + $(call if_qemu,\ + $(QUIET)(echo "$(MOUNT_CMD) --bind /mnt/host/source \ + '$(SYSROOT)/mnt/host/source'" \ + >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")) + $(call if_qemu,\ + $(QUIET)(echo "$(MOUNT_CMD) --bind /proc '$(SYSROOT)/proc'" \ + >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")) + $(call if_qemu,\ + $(QUIET)(echo "$(MOUNT_CMD) --bind /dev '$(SYSROOT)/dev'" \ + >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")) +endef + +define TEST_teardown + @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " + @$(ECHO) "[$(COLOR_YELLOW)TEARDOWN$(COLOR_RESET)]" + $(call if_qemu, $(QUIET)$(SHELL) "$(OUT)$(TARGET_OR_MEMBER).cleanup.test") +endef + +# Use GTEST_ARGS.[arch] if defined. +override GTEST_ARGS.real = \ + $(call if_qemu,$(GTEST_ARGS.qemu.$(QEMU_ARCH)),$(GTEST_ARGS.host.$(HOST_ARCH))) + +define TEST_run + @$(ECHO) -n "TEST $(TARGET_OR_MEMBER) " + @$(ECHO) "[$(COLOR_GREEN)RUN$(COLOR_RESET)]" + $(QUIET)(echo 1 > "$(OUT)$(TARGET_OR_MEMBER).status.test") + $(QUIET)(echo $(ROOT_CMD) SRC="$(SRC)" $(QEMU_CMD) $(VALGRIND_CMD) \ + "$(strip $(call if_qemu, $(SYSROOT_OUT),$(OUT))$(TARGET_OR_MEMBER))" \ + $(if $(filter-out 0,$(words $(GTEST_ARGS.real))),$(GTEST_ARGS.real),\ + $(GTEST_ARGS)) >> "$(OUT)$(TARGET_OR_MEMBER).setup.test") + -$(QUIET)$(call if_qemu,$(SUDO_CMD) $(UNSHARE_CMD) -m) $(SHELL) \ + $(OUT)$(TARGET_OR_MEMBER).setup.test \ + && echo 0 > "$(OUT)$(TARGET_OR_MEMBER).status.test" +endef + +# Recursive list reversal so that we get RMDIR_ON_CLEAN in reverse order. +define reverse +$(if $(1),$(call reverse,$(wordlist 2,$(words $(1)),$(1)))) $(firstword $(1)) +endef + +clean: qemu_clean +clean: CLEAN($(OUT)*.d) CLEAN($(OUT)*.o) CLEAN($(OUT)*.debug) +clean: CLEAN($(OUT)*.test) CLEAN($(OUT)*.depends) +clean: CLEAN($(OUT)*.gcno) CLEAN($(OUT)*.gcda) CLEAN($(OUT)*.gcov) +clean: CLEAN($(OUT)lcov-coverage.info) CLEAN($(OUT)lcov-html) + +clean: + $(QUIET)# Always delete the containing directory last. + $(call silent_rmdir,$(OUT)) + +FORCE: ; +# Empty rule for use when no special targets are needed, like large_tests +NONE: + +.PHONY: clean NONE valgrind NONE +.DEFAULT_GOAL := all +# Don't let make blow away "intermediates" +.PRECIOUS: %.pic.o %.pie.o %.a %.pic.a %.pie.a %.test + +# Start accruing build info +OUT_DIRS = $(OUT) +LD_DIRS = $(OUT) +SRC_DIRS = $(SRC) + +include $(wildcard $(OUT)*.d) +SUBMODULE_DIRS = $(wildcard $(SRC)/*/module.mk) +include $(SUBMODULE_DIRS) + + +else ## In duplicate inclusions of common.mk + +# Get the current inclusion directory without a trailing slash +MODULE := $(patsubst %/,%, \ + $(dir $(lastword $(filter-out %common.mk,$(MAKEFILE_LIST))))) +MODULE := $(subst $(SRC)/,,$(MODULE)) +MODULE_NAME := $(subst /,_,$(MODULE)) +#VPATH := $(MODULE):$(VPATH) + + +# Depth first +$(eval OUT_DIRS += $(OUT)$(MODULE)) +$(eval SRC_DIRS += $(OUT)$(MODULE)) +$(eval LD_DIRS := $(LD_DIRS):$(OUT)$(MODULE)) + +# Add the defaults from this dir to rm_clean +clean: CLEAN($(OUT)$(MODULE)/*.d) CLEAN($(OUT)$(MODULE)/*.o) +clean: CLEAN($(OUT)$(MODULE)/*.debug) CLEAN($(OUT)$(MODULE)/*.test) +clean: CLEAN($(OUT)$(MODULE)/*.depends) +clean: CLEAN($(OUT)$(MODULE)/*.gcno) CLEAN($(OUT)$(MODULE)/*.gcda) +clean: CLEAN($(OUT)$(MODULE)/*.gcov) CLEAN($(OUT)lcov-coverage.info) +clean: CLEAN($(OUT)lcov-html) + +$(info + submodule: $(MODULE_NAME)) +# We must eval otherwise they may be dropped. +MODULE_C_OBJECTS = $(patsubst $(SRC)/$(MODULE)/%.c,$(MODULE)/%.o,\ + $(wildcard $(SRC)/$(MODULE)/*.c)) +$(eval $(MODULE_NAME)_C_OBJECTS ?= $(MODULE_C_OBJECTS)) +MODULE_CXX_OBJECTS = $(patsubst $(SRC)/$(MODULE)/%.cc,$(MODULE)/%.o,\ + $(wildcard $(SRC)/$(MODULE)/*.cc)) +$(eval $(MODULE_NAME)_CXX_OBJECTS ?= $(MODULE_CXX_OBJECTS)) + +# Note, $(MODULE) is implicit in the path to the %.c. +# See $(C_OBJECTS) for more details. +# Register rules for the module objects. +$(eval $(call add_object_rules,$(MODULE_C_OBJECTS),CC,c,CFLAGS,$(SRC)/)) +$(eval $(call add_object_rules,$(MODULE_CXX_OBJECTS),CXX,cc,CXXFLAGS,$(SRC)/)) + +# Continue recursive inclusion of module.mk files +SUBMODULE_DIRS = $(wildcard $(SRC)/$(MODULE)/*/module.mk) +include $(wildcard $(OUT)$(MODULE)/*.d) +include $(SUBMODULE_DIRS) + +endif +endif ## pass-to-subcall wrapper for relocating the call directory diff --git a/gen/mojo/common/common_custom_types__type_mappings b/gen/mojo/common/common_custom_types__type_mappings index 4602371..1906049 100644 --- a/gen/mojo/common/common_custom_types__type_mappings +++ b/gen/mojo/common/common_custom_types__type_mappings @@ -1,93 +1,193 @@ { "c++": { - "mojo.common.mojom.ListValue": { - "typename": "base::ListValue", + "mojo.common.mojom.Value": { + "hashable": false, + "typename": "std::unique_ptr<base::Value>", + "traits_headers": [ + "ipc/ipc_message_utils.h", + "mojo/common/values_struct_traits.h" + ], + "copyable_pass_by_value": false, + "move_only": true, + "nullable_is_same_type": true, + "non_copyable_non_movable": false, + "public_headers": [ + "base/values.h" + ] + }, + "mojo.common.mojom.UnguessableToken": { + "hashable": false, + "typename": "base::UnguessableToken", "traits_headers": [ - "ipc/ipc_message_utils.h", "mojo/common/common_custom_types_struct_traits.h" - ], - "copyable_pass_by_value": false, - "move_only": false, - "nullable_is_same_type": false, + ], + "copyable_pass_by_value": false, + "move_only": false, + "nullable_is_same_type": false, + "non_copyable_non_movable": false, + "public_headers": [ + "base/unguessable_token.h" + ] + }, + "mojo.common.mojom.TextDirection": { + "hashable": false, + "typename": "base::i18n::TextDirection", + "traits_headers": [ + "mojo/common/common_custom_types_struct_traits.h" + ], + "copyable_pass_by_value": false, + "move_only": false, + "nullable_is_same_type": false, + "non_copyable_non_movable": false, + "public_headers": [ + "base/i18n/rtl.h" + ] + }, + "mojo.common.mojom.ListValue": { + "hashable": false, + "typename": "std::unique_ptr<base::ListValue>", + "traits_headers": [ + "ipc/ipc_message_utils.h", + "mojo/common/values_struct_traits.h" + ], + "copyable_pass_by_value": false, + "move_only": true, + "nullable_is_same_type": true, + "non_copyable_non_movable": false, "public_headers": [ "base/values.h" ] - }, + }, "mojo.common.mojom.String16": { - "typename": "base::string16", + "hashable": false, + "typename": "base::string16", "traits_headers": [ - "ipc/ipc_message_utils.h", "mojo/common/common_custom_types_struct_traits.h" - ], - "copyable_pass_by_value": false, - "move_only": false, - "nullable_is_same_type": false, + ], + "copyable_pass_by_value": false, + "move_only": false, + "nullable_is_same_type": false, + "non_copyable_non_movable": false, "public_headers": [ "base/strings/string16.h" ] - }, + }, "mojo.common.mojom.Time": { - "typename": "base::Time", + "hashable": false, + "typename": "base::Time", "traits_headers": [ - "ipc/ipc_message_utils.h", + "ipc/ipc_message_utils.h", "mojo/common/common_custom_types_struct_traits.h" - ], - "copyable_pass_by_value": true, - "move_only": false, - "nullable_is_same_type": false, + ], + "copyable_pass_by_value": true, + "move_only": false, + "nullable_is_same_type": false, + "non_copyable_non_movable": false, "public_headers": [ "base/time/time.h" ] - }, + }, "mojo.common.mojom.TimeDelta": { - "typename": "base::TimeDelta", + "hashable": false, + "typename": "base::TimeDelta", "traits_headers": [ - "ipc/ipc_message_utils.h", + "ipc/ipc_message_utils.h", "mojo/common/common_custom_types_struct_traits.h" - ], - "copyable_pass_by_value": true, - "move_only": false, - "nullable_is_same_type": false, + ], + "copyable_pass_by_value": true, + "move_only": false, + "nullable_is_same_type": false, + "non_copyable_non_movable": false, "public_headers": [ "base/time/time.h" ] - }, + }, "mojo.common.mojom.TimeTicks": { - "typename": "base::TimeTicks", + "hashable": false, + "typename": "base::TimeTicks", "traits_headers": [ - "ipc/ipc_message_utils.h", + "ipc/ipc_message_utils.h", "mojo/common/common_custom_types_struct_traits.h" - ], - "copyable_pass_by_value": true, - "move_only": false, - "nullable_is_same_type": false, + ], + "copyable_pass_by_value": true, + "move_only": false, + "nullable_is_same_type": false, + "non_copyable_non_movable": false, "public_headers": [ "base/time/time.h" ] - }, + }, + "mojo.common.mojom.LegacyListValue": { + "hashable": false, + "typename": "base::ListValue", + "traits_headers": [ + "ipc/ipc_message_utils.h", + "mojo/common/values_struct_traits.h" + ], + "copyable_pass_by_value": false, + "move_only": false, + "nullable_is_same_type": false, + "non_copyable_non_movable": true, + "public_headers": [ + "base/values.h" + ] + }, + "mojo.common.mojom.File": { + "hashable": false, + "typename": "base::File", + "traits_headers": [ + "mojo/common/common_custom_types_struct_traits.h" + ], + "copyable_pass_by_value": false, + "move_only": true, + "nullable_is_same_type": true, + "non_copyable_non_movable": false, + "public_headers": [ + "base/files/file.h" + ] + }, "mojo.common.mojom.FilePath": { - "typename": "base::FilePath", + "hashable": false, + "typename": "base::FilePath", "traits_headers": [ "ipc/ipc_message_utils.h" - ], - "copyable_pass_by_value": false, - "move_only": false, - "nullable_is_same_type": false, + ], + "copyable_pass_by_value": false, + "move_only": false, + "nullable_is_same_type": false, + "non_copyable_non_movable": false, "public_headers": [ "base/files/file_path.h" ] - }, + }, "mojo.common.mojom.DictionaryValue": { - "typename": "base::DictionaryValue", + "hashable": false, + "typename": "std::unique_ptr<base::DictionaryValue>", "traits_headers": [ - "ipc/ipc_message_utils.h" - ], - "copyable_pass_by_value": false, - "move_only": false, - "nullable_is_same_type": false, + "ipc/ipc_message_utils.h", + "mojo/common/values_struct_traits.h" + ], + "copyable_pass_by_value": false, + "move_only": true, + "nullable_is_same_type": true, + "non_copyable_non_movable": false, "public_headers": [ "base/values.h" ] + }, + "mojo.common.mojom.Version": { + "hashable": false, + "typename": "base::Version", + "traits_headers": [ + "mojo/common/common_custom_types_struct_traits.h" + ], + "copyable_pass_by_value": false, + "move_only": false, + "nullable_is_same_type": false, + "non_copyable_non_movable": false, + "public_headers": [ + "base/version.h" + ] } } -} +}
\ No newline at end of file diff --git a/ipc/attachment_broker.h b/ipc/attachment_broker.h deleted file mode 100644 index a106e29..0000000 --- a/ipc/attachment_broker.h +++ /dev/null @@ -1,176 +0,0 @@ -// 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 IPC_ATTACHMENT_BROKER_H_ -#define IPC_ATTACHMENT_BROKER_H_ - -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/process/process_handle.h" -#include "base/synchronization/lock.h" -#include "build/build_config.h" -#include "ipc/brokerable_attachment.h" -#include "ipc/ipc_export.h" -#include "ipc/ipc_listener.h" - -// If the platform has no attachments that need brokering, then it shouldn't -// compile any code that calls member functions of AttachmentBroker. This -// prevents symbols only used by AttachmentBroker and its subclasses from -// making it into the binary. -#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) -#define USE_ATTACHMENT_BROKER 1 -#else -#define USE_ATTACHMENT_BROKER 0 -#endif // defined(OS_WIN) - -namespace base { -class SequencedTaskRunner; -class SingleThreadTaskRunner; -}; - -namespace IPC { - -class AttachmentBroker; -class Endpoint; - -// Classes that inherit from this abstract base class are capable of -// communicating with a broker to send and receive attachments to Chrome IPC -// messages. -class IPC_EXPORT SupportsAttachmentBrokering { - public: - // Returns an AttachmentBroker used to broker attachments of IPC messages to - // other processes. There must be exactly one AttachmentBroker per process. - virtual AttachmentBroker* GetAttachmentBroker() = 0; -}; - -// Responsible for brokering attachments to Chrome IPC messages. On platforms -// that support attachment brokering, every IPC channel should have a reference -// to a AttachmentBroker. -// This class is not thread safe. The implementation of this class assumes that -// it is only ever used on the same thread as its consumers. -class IPC_EXPORT AttachmentBroker : public Listener { - public: - // A standard observer interface that allows consumers of the AttachmentBroker - // to be notified when a new attachment has been received. - class Observer { - public: - virtual void ReceivedBrokerableAttachmentWithId( - const BrokerableAttachment::AttachmentId& id) = 0; - }; - - // Each process has at most one attachment broker. The process is responsible - // for ensuring that |broker| stays alive for as long as the process is - // sending/receiving ipc messages. - static void SetGlobal(AttachmentBroker* broker); - static AttachmentBroker* GetGlobal(); - - AttachmentBroker(); - ~AttachmentBroker() override; - - // Sends |attachment| to |destination_process|. The implementation uses an - // IPC::Channel to communicate with the broker process. This may be the same - // IPC::Channel that is requesting the brokering of an attachment. - // Returns true on success and false otherwise. - virtual bool SendAttachmentToProcess( - const scoped_refptr<BrokerableAttachment>& attachment, - base::ProcessId destination_process) = 0; - - // Returns whether the attachment was available. If the attachment was - // available, populates the output parameter |attachment|. - bool GetAttachmentWithId(BrokerableAttachment::AttachmentId id, - scoped_refptr<BrokerableAttachment>* attachment); - - // Any given observer should only ever add itself once to the observer list. - // Notifications to |observer| will be posted to |runner|. - // The |observer| is expected to call RemoveObserver() before being destroyed. - void AddObserver(Observer* observer, - const scoped_refptr<base::SequencedTaskRunner>& runner); - void RemoveObserver(Observer* observer); - - // These two methods should only be called by the broker process. - // - // Each unprivileged process should have one IPC channel on which it - // communicates attachment information with the broker process. In the broker - // process, these channels must be registered and deregistered with the - // Attachment Broker as they are created and destroyed. - // - // Invocations of Send() on |endpoint| will occur on thread bound to |runner|. - virtual void RegisterCommunicationChannel( - Endpoint* endpoint, - scoped_refptr<base::SingleThreadTaskRunner> runner); - virtual void DeregisterCommunicationChannel(Endpoint* endpoint); - - // In each unprivileged process, exactly one channel should be used to - // communicate brokerable attachments with the broker process. - virtual void RegisterBrokerCommunicationChannel(Endpoint* endpoint); - virtual void DeregisterBrokerCommunicationChannel(Endpoint* endpoint); - - // Informs the attachment broker that a channel endpoint has received its - // peer's PID. - virtual void ReceivedPeerPid(base::ProcessId peer_pid); - - // True if and only if this broker is privileged. - virtual bool IsPrivilegedBroker(); - - protected: - using AttachmentVector = std::vector<scoped_refptr<BrokerableAttachment>>; - - // Adds |attachment| to |attachments_|, and notifies the observers. - void HandleReceivedAttachment( - const scoped_refptr<BrokerableAttachment>& attachment); - - // Informs the observers that a new BrokerableAttachment has been received. - void NotifyObservers(const BrokerableAttachment::AttachmentId& id); - - // Informs the observer identified by |unique_id| that a new - // BrokerableAttachment has been received. - void NotifyObserver(int unique_id, - const BrokerableAttachment::AttachmentId& id); - - // This method is exposed for testing only. - AttachmentVector* get_attachments() { return &attachments_; } - - base::Lock* get_lock() { return &lock_; } - - private: -#if defined(OS_WIN) - FRIEND_TEST_ALL_PREFIXES(AttachmentBrokerUnprivilegedWinTest, - ReceiveValidMessage); - FRIEND_TEST_ALL_PREFIXES(AttachmentBrokerUnprivilegedWinTest, - ReceiveInvalidMessage); -#endif // defined(OS_WIN) - - // A vector of BrokerableAttachments that have been received, but not yet - // consumed. - // A std::vector is used instead of a std::map because this container is - // expected to have few elements, for which a std::vector is expected to have - // better performance. - AttachmentVector attachments_; - - struct ObserverInfo { - ObserverInfo(); - ObserverInfo(const ObserverInfo& other); - ~ObserverInfo(); - - Observer* observer; - int unique_id; - - // Notifications must be dispatched onto |runner|. - scoped_refptr<base::SequencedTaskRunner> runner; - }; - std::vector<ObserverInfo> observers_; - - // This member holds the last id given to an ObserverInfo. - int last_unique_id_; - - // The AttachmentBroker can be accessed from any thread, so modifications to - // internal state must be guarded by a lock. - base::Lock lock_; - DISALLOW_COPY_AND_ASSIGN(AttachmentBroker); -}; - -} // namespace IPC - -#endif // IPC_ATTACHMENT_BROKER_H_ diff --git a/ipc/brokerable_attachment.cc b/ipc/brokerable_attachment.cc deleted file mode 100644 index 96ce5bb..0000000 --- a/ipc/brokerable_attachment.cc +++ /dev/null @@ -1,72 +0,0 @@ -// 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 "ipc/brokerable_attachment.h" - -#include <stddef.h> - -#include "build/build_config.h" -#include "ipc/attachment_broker.h" - -namespace IPC { - -// BrokerableAttachment::AttachmentId ------------------------------------------ -#if !USE_ATTACHMENT_BROKER -// static -BrokerableAttachment::AttachmentId -BrokerableAttachment::AttachmentId::CreateIdWithRandomNonce() { - CHECK(false) << "Platforms that don't support attachment brokering shouldn't " - "be trying to generating a random nonce."; - return AttachmentId(); -} -#endif - -BrokerableAttachment::AttachmentId::AttachmentId() { - for (size_t i = 0; i < BrokerableAttachment::kNonceSize; ++i) - nonce[i] = 0; -} - -BrokerableAttachment::AttachmentId::AttachmentId(const char* start_address, - size_t size) { - DCHECK(size == BrokerableAttachment::kNonceSize); - for (size_t i = 0; i < BrokerableAttachment::kNonceSize; ++i) - nonce[i] = start_address[i]; -} - -void BrokerableAttachment::AttachmentId::SerializeToBuffer(char* start_address, - size_t size) { - DCHECK(size == BrokerableAttachment::kNonceSize); - for (size_t i = 0; i < BrokerableAttachment::kNonceSize; ++i) - start_address[i] = nonce[i]; -} - -// BrokerableAttachment::BrokerableAttachment ---------------------------------- - -BrokerableAttachment::BrokerableAttachment() - : id_(AttachmentId::CreateIdWithRandomNonce()) {} - -BrokerableAttachment::BrokerableAttachment(const AttachmentId& id) : id_(id) {} - -BrokerableAttachment::~BrokerableAttachment() {} - -BrokerableAttachment::AttachmentId BrokerableAttachment::GetIdentifier() const { - return id_; -} - -bool BrokerableAttachment::NeedsBrokering() const { - return GetBrokerableType() == PLACEHOLDER; -} - -BrokerableAttachment::Type BrokerableAttachment::GetType() const { - return TYPE_BROKERABLE_ATTACHMENT; -} - -#if defined(OS_POSIX) -base::PlatformFile BrokerableAttachment::TakePlatformFile() { - NOTREACHED(); - return base::PlatformFile(); -} -#endif // OS_POSIX - -} // namespace IPC diff --git a/ipc/brokerable_attachment.h b/ipc/brokerable_attachment.h deleted file mode 100644 index 50e7fd2..0000000 --- a/ipc/brokerable_attachment.h +++ /dev/null @@ -1,91 +0,0 @@ -// 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 IPC_BROKERABLE_ATTACHMENT_H_ -#define IPC_BROKERABLE_ATTACHMENT_H_ - -#include <stddef.h> -#include <stdint.h> - -#include <algorithm> - -#include "base/macros.h" -#include "build/build_config.h" -#include "ipc/ipc_export.h" -#include "ipc/ipc_message_attachment.h" - -namespace IPC { - -// This subclass of MessageAttachment requires an AttachmentBroker to be -// attached to a Chrome IPC message. -class IPC_EXPORT BrokerableAttachment : public MessageAttachment { - public: - static const size_t kNonceSize = 16; - // An id uniquely identifies an attachment sent via a broker. - struct IPC_EXPORT AttachmentId { - uint8_t nonce[kNonceSize]; - - // Generates an AttachmentId with an unguessable, random nonce. - static AttachmentId CreateIdWithRandomNonce(); - - // Creates an AttachmentId with a zeroed nonce. This should only be used by - // the IPC translation system, which requires that classes have a default - // constructor. - AttachmentId(); - - // Constructs an AttachmentId from a buffer. - AttachmentId(const char* start_address, size_t size); - - // Writes the nonce into a buffer. - void SerializeToBuffer(char* start_address, size_t size); - - bool operator==(const AttachmentId& rhs) const { - return std::equal(nonce, nonce + kNonceSize, rhs.nonce); - } - - bool operator<(const AttachmentId& rhs) const { - return std::lexicographical_compare(nonce, nonce + kNonceSize, rhs.nonce, - rhs.nonce + kNonceSize); - } - }; - - enum BrokerableType { - PLACEHOLDER, - WIN_HANDLE, - MACH_PORT, - }; - - // The identifier is unique across all Chrome processes. - AttachmentId GetIdentifier() const; - - // Whether the attachment still needs information from the broker before it - // can be used. - bool NeedsBrokering() const; - - // Returns TYPE_BROKERABLE_ATTACHMENT - Type GetType() const override; - - virtual BrokerableType GetBrokerableType() const = 0; - -// MessageAttachment override. -#if defined(OS_POSIX) - base::PlatformFile TakePlatformFile() override; -#endif // OS_POSIX - - protected: - BrokerableAttachment(); - BrokerableAttachment(const AttachmentId& id); - ~BrokerableAttachment() override; - - private: - // This member uniquely identifies a BrokerableAttachment across all Chrome - // processes. - const AttachmentId id_; - - DISALLOW_COPY_AND_ASSIGN(BrokerableAttachment); -}; - -} // namespace IPC - -#endif // IPC_BROKERABLE_ATTACHMENT_H_ diff --git a/ipc/ipc.mojom b/ipc/ipc.mojom new file mode 100644 index 0000000..0a4fcfa --- /dev/null +++ b/ipc/ipc.mojom @@ -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. + +module IPC.mojom; + +// NOTE: This MUST match the value of MSG_ROUTING_NONE in src/ipc/ipc_message.h. +const int32 kRoutingIdNone = -2; + +struct SerializedHandle { + handle the_handle; + + enum Type { + MOJO_HANDLE, + PLATFORM_FILE, + WIN_HANDLE, + MACH_PORT, + }; + + Type type; +}; + +// A placeholder interface type since we don't yet support generic associated +// message pipe handles. +interface GenericInterface {}; + +interface Channel { + // Informs the remote end of this client's PID. Must be called exactly once, + // before any calls to Receive() below. + SetPeerPid(int32 pid); + + // Transmits a classical Chrome IPC message. + Receive(array<uint8> data, array<SerializedHandle>? handles); + + // Requests a Channel-associated interface. + GetAssociatedInterface(string name, associated GenericInterface& request); +}; + +// A strictly nominal interface used to identify Channel bootstrap requests. +interface ChannelBootstrap {}; diff --git a/ipc/ipc_channel_handle.h b/ipc/ipc_channel_handle.h index 2e9dc3e..ef31b84 100644 --- a/ipc/ipc_channel_handle.h +++ b/ipc/ipc_channel_handle.h @@ -10,60 +10,31 @@ #include "build/build_config.h" #include "mojo/public/cpp/system/message_pipe.h" -#if defined(OS_POSIX) +#if defined(OS_NACL_SFI) #include "base/file_descriptor_posix.h" -#elif defined(OS_WIN) -#include <windows.h> -#endif // defined (OS_WIN) - -// On Windows, any process can create an IPC channel and others can fetch -// it by name. We pass around the channel names over IPC. -// On Windows the initialization of ChannelHandle with an existing pipe -// handle is provided for convenience. -// NOTE: A ChannelHandle with a pipe handle Will NOT be marshalled over IPC. - -// On POSIX, we instead pass around handles to channel endpoints via IPC. -// When it's time to IPC a new channel endpoint around, we send both the -// channel name as well as a base::FileDescriptor, which is itself a special -// type that knows how to copy a socket endpoint over IPC. -// -// In sum, this data structure can be used to pass channel information by name -// in both Windows and Posix. When passing a handle to a channel over IPC, -// use this data structure only for POSIX. +#endif // defined (OS_NACL_SFI) namespace IPC { +// Note that serialization for this object is defined in the ParamTraits +// template specialization in ipc_message_utils.h. +#if defined(OS_NACL_SFI) struct ChannelHandle { - // Note that serialization for this object is defined in the ParamTraits - // template specialization in ipc_message_utils.h. ChannelHandle() {} - // The name that is passed in should be an absolute path for Posix. - // Otherwise there may be a problem in IPC communication between - // processes with different working directories. - ChannelHandle(const std::string& n) : name(n) {} - ChannelHandle(const char* n) : name(n) {} -#if defined(OS_WIN) - explicit ChannelHandle(HANDLE h) : pipe(h) {} -#elif defined(OS_POSIX) - ChannelHandle(const std::string& n, const base::FileDescriptor& s) - : name(n), socket(s) {} -#endif // defined(OS_POSIX) - ChannelHandle(mojo::MessagePipeHandle h) : mojo_handle(h) {} + explicit ChannelHandle(const base::FileDescriptor& s) : socket(s) {} - std::string name; -#if defined(OS_POSIX) base::FileDescriptor socket; -#elif defined(OS_WIN) - // A simple container to automatically initialize pipe handle - struct PipeHandle { - PipeHandle() : handle(NULL) {} - PipeHandle(HANDLE h) : handle(h) {} - HANDLE handle; - }; - PipeHandle pipe; -#endif // defined (OS_WIN) +}; +#else +struct ChannelHandle { + ChannelHandle() {} + ChannelHandle(mojo::MessagePipeHandle h) : mojo_handle(h) {} + + bool is_mojo_channel_handle() const { return mojo_handle.is_valid(); } + mojo::MessagePipeHandle mojo_handle; }; +#endif // defined(OS_NACL_SFI) } // namespace IPC diff --git a/ipc/ipc_listener.h b/ipc/ipc_listener.h index 0277086..d7ad75c 100644 --- a/ipc/ipc_listener.h +++ b/ipc/ipc_listener.h @@ -7,8 +7,11 @@ #include <stdint.h> +#include <string> + #include "build/build_config.h" #include "ipc/ipc_export.h" +#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" namespace IPC { @@ -32,6 +35,12 @@ class IPC_EXPORT Listener { // Called when a message's deserialization failed. virtual void OnBadMessageReceived(const Message& message) {} + // Called when an associated interface request is received on a Channel and + // the Channel has no registered handler for it. + virtual void OnAssociatedInterfaceRequest( + const std::string& interface_name, + mojo::ScopedInterfaceEndpointHandle handle) {} + #if defined(OS_POSIX) // Called on the server side when a channel that listens for connections // denies an attempt to connect. diff --git a/ipc/ipc_message.cc b/ipc/ipc_message.cc index 83ee495..f5e9ac7 100644 --- a/ipc/ipc_message.cc +++ b/ipc/ipc_message.cc @@ -11,10 +11,8 @@ #include "base/atomic_sequence_num.h" #include "base/logging.h" #include "build/build_config.h" -#include "ipc/attachment_broker.h" #include "ipc/ipc_message_attachment.h" #include "ipc/ipc_message_attachment_set.h" -#include "ipc/placeholder_brokerable_attachment.h" #if defined(OS_POSIX) #include "base/file_descriptor_posix.h" @@ -52,9 +50,6 @@ Message::~Message() { Message::Message() : base::Pickle(sizeof(Header)) { header()->routing = header()->type = 0; header()->flags = GetRefNumUpper24(); -#if USE_ATTACHMENT_BROKER - header()->num_brokered_attachments = 0; -#endif #if defined(OS_POSIX) header()->num_fds = 0; header()->pad = 0; @@ -68,9 +63,6 @@ Message::Message(int32_t routing_id, uint32_t type, PriorityValue priority) header()->type = type; DCHECK((priority & 0xffffff00) == 0); header()->flags = priority | GetRefNumUpper24(); -#if USE_ATTACHMENT_BROKER - header()->num_brokered_attachments = 0; -#endif #if defined(OS_POSIX) header()->num_fds = 0; header()->pad = 0; @@ -86,12 +78,10 @@ Message::Message(const char* data, int data_len) Message::Message(const Message& other) : base::Pickle(other) { Init(); attachment_set_ = other.attachment_set_; - sender_pid_ = other.sender_pid_; } void Message::Init() { dispatch_error_ = false; - sender_pid_ = base::kNullProcessId; #ifdef IPC_MESSAGE_LOG_ENABLED received_time_ = 0; dont_log_ = false; @@ -102,7 +92,6 @@ void Message::Init() { Message& Message::operator=(const Message& other) { *static_cast<base::Pickle*>(this) = other; attachment_set_ = other.attachment_set_; - sender_pid_ = other.sender_pid_; return *this; } @@ -146,26 +135,6 @@ Message::NextMessageInfo::NextMessageInfo() message_end(nullptr) {} Message::NextMessageInfo::~NextMessageInfo() {} -Message::SerializedAttachmentIds -Message::SerializedIdsOfBrokerableAttachments() { - DCHECK(HasBrokerableAttachments()); - std::vector<scoped_refptr<IPC::BrokerableAttachment>> attachments( - attachment_set_->GetBrokerableAttachments()); - CHECK_LE(attachments.size(), std::numeric_limits<size_t>::max() / - BrokerableAttachment::kNonceSize); - size_t size = attachments.size() * BrokerableAttachment::kNonceSize; - char* buffer = static_cast<char*>(malloc(size)); - for (size_t i = 0; i < attachments.size(); ++i) { - char* start_range = buffer + i * BrokerableAttachment::kNonceSize; - BrokerableAttachment::AttachmentId id = attachments[i]->GetIdentifier(); - id.SerializeToBuffer(start_range, BrokerableAttachment::kNonceSize); - } - SerializedAttachmentIds ids; - ids.buffer = buffer; - ids.size = size; - return ids; -} - // static void Message::FindNext(const char* range_start, const char* range_end, @@ -182,43 +151,6 @@ void Message::FindNext(const char* range_start, bool have_entire_pickle = static_cast<size_t>(range_end - range_start) >= pickle_size; -#if USE_ATTACHMENT_BROKER - // TODO(dskiba): determine message_size when entire pickle is not available - - if (!have_entire_pickle) - return; - - const char* pickle_end = range_start + pickle_size; - - // The data is not copied. - Message message(range_start, static_cast<int>(pickle_size)); - size_t num_attachments = message.header()->num_brokered_attachments; - - // Check for possible overflows. - size_t max_size_t = std::numeric_limits<size_t>::max(); - if (num_attachments >= max_size_t / BrokerableAttachment::kNonceSize) - return; - - size_t attachment_length = num_attachments * BrokerableAttachment::kNonceSize; - if (pickle_size > max_size_t - attachment_length) - return; - - // Check whether the range includes the attachments. - size_t buffer_length = static_cast<size_t>(range_end - range_start); - if (buffer_length < attachment_length + pickle_size) - return; - - for (size_t i = 0; i < num_attachments; ++i) { - const char* attachment_start = - pickle_end + i * BrokerableAttachment::kNonceSize; - BrokerableAttachment::AttachmentId id(attachment_start, - BrokerableAttachment::kNonceSize); - info->attachment_ids.push_back(id); - } - info->message_end = - pickle_end + num_attachments * BrokerableAttachment::kNonceSize; - info->message_size = info->message_end - range_start; -#else info->message_size = pickle_size; if (!have_entire_pickle) @@ -227,53 +159,32 @@ void Message::FindNext(const char* range_start, const char* pickle_end = range_start + pickle_size; info->message_end = pickle_end; -#endif // USE_ATTACHMENT_BROKER info->pickle_end = pickle_end; info->message_found = true; } -bool Message::AddPlaceholderBrokerableAttachmentWithId( - BrokerableAttachment::AttachmentId id) { - scoped_refptr<PlaceholderBrokerableAttachment> attachment( - new PlaceholderBrokerableAttachment(id)); - return attachment_set()->AddAttachment(attachment); -} - bool Message::WriteAttachment( scoped_refptr<base::Pickle::Attachment> attachment) { - bool brokerable; size_t index; bool success = attachment_set()->AddAttachment( make_scoped_refptr(static_cast<MessageAttachment*>(attachment.get())), - &index, &brokerable); + &index); DCHECK(success); // NOTE: If you add more data to the pickle, make sure to update // PickleSizer::AddAttachment. - // Write the type of descriptor. - WriteBool(brokerable); - // Write the index of the descriptor so that we don't have to // keep the current descriptor as extra decoding state when deserialising. WriteInt(static_cast<int>(index)); -#if USE_ATTACHMENT_BROKER - if (brokerable) - header()->num_brokered_attachments++; -#endif - return success; } bool Message::ReadAttachment( base::PickleIterator* iter, scoped_refptr<base::Pickle::Attachment>* attachment) const { - bool brokerable; - if (!iter->ReadBool(&brokerable)) - return false; - int index; if (!iter->ReadInt(&index)) return false; @@ -282,9 +193,7 @@ bool Message::ReadAttachment( if (!attachment_set) return false; - *attachment = brokerable - ? attachment_set->GetBrokerableAttachmentAt(index) - : attachment_set->GetNonBrokerableAttachmentAt(index); + *attachment = attachment_set->GetAttachmentAt(index); return nullptr != attachment->get(); } @@ -293,13 +202,4 @@ bool Message::HasAttachments() const { return attachment_set_.get() && !attachment_set_->empty(); } -bool Message::HasMojoHandles() const { - return attachment_set_.get() && attachment_set_->num_mojo_handles() > 0; -} - -bool Message::HasBrokerableAttachments() const { - return attachment_set_.get() && - attachment_set_->num_brokerable_attachments() > 0; -} - } // namespace IPC diff --git a/ipc/ipc_message.h b/ipc/ipc_message.h index 56c3a46..43e9ae3 100644 --- a/ipc/ipc_message.h +++ b/ipc/ipc_message.h @@ -15,8 +15,6 @@ #include "base/pickle.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" -#include "ipc/attachment_broker.h" -#include "ipc/brokerable_attachment.h" #include "ipc/ipc_export.h" #if !defined(NDEBUG) @@ -32,7 +30,6 @@ class ChannelReader; //------------------------------------------------------------------------------ struct LogData; -class MessageAttachment; class MessageAttachmentSet; class IPC_EXPORT Message : public base::Pickle { @@ -191,34 +188,13 @@ class IPC_EXPORT Message : public base::Pickle { // The end address of the message should be used to determine the start // address of the next message. const char* message_end; - // If the message has brokerable attachments, this vector will contain the - // ids of the brokerable attachments. The caller of FindNext() is - // responsible for adding the attachments to the message. - std::vector<BrokerableAttachment::AttachmentId> attachment_ids; }; - struct SerializedAttachmentIds { - void* buffer; - size_t size; - }; - // Creates a buffer that contains a serialization of the ids of the brokerable - // attachments of the message. This buffer is intended to be sent over the IPC - // channel immediately after the pickled message. The caller takes ownership - // of the buffer. - // This method should only be called if the message has brokerable - // attachments. - SerializedAttachmentIds SerializedIdsOfBrokerableAttachments(); - // |info| is an output parameter and must not be nullptr. static void FindNext(const char* range_start, const char* range_end, NextMessageInfo* info); - // Adds a placeholder brokerable attachment that must be replaced before the - // message can be dispatched. - bool AddPlaceholderBrokerableAttachmentWithId( - BrokerableAttachment::AttachmentId id); - // WriteAttachment appends |attachment| to the end of the set. It returns // false iff the set is full. bool WriteAttachment( @@ -230,13 +206,6 @@ class IPC_EXPORT Message : public base::Pickle { scoped_refptr<base::Pickle::Attachment>* attachment) const override; // Returns true if there are any attachment in this message. bool HasAttachments() const override; - // Returns true if there are any MojoHandleAttachments in this message. - bool HasMojoHandles() const; - // Whether the message has any brokerable attachments. - bool HasBrokerableAttachments() const; - - void set_sender_pid(base::ProcessId id) { sender_pid_ = id; } - base::ProcessId get_sender_pid() const { return sender_pid_; } #ifdef IPC_MESSAGE_LOG_ENABLED // Adds the outgoing time from Time::Now() at the end of the message and sets @@ -273,12 +242,6 @@ class IPC_EXPORT Message : public base::Pickle { int32_t routing; // ID of the view that this message is destined for uint32_t type; // specifies the user-defined message type uint32_t flags; // specifies control flags for the message -#if USE_ATTACHMENT_BROKER - // The number of brokered attachments included with this message. The - // ids of the brokered attachment ids are sent immediately after the pickled - // message, before the next pickled message is sent. - uint32_t num_brokered_attachments; -#endif #if defined(OS_POSIX) uint16_t num_fds; // the number of descriptors included with this message uint16_t pad; // explicitly initialize this to appease valgrind @@ -312,10 +275,6 @@ class IPC_EXPORT Message : public base::Pickle { return attachment_set_.get(); } - // The process id of the sender of the message. This member is populated with - // a valid value for every message dispatched to listeners. - base::ProcessId sender_pid_; - #ifdef IPC_MESSAGE_LOG_ENABLED // Used for logging. mutable int64_t received_time_; diff --git a/ipc/ipc_message_attachment.h b/ipc/ipc_message_attachment.h index 7f7137d..9ff1de8 100644 --- a/ipc/ipc_message_attachment.h +++ b/ipc/ipc_message_attachment.h @@ -10,6 +10,7 @@ #include "base/memory/ref_counted.h" #include "base/pickle.h" #include "build/build_config.h" +#include "ipc/ipc.mojom.h" #include "ipc/ipc_export.h" namespace IPC { @@ -18,18 +19,10 @@ namespace IPC { // or a mojo |MessagePipe|. |GetType()| returns the type of the subclass. class IPC_EXPORT MessageAttachment : public base::Pickle::Attachment { public: - enum Type { - TYPE_PLATFORM_FILE, // The instance is |PlatformFileAttachment|. - TYPE_MOJO_HANDLE, // The instance is |MojoHandleAttachment|. - TYPE_BROKERABLE_ATTACHMENT, // The instance is |BrokerableAttachment|. - }; + using Type = mojom::SerializedHandle::Type; virtual Type GetType() const = 0; -#if defined(OS_POSIX) - virtual base::PlatformFile TakePlatformFile() = 0; -#endif // OS_POSIX - protected: friend class base::RefCountedThreadSafe<MessageAttachment>; MessageAttachment(); diff --git a/ipc/ipc_message_attachment_set.cc b/ipc/ipc_message_attachment_set.cc index 3b7eefb..b9a990d 100644 --- a/ipc/ipc_message_attachment_set.cc +++ b/ipc/ipc_message_attachment_set.cc @@ -11,16 +11,8 @@ #include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "build/build_config.h" -#include "ipc/brokerable_attachment.h" #include "ipc/ipc_message_attachment.h" -#if defined(OS_POSIX) -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> -#include "ipc/ipc_platform_file_attachment_posix.h" -#endif // OS_POSIX - namespace IPC { namespace { @@ -43,7 +35,7 @@ MessageAttachmentSet::MessageAttachmentSet() } MessageAttachmentSet::~MessageAttachmentSet() { - if (consumed_descriptor_highwater_ == num_non_brokerable_attachments()) + if (consumed_descriptor_highwater_ == size()) return; // We close all the owning descriptors. If this message should have @@ -54,39 +46,24 @@ MessageAttachmentSet::~MessageAttachmentSet() { // (which could a DOS against the browser by a rogue renderer) then all // the descriptors have their close flag set and we free all the extra // kernel resources. - LOG(WARNING) << "MessageAttachmentSet destroyed with unconsumed descriptors: " - << consumed_descriptor_highwater_ << "/" << num_descriptors(); + LOG(WARNING) << "MessageAttachmentSet destroyed with unconsumed attachments: " + << consumed_descriptor_highwater_ << "/" << size(); } unsigned MessageAttachmentSet::num_descriptors() const { return count_attachments_of_type(attachments_, - MessageAttachment::TYPE_PLATFORM_FILE); -} - -unsigned MessageAttachmentSet::num_mojo_handles() const { - return count_attachments_of_type(attachments_, - MessageAttachment::TYPE_MOJO_HANDLE); -} - -unsigned MessageAttachmentSet::num_brokerable_attachments() const { - return static_cast<unsigned>(brokerable_attachments_.size()); -} - -unsigned MessageAttachmentSet::num_non_brokerable_attachments() const { - return static_cast<unsigned>(attachments_.size()); + MessageAttachment::Type::PLATFORM_FILE); } unsigned MessageAttachmentSet::size() const { - return static_cast<unsigned>(attachments_.size() + - brokerable_attachments_.size()); + return static_cast<unsigned>(attachments_.size()); } bool MessageAttachmentSet::AddAttachment( scoped_refptr<MessageAttachment> attachment, - size_t* index, - bool* brokerable) { + size_t* index) { #if defined(OS_POSIX) - if (attachment->GetType() == MessageAttachment::TYPE_PLATFORM_FILE && + if (attachment->GetType() == MessageAttachment::Type::PLATFORM_FILE && num_descriptors() == kMaxDescriptorsPerMessage) { DLOG(WARNING) << "Cannot add file descriptor. MessageAttachmentSet full."; return false; @@ -94,19 +71,12 @@ bool MessageAttachmentSet::AddAttachment( #endif switch (attachment->GetType()) { - case MessageAttachment::TYPE_PLATFORM_FILE: - case MessageAttachment::TYPE_MOJO_HANDLE: + case MessageAttachment::Type::PLATFORM_FILE: + case MessageAttachment::Type::MOJO_HANDLE: + case MessageAttachment::Type::WIN_HANDLE: + case MessageAttachment::Type::MACH_PORT: attachments_.push_back(attachment); *index = attachments_.size() - 1; - *brokerable = false; - return true; - case MessageAttachment::TYPE_BROKERABLE_ATTACHMENT: - BrokerableAttachment* brokerable_attachment = - static_cast<BrokerableAttachment*>(attachment.get()); - scoped_refptr<BrokerableAttachment> a(brokerable_attachment); - brokerable_attachments_.push_back(a); - *index = brokerable_attachments_.size() - 1; - *brokerable = true; return true; } return false; @@ -114,16 +84,14 @@ bool MessageAttachmentSet::AddAttachment( bool MessageAttachmentSet::AddAttachment( scoped_refptr<MessageAttachment> attachment) { - bool brokerable; size_t index; - return AddAttachment(attachment, &index, &brokerable); + return AddAttachment(attachment, &index); } -scoped_refptr<MessageAttachment> -MessageAttachmentSet::GetNonBrokerableAttachmentAt(unsigned index) { - if (index >= num_non_brokerable_attachments()) { - DLOG(WARNING) << "Accessing out of bound index:" << index << "/" - << num_non_brokerable_attachments(); +scoped_refptr<MessageAttachment> MessageAttachmentSet::GetAttachmentAt( + unsigned index) { + if (index >= size()) { + DLOG(WARNING) << "Accessing out of bound index:" << index << "/" << size(); return scoped_refptr<MessageAttachment>(); } @@ -148,8 +116,7 @@ MessageAttachmentSet::GetNonBrokerableAttachmentAt(unsigned index) { // end of the array and index 0 is requested, we reset the highwater value. // TODO(morrita): This is absurd. This "wringle" disallow to introduce clearer // ownership model. Only client is NaclIPCAdapter. See crbug.com/415294 - if (index == 0 && - consumed_descriptor_highwater_ == num_non_brokerable_attachments()) { + if (index == 0 && consumed_descriptor_highwater_ == size()) { consumed_descriptor_highwater_ = 0; } @@ -161,90 +128,9 @@ MessageAttachmentSet::GetNonBrokerableAttachmentAt(unsigned index) { return attachments_[index]; } -scoped_refptr<MessageAttachment> -MessageAttachmentSet::GetBrokerableAttachmentAt(unsigned index) { - if (index >= num_brokerable_attachments()) { - DLOG(WARNING) << "Accessing out of bound index:" << index << "/" - << num_brokerable_attachments(); - return scoped_refptr<MessageAttachment>(); - } - - scoped_refptr<BrokerableAttachment> brokerable_attachment( - brokerable_attachments_[index]); - return scoped_refptr<MessageAttachment>(brokerable_attachment.get()); -} - void MessageAttachmentSet::CommitAllDescriptors() { attachments_.clear(); consumed_descriptor_highwater_ = 0; } -std::vector<scoped_refptr<IPC::BrokerableAttachment>> -MessageAttachmentSet::GetBrokerableAttachments() const { - return brokerable_attachments_; -} - -void MessageAttachmentSet::ReplacePlaceholderWithAttachment( - const scoped_refptr<BrokerableAttachment>& attachment) { - DCHECK_NE(BrokerableAttachment::PLACEHOLDER, attachment->GetBrokerableType()); - for (auto it = brokerable_attachments_.begin(); - it != brokerable_attachments_.end(); ++it) { - if ((*it)->GetBrokerableType() == BrokerableAttachment::PLACEHOLDER && - (*it)->GetIdentifier() == attachment->GetIdentifier()) { - *it = attachment; - return; - } - } - - // This function should only be called if there is a placeholder ready to be - // replaced. - NOTREACHED(); -} - -#if defined(OS_POSIX) - -void MessageAttachmentSet::PeekDescriptors(base::PlatformFile* buffer) const { - for (size_t i = 0; i != attachments_.size(); ++i) - buffer[i] = internal::GetPlatformFile(attachments_[i]); -} - -bool MessageAttachmentSet::ContainsDirectoryDescriptor() const { - struct stat st; - - for (auto i = attachments_.begin(); i != attachments_.end(); ++i) { - if (fstat(internal::GetPlatformFile(*i), &st) == 0 && S_ISDIR(st.st_mode)) - return true; - } - - return false; -} - -void MessageAttachmentSet::ReleaseFDsToClose( - std::vector<base::PlatformFile>* fds) { - for (size_t i = 0; i < attachments_.size(); ++i) { - internal::PlatformFileAttachment* file = - static_cast<internal::PlatformFileAttachment*>(attachments_[i].get()); - if (file->Owns()) - fds->push_back(file->TakePlatformFile()); - } - - CommitAllDescriptors(); -} - -void MessageAttachmentSet::AddDescriptorsToOwn(const base::PlatformFile* buffer, - unsigned count) { - DCHECK(count <= kMaxDescriptorsPerMessage); - DCHECK_EQ(num_descriptors(), 0u); - DCHECK_EQ(consumed_descriptor_highwater_, 0u); - - attachments_.reserve(count); - for (unsigned i = 0; i < count; ++i) - AddAttachment( - new internal::PlatformFileAttachment(base::ScopedFD(buffer[i]))); -} - -#endif // OS_POSIX - } // namespace IPC - - diff --git a/ipc/ipc_message_attachment_set.h b/ipc/ipc_message_attachment_set.h index 764c818..de37211 100644 --- a/ipc/ipc_message_attachment_set.h +++ b/ipc/ipc_message_attachment_set.h @@ -14,31 +14,17 @@ #include "build/build_config.h" #include "ipc/ipc_export.h" -#if defined(OS_POSIX) -#include "base/files/file.h" -#endif - namespace IPC { -class BrokerableAttachment; class MessageAttachment; // ----------------------------------------------------------------------------- // A MessageAttachmentSet is an ordered set of MessageAttachment objects -// associated with an IPC message. There are three types of MessageAttachments: -// 1) TYPE_PLATFORM_FILE is transmitted over the Channel's underlying -// UNIX domain socket -// 2) TYPE_MOJO_HANDLE is transmitted over the Mojo MessagePipe. -// 3) TYPE_BROKERABLE_ATTACHMENT is transmitted by the Attachment Broker. -// Any given IPC Message can have attachments of type (1) or (2), but not both. -// These are stored in |attachments_|. Attachments of type (3) are stored in -// |brokerable_attachments_|. -// -// To produce a deterministic ordering, all attachments in |attachments_| are -// considered to come before those in |brokerable_attachments_|. These -// attachments are transmitted across different communication channels, and -// multiplexed by the receiver, so ordering between them cannot be guaranteed. +// associated with an IPC message. All attachments are wrapped in a mojo handle +// if necessary and sent over the mojo message pipe. // +// For ChannelNacl under SFI NaCl, only Type::PLATFORM_FILE is supported. In +// that case, the FD is sent over socket. // ----------------------------------------------------------------------------- class IPC_EXPORT MessageAttachmentSet : public base::RefCountedThreadSafe<MessageAttachmentSet> { @@ -47,57 +33,31 @@ class IPC_EXPORT MessageAttachmentSet // Return the number of attachments unsigned size() const; - // Return the number of file descriptors - unsigned num_descriptors() const; - // Return the number of mojo handles in the attachment set - unsigned num_mojo_handles() const; - // Return the number of brokerable attachments in the attachment set. - unsigned num_brokerable_attachments() const; - // Return the number of non-brokerable attachments in the attachment set. - unsigned num_non_brokerable_attachments() const; // Return true if no unconsumed descriptors remain - bool empty() const { return 0 == size(); } + bool empty() const { return attachments_.empty(); } // Returns whether the attachment was successfully added. // |index| is an output variable. On success, it contains the index of the // newly added attachment. - // |brokerable| is an output variable. On success, it describes which vector - // the attachment was added to. bool AddAttachment(scoped_refptr<MessageAttachment> attachment, - size_t* index, - bool* brokerable); + size_t* index); // Similar to the above method, but without output variables. bool AddAttachment(scoped_refptr<MessageAttachment> attachment); - // Take the nth non-brokerable attachment from the beginning of the vector, - // Code using this /must/ access the attachments in order, and must do it at - // most once. + // Take the nth from the beginning of the vector, Code using this /must/ + // access the attachments in order, and must do it at most once. // // This interface is designed for the deserialising code as it doesn't // support close flags. // returns: an attachment, or nullptr on error - scoped_refptr<MessageAttachment> GetNonBrokerableAttachmentAt(unsigned index); + scoped_refptr<MessageAttachment> GetAttachmentAt(unsigned index); - // Similar to GetNonBrokerableAttachmentAt, but there are no ordering - // requirements. - scoped_refptr<MessageAttachment> GetBrokerableAttachmentAt(unsigned index); - - // This must be called after transmitting the descriptors returned by - // PeekDescriptors. It marks all the non-brokerable descriptors as consumed - // and closes those which are auto-close. + // Marks all the descriptors as consumed and closes those which are + // auto-close. void CommitAllDescriptors(); - // Returns a vector of all brokerable attachments. - std::vector<scoped_refptr<IPC::BrokerableAttachment>> - GetBrokerableAttachments() const; - - // Replaces a placeholder brokerable attachment with |attachment|, matching - // them by their id. - void ReplacePlaceholderWithAttachment( - const scoped_refptr<BrokerableAttachment>& attachment); - #if defined(OS_POSIX) // This is the maximum number of descriptors per message. We need to know this // because the control message kernel interface has to be given a buffer which @@ -108,32 +68,6 @@ class IPC_EXPORT MessageAttachmentSet // In debugging mode, it's a fatal error to try and add more than this number // of descriptors to a MessageAttachmentSet. static const size_t kMaxDescriptorsPerMessage = 7; - - // --------------------------------------------------------------------------- - // Interfaces for transmission... - - // Fill an array with file descriptors without 'consuming' them. - // CommitAllDescriptors must be called after these descriptors have been - // transmitted. - // buffer: (output) a buffer of, at least, size() integers. - void PeekDescriptors(base::PlatformFile* buffer) const; - // Returns true if any contained file descriptors appear to be handles to a - // directory. - bool ContainsDirectoryDescriptor() const; - // Fetch all filedescriptors with the "auto close" property. Used instead of - // CommitAllDescriptors() when closing must be handled manually. - void ReleaseFDsToClose(std::vector<base::PlatformFile>* fds); - - // --------------------------------------------------------------------------- - - // --------------------------------------------------------------------------- - // Interfaces for receiving... - - // Set the contents of the set from the given buffer. This set must be empty - // before calling. The auto-close flag is set on all the descriptors so that - // unconsumed descriptors are closed on destruction. - void AddDescriptorsToOwn(const base::PlatformFile* buffer, unsigned count); - #endif // OS_POSIX // --------------------------------------------------------------------------- @@ -143,17 +77,16 @@ class IPC_EXPORT MessageAttachmentSet ~MessageAttachmentSet(); - // All elements either have type TYPE_PLATFORM_FILE or TYPE_MOJO_HANDLE. - std::vector<scoped_refptr<MessageAttachment>> attachments_; + // Return the number of file descriptors + unsigned num_descriptors() const; - // All elements have type TYPE_BROKERABLE_ATTACHMENT. - std::vector<scoped_refptr<BrokerableAttachment>> brokerable_attachments_; + std::vector<scoped_refptr<MessageAttachment>> attachments_; // This contains the index of the next descriptor which should be consumed. // It's used in a couple of ways. Firstly, at destruction we can check that // all the descriptors have been read (with GetNthDescriptor). Secondly, we // can check that they are read in order. - mutable unsigned consumed_descriptor_highwater_; + unsigned consumed_descriptor_highwater_; DISALLOW_COPY_AND_ASSIGN(MessageAttachmentSet); }; diff --git a/ipc/ipc_message_start.h b/ipc/ipc_message_start.h index c1e1b9f..3c41f04 100644 --- a/ipc/ipc_message_start.h +++ b/ipc/ipc_message_start.h @@ -29,9 +29,7 @@ enum IPCMessageStart { FileUtilitiesMsgStart, DatabaseMsgStart, DOMStorageMsgStart, - IndexedDBMsgStart, SpeechRecognitionMsgStart, - AutofillMsgStart, SafeBrowsingMsgStart, P2PMsgStart, ResourceMsgStart, @@ -86,7 +84,6 @@ enum IPCMessageStart { ScreenOrientationMsgStart, MediaStreamTrackMetricsHostMsgStart, ChromeExtensionMsgStart, - TranslateMsgStart, PushMessagingMsgStart, GinJavaBridgeMsgStart, ChromeUtilityPrintingMsgStart, @@ -110,13 +107,11 @@ enum IPCMessageStart { CastCryptoMsgStart, CastChannelMsgStart, DataReductionProxyStart, - ContentSettingsMsgStart, ChromeAppBannerMsgStart, AttachmentBrokerMsgStart, RenderProcessMsgStart, PageLoadMetricsMsgStart, MemoryMsgStart, - MediaSessionMsgStart, IPCTestMsgStart, ArcInstanceMsgStart, ArcInstanceHostMsgStart, diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc index 60c12ab..bf8daa5 100644 --- a/ipc/ipc_message_utils.cc +++ b/ipc/ipc_message_utils.cc @@ -13,6 +13,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" +#include "base/unguessable_token.h" #include "base/values.h" #include "build/build_config.h" #include "ipc/ipc_channel_handle.h" @@ -21,6 +22,7 @@ #include "ipc/ipc_mojo_param_traits.h" #if defined(OS_POSIX) +#include "base/file_descriptor_posix.h" #include "ipc/ipc_platform_file_attachment_posix.h" #endif @@ -41,7 +43,7 @@ namespace IPC { namespace { -const int kMaxRecursionDepth = 100; +const int kMaxRecursionDepth = 200; template<typename CharType> void LogBytes(const std::vector<CharType>& data, std::string* out) { @@ -76,25 +78,25 @@ void GetValueSize(base::PickleSizer* sizer, const base::Value* value, int recursion) { if (recursion > kMaxRecursionDepth) { - LOG(WARNING) << "Max recursion depth hit in GetValueSize."; + LOG(ERROR) << "Max recursion depth hit in GetValueSize."; return; } sizer->AddInt(); switch (value->GetType()) { - case base::Value::TYPE_NULL: + case base::Value::Type::NONE: break; - case base::Value::TYPE_BOOLEAN: + case base::Value::Type::BOOLEAN: sizer->AddBool(); break; - case base::Value::TYPE_INTEGER: + case base::Value::Type::INTEGER: sizer->AddInt(); break; - case base::Value::TYPE_DOUBLE: + case base::Value::Type::DOUBLE: sizer->AddDouble(); break; - case base::Value::TYPE_STRING: { - const base::StringValue* result; + case base::Value::Type::STRING: { + const base::Value* result; value->GetAsString(&result); if (value->GetAsString(&result)) { DCHECK(result); @@ -107,13 +109,11 @@ void GetValueSize(base::PickleSizer* sizer, } break; } - case base::Value::TYPE_BINARY: { - const base::BinaryValue* binary = - static_cast<const base::BinaryValue*>(value); - sizer->AddData(static_cast<int>(binary->GetSize())); + case base::Value::Type::BINARY: { + sizer->AddData(static_cast<int>(value->GetSize())); break; } - case base::Value::TYPE_DICTIONARY: { + case base::Value::Type::DICTIONARY: { sizer->AddInt(); const base::DictionaryValue* dict = static_cast<const base::DictionaryValue*>(value); @@ -124,7 +124,7 @@ void GetValueSize(base::PickleSizer* sizer, } break; } - case base::Value::TYPE_LIST: { + case base::Value::Type::LIST: { sizer->AddInt(); const base::ListValue* list = static_cast<const base::ListValue*>(value); for (const auto& entry : *list) { @@ -140,50 +140,48 @@ void GetValueSize(base::PickleSizer* sizer, void WriteValue(base::Pickle* m, const base::Value* value, int recursion) { bool result; if (recursion > kMaxRecursionDepth) { - LOG(WARNING) << "Max recursion depth hit in WriteValue."; + LOG(ERROR) << "Max recursion depth hit in WriteValue."; return; } - m->WriteInt(value->GetType()); + m->WriteInt(static_cast<int>(value->GetType())); switch (value->GetType()) { - case base::Value::TYPE_NULL: + case base::Value::Type::NONE: break; - case base::Value::TYPE_BOOLEAN: { + case base::Value::Type::BOOLEAN: { bool val; result = value->GetAsBoolean(&val); DCHECK(result); WriteParam(m, val); break; } - case base::Value::TYPE_INTEGER: { + case base::Value::Type::INTEGER: { int val; result = value->GetAsInteger(&val); DCHECK(result); WriteParam(m, val); break; } - case base::Value::TYPE_DOUBLE: { + case base::Value::Type::DOUBLE: { double val; result = value->GetAsDouble(&val); DCHECK(result); WriteParam(m, val); break; } - case base::Value::TYPE_STRING: { + case base::Value::Type::STRING: { std::string val; result = value->GetAsString(&val); DCHECK(result); WriteParam(m, val); break; } - case base::Value::TYPE_BINARY: { - const base::BinaryValue* binary = - static_cast<const base::BinaryValue*>(value); - m->WriteData(binary->GetBuffer(), static_cast<int>(binary->GetSize())); + case base::Value::Type::BINARY: { + m->WriteData(value->GetBuffer(), static_cast<int>(value->GetSize())); break; } - case base::Value::TYPE_DICTIONARY: { + case base::Value::Type::DICTIONARY: { const base::DictionaryValue* dict = static_cast<const base::DictionaryValue*>(value); @@ -196,7 +194,7 @@ void WriteValue(base::Pickle* m, const base::Value* value, int recursion) { } break; } - case base::Value::TYPE_LIST: { + case base::Value::Type::LIST: { const base::ListValue* list = static_cast<const base::ListValue*>(value); WriteParam(m, static_cast<int>(list->GetSize())); for (const auto& entry : *list) { @@ -254,7 +252,7 @@ bool ReadValue(const base::Pickle* m, base::Value** value, int recursion) { if (recursion > kMaxRecursionDepth) { - LOG(WARNING) << "Max recursion depth hit in ReadValue."; + LOG(ERROR) << "Max recursion depth hit in ReadValue."; return false; } @@ -262,39 +260,39 @@ bool ReadValue(const base::Pickle* m, if (!ReadParam(m, iter, &type)) return false; - switch (type) { - case base::Value::TYPE_NULL: + switch (static_cast<base::Value::Type>(type)) { + case base::Value::Type::NONE: *value = base::Value::CreateNullValue().release(); break; - case base::Value::TYPE_BOOLEAN: { + case base::Value::Type::BOOLEAN: { bool val; if (!ReadParam(m, iter, &val)) return false; - *value = new base::FundamentalValue(val); + *value = new base::Value(val); break; } - case base::Value::TYPE_INTEGER: { + case base::Value::Type::INTEGER: { int val; if (!ReadParam(m, iter, &val)) return false; - *value = new base::FundamentalValue(val); + *value = new base::Value(val); break; } - case base::Value::TYPE_DOUBLE: { + case base::Value::Type::DOUBLE: { double val; if (!ReadParam(m, iter, &val)) return false; - *value = new base::FundamentalValue(val); + *value = new base::Value(val); break; } - case base::Value::TYPE_STRING: { + case base::Value::Type::STRING: { std::string val; if (!ReadParam(m, iter, &val)) return false; - *value = new base::StringValue(val); + *value = new base::Value(val); break; } - case base::Value::TYPE_BINARY: { + case base::Value::Type::BINARY: { const char* data; int length; if (!iter->ReadData(&data, &length)) @@ -304,14 +302,14 @@ bool ReadValue(const base::Pickle* m, *value = val.release(); break; } - case base::Value::TYPE_DICTIONARY: { + case base::Value::Type::DICTIONARY: { std::unique_ptr<base::DictionaryValue> val(new base::DictionaryValue()); if (!ReadDictionaryValue(m, iter, val.get(), recursion)) return false; *value = val.release(); break; } - case base::Value::TYPE_LIST: { + case base::Value::Type::LIST: { std::unique_ptr<base::ListValue> val(new base::ListValue()); if (!ReadListValue(m, iter, val.get(), recursion)) return false; @@ -586,28 +584,6 @@ void ParamTraits<std::vector<bool> >::Log(const param_type& p, std::string* l) { } } -void ParamTraits<BrokerableAttachment::AttachmentId>::Write( - base::Pickle* m, - const param_type& p) { - m->WriteBytes(p.nonce, BrokerableAttachment::kNonceSize); -} - -bool ParamTraits<BrokerableAttachment::AttachmentId>::Read( - const base::Pickle* m, - base::PickleIterator* iter, - param_type* r) { - const char* data; - if (!iter->ReadBytes(&data, BrokerableAttachment::kNonceSize)) - return false; - memcpy(r->nonce, data, BrokerableAttachment::kNonceSize); - return true; -} - -void ParamTraits<BrokerableAttachment::AttachmentId>::Log(const param_type& p, - std::string* l) { - l->append(base::HexEncode(p.nonce, BrokerableAttachment::kNonceSize)); -} - void ParamTraits<base::DictionaryValue>::GetSize(base::PickleSizer* sizer, const param_type& p) { GetValueSize(sizer, &p, 0); @@ -622,7 +598,8 @@ bool ParamTraits<base::DictionaryValue>::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { int type; - if (!ReadParam(m, iter, &type) || type != base::Value::TYPE_DICTIONARY) + if (!ReadParam(m, iter, &type) || + type != static_cast<int>(base::Value::Type::DICTIONARY)) return false; return ReadDictionaryValue(m, iter, r, 0); @@ -678,8 +655,14 @@ bool ParamTraits<base::FileDescriptor>::Read(const base::Pickle* m, if (!m->ReadAttachment(iter, &attachment)) return false; + if (static_cast<MessageAttachment*>(attachment.get())->GetType() != + MessageAttachment::Type::PLATFORM_FILE) { + return false; + } + *r = base::FileDescriptor( - static_cast<MessageAttachment*>(attachment.get())->TakePlatformFile(), + static_cast<internal::PlatformFileAttachment*>(attachment.get()) + ->TakePlatformFile(), true); return true; } @@ -832,7 +815,8 @@ bool ParamTraits<base::ListValue>::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { int type; - if (!ReadParam(m, iter, &type) || type != base::Value::TYPE_LIST) + if (!ReadParam(m, iter, &type) || + type != static_cast<int>(base::Value::Type::LIST)) return false; return ReadListValue(m, iter, r, 0); @@ -998,47 +982,81 @@ void ParamTraits<base::TimeTicks>::Log(const param_type& p, std::string* l) { ParamTraits<int64_t>::Log(p.ToInternalValue(), l); } +// If base::UnguessableToken is no longer 128 bits, the IPC serialization logic +// below should be updated. +static_assert(sizeof(base::UnguessableToken) == 2 * sizeof(uint64_t), + "base::UnguessableToken should be of size 2 * sizeof(uint64_t)."); + +void ParamTraits<base::UnguessableToken>::GetSize(base::PickleSizer* sizer, + const param_type& p) { + sizer->AddBytes(2 * sizeof(uint64_t)); +} + +void ParamTraits<base::UnguessableToken>::Write(base::Pickle* m, + const param_type& p) { + DCHECK(!p.is_empty()); + + ParamTraits<uint64_t>::Write(m, p.GetHighForSerialization()); + ParamTraits<uint64_t>::Write(m, p.GetLowForSerialization()); +} + +bool ParamTraits<base::UnguessableToken>::Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r) { + uint64_t high, low; + if (!ParamTraits<uint64_t>::Read(m, iter, &high) || + !ParamTraits<uint64_t>::Read(m, iter, &low)) + return false; + + // Receiving a zeroed UnguessableToken is a security issue. + if (high == 0 && low == 0) + return false; + + *r = base::UnguessableToken::Deserialize(high, low); + return true; +} + +void ParamTraits<base::UnguessableToken>::Log(const param_type& p, + std::string* l) { + l->append(p.ToString()); +} + void ParamTraits<IPC::ChannelHandle>::GetSize(base::PickleSizer* sizer, const param_type& p) { - GetParamSize(sizer, p.name); -#if defined(OS_POSIX) +#if defined(OS_NACL_SFI) GetParamSize(sizer, p.socket); -#endif +#else GetParamSize(sizer, p.mojo_handle); +#endif } void ParamTraits<IPC::ChannelHandle>::Write(base::Pickle* m, const param_type& p) { -#if defined(OS_WIN) - // On Windows marshalling pipe handle is not supported. - DCHECK(p.pipe.handle == NULL); -#endif // defined (OS_WIN) - WriteParam(m, p.name); -#if defined(OS_POSIX) +#if defined(OS_NACL_SFI) WriteParam(m, p.socket); -#endif +#else WriteParam(m, p.mojo_handle); +#endif } bool ParamTraits<IPC::ChannelHandle>::Read(const base::Pickle* m, base::PickleIterator* iter, param_type* r) { - return ReadParam(m, iter, &r->name) -#if defined(OS_POSIX) - && ReadParam(m, iter, &r->socket) +#if defined(OS_NACL_SFI) + return ReadParam(m, iter, &r->socket); +#else + return ReadParam(m, iter, &r->mojo_handle); #endif - && ReadParam(m, iter, &r->mojo_handle); } void ParamTraits<IPC::ChannelHandle>::Log(const param_type& p, std::string* l) { - l->append(base::StringPrintf("ChannelHandle(%s", p.name.c_str())); -#if defined(OS_POSIX) - l->append(", "); + l->append("ChannelHandle("); +#if defined(OS_NACL_SFI) ParamTraits<base::FileDescriptor>::Log(p.socket, l); -#endif - l->append(", "); +#else LogParam(p.mojo_handle, l); +#endif l->append(")"); } diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h index 6712d3c..40dd670 100644 --- a/ipc/ipc_message_utils.h +++ b/ipc/ipc_message_utils.h @@ -27,7 +27,6 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "build/build_config.h" -#include "ipc/brokerable_attachment.h" #include "ipc/ipc_message_start.h" #include "ipc/ipc_param_traits.h" #include "ipc/ipc_sync_message.h" @@ -40,6 +39,7 @@ class NullableString16; class Time; class TimeDelta; class TimeTicks; +class UnguessableToken; struct FileDescriptor; #if (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_WIN) @@ -307,6 +307,37 @@ struct IPC_EXPORT ParamTraits<double> { static void Log(const param_type& p, std::string* l); }; +template <class P, size_t Size> +struct ParamTraits<P[Size]> { + using param_type = P[Size]; + static void GetSize(base::PickleSizer* sizer, const param_type& p) { + for (const P& element : p) + GetParamSize(sizer, element); + } + static void Write(base::Pickle* m, const param_type& p) { + for (const P& element : p) + WriteParam(m, element); + } + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r) { + for (P& element : *r) { + if (!ReadParam(m, iter, &element)) + return false; + } + return true; + } + static void Log(const param_type& p, std::string* l) { + l->append("["); + for (const P& element : p) { + if (&element != &p[0]) + l->append(" "); + LogParam(element, l); + } + l->append("]"); + } +}; + // STL ParamTraits ------------------------------------------------------------- template <> @@ -512,17 +543,6 @@ struct ParamTraits<std::pair<A, B> > { } }; -// IPC ParamTraits ------------------------------------------------------------- -template <> -struct IPC_EXPORT ParamTraits<BrokerableAttachment::AttachmentId> { - typedef BrokerableAttachment::AttachmentId param_type; - static void Write(base::Pickle* m, const param_type& p); - static bool Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* r); - static void Log(const param_type& p, std::string* l); -}; - // Base ParamTraits ------------------------------------------------------------ template <> @@ -667,6 +687,17 @@ struct IPC_EXPORT ParamTraits<base::TimeTicks> { }; template <> +struct IPC_EXPORT ParamTraits<base::UnguessableToken> { + typedef base::UnguessableToken param_type; + static void GetSize(base::PickleSizer* sizer, const param_type& p); + static void Write(base::Pickle* m, const param_type& p); + static bool Read(const base::Pickle* m, + base::PickleIterator* iter, + param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> struct ParamTraits<std::tuple<>> { typedef std::tuple<> param_type; static void GetSize(base::PickleSizer* sizer, const param_type& p) {} @@ -1099,10 +1130,9 @@ struct IPC_EXPORT ParamTraits<MSG> { // Generic message subclasses // defined in ipc_logging.cc -IPC_EXPORT void GenerateLogData(const std::string& channel, - const Message& message, - LogData* data, bool get_params); - +IPC_EXPORT void GenerateLogData(const Message& message, + LogData* data, + bool get_params); #if defined(IPC_MESSAGE_LOG_ENABLED) inline void AddOutputParamsToLog(const Message* msg, std::string* l) { @@ -1129,7 +1159,7 @@ inline void ConnectMessageAndReply(const Message* msg, Message* reply) { // output parameters at that point. Instead, save its data and log it // with the outgoing reply message when it's sent. LogData* data = new LogData; - GenerateLogData("", *msg, data, true); + GenerateLogData(*msg, data, true); msg->set_dont_log(); reply->set_sync_log_data(data); } diff --git a/ipc/ipc_mojo_handle_attachment.cc b/ipc/ipc_mojo_handle_attachment.cc index 819a12b..e3421c3 100644 --- a/ipc/ipc_mojo_handle_attachment.cc +++ b/ipc/ipc_mojo_handle_attachment.cc @@ -18,16 +18,9 @@ MojoHandleAttachment::~MojoHandleAttachment() { } MessageAttachment::Type MojoHandleAttachment::GetType() const { - return TYPE_MOJO_HANDLE; + return Type::MOJO_HANDLE; } -#if defined(OS_POSIX) -base::PlatformFile MojoHandleAttachment::TakePlatformFile() { - NOTREACHED(); - return base::kInvalidPlatformFile; -} -#endif // OS_POSIX - mojo::ScopedHandle MojoHandleAttachment::TakeHandle() { return std::move(handle_); } diff --git a/ipc/ipc_mojo_handle_attachment.h b/ipc/ipc_mojo_handle_attachment.h index 6aa1888..d615276 100644 --- a/ipc/ipc_mojo_handle_attachment.h +++ b/ipc/ipc_mojo_handle_attachment.h @@ -26,11 +26,6 @@ class IPC_EXPORT MojoHandleAttachment : public MessageAttachment { Type GetType() const override; -#if defined(OS_POSIX) - // Should not be called. - base::PlatformFile TakePlatformFile() override; -#endif // OS_POSIX - // Returns the owning handle transferring the ownership. mojo::ScopedHandle TakeHandle(); diff --git a/ipc/ipc_mojo_message_helper.cc b/ipc/ipc_mojo_message_helper.cc index 8f86945..a87a2d6 100644 --- a/ipc/ipc_mojo_message_helper.cc +++ b/ipc/ipc_mojo_message_helper.cc @@ -32,7 +32,7 @@ bool MojoMessageHelper::ReadMessagePipeFrom( MessageAttachment::Type type = static_cast<MessageAttachment*>(attachment.get())->GetType(); - if (type != MessageAttachment::TYPE_MOJO_HANDLE) { + if (type != MessageAttachment::Type::MOJO_HANDLE) { LOG(ERROR) << "Unxpected attachment type:" << type; return false; } diff --git a/ipc/ipc_platform_file_attachment_posix.cc b/ipc/ipc_platform_file_attachment_posix.cc index b130ab2..7111cfa 100644 --- a/ipc/ipc_platform_file_attachment_posix.cc +++ b/ipc/ipc_platform_file_attachment_posix.cc @@ -20,7 +20,7 @@ PlatformFileAttachment::~PlatformFileAttachment() { } MessageAttachment::Type PlatformFileAttachment::GetType() const { - return TYPE_PLATFORM_FILE; + return Type::PLATFORM_FILE; } base::PlatformFile PlatformFileAttachment::TakePlatformFile() { @@ -30,7 +30,7 @@ base::PlatformFile PlatformFileAttachment::TakePlatformFile() { base::PlatformFile GetPlatformFile( scoped_refptr<MessageAttachment> attachment) { - DCHECK_EQ(attachment->GetType(), MessageAttachment::TYPE_PLATFORM_FILE); + DCHECK_EQ(attachment->GetType(), MessageAttachment::Type::PLATFORM_FILE); return static_cast<PlatformFileAttachment*>(attachment.get())->file(); } diff --git a/ipc/ipc_platform_file_attachment_posix.h b/ipc/ipc_platform_file_attachment_posix.h index d1eff60..9b07900 100644 --- a/ipc/ipc_platform_file_attachment_posix.h +++ b/ipc/ipc_platform_file_attachment_posix.h @@ -23,7 +23,7 @@ class IPC_EXPORT PlatformFileAttachment : public MessageAttachment { explicit PlatformFileAttachment(base::ScopedFD file); Type GetType() const override; - base::PlatformFile TakePlatformFile() override; + base::PlatformFile TakePlatformFile(); base::PlatformFile file() const { return file_; } bool Owns() const { return owning_.is_valid(); } diff --git a/ipc/placeholder_brokerable_attachment.cc b/ipc/placeholder_brokerable_attachment.cc deleted file mode 100644 index e64e398..0000000 --- a/ipc/placeholder_brokerable_attachment.cc +++ /dev/null @@ -1,14 +0,0 @@ -// 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 "ipc/placeholder_brokerable_attachment.h" - -namespace IPC { - -BrokerableAttachment::BrokerableType -PlaceholderBrokerableAttachment::GetBrokerableType() const { - return PLACEHOLDER; -} - -} // namespace IPC diff --git a/ipc/placeholder_brokerable_attachment.h b/ipc/placeholder_brokerable_attachment.h deleted file mode 100644 index a8b08ef..0000000 --- a/ipc/placeholder_brokerable_attachment.h +++ /dev/null @@ -1,29 +0,0 @@ -// 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 IPC_PLACEHOLDER_BROKERABLE_ATTACHMENT_H_ -#define IPC_PLACEHOLDER_BROKERABLE_ATTACHMENT_H_ - -#include "base/macros.h" -#include "ipc/brokerable_attachment.h" -#include "ipc/ipc_export.h" - -namespace IPC { - -// This subclass of BrokerableAttachment has an AttachmentId, and nothing else. -// It is intended to be replaced by the attachment broker. -class IPC_EXPORT PlaceholderBrokerableAttachment : public BrokerableAttachment { - public: - PlaceholderBrokerableAttachment(const AttachmentId& id) - : BrokerableAttachment(id){}; - BrokerableType GetBrokerableType() const override; - - protected: - ~PlaceholderBrokerableAttachment() override{}; - DISALLOW_COPY_AND_ASSIGN(PlaceholderBrokerableAttachment); -}; - -} // namespace IPC - -#endif // IPC_PLACEHOLDER_BROKERABLE_ATTACHMENT_H_ diff --git a/libmojo.pc.in b/libmojo.pc.in new file mode 100644 index 0000000..cceadd4 --- /dev/null +++ b/libmojo.pc.in @@ -0,0 +1,13 @@ +bslot=@BSLOT@ +prefix=/usr +exec_prefix=${prefix} +libdir=${exec_prefix}/@LIB@ +includedir=${prefix}/include + +Name: libmojo +Description: Chrome Mojo IPC library +Requires.private: +Version: ${bslot} +Libs: -lmojo-${bslot}.pie +Libs.private: +Cflags: -I${includedir}/libmojo-${bslot} -Wno-cast-qual -Wno-cast-align diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn index 2dac654..070e2d1 100644 --- a/mojo/BUILD.gn +++ b/mojo/BUILD.gn @@ -13,9 +13,6 @@ group("mojo") { ] 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" ] } @@ -23,7 +20,7 @@ group("mojo") { deps += [ "//mojo/android" ] } - deps += [ "//services/shell:all" ] + deps += [ "//services/service_manager:all" ] } group("tests") { @@ -31,17 +28,14 @@ group("tests") { deps = [ "//ipc:ipc_tests", "//mojo/common:mojo_common_unittests", - "//mojo/converters/blink:blink_converters_unittests", - "//mojo/edk/js/test:js_integration_tests", - "//mojo/edk/js/test:js_unittests", + "//mojo/edk/js/tests", "//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/shell/public/cpp/tests:mojo_public_application_unittests", - "//services/shell/runner/host:mojo_runner_host_unittests", - "//services/shell/tests", + "//services/service_manager/public/cpp/tests:mojo_public_application_unittests", + "//services/service_manager/tests", ] } @@ -3,5 +3,5 @@ include_rules = [ "+build", "+testing", - "+services/shell", + "+services/service_manager", ] diff --git a/mojo/android/BUILD.gn b/mojo/android/BUILD.gn index 813701c..1a8cdbd 100644 --- a/mojo/android/BUILD.gn +++ b/mojo/android/BUILD.gn @@ -29,6 +29,7 @@ generate_jni("system_java_jni_headers") { 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" @@ -40,12 +41,15 @@ source_set("libsystem_java") { "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/message_pump", + "//mojo/public/c/system", + "//mojo/public/cpp/system", ] } @@ -59,11 +63,12 @@ android_library("system_java") { "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", + "//mojo/public/java:system_java", ] } @@ -90,6 +95,7 @@ android_library("mojo_javatests") { "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 = [ @@ -99,8 +105,13 @@ android_library("mojo_javatests") { "//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", - "//mojo/public/java:system", + "//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/", ] } @@ -120,10 +131,9 @@ shared_library("mojo_java_unittests") { ":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", ] @@ -136,10 +146,10 @@ instrumentation_test_apk("mojo_test_apk") { ":system_java", "//base:base_java", "//mojo/public/interfaces/bindings/tests:test_interfaces", - "//mojo/public/java:bindings", + "//mojo/public/java:bindings_java", + "//third_party/android_support_test_runner:runner_java", ] 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 d2d9423..9e1a593 100644 --- a/mojo/android/javatests/init_library.cc +++ b/mojo/android/javatests/init_library.cc @@ -6,27 +6,33 @@ #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 }, + {"CoreImpl", mojo::android::RegisterCoreImpl}, + {"MojoTestCase", mojo::android::RegisterMojoTestCase}, + {"ValidationTestUtil", mojo::android::RegisterValidationTestUtil}, + {"WatcherImpl", mojo::android::RegisterWatcherImpl}, }; bool RegisterJNI(JNIEnv* env) { return base::android::RegisterJni(env) && - RegisterNativeMethods(env, kMojoRegisteredMethods, - arraysize(kMojoRegisteredMethods)); + RegisterNativeMethods(env, kMojoRegisteredMethods, + arraysize(kMojoRegisteredMethods)); } -bool Init() { +bool NativeInit() { + if (!base::android::OnJNIOnLoadInit()) + return false; + mojo::edk::Init(); return true; } @@ -34,13 +40,11 @@ bool Init() { } // namespace JNI_EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { - 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)) + base::android::InitVM(vm); + JNIEnv* env = base::android::AttachCurrentThread(); + if (!base::android::OnJNIOnLoadRegisterJNI(env) || !RegisterJNI(env) || + !NativeInit()) { 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 6d88985..fc59009 100644 --- a/mojo/android/javatests/mojo_test_case.cc +++ b/mojo/android/javatests/mojo_test_case.cc @@ -16,12 +16,13 @@ #include "base/test/test_support_android.h" #include "base/threading/thread_task_runner_handle.h" #include "jni/MojoTestCase_jni.h" -#include "mojo/message_pump/message_pump_mojo.h" + +using base::android::JavaParamRef; namespace { struct TestEnvironment { - TestEnvironment() : message_loop(mojo::common::MessagePumpMojo::Create()) {} + TestEnvironment() {} 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 c7348ca..f4d7ab7 100644 --- a/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java +++ b/mojo/android/javatests/src/org/chromium/mojo/MojoTestCase.java @@ -4,7 +4,6 @@ package org.chromium.mojo; -import android.content.Context; import android.test.InstrumentationTestCase; import org.chromium.base.ContextUtils; @@ -26,9 +25,9 @@ public class MojoTestCase extends InstrumentationTestCase { @Override protected void setUp() throws Exception { super.setUp(); - Context appContext = getInstrumentation().getTargetContext().getApplicationContext(); - ContextUtils.initApplicationContext(appContext); - LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER).ensureInitialized(appContext); + ContextUtils.initApplicationContext( + getInstrumentation().getTargetContext().getApplicationContext()); + LibraryLoader.get(LibraryProcessType.PROCESS_BROWSER).ensureInitialized(); 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 a46a157..38bd348 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.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.SmallTest; import junit.framework.TestCase; @@ -34,8 +34,8 @@ public class BindingsHelperTest extends TestCase { 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 6886023..d280c77 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.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.SmallTest; import junit.framework.TestCase; @@ -21,12 +21,15 @@ import org.chromium.mojo.bindings.test.mojom.sample.Foo; 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. @@ -81,6 +84,15 @@ public class BindingsTest extends TestCase { 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()); @@ -209,4 +221,21 @@ public class BindingsTest extends TestCase { 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 f0c0f7c..eea92ab 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.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.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 dfb8e21..497be65 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.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.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 9c26875..15f9f1f 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.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.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 56f4b40..cabe230 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.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.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 c101675..8cdd4ab 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.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.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 f96b1e3..d8bd3e8 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.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.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 8af8be1..b2e6ac8 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.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.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 f2edb01..51dbd22 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.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.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 0b632a9..5affb8f 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.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.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 a7e213c..2c17e3a 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.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.SmallTest; import junit.framework.TestCase; @@ -17,6 +17,8 @@ import org.chromium.mojo.bindings.test.mojom.mojo.Struct5; 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 . @@ -128,4 +130,46 @@ public class SerializationTest extends TestCase { 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 e5c4549..8424618 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.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.SmallTest; import org.chromium.base.test.util.UrlUtils; import org.chromium.mojo.HandleMock; @@ -13,6 +13,7 @@ import org.chromium.mojo.bindings.test.mojom.mojo.ConformanceTestInterface; 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; @@ -189,8 +190,10 @@ public class ValidationTest extends MojoTestCase { */ @SmallTest public void testConformance() throws FileNotFoundException { - runTest("conformance_", ConformanceTestInterface.MANAGER.buildStub(null, - ConformanceTestInterface.MANAGER.buildProxy(null, new SinkMessageReceiver()))); + runTest("conformance_", + ConformanceTestInterface.MANAGER.buildStub(CoreImpl.getInstance(), + ConformanceTestInterface.MANAGER.buildProxy( + CoreImpl.getInstance(), 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 14259ef..623abc3 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.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.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 16c16f2..77a9bda 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,12 +4,9 @@ package org.chromium.mojo.system.impl; -import android.test.suitebuilder.annotation.SmallTest; +import android.support.test.filters.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; @@ -584,266 +581,6 @@ public class CoreImplTest extends MojoTestCase { } } - 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 fb4d140..75f79b3 100644 --- a/mojo/android/javatests/validation_test_util.cc +++ b/mojo/android/javatests/validation_test_util.cc @@ -14,6 +14,9 @@ #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 { @@ -34,8 +37,7 @@ ScopedJavaLocalRef<jobject> ParseData( input, &data, &num_handles, &error_message)) { ScopedJavaLocalRef<jstring> j_error_message = base::android::ConvertUTF8ToJavaString(env, error_message); - return Java_ValidationTestUtil_buildData(env, NULL, 0, - j_error_message.obj()); + return Java_ValidationTestUtil_buildData(env, nullptr, 0, j_error_message); } void* data_ptr = &data[0]; if (!data_ptr) { @@ -44,7 +46,8 @@ ScopedJavaLocalRef<jobject> ParseData( } jobject byte_buffer = env->NewDirectByteBuffer(data_ptr, data.size()); - return Java_ValidationTestUtil_buildData(env, byte_buffer, num_handles, NULL); + return Java_ValidationTestUtil_buildData(env, byte_buffer, num_handles, + nullptr); } } // namespace android diff --git a/mojo/android/system/base_run_loop.cc b/mojo/android/system/base_run_loop.cc index e48d2f0..22511f3 100644 --- a/mojo/android/system/base_run_loop.cc +++ b/mojo/android/system/base_run_loop.cc @@ -10,30 +10,31 @@ #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" -#include "mojo/message_pump/message_pump_mojo.h" + +using base::android::JavaParamRef; namespace mojo { namespace android { static jlong CreateBaseRunLoop(JNIEnv* env, const JavaParamRef<jobject>& jcaller) { - base::MessageLoop* message_loop = - new base::MessageLoop(common::MessagePumpMojo::Create()); + base::MessageLoop* message_loop = new base::MessageLoop; return reinterpret_cast<uintptr_t>(message_loop); } static void Run(JNIEnv* env, - const JavaParamRef<jobject>& jcaller, - jlong runLoopID) { - reinterpret_cast<base::MessageLoop*>(runLoopID)->Run(); + const JavaParamRef<jobject>& jcaller) { + base::RunLoop().Run(); } static void RunUntilIdle(JNIEnv* env, - const JavaParamRef<jobject>& jcaller, - jlong runLoopID) { - reinterpret_cast<base::MessageLoop*>(runLoopID)->RunUntilIdle(); + const JavaParamRef<jobject>& jcaller) { + base::RunLoop().RunUntilIdle(); } static void Quit(JNIEnv* env, @@ -45,7 +46,7 @@ static void Quit(JNIEnv* env, static void RunJavaRunnable( const base::android::ScopedJavaGlobalRef<jobject>& runnable_ref) { Java_BaseRunLoop_runRunnable(base::android::AttachCurrentThread(), - runnable_ref.obj()); + runnable_ref); } static void PostDelayedTask(JNIEnv* env, @@ -58,9 +59,10 @@ static void PostDelayedTask(JNIEnv* env, // use it across threads. |RunJavaRunnable| will acquire a new JNIEnv before // running the Runnable. runnable_ref.Reset(env, runnable); - reinterpret_cast<base::MessageLoop*>(runLoopID)->PostDelayedTask( - FROM_HERE, base::Bind(&RunJavaRunnable, runnable_ref), - base::TimeDelta::FromMicroseconds(delay)); + reinterpret_cast<base::MessageLoop*>(runLoopID) + ->task_runner() + ->PostDelayedTask(FROM_HERE, base::Bind(&RunJavaRunnable, runnable_ref), + base::TimeDelta::FromMicroseconds(delay)); } static void DeleteMessageLoop(JNIEnv* env, @@ -78,4 +80,3 @@ bool RegisterBaseRunLoop(JNIEnv* env) { } // namespace android } // namespace mojo - diff --git a/mojo/android/system/core_impl.cc b/mojo/android/system/core_impl.cc index 526c050..5cbd754 100644 --- a/mojo/android/system/core_impl.cc +++ b/mojo/android/system/core_impl.cc @@ -7,56 +7,20 @@ #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(); @@ -365,51 +329,6 @@ static int Unmap(JNIEnv* env, 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 dfe92ef..3db6670 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 @@ class BaseRunLoop implements RunLoop { @Override public void run() { assert mRunLoopID != 0 : "The run loop cannot run once closed"; - nativeRun(mRunLoopID); + nativeRun(); } @Override public void runUntilIdle() { assert mRunLoopID != 0 : "The run loop cannot run once closed"; - nativeRunUntilIdle(mRunLoopID); + nativeRunUntilIdle(); } @Override @@ -66,8 +66,8 @@ class BaseRunLoop implements RunLoop { } private native long nativeCreateBaseRunLoop(); - private native void nativeRun(long runLoopID); - private native void nativeRunUntilIdle(long runLoopID); + private native void nativeRun(); + private native void nativeRunUntilIdle(); 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 a20ea0a..8330586 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,7 +7,6 @@ package org.chromium.mojo.system.impl; 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; @@ -23,6 +22,7 @@ import org.chromium.mojo.system.SharedBufferHandle; 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 @@ import java.util.List; */ @JNINamespace("mojo::android") @MainDex -public class CoreImpl implements Core, AsyncWaiter { +public class CoreImpl implements Core { /** * Discard flag for the |MojoReadData| operation. */ @@ -216,11 +216,11 @@ public class CoreImpl implements Core, AsyncWaiter { } /** - * @see Core#getDefaultAsyncWaiter() + * @see Core#getWatcher() */ @Override - public AsyncWaiter getDefaultAsyncWaiter() { - return this; + public Watcher getWatcher() { + return new WatcherImpl(); } /** @@ -251,15 +251,6 @@ public class CoreImpl implements Core, AsyncWaiter { 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); } @@ -497,59 +488,6 @@ public class CoreImpl implements Core, AsyncWaiter { 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); @@ -629,10 +567,5 @@ public class CoreImpl implements Core, AsyncWaiter { 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 new file mode 100644 index 0000000..094ad90 --- /dev/null +++ b/mojo/android/system/src/org/chromium/mojo/system/impl/WatcherImpl.java @@ -0,0 +1,63 @@ +// 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/android/system/watcher_impl.cc b/mojo/android/system/watcher_impl.cc new file mode 100644 index 0000000..28d8c9f --- /dev/null +++ b/mojo/android/system/watcher_impl.cc @@ -0,0 +1,109 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/android/system/watcher_impl.h" + +#include <stddef.h> +#include <stdint.h> + +#include "base/android/base_jni_registrar.h" +#include "base/android/jni_android.h" +#include "base/android/jni_registrar.h" +#include "base/android/library_loader/library_loader_hooks.h" +#include "base/android/scoped_java_ref.h" +#include "base/bind.h" +#include "jni/WatcherImpl_jni.h" +#include "mojo/public/cpp/system/handle.h" +#include "mojo/public/cpp/system/watcher.h" + +namespace mojo { +namespace android { + +using base::android::JavaParamRef; + +namespace { + +class WatcherImpl { + public: + WatcherImpl() : watcher_(FROM_HERE) {} + + ~WatcherImpl() = default; + + jint Start(JNIEnv* env, + const JavaParamRef<jobject>& jcaller, + jint mojo_handle, + jint signals) { + java_watcher_.Reset(env, jcaller); + + auto ready_callback = + base::Bind(&WatcherImpl::OnHandleReady, base::Unretained(this)); + + MojoResult result = + watcher_.Start(mojo::Handle(static_cast<MojoHandle>(mojo_handle)), + static_cast<MojoHandleSignals>(signals), ready_callback); + + if (result != MOJO_RESULT_OK) + java_watcher_.Reset(); + + return result; + } + + void Cancel() { + java_watcher_.Reset(); + watcher_.Cancel(); + } + + private: + void OnHandleReady(MojoResult result) { + DCHECK(!java_watcher_.is_null()); + + base::android::ScopedJavaGlobalRef<jobject> java_watcher_preserver; + if (result == MOJO_RESULT_CANCELLED) + java_watcher_preserver = std::move(java_watcher_); + + Java_WatcherImpl_onHandleReady( + base::android::AttachCurrentThread(), + java_watcher_.is_null() ? java_watcher_preserver : java_watcher_, + result); + } + + Watcher watcher_; + base::android::ScopedJavaGlobalRef<jobject> java_watcher_; + + DISALLOW_COPY_AND_ASSIGN(WatcherImpl); +}; + +} // namespace + +static jlong CreateWatcher(JNIEnv* env, const JavaParamRef<jobject>& jcaller) { + return reinterpret_cast<jlong>(new WatcherImpl); +} + +static jint Start(JNIEnv* env, + const JavaParamRef<jobject>& jcaller, + jlong watcher_ptr, + jint mojo_handle, + jint signals) { + auto* watcher = reinterpret_cast<WatcherImpl*>(watcher_ptr); + return watcher->Start(env, jcaller, mojo_handle, signals); +} + +static void Cancel(JNIEnv* env, + const JavaParamRef<jobject>& jcaller, + jlong watcher_ptr) { + reinterpret_cast<WatcherImpl*>(watcher_ptr)->Cancel(); +} + +static void Delete(JNIEnv* env, + const JavaParamRef<jobject>& jcaller, + jlong watcher_ptr) { + delete reinterpret_cast<WatcherImpl*>(watcher_ptr); +} + +bool RegisterWatcherImpl(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace android +} // namespace mojo diff --git a/mojo/android/system/watcher_impl.h b/mojo/android/system/watcher_impl.h new file mode 100644 index 0000000..784f007 --- /dev/null +++ b/mojo/android/system/watcher_impl.h @@ -0,0 +1,20 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_ANDROID_SYSTEM_WATCHER_IMPL_H_ +#define MOJO_ANDROID_SYSTEM_WATCHER_IMPL_H_ + +#include <jni.h> + +#include "base/android/jni_android.h" + +namespace mojo { +namespace android { + +JNI_EXPORT bool RegisterWatcherImpl(JNIEnv* env); + +} // namespace android +} // namespace mojo + +#endif // MOJO_ANDROID_SYSTEM_WATCHER_IMPL_H_ diff --git a/mojo/common/BUILD.gn b/mojo/common/BUILD.gn index 6dfed8e..9e74e58 100644 --- a/mojo/common/BUILD.gn +++ b/mojo/common/BUILD.gn @@ -12,29 +12,27 @@ group("common") { ] } -# GYP version: mojo/mojo_base.gyp:mojo_common_custom_types mojom("common_custom_types") { sources = [ - "common_custom_types.mojom", + "file.mojom", + "file_path.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" ] @@ -46,21 +44,21 @@ component("common_base") { ] } -# 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", @@ -75,16 +73,21 @@ test("mojo_common_unittests") { sources = [ "common_custom_types_unittest.cc", - "common_type_converters_unittest.cc", + "struct_traits_unittest.cc", ] } -test("mojo_common_perftests") { +source_set("struct_traits") { + sources = [ + "common_custom_types_struct_traits.cc", + "common_custom_types_struct_traits.h", + ] deps = [ - ":common", - "//base", - "//mojo/edk/test:run_all_perftests", - "//mojo/public/cpp/test_support:test_utils", - "//testing/gtest", + ":common_custom_types_shared_cpp_sources", + "//base:base", + "//mojo/public/cpp/system", + ] + public_deps = [ + "//base:i18n", ] } diff --git a/mojo/common/DEPS b/mojo/common/DEPS index 588c68d..e8ac428 100644 --- a/mojo/common/DEPS +++ b/mojo/common/DEPS @@ -1,16 +1,6 @@ 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/common_custom_types.typemap b/mojo/common/common_custom_types.typemap deleted file mode 100644 index 0b63f38..0000000 --- a/mojo/common/common_custom_types.typemap +++ /dev/null @@ -1,24 +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/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 3f9f7d9..6289504 100644 --- a/mojo/common/common_custom_types_struct_traits.cc +++ b/mojo/common/common_custom_types_struct_traits.cc @@ -1,18 +1,108 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// 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/common/common_custom_types_struct_traits.h" +#include "mojo/public/cpp/system/platform_handle.h" + namespace mojo { // static -bool StructTraits<common::mojom::String16, base::string16>::Read( - common::mojom::String16DataView data, base::string16* out) { - std::vector<uint16_t> view; - data.ReadData(&view); +bool StructTraits<common::mojom::String16DataView, base::string16>::Read( + common::mojom::String16DataView data, + base::string16* out) { + ArrayDataView<uint16_t> view; + data.GetDataDataView(&view); out->assign(reinterpret_cast<const base::char16*>(view.data()), view.size()); return true; } -} // mojo +// 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 diff --git a/mojo/common/common_custom_types_struct_traits.h b/mojo/common/common_custom_types_struct_traits.h index 4cbb3f9..b20c795 100644 --- a/mojo/common/common_custom_types_struct_traits.h +++ b/mojo/common/common_custom_types_struct_traits.h @@ -1,45 +1,69 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef MOJO_COMMON_COMMON_CUSTOM_TYPES_STRUCT_TRAITS_H_ #define MOJO_COMMON_COMMON_CUSTOM_TYPES_STRUCT_TRAITS_H_ -#include <stdint.h> -#include <vector> - -#include "base/strings/string16.h" -#include "base/time/time.h" -#include "mojo/common/string16.mojom.h" -#include "mojo/common/time.mojom.h" -#include "mojo/public/cpp/bindings/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" namespace mojo { template <> -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()); +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())); } - static bool Read(common::mojom::String16DataView data, base::string16* output); + + static bool Read(common::mojom::String16DataView data, base::string16* out); }; template <> -struct StructTraits<common::mojom::Time, base::Time> { - static int64_t internal_value(const base::Time& time) { - return time.ToInternalValue(); +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); +}; - static bool Read(common::mojom::TimeDataView data, base::Time* time) { - *time = - base::Time() + base::TimeDelta::FromMicroseconds(data.internal_value()); - return true; +// 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::TimeDelta, base::TimeDelta> { +struct StructTraits<common::mojom::TimeDeltaDataView, base::TimeDelta> { static int64_t microseconds(const base::TimeDelta& delta) { return delta.InMicroseconds(); } @@ -52,19 +76,23 @@ struct StructTraits<common::mojom::TimeDelta, base::TimeDelta> { }; template <> -struct StructTraits<common::mojom::TimeTicks, base::TimeTicks> { - static int64_t internal_value(const base::TimeTicks& time) { - return time.ToInternalValue(); - } +struct StructTraits<common::mojom::FileDataView, base::File> { + static bool IsNull(const base::File& file) { return !file.IsValid(); } - static bool Read(common::mojom::TimeTicksDataView data, - base::TimeTicks* time) { - *time = base::TimeTicks() + - base::TimeDelta::FromMicroseconds(data.internal_value()); - return true; - } + 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); }; -} // mojo +} // namespace 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 fe6bb5d..e3571d9 100644 --- a/mojo/common/common_custom_types_unittest.cc +++ b/mojo/common/common_custom_types_unittest.cc @@ -3,10 +3,13 @@ // 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" @@ -23,33 +26,38 @@ struct BounceTestTraits { } }; +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)); - } +struct PassTraits<base::Time> { + using Type = base::Time; }; template <> -struct BounceTestTraits<base::ListValue> { - static void ExpectEquality(const base::ListValue& a, - const base::ListValue& b) { - EXPECT_TRUE(a.Equals(&b)); - } +struct PassTraits<base::TimeDelta> { + using Type = base::TimeDelta; +}; + +template <> +struct PassTraits<base::TimeTicks> { + using Type = base::TimeTicks; }; template <typename T> void DoExpectResponse(T* expected_value, const base::Closure& closure, - const T& value) { + typename PassTraits<T>::Type value) { BounceTestTraits<T>::ExpectEquality(*expected_value, value); closure.Run(); } template <typename T> -base::Callback<void(const T&)> ExpectResponse(T* expected_value, - const base::Closure& closure) { +base::Callback<void(typename PassTraits<T>::Type)> ExpectResponse( + T* expected_value, + const base::Closure& closure) { return base::Bind(&DoExpectResponse<T>, expected_value, closure); } @@ -68,24 +76,38 @@ class TestFilePathImpl : public TestFilePath { 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(const base::Time& in, - const BounceTimeCallback& callback) override { + void BounceTime(base::Time in, const BounceTimeCallback& callback) override { callback.Run(in); } - void BounceTimeDelta(const base::TimeDelta& in, - const BounceTimeDeltaCallback& callback) override { + void BounceTimeDelta(base::TimeDelta in, + const BounceTimeDeltaCallback& callback) override { callback.Run(in); } - void BounceTimeTicks(const base::TimeTicks& in, - const BounceTimeTicksCallback& callback) override { + void BounceTimeTicks(base::TimeTicks in, + const BounceTimeTicksCallback& callback) override { callback.Run(in); } @@ -100,19 +122,70 @@ class TestValueImpl : public TestValue { // TestValue implementation: void BounceDictionaryValue( - const base::DictionaryValue& in, + std::unique_ptr<base::DictionaryValue> in, const BounceDictionaryValueCallback& callback) override { - callback.Run(in); + callback.Run(std::move(in)); } - void BounceListValue(const base::ListValue& in, + + void BounceListValue(std::unique_ptr<base::ListValue> in, const BounceListValueCallback& callback) override { - callback.Run(in); + callback.Run(std::move(in)); + } + + void BounceValue(std::unique_ptr<base::Value> in, + const BounceValueCallback& callback) override { + callback.Run(std::move(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() {} @@ -130,7 +203,7 @@ TEST_F(CommonCustomTypesTest, FilePath) { base::RunLoop run_loop; TestFilePathPtr ptr; - TestFilePathImpl impl(GetProxy(&ptr)); + TestFilePathImpl impl(MakeRequest(&ptr)); base::FilePath dir(FILE_PATH_LITERAL("hello")); base::FilePath file = dir.Append(FILE_PATH_LITERAL("world")); @@ -140,11 +213,24 @@ TEST_F(CommonCustomTypesTest, FilePath) { 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(GetProxy(&ptr)); + TestTimeImpl impl(MakeRequest(&ptr)); base::Time t = base::Time::Now(); @@ -157,7 +243,7 @@ TEST_F(CommonCustomTypesTest, TimeDelta) { base::RunLoop run_loop; TestTimePtr ptr; - TestTimeImpl impl(GetProxy(&ptr)); + TestTimeImpl impl(MakeRequest(&ptr)); base::TimeDelta t = base::TimeDelta::FromDays(123); @@ -170,7 +256,7 @@ TEST_F(CommonCustomTypesTest, TimeTicks) { base::RunLoop run_loop; TestTimePtr ptr; - TestTimeImpl impl(GetProxy(&ptr)); + TestTimeImpl impl(MakeRequest(&ptr)); base::TimeTicks t = base::TimeTicks::Now(); @@ -181,43 +267,164 @@ TEST_F(CommonCustomTypesTest, TimeTicks) { TEST_F(CommonCustomTypesTest, Value) { TestValuePtr ptr; - TestValueImpl impl(GetProxy(&ptr)); - - 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)); + TestValueImpl impl(MakeRequest(&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); { 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)); - } - { - base::RunLoop run_loop; - ptr->BounceDictionaryValue( - dict, ExpectResponse(&dict, run_loop.QuitClosure())); - run_loop.Run(); + dict->Set("list", std::move(dict_list)); } - 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> 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()); { 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)); } - { - base::RunLoop run_loop; - ptr->BounceListValue(list, ExpectResponse(&list, run_loop.QuitClosure())); - run_loop.Run(); + 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); } } diff --git a/mojo/common/common_type_converters.cc b/mojo/common/common_type_converters.cc deleted file mode 100644 index 92ae3e2..0000000 --- a/mojo/common/common_type_converters.cc +++ /dev/null @@ -1,84 +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. - -#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 deleted file mode 100644 index a065f05..0000000 --- a/mojo/common/common_type_converters.h +++ /dev/null @@ -1,66 +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. - -#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 deleted file mode 100644 index 1740d06..0000000 --- a/mojo/common/common_type_converters_unittest.cc +++ /dev/null @@ -1,130 +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. - -#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 1133e11..27bd893 100644 --- a/mojo/common/data_pipe_drainer.cc +++ b/mojo/common/data_pipe_drainer.cc @@ -15,7 +15,10 @@ namespace common { DataPipeDrainer::DataPipeDrainer(Client* client, mojo::ScopedDataPipeConsumerHandle source) - : client_(client), source_(std::move(source)), weak_factory_(this) { + : client_(client), + source_(std::move(source)), + handle_watcher_(FROM_HERE), + 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 deleted file mode 100644 index 841dfde..0000000 --- a/mojo/common/data_pipe_file_utils.cc +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/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 8540ac6..bed5e85 100644 --- a/mojo/common/data_pipe_utils.cc +++ b/mojo/common/data_pipe_utils.cc @@ -4,16 +4,9 @@ #include "mojo/common/data_pipe_utils.h" -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> #include <utility> -#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" +#include "base/bind.h" namespace mojo { namespace common { @@ -58,12 +51,7 @@ size_t CopyToStringHelper( return num_bytes; } -size_t CopyToFileHelper(FILE* fp, const void* buffer, uint32_t num_bytes) { - return fwrite(buffer, 1, num_bytes, fp); -} - -} // namespace - +} // namespace // TODO(hansmuller): Add a max_size parameter. bool BlockingCopyToString(ScopedDataPipeConsumerHandle source, @@ -107,25 +95,5 @@ bool MOJO_COMMON_EXPORT BlockingCopyFromString( } } -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 426912c..a3f7c09 100644 --- a/mojo/common/data_pipe_utils.h +++ b/mojo/common/data_pipe_utils.h @@ -2,41 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MOJO_SHELL_DATA_PIPE_UTILS_H_ -#define MOJO_SHELL_DATA_PIPE_UTILS_H_ +#ifndef MOJO_COMMON_DATA_PIPE_UTILS_H_ +#define MOJO_COMMON_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/core.h" - -namespace base { -class FilePath; -class TaskRunner; -} +#include "mojo/public/cpp/system/data_pipe.h" 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. @@ -48,13 +26,7 @@ bool MOJO_COMMON_EXPORT BlockingCopyFromString( 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_SHELL_DATA_PIPE_UTILS_H_ +#endif // MOJO_COMMON_DATA_PIPE_UTILS_H_ diff --git a/mojo/common/common_custom_types.mojom b/mojo/common/file.mojom index 4a04dbc..fe22473 100644 --- a/mojo/common/common_custom_types.mojom +++ b/mojo/common/file.mojom @@ -4,11 +4,7 @@ module mojo.common.mojom; -[Native] -struct FilePath; - -[Native] -struct ListValue; - -[Native] -struct DictionaryValue; +// 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 new file mode 100644 index 0000000..26d4941 --- /dev/null +++ b/mojo/common/file.typemap @@ -0,0 +1,13 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +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 eb1ace2..173c867 100644 --- a/mojo/common/string16.mojom +++ b/mojo/common/string16.mojom @@ -10,4 +10,3 @@ module mojo.common.mojom; struct String16 { array<uint16> data; }; - diff --git a/mojo/common/string16.typemap b/mojo/common/string16.typemap new file mode 100644 index 0000000..223de29 --- /dev/null +++ b/mojo/common/string16.typemap @@ -0,0 +1,12 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +mojom = "//mojo/common/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 db91a4f..0f13680 100644 --- a/mojo/common/test_common_custom_types.mojom +++ b/mojo/common/test_common_custom_types.mojom @@ -4,13 +4,24 @@ module mojo.common.test; -import "mojo/common/common_custom_types.mojom"; +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"; 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) @@ -20,8 +31,31 @@ interface TestTime { }; 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 new file mode 100644 index 0000000..7d65124 --- /dev/null +++ b/mojo/common/text_direction.mojom @@ -0,0 +1,12 @@ +// 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 new file mode 100644 index 0000000..1f5be8e --- /dev/null +++ b/mojo/common/text_direction.typemap @@ -0,0 +1,12 @@ +# 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.mojom b/mojo/common/time.mojom index b403bca..43dfcc8 100644 --- a/mojo/common/time.mojom +++ b/mojo/common/time.mojom @@ -4,18 +4,12 @@ module mojo.common.mojom; -struct Time { - // The internal value is expressed in terms of microseconds since a fixed but - // intentionally unspecified epoch. - int64 internal_value; -}; +[Native] +struct Time; struct TimeDelta { int64 microseconds; }; -struct TimeTicks { - // The internal value is expressed in terms of microseconds since a fixed but - // intentionally unspecified epoch. - int64 internal_value; -}; +[Native] +struct TimeTicks; diff --git a/mojo/common/time.typemap b/mojo/common/time.typemap index 20661f6..99e9e3a 100644 --- a/mojo/common/time.typemap +++ b/mojo/common/time.typemap @@ -4,9 +4,12 @@ mojom = "//mojo/common/time.mojom" public_headers = [ "//base/time/time.h" ] -traits_headers = [ "//mojo/common/time_struct_traits.h" ] +traits_headers = [ + "//ipc/ipc_message_utils.h", + "//mojo/common/common_custom_types_struct_traits.h", +] public_deps = [ - "//base", + "//ipc", "//mojo/common:struct_traits", ] diff --git a/mojo/common/unguessable_token.mojom b/mojo/common/unguessable_token.mojom new file mode 100644 index 0000000..3279717 --- /dev/null +++ b/mojo/common/unguessable_token.mojom @@ -0,0 +1,11 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module mojo.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 new file mode 100644 index 0000000..ec7b194 --- /dev/null +++ b/mojo/common/unguessable_token.typemap @@ -0,0 +1,12 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +mojom = "//mojo/common/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 deleted file mode 100644 index 6055cbe..0000000 --- a/mojo/common/user_agent.cc +++ /dev/null @@ -1,25 +0,0 @@ -// 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 deleted file mode 100644 index 031b102..0000000 --- a/mojo/common/user_agent.h +++ /dev/null @@ -1,20 +0,0 @@ -// 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 new file mode 100644 index 0000000..6ddf6e6 --- /dev/null +++ b/mojo/common/version.mojom @@ -0,0 +1,10 @@ +// 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 new file mode 100644 index 0000000..fa7fed9 --- /dev/null +++ b/mojo/common/version.typemap @@ -0,0 +1,12 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +mojom = "//mojo/common/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 deleted file mode 100644 index 0bb9295..0000000 --- a/mojo/converters/blink/BUILD.gn +++ /dev/null @@ -1,44 +0,0 @@ -# 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 deleted file mode 100644 index 6678b7c..0000000 --- a/mojo/converters/blink/DEPS +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index 1114a5f..0000000 --- a/mojo/converters/blink/blink_input_events_type_converters.cc +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/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 deleted file mode 100644 index 3231eab..0000000 --- a/mojo/converters/blink/blink_input_events_type_converters.h +++ /dev/null @@ -1,31 +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_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 deleted file mode 100644 index 1a77184..0000000 --- a/mojo/converters/blink/mojo_blink_export.h +++ /dev/null @@ -1,32 +0,0 @@ -// 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 f20fd40..8105bed 100644 --- a/mojo/edk/embedder/BUILD.gn +++ b/mojo/edk/embedder/BUILD.gn @@ -7,13 +7,16 @@ import("//build/config/nacl/config.gni") 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", ] @@ -33,11 +36,14 @@ source_set("embedder") { 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", @@ -52,7 +58,6 @@ source_set("embedder") { defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ] public_deps = [ - ":delegates", ":headers", ":platform", "//base", @@ -77,6 +82,9 @@ source_set("platform") { 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", @@ -93,6 +101,9 @@ source_set("platform") { "platform_shared_buffer.h", "scoped_platform_handle.h", ] + if (!is_nacl) { + sources += [ "named_platform_handle_utils_posix.cc" ] + } defines = [ "MOJO_SYSTEM_IMPL_IMPLEMENTATION" ] @@ -113,25 +124,6 @@ source_set("platform") { } } -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 f976fcb..6def874 100644 --- a/mojo/edk/embedder/README.md +++ b/mojo/edk/embedder/README.md @@ -1,13 +1,327 @@ -Mojo Embedder API -================= - -The Mojo Embedder API is an unstable, internal API to the Mojo system -implementation. It should be used by code running on top of the system-level -APIs to set up the Mojo environment (instead of directly instantiating things -from src/mojo/edk/system). - -Example uses: Mojo shell, to set up the Mojo environment for Mojo apps; Chromium -code, to set up the Mojo IPC system for use between processes. Note that most -code should use the Mojo Public API (under src/mojo/public) instead. The -Embedder API should only be used to initialize the environment, set up the -initial MessagePipe between two processes, etc. +# Mojo Embedder Development Kit (EDK) + +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. + +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. diff --git a/mojo/edk/embedder/connection_params.cc b/mojo/edk/embedder/connection_params.cc new file mode 100644 index 0000000..9b7ec54 --- /dev/null +++ b/mojo/edk/embedder/connection_params.cc @@ -0,0 +1,28 @@ +// 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 new file mode 100644 index 0000000..25ffdde --- /dev/null +++ b/mojo/edk/embedder/connection_params.h @@ -0,0 +1,34 @@ +// 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 c90acbd..0fdda5c 100644 --- a/mojo/edk/embedder/embedder.cc +++ b/mojo/edk/embedder/embedder.cc @@ -5,6 +5,7 @@ #include "mojo/edk/embedder/embedder.h" #include <stdint.h> +#include <utility> #include "base/bind.h" #include "base/location.h" @@ -17,8 +18,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" @@ -33,7 +34,6 @@ class PlatformSupport; namespace internal { Core* g_core; -ProcessDelegate* g_process_delegate; Core* GetCore() { return g_core; } @@ -42,30 +42,9 @@ Core* GetCore() { return g_core; } 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(std::move(pipe)); + internal::g_core->InitChild(ConnectionParams(std::move(pipe))); } void SetParentPipeHandleFromCommandLine() { @@ -76,6 +55,21 @@ void SetParentPipeHandleFromCommandLine() { 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); @@ -84,6 +78,10 @@ void Init() { 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) { @@ -115,19 +113,18 @@ MojoResult PassSharedMemoryHandle( mojo_handle, shared_memory_handle, num_bytes, read_only); } -void InitIPCSupport(ProcessDelegate* process_delegate, - scoped_refptr<base::TaskRunner> io_thread_task_runner) { +void InitIPCSupport(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; } -void ShutdownIPCSupport() { - CHECK(internal::g_process_delegate); +scoped_refptr<base::TaskRunner> GetIOTaskRunner() { + return internal::g_core->GetNodeController()->io_task_runner(); +} + +void ShutdownIPCSupport(const base::Closure& callback) { CHECK(internal::g_core); - internal::g_core->RequestShutdown( - base::Bind(&ProcessDelegate::OnShutdownComplete, - base::Unretained(internal::g_process_delegate))); + internal::g_core->RequestShutdown(callback); } #if defined(OS_MACOSX) && !defined(OS_IOS) @@ -137,14 +134,7 @@ void SetMachPortProvider(base::PortProvider* port_provider) { } #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 9e83bbe..97258e5 100644 --- a/mojo/edk/embedder/embedder.h +++ b/mojo/edk/embedder/embedder.h @@ -16,6 +16,7 @@ #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" @@ -27,10 +28,6 @@ class PortProvider; 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...()| @@ -40,40 +37,43 @@ using ProcessErrorCallback = base::Callback<void(const std::string& error)>; // Allows changing the default max message size. Must be called before Init. MOJO_SYSTEM_IMPL_EXPORT void SetMaxMessageSize(size_t bytes); -// 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. +// 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. 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,22 +126,20 @@ PassSharedMemoryHandle(MojoHandle mojo_handle, // // This subsystem may be shut down using |ShutdownIPCSupport()|. None of the IPC // functions may be called after this is called. - -// 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. +// +// |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); +// Retrieves the TaskRunner used for IPC I/O, as set by InitIPCSupport. +MOJO_SYSTEM_IMPL_EXPORT scoped_refptr<base::TaskRunner> GetIOTaskRunner(); + // 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 the ProcessDelegate's -// |OnShutdownComplete()| method is invoked. -MOJO_SYSTEM_IMPL_EXPORT void ShutdownIPCSupport(); +// the system was initialized. Upon completion, |callback| is invoked on an +// arbitrary thread. +MOJO_SYSTEM_IMPL_EXPORT void ShutdownIPCSupport(const base::Closure& callback); #if defined(OS_MACOSX) && !defined(OS_IOS) // Set the |base::PortProvider| for this process. Can be called on any thread, @@ -150,25 +148,14 @@ MOJO_SYSTEM_IMPL_EXPORT void SetMachPortProvider( base::PortProvider* port_provider); #endif -// 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. +// Creates a message pipe from a token in a child process. This token must have +// been acquired by a corresponding call to +// PendingProcessConnection::CreateMessagePipe. MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle CreateChildMessagePipe(const std::string& token); -// 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. +// Generates a random ASCII token string for use with various APIs that expect +// a globally unique token string. 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 127a74f..d5a87e5 100644 --- a/mojo/edk/embedder/embedder_unittest.cc +++ b/mojo/edk/embedder/embedder_unittest.cc @@ -10,16 +10,24 @@ #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" @@ -183,13 +191,12 @@ TEST_F(EmbedderTest, ChannelsHandlePassing) { } TEST_F(EmbedderTest, PipeSetup) { - std::string child_token = GenerateRandomToken(); - std::string pipe_token = GenerateRandomToken(); - - ScopedMessagePipeHandle parent_mp = - CreateParentMessagePipe(pipe_token, child_token); - ScopedMessagePipeHandle child_mp = - CreateChildMessagePipe(pipe_token); + // 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); const std::string kHello = "hello"; WriteMessage(parent_mp.get().value(), kHello); @@ -200,13 +207,11 @@ TEST_F(EmbedderTest, PipeSetup) { TEST_F(EmbedderTest, PipeSetup_LaunchDeath) { PlatformChannelPair pair; - 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); + PendingProcessConnection process; + std::string pipe_token; + ScopedMessagePipeHandle parent_mp = process.CreateMessagePipe(&pipe_token); + process.Connect(base::GetCurrentProcessHandle(), + ConnectionParams(pair.PassServerHandle())); // Close the remote end, simulating child death before the child connects to // the reserved port. @@ -221,13 +226,14 @@ TEST_F(EmbedderTest, PipeSetup_LaunchDeath) { TEST_F(EmbedderTest, PipeSetup_LaunchFailure) { PlatformChannelPair pair; - std::string child_token = GenerateRandomToken(); - std::string pipe_token = GenerateRandomToken(); + auto process = base::MakeUnique<PendingProcessConnection>(); + std::string pipe_token; + ScopedMessagePipeHandle parent_mp = process->CreateMessagePipe(&pipe_token); - ScopedMessagePipeHandle parent_mp = - CreateParentMessagePipe(pipe_token, child_token); + // Ensure that if a PendingProcessConnection goes away before Connect() is + // called, any message pipes associated with it detect peer closure. + process.reset(); - ChildProcessLaunchFailed(child_token); EXPECT_EQ(MOJO_RESULT_OK, MojoWait(parent_mp.get().value(), MOJO_HANDLE_SIGNAL_PEER_CLOSED, MOJO_DEADLINE_INDEFINITE, @@ -557,6 +563,100 @@ DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessMixMachAndFdsClient, EmbedderTest, #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 new file mode 100644 index 0000000..5a83ae3 --- /dev/null +++ b/mojo/edk/embedder/named_platform_channel_pair.h @@ -0,0 +1,73 @@ +// 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 new file mode 100644 index 0000000..15ca656 --- /dev/null +++ b/mojo/edk/embedder/named_platform_handle.h @@ -0,0 +1,51 @@ +// 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 new file mode 100644 index 0000000..d6be76e --- /dev/null +++ b/mojo/edk/embedder/pending_process_connection.cc @@ -0,0 +1,50 @@ +// 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 new file mode 100644 index 0000000..ca18227 --- /dev/null +++ b/mojo/edk/embedder/pending_process_connection.h @@ -0,0 +1,124 @@ +// 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 f80de89..9c93f76 100644 --- a/mojo/edk/embedder/platform_channel_pair.h +++ b/mojo/edk/embedder/platform_channel_pair.h @@ -69,6 +69,7 @@ class MOJO_SYSTEM_IMPL_EXPORT PlatformChannelPair { // |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 55f3ae2..4760e95 100644 --- a/mojo/edk/embedder/platform_channel_pair_posix.cc +++ b/mojo/edk/embedder/platform_channel_pair_posix.cc @@ -35,6 +35,15 @@ namespace edk { 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) { @@ -44,6 +53,7 @@ bool IsTargetDescriptorUsed( } return false; } +#endif } // namespace @@ -92,13 +102,21 @@ ScopedPlatformHandle 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)); } @@ -124,6 +142,12 @@ void PlatformChannelPair::PrepareToPassClientHandleToChildProcess( 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.) @@ -141,6 +165,7 @@ PlatformChannelPair::PrepareToPassClientHandleToChildProcessAsString( 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 e05cd79..a3fd275 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 @@ TEST_F(PlatformChannelPairPosixTest, SendReceiveFDs) { for (size_t j = 1; j <= i; j++) { base::FilePath unused; base::ScopedFILE fp( - base::CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused)); + base::CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &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 @@ TEST_F(PlatformChannelPairPosixTest, AppendReceivedFDs) { { base::FilePath unused; base::ScopedFILE fp( - base::CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused)); + base::CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &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 3171844..689b6ee 100644 --- a/mojo/edk/embedder/platform_channel_utils_posix.cc +++ b/mojo/edk/embedder/platform_channel_utils_posix.cc @@ -8,9 +8,13 @@ #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> @@ -22,6 +26,55 @@ 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 @@ -193,5 +246,37 @@ ssize_t PlatformChannelRecvmsg(PlatformHandle h, 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 8b24bd0..23cfa92 100644 --- a/mojo/edk/embedder/platform_channel_utils_posix.h +++ b/mojo/edk/embedder/platform_channel_utils_posix.h @@ -18,6 +18,7 @@ struct iovec; // Declared in <sys/uio.h>. 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 @@ -70,6 +71,16 @@ PlatformChannelRecvmsg(PlatformHandle h, 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 4f76009..4866e75 100644 --- a/mojo/edk/embedder/platform_handle.h +++ b/mojo/edk/embedder/platform_handle.h @@ -53,6 +53,9 @@ struct MOJO_SYSTEM_IMPL_EXPORT PlatformHandle { 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 fc2a0db..5604f96 100644 --- a/mojo/edk/embedder/platform_handle_utils_posix.cc +++ b/mojo/edk/embedder/platform_handle_utils_posix.cc @@ -15,7 +15,9 @@ ScopedPlatformHandle DuplicatePlatformHandle(PlatformHandle platform_handle) { DCHECK(platform_handle.is_valid()); // Note that |dup()| returns -1 on error (which is exactly the value we use // for invalid |PlatformHandle| FDs). - return ScopedPlatformHandle(PlatformHandle(dup(platform_handle.handle))); + PlatformHandle duped(dup(platform_handle.handle)); + duped.needs_connection = platform_handle.needs_connection; + return ScopedPlatformHandle(duped); } } // namespace edk diff --git a/mojo/edk/embedder/platform_shared_buffer.cc b/mojo/edk/embedder/platform_shared_buffer.cc index 53d8edc..58af44d 100644 --- a/mojo/edk/embedder/platform_shared_buffer.cc +++ b/mojo/edk/embedder/platform_shared_buffer.cc @@ -253,21 +253,28 @@ bool PlatformSharedBuffer::InitFromPlatformHandle( bool PlatformSharedBuffer::InitFromPlatformHandlePair( ScopedPlatformHandle rw_platform_handle, ScopedPlatformHandle ro_platform_handle) { -#if defined(OS_WIN) || defined(OS_MACOSX) +#if defined(OS_MACOSX) NOTREACHED(); return false; -#else - DCHECK(!shared_memory_); +#else // defined(OS_MACOSX) +#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); - ro_shared_memory_.reset(new base::SharedMemory(ro_handle, true)); +#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 + +#endif // defined(OS_MACOSX) } void PlatformSharedBuffer::InitFromSharedMemoryHandle( diff --git a/mojo/edk/embedder/process_delegate.h b/mojo/edk/embedder/process_delegate.h deleted file mode 100644 index 144f4a3..0000000 --- a/mojo/edk/embedder/process_delegate.h +++ /dev/null @@ -1,31 +0,0 @@ -// 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 deleted file mode 100644 index c5a78c3..0000000 --- a/mojo/edk/js/test/BUILD.gn +++ /dev/null @@ -1,46 +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. - -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 deleted file mode 100644 index b36c47f..0000000 --- a/mojo/edk/js/test/hexdump.js +++ /dev/null @@ -1,34 +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. - -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 deleted file mode 100644 index 00d3e96..0000000 --- a/mojo/edk/js/test/run_js_integration_tests.cc +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "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 deleted file mode 100644 index d0e8edc..0000000 --- a/mojo/edk/js/test/run_js_tests.cc +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "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 b21cdbc..41850d7 100644 --- a/mojo/edk/js/tests/BUILD.gn +++ b/mojo/edk/js/tests/BUILD.gn @@ -2,21 +2,34 @@ # 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("//mojo/public/tools/bindings/mojom.gni") +import("//testing/test.gni") -source_set("js_to_cpp_tests") { +# TODO(hansmuller): The organization of tests in this directory is weird: +# * Really, js_unittests tests public stuff, so that should live in public +# and be reworked as some sort of apptest. +# * Both js_unittests and js_integration_tests should auto-generate their +# tests somehow. The .cc files are just test runner stubs, including +# explicit lists of .js files. + +group("tests") { testonly = true + deps = [ + ":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:test_support", + "//mojo/edk/test:run_all_unittests", "//mojo/public/cpp/bindings", "//mojo/public/cpp/system", - "//mojo/public/interfaces/bindings/tests:test_interfaces", - "//mojo/public/interfaces/bindings/tests:test_interfaces_experimental", + "//mojo/public/js:bindings", ] sources = [ @@ -24,9 +37,7 @@ source_set("js_to_cpp_tests") { ] data = [ - "connection_tests.js", "js_to_cpp_tests.js", - "sample_service_tests.js", ] configs += [ "//v8:external_startup_data" ] @@ -37,3 +48,22 @@ mojom("js_to_cpp_bindings") { "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 deleted file mode 100644 index 0b4b213..0000000 --- a/mojo/edk/js/tests/connection_tests.js +++ /dev/null @@ -1,157 +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. - -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 6d3426d..e5e6bd1 100644 --- a/mojo/edk/js/tests/js_to_cpp_tests.cc +++ b/mojo/edk/js/tests/js_to_cpp_tests.cc @@ -15,14 +15,15 @@ #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" @@ -104,12 +105,11 @@ js_to_cpp::EchoArgsPtr BuildSampleEchoArgs() { 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 = std::move(string_array); + args->name.emplace("coming"); + args->string_array.emplace(3); + (*args->string_array)[0] = "one"; + (*args->string_array)[1] = "two"; + (*args->string_array)[2] = "three"; return args; } @@ -128,10 +128,10 @@ void CheckSampleEchoArgs(js_to_cpp::EchoArgsPtr arg) { 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()); + 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]); CheckDataPipe(std::move(arg->data_handle)); CheckMessagePipe(arg->message_handle.get()); } @@ -146,18 +146,23 @@ void CheckSampleEchoArgsList(const js_to_cpp::EchoArgsListPtr& list) { // 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; +void CheckCorruptedString(const std::string& arg) { 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()) +void CheckCorruptedString(const base::Optional<std::string>& arg) { + if (!arg) return; - for (size_t i = 0; i < string_array.size(); ++i) - CheckCorruptedString(string_array[i]); + 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]); } void CheckCorruptedDataPipe(MojoHandle data_pipe_handle) { @@ -297,7 +302,7 @@ class EchoCppSideConnection : public CppSideConnection { 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>()); + EXPECT_EQ(std::string("going"), *special_arg->name); CheckSampleEchoArgsList(list->next); } @@ -383,11 +388,11 @@ class JsToCppTest : public testing::Test { cpp_side->set_run_loop(&run_loop_); js_to_cpp::JsSidePtr js_side; - auto js_side_proxy = GetProxy(&js_side); + auto js_side_proxy = MakeRequest(&js_side); cpp_side->set_js_side(js_side.get()); js_to_cpp::CppSidePtr cpp_side_ptr; - cpp_side->Bind(GetProxy(&cpp_side_ptr)); + cpp_side->Bind(MakeRequest(&cpp_side_ptr)); js_side->SetCppSide(std::move(cpp_side_ptr)); @@ -399,7 +404,7 @@ class JsToCppTest : public testing::Test { gin::IsolateHolder::Initialize(gin::IsolateHolder::kStrictMode, gin::IsolateHolder::kStableV8Extras, gin::ArrayBufferAllocator::SharedInstance()); - gin::IsolateHolder instance; + gin::IsolateHolder instance(base::ThreadTaskRunnerHandle::Get()); MojoRunnerDelegate delegate; gin::ShellRunner runner(&delegate, instance.isolate()); delegate.Start(&runner, js_side_proxy.PassMessagePipe().release().value(), @@ -429,12 +434,18 @@ TEST_F(JsToCppTest, Echo) { } 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 ddecc4b..6b69fca 100644 --- a/mojo/edk/js/tests/js_to_cpp_tests.js +++ b/mojo/edk/js/tests/js_to_cpp_tests.js @@ -6,10 +6,9 @@ define('mojo/edk/js/tests/js_to_cpp_tests', [ '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, connection, connector, core) { +], function (console, jsToCpp, bindings, connector, core) { var retainedJsSide; var retainedJsSideStub; var sampleData; @@ -22,11 +21,9 @@ define('mojo/edk/js/tests/js_to_cpp_tests', [ }; 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(); @@ -220,9 +217,7 @@ define('mojo/edk/js/tests/js_to_cpp_tests', [ for (i = 0; i < sampleMessage.length; ++i) { sampleMessage[i] = 255 - i; } - retainedJsSideStub = - connection.bindHandleToStub(jsSideRequestHandle, jsToCpp.JsSide); retainedJsSide = new JsSideConnection; - bindings.StubBindings(retainedJsSideStub).delegate = retainedJsSide; + retainedJsSide.binding.bind(jsSideRequestHandle); }; }); diff --git a/mojo/edk/js/tests/sample_service_tests.js b/mojo/edk/js/tests/sample_service_tests.js deleted file mode 100644 index 2b065a8..0000000 --- a/mojo/edk/js/tests/sample_service_tests.js +++ /dev/null @@ -1,176 +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. - -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 84f1f30..b0acf23 100644 --- a/mojo/edk/system/BUILD.gn +++ b/mojo/edk/system/BUILD.gn @@ -20,9 +20,10 @@ component("system") { "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", @@ -75,7 +76,6 @@ component("system") { 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 @@ component("system") { if (is_nacl && !is_nacl_nonsfi) { sources -= [ - "broker_host_posix.cc", + "broker_host.cc", "broker_posix.cc", "channel_posix.cc", ] @@ -154,6 +154,7 @@ source_set("test_utils") { test("mojo_system_unittests") { sources = [ "awakable_list_unittest.cc", + "channel_unittest.cc", "core_test_base.cc", "core_test_base.h", "core_unittest.cc", @@ -186,6 +187,7 @@ test("mojo_system_unittests") { "//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_posix.cc b/mojo/edk/system/broker_host.cc index ed8e213..6096034 100644 --- a/mojo/edk/system/broker_host_posix.cc +++ b/mojo/edk/system/broker_host.cc @@ -4,17 +4,14 @@ #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/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" @@ -22,18 +19,17 @@ 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) { +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, std::move(platform_handle), + channel_ = Channel::Create(this, ConnectionParams(std::move(platform_handle)), base::ThreadTaskRunnerHandle::Get()); channel_->Start(); } @@ -46,32 +42,70 @@ BrokerHost::~BrokerHost() { channel_->ShutDown(); } -void BrokerHost::SendChannel(ScopedPlatformHandle handle) { +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(); - message->SetHandles(std::move(handles)); + // 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; } -void BrokerHost::OnBufferRequest(size_t num_bytes) { - scoped_refptr<PlatformSharedBuffer> buffer; +#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; - 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; - } + 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); @@ -80,6 +114,7 @@ void BrokerHost::OnBufferRequest(size_t num_bytes) { 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)); } @@ -101,29 +136,18 @@ void BrokerHost::OnChannelMessage(const void* payload, const BufferRequestData* request = reinterpret_cast<const BufferRequestData*>(header + 1); OnBufferRequest(request->size); - return; } break; default: + LOG(ERROR) << "Unexpected broker message type: " << header->type; break; } - - LOG(ERROR) << "Unexpected broker message type: " << header->type; } -void BrokerHost::OnChannelError() { - if (channel_) { - channel_->ShutDown(); - channel_ = nullptr; - } +void BrokerHost::OnChannelError() { delete this; } - delete this; -} - -void BrokerHost::WillDestroyCurrentMessageLoop() { - 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 b8f68c4..a7995d2 100644 --- a/mojo/edk/system/broker_host.h +++ b/mojo/edk/system/broker_host.h @@ -5,8 +5,13 @@ #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" @@ -18,14 +23,21 @@ namespace edk { class BrokerHost : public Channel::Delegate, public base::MessageLoop::DestructionObserver { public: - explicit BrokerHost(ScopedPlatformHandle platform_handle); + BrokerHost(base::ProcessHandle client_process, ScopedPlatformHandle handle); // Send |handle| to the child, to be used to establish a NodeChannel to us. - void SendChannel(ScopedPlatformHandle handle); + 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 private: ~BrokerHost() override; + bool PrepareHandlesForClient(PlatformHandleVector* handles); + // Channel::Delegate: void OnChannelMessage(const void* payload, size_t payload_size, @@ -35,7 +47,11 @@ class BrokerHost : public Channel::Delegate, // base::MessageLoop::DestructionObserver: void WillDestroyCurrentMessageLoop() override; - void OnBufferRequest(size_t num_bytes); + void OnBufferRequest(uint32_t num_bytes); + +#if defined(OS_WIN) + base::ProcessHandle client_process_; +#endif scoped_refptr<Channel> channel_; diff --git a/mojo/edk/system/broker_messages.h b/mojo/edk/system/broker_messages.h index a67be15..0f0dd9d 100644 --- a/mojo/edk/system/broker_messages.h +++ b/mojo/edk/system/broker_messages.h @@ -23,26 +23,41 @@ struct BrokerMessageHeader { uint32_t padding; }; -static_assert(sizeof(BrokerMessageHeader) % kChannelMessageAlignment == 0, +static_assert(IsAlignedForChannelMessage(sizeof(BrokerMessageHeader)), "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, - T** out_message_data) { - const size_t message_size = sizeof(BrokerMessageHeader) + sizeof(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; 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 62b37b3..8742f70 100644 --- a/mojo/edk/system/broker_posix.cc +++ b/mojo/edk/system/broker_posix.cc @@ -38,7 +38,7 @@ bool WaitForBrokerMessage(PlatformHandle platform_handle, PLOG(ERROR) << "Recvmsg error"; error = true; } else if (static_cast<size_t>(read_result) != message->data_num_bytes()) { - LOG(ERROR) << "Invalid node channel message. Expected " << message->data_num_bytes() << " got " << read_result; + LOG(ERROR) << "Invalid node channel message"; error = true; } else if (incoming_platform_handles.size() != expected_num_handles) { LOG(ERROR) << "Received unexpected number of handles"; @@ -94,7 +94,7 @@ scoped_refptr<PlatformSharedBuffer> Broker::GetSharedBuffer(size_t num_bytes) { BufferRequestData* buffer_request; Channel::MessagePtr out_message = CreateBrokerMessage( - BrokerMessageType::BUFFER_REQUEST, 0, &buffer_request); + BrokerMessageType::BUFFER_REQUEST, 0, 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 56509b5..8a44d36 100644 --- a/mojo/edk/system/channel.cc +++ b/mojo/edk/system/channel.cc @@ -4,6 +4,7 @@ #include "mojo/edk/system/channel.h" +#include <stddef.h> #include <string.h> #include <algorithm> @@ -26,27 +27,47 @@ namespace edk { namespace { -static_assert(sizeof(Channel::Message::Header) % kChannelMessageAlignment == 0, - "Invalid Header size."); +static_assert( + IsAlignedForChannelMessage(sizeof(Channel::Message::LegacyHeader)), + "Invalid LegacyHeader size."); -#if defined(MOJO_EDK_LEGACY_PROTOCOL) -static_assert(sizeof(Channel::Message::Header) == 8, - "Header must be 8 bytes on ChromeOS and Android"); -#endif +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."); } // namespace const size_t kReadBufferSize = 4096; -const size_t kMaxUnusedReadBufferCapacity = 64 * 1024; +const size_t kMaxUnusedReadBufferCapacity = 4096; 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, - Header::MessageType message_type) + 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. @@ -62,16 +83,16 @@ Channel::Message::Message(size_t payload_size, } #endif // Pad extra header data to be aliged to |kChannelMessageAlignment| bytes. - if (extra_header_size % kChannelMessageAlignment) { + if (!IsAlignedForChannelMessage(extra_header_size)) { extra_header_size += kChannelMessageAlignment - (extra_header_size % kChannelMessageAlignment); } - DCHECK_EQ(0u, extra_header_size % kChannelMessageAlignment); -#if defined(MOJO_EDK_LEGACY_PROTOCOL) - DCHECK_EQ(0u, extra_header_size); -#endif + 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); - size_ = sizeof(Header) + extra_header_size + payload_size; + size_ = header_size + 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 @@ -79,21 +100,21 @@ Channel::Message::Message(size_t payload_size, // 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, sizeof(Header) + extra_header_size); - header_ = reinterpret_cast<Header*>(data_); + memset(data_, 0, header_size + extra_header_size); DCHECK_LE(size_, std::numeric_limits<uint32_t>::max()); - header_->num_bytes = static_cast<uint32_t>(size_); + legacy_header()->num_bytes = static_cast<uint32_t>(size_); - DCHECK_LE(sizeof(Header) + extra_header_size, + DCHECK_LE(header_size + extra_header_size, std::numeric_limits<uint16_t>::max()); - 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 + 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); + } if (max_handles_ > 0) { #if defined(OS_WIN) @@ -121,79 +142,93 @@ Channel::Message::~Message() { // static Channel::MessagePtr Channel::Message::Deserialize(const void* data, size_t data_num_bytes) { - if (data_num_bytes < sizeof(Header)) + if (data_num_bytes < sizeof(LegacyHeader)) return nullptr; - const Header* header = reinterpret_cast<const Header*>(data); - if (header->num_bytes != data_num_bytes) { - DLOG(ERROR) << "Decoding invalid message: " << header->num_bytes + 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 << " != " << data_num_bytes; return nullptr; } -#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; + 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; } - 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 < sizeof(MachPortsExtraHeader)) { + if (extra_header_size > 0 && + extra_header_size < sizeof(MachPortsExtraHeader)) { DLOG(ERROR) << "Decoding invalid message: " << extra_header_size << " < " << sizeof(MachPortsExtraHeader); return nullptr; } - uint32_t max_handles = (extra_header_size - sizeof(MachPortsExtraHeader)) / - sizeof(MachPortsEntry); + uint32_t max_handles = + extra_header_size == 0 + ? 0 + : (extra_header_size - sizeof(MachPortsExtraHeader)) / + sizeof(MachPortsEntry); #else const uint32_t max_handles = 0; #endif // defined(OS_WIN) - if (header->num_handles > max_handles || max_handles > kMaxAttachedHandles) { - DLOG(ERROR) << "Decoding invalid message:" << header->num_handles - << " > " << max_handles; + 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; return nullptr; } - MessagePtr message(new Message(payload_size, max_handles)); + MessagePtr message( + new Message(payload_size, max_handles, legacy_header->message_type)); DCHECK_EQ(message->data_num_bytes(), data_num_bytes); // Copy all payload bytes. if (payload_size) memcpy(message->mutable_payload(), payload, payload_size); -#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 (header) { + 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()); + 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; } -#endif - message->header_->num_handles = header->num_handles; #if defined(OS_WIN) - 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)); + 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); } message->SetHandles(std::move(handles)); #endif @@ -201,12 +236,46 @@ Channel::MessagePtr Channel::Message::Deserialize(const void* data, 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 defined(MOJO_EDK_LEGACY_PROTOCOL) - return header_->num_bytes - sizeof(Header); -#else - return size_ - header_->num_header_bytes; -#endif + 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(OS_MACOSX) && !defined(OS_IOS) @@ -224,31 +293,44 @@ bool Channel::Message::has_mach_ports() const { } #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 defined(MOJO_EDK_LEGACY_PROTOCOL) - // Old semantics for ChromeOS and Android - if (header_->num_handles == 0) { - CHECK(!new_handles || new_handles->size() == 0); + 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); 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; @@ -280,12 +362,12 @@ ScopedPlatformHandleVectorPtr Channel::Message::TakeHandles() { } mach_ports_header_->num_ports = 0; } - header_->num_handles = 0; - return std::move(handle_vector_); -#else - header_->num_handles = 0; - return std::move(handle_vector_); #endif + if (is_legacy_message()) + legacy_header()->num_handles = 0; + else + header()->num_handles = 0; + return std::move(handle_vector_); } ScopedPlatformHandleVectorPtr Channel::Message::TakeHandlesForTransport() { @@ -491,58 +573,71 @@ char* Channel::GetReadBuffer(size_t *buffer_capacity) { 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::Header)) { + while (read_buffer_->num_occupied_bytes() >= sizeof(Message::LegacyHeader)) { // 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 (reinterpret_cast<uintptr_t>(read_buffer_->occupied_bytes()) % - kChannelMessageAlignment != 0) + if (!IsAlignedForChannelMessage( + reinterpret_cast<uintptr_t>(read_buffer_->occupied_bytes()))) { 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()); - // 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; + if (legacy_header->num_bytes < sizeof(Message::LegacyHeader) || + legacy_header->num_bytes > kMaxChannelMessageSize) { + LOG(ERROR) << "Invalid message size: " << legacy_header->num_bytes; return false; } - if (read_buffer_->num_occupied_bytes() < header->num_bytes) { + if (read_buffer_->num_occupied_bytes() < legacy_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 = - header->num_bytes - read_buffer_->num_occupied_bytes(); + legacy_header->num_bytes - read_buffer_->num_occupied_bytes(); return true; } -#if defined(MOJO_EDK_LEGACY_PROTOCOL) + const Message::Header* header = nullptr; + if (legacy_header->message_type != Message::MessageType::NORMAL_LEGACY) { + header = reinterpret_cast<const Message::Header*>(legacy_header); + } + size_t extra_header_size = 0; const void* extra_header = 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 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 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 (header->num_handles > 0) { - if (!GetReadPlatformHandles(header->num_handles, extra_header, - extra_header_size, &handles)) { + if (num_handles > 0) { + if (!GetReadPlatformHandles(num_handles, extra_header, extra_header_size, + &handles)) { return false; } @@ -553,8 +648,9 @@ bool Channel::OnReadComplete(size_t bytes_read, size_t *next_read_size_hint) { } // We've got a complete message! Dispatch it and try another. - if (header->message_type != Message::Header::MessageType::NORMAL) { - if (!OnControlMessage(header->message_type, payload, payload_size, + 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, std::move(handles))) { return false; } @@ -564,7 +660,7 @@ bool Channel::OnReadComplete(size_t bytes_read, size_t *next_read_size_hint) { did_dispatch_message = true; } - read_buffer_->Discard(header->num_bytes); + read_buffer_->Discard(legacy_header->num_bytes); } *next_read_size_hint = did_dispatch_message ? 0 : kReadBufferSize; @@ -576,7 +672,7 @@ void Channel::OnError() { delegate_->OnChannelError(); } -bool Channel::OnControlMessage(Message::Header::MessageType message_type, +bool Channel::OnControlMessage(Message::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 aa6d70c..33a510c 100644 --- a/mojo/edk/system/channel.h +++ b/mojo/edk/system/channel.h @@ -10,6 +10,7 @@ #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" @@ -18,53 +19,70 @@ namespace edk { 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 Channel : public base::RefCountedThreadSafe<Channel> { +class MOJO_SYSTEM_IMPL_EXPORT Channel + : public base::RefCountedThreadSafe<Channel> { public: struct Message; using MessagePtr = std::unique_ptr<Message>; // A message to be written to a channel. - struct Message { -#pragma pack(push, 1) - struct Header { - enum class MessageType : uint16_t { - // A normal message. - NORMAL = 0, + 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, + // 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, + }; +#pragma pack(push, 1) + // Old message wire format for ChromeOS and Android, used by NORMAL_LEGACY + // messages. + struct LegacyHeader { // 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; -#else + }; + + // 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; + // 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) @@ -85,7 +103,7 @@ class Channel : public base::RefCountedThreadSafe<Channel> { // Actual number of Mach ports encoded in the extra header. uint16_t num_ports; - // Array of encoded Mach ports. If |num_ports| > 0, |entires[0]| through + // Array of encoded Mach ports. If |num_ports| > 0, |entries[0]| through // to |entries[num_ports-1]| inclusive are valid. MachPortsEntry entries[0]; }; @@ -104,10 +122,8 @@ class Channel : public base::RefCountedThreadSafe<Channel> { // 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, - Header::MessageType message_type = Header::MessageType::NORMAL); - + Message(size_t payload_size, size_t max_handles); + Message(size_t payload_size, size_t max_handles, MessageType message_type); ~Message(); // Constructs a Message from serialized message data. @@ -116,30 +132,24 @@ class Channel : public base::RefCountedThreadSafe<Channel> { const void* data() const { return data_; } size_t data_num_bytes() const { return size_; } -#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); - } - - void* mutable_payload() { return data_ + header_->num_header_bytes; } - const void* payload() const { return data_ + header_->num_header_bytes; } + const void* extra_header() const; + void* mutable_extra_header(); + size_t extra_header_size() const; + + void* mutable_payload(); + const void* payload() const; 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; } + size_t num_handles() const; + bool has_handles() const; #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); @@ -161,11 +171,12 @@ class Channel : public base::RefCountedThreadSafe<Channel> { PlatformHandleVector* handles); #endif + void SetVersionForTest(uint16_t version_number); + private: - size_t size_; - size_t max_handles_; - char* data_; - Header* header_; + size_t size_ = 0; + size_t max_handles_ = 0; + char* data_ = nullptr; ScopedPlatformHandleVectorPtr handle_vector_; @@ -203,7 +214,7 @@ class Channel : public base::RefCountedThreadSafe<Channel> { // |delegate| is destroyed. static scoped_refptr<Channel> Create( Delegate* delegate, - ScopedPlatformHandle platform_handle, + ConnectionParams connection_params, scoped_refptr<base::TaskRunner> io_task_runner); // Request that the channel be shut down. This should always be called before @@ -270,7 +281,7 @@ class Channel : public base::RefCountedThreadSafe<Channel> { // Handles a received control message. Returns |true| if the message is // accepted, or |false| otherwise. - virtual bool OnControlMessage(Message::Header::MessageType message_type, + virtual bool OnControlMessage(Message::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 16a9304..8b4ca7f 100644 --- a/mojo/edk/system/channel_posix.cc +++ b/mojo/edk/system/channel_posix.cc @@ -88,17 +88,18 @@ class ChannelPosix : public Channel, public base::MessageLoopForIO::Watcher { public: ChannelPosix(Delegate* delegate, - ScopedPlatformHandle handle, + ConnectionParams connection_params, scoped_refptr<base::TaskRunner> io_task_runner) : Channel(delegate), self_(this), - handle_(std::move(handle)), + handle_(connection_params.TakeChannelHandle()), io_task_runner_(io_task_runner) #if defined(OS_MACOSX) , handles_to_close_(new PlatformHandleVector) #endif { + CHECK(handle_.is_valid()); } void Start() override { @@ -210,12 +211,22 @@ class ChannelPosix : public Channel, void StartOnIOThread() { DCHECK(!read_watcher_); DCHECK(!write_watcher_); - 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); + read_watcher_.reset( + new base::MessageLoopForIO::FileDescriptorWatcher(FROM_HERE)); 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() { @@ -265,6 +276,24 @@ class ChannelPosix : public Channel, // 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; @@ -321,6 +350,10 @@ class ChannelPosix : public Channel, // 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); @@ -355,7 +388,7 @@ class ChannelPosix : public Channel, } MessagePtr fds_message( new Channel::Message(sizeof(fds[0]) * fds.size(), 0, - Message::Header::MessageType::HANDLES_SENT)); + Message::MessageType::HANDLES_SENT)); memcpy(fds_message->mutable_payload(), fds.data(), sizeof(fds[0]) * fds.size()); outgoing_messages_.emplace_back(std::move(fds_message), 0); @@ -370,8 +403,26 @@ class ChannelPosix : public Channel, } if (result < 0) { - if (errno != EAGAIN && errno != EWOULDBLOCK) + 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 + ) { return false; + } message_view.SetHandles(std::move(handles)); outgoing_messages_.emplace_front(std::move(message_view)); WaitForWriteOnIOThreadNoLock(); @@ -412,22 +463,22 @@ class ChannelPosix : public Channel, } #if defined(OS_MACOSX) - bool OnControlMessage(Message::Header::MessageType message_type, + bool OnControlMessage(Message::MessageType message_type, const void* payload, size_t payload_size, ScopedPlatformHandleVectorPtr handles) override { switch (message_type) { - case Message::Header::MessageType::HANDLES_SENT: { + case Message::MessageType::HANDLES_SENT: { if (payload_size == 0) break; MessagePtr message(new Channel::Message( - payload_size, 0, Message::Header::MessageType::HANDLES_SENT_ACK)); + payload_size, 0, Message::MessageType::HANDLES_SENT_ACK)); memcpy(message->mutable_payload(), payload, payload_size); Write(std::move(message)); return true; } - case Message::Header::MessageType::HANDLES_SENT_ACK: { + case Message::MessageType::HANDLES_SENT_ACK: { size_t num_fds = payload_size / sizeof(int); if (num_fds == 0 || payload_size % sizeof(int) != 0) break; @@ -511,9 +562,10 @@ class ChannelPosix : public Channel, // static scoped_refptr<Channel> Channel::Create( Delegate* delegate, - ScopedPlatformHandle platform_handle, + ConnectionParams connection_params, scoped_refptr<base::TaskRunner> io_task_runner) { - return new ChannelPosix(delegate, std::move(platform_handle), io_task_runner); + return new ChannelPosix(delegate, std::move(connection_params), + io_task_runner); } } // namespace edk diff --git a/mojo/edk/system/channel_win.cc b/mojo/edk/system/channel_win.cc index c989344..c15df16 100644 --- a/mojo/edk/system/channel_win.cc +++ b/mojo/edk/system/channel_win.cc @@ -19,6 +19,7 @@ #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 { @@ -137,8 +138,8 @@ class ChannelWin : public Channel, const HandleEntry* extra_header_handles = reinterpret_cast<const HandleEntry*>(extra_header); for (size_t i = 0; i < num_handles; i++) { - (*handles)->at(i).handle = reinterpret_cast<HANDLE>( - static_cast<uintptr_t>(extra_header_handles[i].handle)); + (*handles)->at(i).handle = + base::win::Uint32ToHandle(extra_header_handles[i].handle); } return true; } @@ -349,9 +350,10 @@ class ChannelWin : public Channel, // static scoped_refptr<Channel> Channel::Create( Delegate* delegate, - ScopedPlatformHandle platform_handle, + ConnectionParams connection_params, scoped_refptr<base::TaskRunner> io_task_runner) { - return new ChannelWin(delegate, std::move(platform_handle), io_task_runner); + return new ChannelWin(delegate, connection_params.TakeChannelHandle(), + io_task_runner); } } // namespace edk diff --git a/mojo/edk/system/core.cc b/mojo/edk/system/core.cc index ada3753..1e0bf4e 100644 --- a/mojo/edk/system/core.cc +++ b/mojo/edk/system/core.cc @@ -44,8 +44,8 @@ namespace { // This is an unnecessarily large limit that is relatively easy to enforce. const uint32_t kMaxHandlesPerMessage = 1024 * 1024; -// 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. +// 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. const uint64_t kUnknownPipeIdForDebug = 0x7f7f7f7f7f7f7f7fUL; void CallWatchCallback(MojoWatchCallback callback, @@ -166,13 +166,17 @@ scoped_refptr<Dispatcher> Core::GetDispatcher(MojoHandle handle) { return handles_.GetDispatcher(handle); } +void Core::SetDefaultProcessErrorCallback( + const ProcessErrorCallback& callback) { + default_process_error_callback_ = callback; +} + void Core::AddChild(base::ProcessHandle process_handle, - ScopedPlatformHandle platform_handle, + ConnectionParams connection_params, const std::string& child_token, const ProcessErrorCallback& process_error_callback) { GetNodeController()->ConnectToChild(process_handle, - std::move(platform_handle), - child_token, + std::move(connection_params), child_token, process_error_callback); } @@ -181,8 +185,26 @@ void Core::ChildLaunchFailed(const std::string& child_token) { GetNodeController()->CloseChildPorts(child_token); } -void Core::InitChild(ScopedPlatformHandle platform_handle) { - GetNodeController()->ConnectToParent(std::move(platform_handle)); +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::SetMachPortProvider(base::PortProvider* port_provider) { @@ -310,15 +332,7 @@ MojoResult Core::PassSharedMemoryHandle( } void Core::RequestShutdown(const base::Closure& 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); + GetNodeController()->RequestShutdown(callback); } ScopedMessagePipeHandle Core::CreateParentMessagePipe( @@ -754,6 +768,8 @@ MojoResult Core::NotifyBadMessage(MojoMessageHandle message, 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; } @@ -774,12 +790,12 @@ MojoResult Core::CreateDataPipe( 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: Use Configuration to get default data pipe capacity. + // TODO(rockot): 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: Broker through the parent when necessary. + // TODO(rockot): Broker through the parent when necessary. scoped_refptr<PlatformSharedBuffer> ring_buffer = GetNodeController()->CreateSharedBuffer( create_options.capacity_num_bytes); @@ -1085,7 +1101,7 @@ MojoResult Core::WaitManyInternal(const MojoHandle* handles, 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 64298f9..1e20a87 100644 --- a/mojo/edk/system/core.h +++ b/mojo/edk/system/core.h @@ -6,6 +6,7 @@ #define MOJO_EDK_SYSTEM_CORE_H_ #include <memory> +#include <string> #include <vector> #include "base/callback.h" @@ -39,7 +40,7 @@ namespace edk { // are thread-safe. class MOJO_SYSTEM_IMPL_EXPORT Core { public: - explicit Core(); + Core(); virtual ~Core(); // Called exactly once, shortly after construction, and before any other @@ -51,17 +52,27 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { 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, - ScopedPlatformHandle platform_handle, + ConnectionParams connection_params, 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(ScopedPlatformHandle platform_handle); + void InitChild(ConnectionParams connection_params); // Creates a message pipe endpoint associated with |token|, which a child // holding the token can later locate and connect to. @@ -262,7 +273,7 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { 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 @@ -284,6 +295,10 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { // 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 33a7068..814ce4b 100644 --- a/mojo/edk/system/core_unittest.cc +++ b/mojo/edk/system/core_unittest.cc @@ -846,14 +846,16 @@ TEST_F(CoreTest, DataPipe) { MOJO_RESULT_FAILED_PRECONDITION, core()->Wait(ch, MOJO_HANDLE_SIGNAL_WRITABLE, 0, &hss)); ASSERT_EQ(0u, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, 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); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfiable_signals); // Write. @@ -872,8 +874,10 @@ TEST_F(CoreTest, DataPipe) { hss = kEmptyMojoHandleSignalsState; ASSERT_EQ(MOJO_RESULT_OK, core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Peek one character. @@ -994,8 +998,9 @@ TEST_F(CoreTest, DataPipe) { ASSERT_EQ( MOJO_RESULT_DEADLINE_EXCEEDED, core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss)); - ASSERT_EQ(0u, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + EXPECT_EQ(0u, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfiable_signals); // TODO(vtl): More. @@ -1081,8 +1086,10 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing2) { ASSERT_EQ(MOJO_RESULT_OK, core()->Wait(ch_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); num_bytes = kBufferSize; ASSERT_EQ(MOJO_RESULT_OK, @@ -1133,8 +1140,10 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing2) { ASSERT_EQ(MOJO_RESULT_OK, core()->Wait(ch_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); num_bytes = kBufferSize; ASSERT_EQ(MOJO_RESULT_OK, @@ -1187,8 +1196,10 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing2) { hss = kEmptyMojoHandleSignalsState; ASSERT_EQ(MOJO_RESULT_OK, core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 1000000000, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, 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 23cb2e0..c908e3a 100644 --- a/mojo/edk/system/data_pipe_consumer_dispatcher.cc +++ b/mojo/edk/system/data_pipe_consumer_dispatcher.cc @@ -124,6 +124,8 @@ MojoResult DataPipeConsumerDispatcher::ReadData(void* elements, 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; @@ -204,6 +206,7 @@ MojoResult DataPipeConsumerDispatcher::BeginReadData(const void** buffer, 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; @@ -419,8 +422,10 @@ DataPipeConsumerDispatcher::Deserialize(const void* data, 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; @@ -473,16 +478,25 @@ DataPipeConsumerDispatcher::GetHandleSignalsStateNoLock() const { 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; } @@ -526,8 +540,8 @@ void DataPipeConsumerDispatcher::UpdateSignalsStateNoLock() { } else if (rv == ports::OK && port_status.has_messages && !in_transit_) { ports::ScopedMessage message; do { - int rv = node_controller_->node()->GetMessageIf(control_port_, nullptr, - &message); + int rv = node_controller_->node()->GetMessage( + control_port_, &message, nullptr); if (rv != ports::OK) peer_closed_ = true; if (message) { @@ -562,8 +576,11 @@ void DataPipeConsumerDispatcher::UpdateSignalsStateNoLock() { } while (message); } - if (peer_closed_ != was_peer_closed || - bytes_available_ != previous_bytes_available) { + 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) { 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 6a7fb1c..b323c16 100644 --- a/mojo/edk/system/data_pipe_consumer_dispatcher.h +++ b/mojo/edk/system/data_pipe_consumer_dispatcher.h @@ -24,7 +24,6 @@ namespace mojo { namespace edk { -struct DataPipeControlMessage; class NodeController; // This is the Dispatcher implementation for the consumer handle for data @@ -118,6 +117,9 @@ class MOJO_SYSTEM_IMPL_EXPORT DataPipeConsumerDispatcher final 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 82ee594..ec84ea3 100644 --- a/mojo/edk/system/data_pipe_control_message.h +++ b/mojo/edk/system/data_pipe_control_message.h @@ -17,7 +17,6 @@ namespace mojo { 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 d056e7d..8c1993a 100644 --- a/mojo/edk/system/data_pipe_producer_dispatcher.cc +++ b/mojo/edk/system/data_pipe_producer_dispatcher.cc @@ -137,9 +137,8 @@ MojoResult DataPipeProducerDispatcher::WriteData(const void* elements, if (*num_bytes == 0) return MOJO_RESULT_OK; // Nothing to do. - 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) { + if ((flags & MOJO_WRITE_DATA_FLAG_ALL_OR_NONE) && + (*num_bytes > available_capacity_)) { // Don't return "should wait" since you can't wait for a specified amount of // data. return MOJO_RESULT_OUT_OF_RANGE; @@ -403,6 +402,7 @@ DataPipeProducerDispatcher::Deserialize(const void* data, dispatcher->available_capacity_ = state->available_capacity; dispatcher->peer_closed_ = state->flags & kFlagPeerClosed; dispatcher->InitializeNoLock(); + dispatcher->UpdateSignalsStateNoLock(); } return dispatcher; @@ -504,8 +504,8 @@ void DataPipeProducerDispatcher::UpdateSignalsStateNoLock() { } else if (rv == ports::OK && port_status.has_messages && !in_transit_) { ports::ScopedMessage message; do { - int rv = node_controller_->node()->GetMessageIf(control_port_, nullptr, - &message); + int rv = node_controller_->node()->GetMessage( + control_port_, &message, nullptr); 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 526444c..610aeac 100644 --- a/mojo/edk/system/data_pipe_unittest.cc +++ b/mojo/edk/system/data_pipe_unittest.cc @@ -12,6 +12,7 @@ #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" @@ -20,6 +21,7 @@ #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 { @@ -40,6 +42,9 @@ const size_t kMultiprocessCapacity = 37; 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), @@ -159,7 +164,8 @@ TEST_F(DataPipeTest, Basic) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &state)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, state.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, + state.satisfied_signals); elements[0] = -1; elements[1] = -1; @@ -245,8 +251,10 @@ TEST_F(DataPipeTest, SimpleReadWrite) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Query. @@ -352,8 +360,10 @@ TEST_F(DataPipeTest, BasicProducerWaiting) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Peek one element. @@ -477,8 +487,9 @@ TEST_F(DataPipeTest, BasicConsumerWaiting) { ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_WRITABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(0u, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + EXPECT_EQ(0u, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfiable_signals); // Write two elements. @@ -491,8 +502,10 @@ TEST_F(DataPipeTest, BasicConsumerWaiting) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Discard one element. @@ -505,8 +518,9 @@ TEST_F(DataPipeTest, BasicConsumerWaiting) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Peek one element. @@ -522,8 +536,9 @@ TEST_F(DataPipeTest, BasicConsumerWaiting) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Read one element. @@ -546,8 +561,10 @@ TEST_F(DataPipeTest, BasicConsumerWaiting) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Close the producer. @@ -558,8 +575,10 @@ TEST_F(DataPipeTest, BasicConsumerWaiting) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE) != 0); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Wait for the peer closed signal. @@ -567,8 +586,11 @@ TEST_F(DataPipeTest, BasicConsumerWaiting) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_PEER_CLOSED) != 0); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Read one element. @@ -589,6 +611,64 @@ TEST_F(DataPipeTest, BasicConsumerWaiting) { 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) { @@ -606,7 +686,7 @@ TEST_F(DataPipeTest, ConsumerWaitingTwoPhase) { 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, true)); + ASSERT_EQ(MOJO_RESULT_OK, BeginWriteData(&buffer, &num_bytes, false)); EXPECT_TRUE(buffer); EXPECT_GE(num_bytes, static_cast<uint32_t>(3u * sizeof(elements[0]))); elements = static_cast<int32_t*>(buffer); @@ -619,8 +699,10 @@ TEST_F(DataPipeTest, ConsumerWaitingTwoPhase) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Read one element. @@ -639,8 +721,9 @@ TEST_F(DataPipeTest, ConsumerWaitingTwoPhase) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Read one element. @@ -704,7 +787,8 @@ TEST_F(DataPipeTest, BasicTwoPhaseWaiting) { ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss)); ASSERT_EQ(0u, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfiable_signals); static_cast<int32_t*>(write_ptr)[0] = 123; @@ -723,8 +807,10 @@ TEST_F(DataPipeTest, BasicTwoPhaseWaiting) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Start another two-phase write and check that it's readable even in the @@ -740,8 +826,10 @@ TEST_F(DataPipeTest, BasicTwoPhaseWaiting) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // End the two-phase write without writing anything. @@ -767,7 +855,8 @@ TEST_F(DataPipeTest, BasicTwoPhaseWaiting) { ASSERT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss)); ASSERT_EQ(0u, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfiable_signals); // End the two-phase read without reading anything. @@ -778,7 +867,8 @@ TEST_F(DataPipeTest, BasicTwoPhaseWaiting) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, 0, &hss)); ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfiable_signals); } @@ -797,7 +887,7 @@ TEST_F(DataPipeTest, AllOrNone) { ASSERT_EQ(MOJO_RESULT_OK, Create(&options)); MojoHandleSignalsState hss; - // Try writing way too much. + // Try writing more than the total capacity of the pipe. uint32_t num_bytes = 20u * sizeof(int32_t); int32_t buffer[100]; Seq(0, arraysize(buffer), buffer); @@ -823,8 +913,10 @@ TEST_F(DataPipeTest, AllOrNone) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Half full. @@ -832,12 +924,11 @@ TEST_F(DataPipeTest, AllOrNone) { ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes)); ASSERT_EQ(5u * sizeof(int32_t), num_bytes); - /* TODO(jam): enable if we end up observing max capacity - // Too much. + // Try writing more than the available capacity of the pipe, but less than the + // total capacity. 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); @@ -913,9 +1004,9 @@ TEST_F(DataPipeTest, AllOrNone) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals); // Try reading too much; "failed precondition" since the producer is closed. @@ -978,8 +1069,9 @@ TEST_F(DataPipeTest, WrapAround) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE) != 0); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Read 10 bytes. @@ -1141,8 +1233,10 @@ TEST_F(DataPipeTest, TwoPhaseWriteReadCloseConsumer) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Start two-phase read. @@ -1231,9 +1325,11 @@ TEST_F(DataPipeTest, WriteCloseProducerReadNoData) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, hss.satisfiable_signals); // Peek that data. @@ -1290,8 +1386,10 @@ TEST_F(DataPipeTest, TwoPhaseReadMemoryStable) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Begin a two-phase read. @@ -1315,8 +1413,9 @@ TEST_F(DataPipeTest, TwoPhaseReadMemoryStable) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Read the two phase memory to check it's still valid. @@ -1402,8 +1501,10 @@ TEST_F(DataPipeTest, TwoPhaseMoreInvalidArguments) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // One element available. @@ -1470,8 +1571,10 @@ TEST_F(DataPipeTest, SendProducer) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Check the data. @@ -1511,8 +1614,10 @@ TEST_F(DataPipeTest, SendProducer) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE, &hss)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, hss.satisfiable_signals); // Check the second write. @@ -1550,9 +1655,11 @@ TEST_F(DataPipeTest, ConsumerWithClosedProducerSent) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, MOJO_DEADLINE_INDEFINITE, &state)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, state.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, state.satisfiable_signals); // Now send the consumer over a MP so that it's serialized. @@ -1575,9 +1682,11 @@ TEST_F(DataPipeTest, ConsumerWithClosedProducerSent) { ASSERT_EQ(MOJO_RESULT_OK, MojoWait(consumer_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, MOJO_DEADLINE_INDEFINITE, &state)); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, state.satisfied_signals); - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, state.satisfiable_signals); int32_t read_data; @@ -1649,8 +1758,8 @@ bool ReadAllData(MojoHandle consumer, 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_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, - hss.satisfiable_signals); + EXPECT_TRUE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE); + EXPECT_TRUE(hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_PEER_CLOSED); } return num_bytes == 0; @@ -1898,6 +2007,90 @@ TEST_F(DataPipeTest, CreateInChild) { 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 e41817d..b570793 100644 --- a/mojo/edk/system/handle_table.cc +++ b/mojo/edk/system/handle_table.cc @@ -21,7 +21,8 @@ MojoHandle HandleTable::AddDispatcher(scoped_refptr<Dispatcher> dispatcher) { return MOJO_HANDLE_INVALID; MojoHandle handle = next_available_handle_++; - auto result = handles_.insert(std::make_pair(handle, Entry(dispatcher))); + auto result = + handles_.insert(std::make_pair(handle, Entry(std::move(dispatcher)))); DCHECK(result.second); return handle; @@ -66,7 +67,7 @@ MojoResult HandleTable::GetAndRemoveDispatcher( if (it->second.busy) return MOJO_RESULT_BUSY; - *dispatcher = it->second.dispatcher; + *dispatcher = std::move(it->second.dispatcher); handles_.erase(it); return MOJO_RESULT_OK; } @@ -124,7 +125,7 @@ void HandleTable::GetActiveHandlesForTest(std::vector<MojoHandle>* handles) { HandleTable::Entry::Entry() {} HandleTable::Entry::Entry(scoped_refptr<Dispatcher> dispatcher) - : dispatcher(dispatcher) {} + : dispatcher(std::move(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 f06054d..f27336b 100644 --- a/mojo/edk/system/message_pipe_dispatcher.cc +++ b/mojo/edk/system/message_pipe_dispatcher.cc @@ -14,6 +14,7 @@ #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" @@ -59,6 +60,103 @@ class MessagePipeDispatcher::PortObserverThunk 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, @@ -141,7 +239,7 @@ MojoResult MessagePipeDispatcher::WriteMessage( size_t num_bytes = message->num_bytes(); int rv = node_controller_->SendMessage(port_, message->TakePortsMessage()); - DVLOG(2) << "Sent message on pipe " << pipe_id_ << " endpoint " << endpoint_ + DVLOG(4) << "Sent message on pipe " << pipe_id_ << " endpoint " << endpoint_ << " [port=" << port_.name() << "; rv=" << rv << "; num_bytes=" << num_bytes << "]"; @@ -151,8 +249,6 @@ MojoResult MessagePipeDispatcher::WriteMessage( 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; } @@ -186,50 +282,9 @@ MojoResult MessagePipeDispatcher::ReadMessage( // This flag exists to support both new and old API behavior. ports::ScopedMessage ports_message; - 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); + 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); if (invalid_message) return MOJO_RESULT_UNKNOWN; @@ -258,8 +313,6 @@ MojoResult MessagePipeDispatcher::ReadMessage( // 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; } @@ -530,15 +583,11 @@ void MessagePipeDispatcher::OnPortStatusChanged() { if (node_controller_->node()->GetStatus(port_, &port_status) == ports::OK) { if (port_status.has_messages) { ports::ScopedMessage unused; - 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_ + PeekSizeMessageFilter filter; + node_controller_->node()->GetMessage(port_, &unused, &filter); + DVLOG(4) << "New message detected on message pipe " << pipe_id_ << " endpoint " << endpoint_ << " [port=" << port_.name() - << "; size=" << message_size << "]"; + << "; size=" << filter.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 fddd0fd..6743222 100644 --- a/mojo/edk/system/message_pipe_dispatcher.h +++ b/mojo/edk/system/message_pipe_dispatcher.h @@ -21,7 +21,6 @@ namespace mojo { 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 fcfaeca..8f48950 100644 --- a/mojo/edk/system/message_pipe_unittest.cc +++ b/mojo/edk/system/message_pipe_unittest.cc @@ -706,6 +706,17 @@ TEST_F(FuseMessagePipeTest, FuseAfterPeerWriteAndClosure) { 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 6e2c6f1..498980c 100644 --- a/mojo/edk/system/multiprocess_message_pipe_unittest.cc +++ b/mojo/edk/system/multiprocess_message_pipe_unittest.cc @@ -18,6 +18,8 @@ #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" @@ -29,6 +31,7 @@ #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" @@ -68,6 +71,16 @@ class MultiprocessMessagePipeTest : public test::MojoTestBase { }; }; +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, @@ -116,7 +129,7 @@ DEFINE_TEST_CLIENT_WITH_PIPE(EchoEcho, MultiprocessMessagePipeTest, h) { return rv; } -TEST_F(MultiprocessMessagePipeTest, Basic) { +TEST_P(MultiprocessMessagePipeTestWithPeerSupport, Basic) { RUN_CHILD_ON_PIPE(EchoEcho, h) std::string hello("hello"); ASSERT_EQ(MOJO_RESULT_OK, @@ -152,7 +165,7 @@ TEST_F(MultiprocessMessagePipeTest, Basic) { END_CHILD_AND_EXPECT_EXIT_CODE(1 % 100); } -TEST_F(MultiprocessMessagePipeTest, QueueMessages) { +TEST_P(MultiprocessMessagePipeTestWithPeerSupport, QueueMessages) { static const size_t kNumMessages = 1001; RUN_CHILD_ON_PIPE(EchoEcho, h) for (size_t i = 0; i < kNumMessages; i++) { @@ -415,7 +428,7 @@ TEST_P(MultiprocessMessagePipeTestWithPipeCount, PlatformHandlePassing) { for (size_t i = 0; i < pipe_count; ++i) { base::FilePath unused; base::ScopedFILE fp( - CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused)); + CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &unused)); const std::string world("world"); CHECK_EQ(fwrite(&world[0], 1, world.size(), fp.get()), world.size()); fflush(fp.get()); @@ -430,7 +443,8 @@ TEST_P(MultiprocessMessagePipeTestWithPipeCount, PlatformHandlePassing) { } char message[128]; - sprintf(message, "hello %d", static_cast<int>(pipe_count)); + snprintf(message, sizeof(message), "hello %d", + static_cast<int>(pipe_count)); ASSERT_EQ(MOJO_RESULT_OK, MojoWriteMessage(h, message, static_cast<uint32_t>(strlen(message)), @@ -452,9 +466,9 @@ TEST_P(MultiprocessMessagePipeTestWithPipeCount, PlatformHandlePassing) { #if !defined(OS_ANDROID) INSTANTIATE_TEST_CASE_P(PipeCount, MultiprocessMessagePipeTestWithPipeCount, - // TODO: Re-enable the 140-pipe case when ChannelPosix - // has support for sending lots of handles. - testing::Values(1u, 128u/*, 140u*/)); + // TODO(rockot): 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) { @@ -509,7 +523,7 @@ DEFINE_TEST_CLIENT_WITH_PIPE(CheckMessagePipe, MultiprocessMessagePipeTest, h) { return 0; } -TEST_F(MultiprocessMessagePipeTest, MessagePipePassing) { +TEST_P(MultiprocessMessagePipeTestWithPeerSupport, MessagePipePassing) { RUN_CHILD_ON_PIPE(CheckMessagePipe, h) MojoCreateSharedBufferOptions options; options.struct_size = sizeof(options); @@ -551,7 +565,7 @@ TEST_F(MultiprocessMessagePipeTest, MessagePipePassing) { END_CHILD() } -TEST_F(MultiprocessMessagePipeTest, MessagePipeTwoPassing) { +TEST_P(MultiprocessMessagePipeTestWithPeerSupport, MessagePipeTwoPassing) { RUN_CHILD_ON_PIPE(CheckMessagePipe, h) MojoHandle mp1, mp2; ASSERT_EQ(MOJO_RESULT_OK, @@ -681,11 +695,9 @@ TEST_F(MultiprocessMessagePipeTest, DataPipeConsumer) { END_CHILD(); } -TEST_F(MultiprocessMessagePipeTest, CreateMessagePipe) { +TEST_P(MultiprocessMessagePipeTestWithPeerSupport, 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')); @@ -693,7 +705,7 @@ TEST_F(MultiprocessMessagePipeTest, CreateMessagePipe) { CloseHandle(p1); } -TEST_F(MultiprocessMessagePipeTest, PassMessagePipeLocal) { +TEST_P(MultiprocessMessagePipeTestWithPeerSupport, PassMessagePipeLocal) { MojoHandle p0, p1; CreateMessagePipe(&p0, &p1); VerifyTransmission(p0, p1, "testing testing"); @@ -733,7 +745,7 @@ DEFINE_TEST_CLIENT_WITH_PIPE(ChannelEchoClient, MultiprocessMessagePipeTest, return 0; } -TEST_F(MultiprocessMessagePipeTest, MultiprocessChannelPipe) { +TEST_P(MultiprocessMessagePipeTestWithPeerSupport, MultiprocessChannelPipe) { RUN_CHILD_ON_PIPE(ChannelEchoClient, h) VerifyEcho(h, "in an interstellar burst"); VerifyEcho(h, "i am back to save the universe"); @@ -758,7 +770,8 @@ DEFINE_TEST_CLIENT_WITH_PIPE(EchoServiceClient, MultiprocessMessagePipeTest, return 0; } -TEST_F(MultiprocessMessagePipeTest, PassMessagePipeCrossProcess) { +TEST_P(MultiprocessMessagePipeTestWithPeerSupport, + PassMessagePipeCrossProcess) { MojoHandle p0, p1; CreateMessagePipe(&p0, &p1); RUN_CHILD_ON_PIPE(EchoServiceClient, h) @@ -815,7 +828,8 @@ DEFINE_TEST_CLIENT_WITH_PIPE(EchoServiceFactoryClient, return 0; } -TEST_F(MultiprocessMessagePipeTest, PassMoarMessagePipesCrossProcess) { +TEST_P(MultiprocessMessagePipeTestWithPeerSupport, + PassMoarMessagePipesCrossProcess) { MojoHandle echo_factory_proxy, echo_factory_request; CreateMessagePipe(&echo_factory_proxy, &echo_factory_request); @@ -860,7 +874,8 @@ TEST_F(MultiprocessMessagePipeTest, PassMoarMessagePipesCrossProcess) { CloseHandle(echo_proxy_c); } -TEST_F(MultiprocessMessagePipeTest, ChannelPipesWithMultipleChildren) { +TEST_P(MultiprocessMessagePipeTestWithPeerSupport, + ChannelPipesWithMultipleChildren) { RUN_CHILD_ON_PIPE(ChannelEchoClient, a) RUN_CHILD_ON_PIPE(ChannelEchoClient, b) VerifyEcho(a, "hello child 0"); @@ -890,7 +905,7 @@ DEFINE_TEST_CLIENT_TEST_WITH_PIPE(PingPongPipeClient, EXPECT_EQ("quit", ReadMessage(h)); } -TEST_F(MultiprocessMessagePipeTest, PingPongPipe) { +TEST_P(MultiprocessMessagePipeTestWithPeerSupport, PingPongPipe) { MojoHandle p0, p1; CreateMessagePipe(&p0, &p1); @@ -983,7 +998,7 @@ DEFINE_TEST_CLIENT_WITH_PIPE(CommandDrivenClient, MultiprocessMessagePipeTest, } } - for (auto& pipe: named_pipes) + for (auto& pipe : named_pipes) CloseHandle(pipe.second); return 0; @@ -1002,8 +1017,8 @@ TEST_F(MultiprocessMessagePipeTest, ChildToChildPipes) { b.SendHandle("y", p1); // Make sure they can talk. - a.Send("say:x:hello sir"); - b.Send("hear:y:hello sir"); + a.Send("say:x:hello"); + b.Send("hear:y:hello"); b.Send("say:y:i love multiprocess pipes!"); a.Send("hear:x:i love multiprocess pipes!"); @@ -1096,11 +1111,12 @@ DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReceivePipeWithClosedPeer, MojoHandle p; EXPECT_EQ("foo", ReadMessageWithHandles(h, &p, 1)); - EXPECT_EQ(MOJO_RESULT_OK, MojoWait(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED, - MOJO_DEADLINE_INDEFINITE, nullptr)); + auto result = MojoWait(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED, + MOJO_DEADLINE_INDEFINITE, nullptr); + EXPECT_EQ(MOJO_RESULT_OK, result); } -TEST_F(MultiprocessMessagePipeTest, SendPipeThenClosePeer) { +TEST_P(MultiprocessMessagePipeTestWithPeerSupport, SendPipeThenClosePeer) { RUN_CHILD_ON_PIPE(ReceivePipeWithClosedPeer, h) MojoHandle a, b; CreateMessagePipe(&a, &b); @@ -1176,8 +1192,7 @@ TEST_F(MultiprocessMessagePipeTest, END_CHILD() } - -TEST_F(MultiprocessMessagePipeTest, SendClosePeerSend) { +TEST_P(MultiprocessMessagePipeTestWithPeerSupport, SendClosePeerSend) { MojoHandle a, b; CreateMessagePipe(&a, &b); @@ -1220,7 +1235,7 @@ DEFINE_TEST_CLIENT_TEST_WITH_PIPE(WriteCloseSendPeerClient, EXPECT_EQ("quit", ReadMessage(h)); } -TEST_F(MultiprocessMessagePipeTest, WriteCloseSendPeer) { +TEST_P(MultiprocessMessagePipeTestWithPeerSupport, WriteCloseSendPeer) { MojoHandle pipe[2]; CreateMessagePipe(&pipe[0], &pipe[1]); @@ -1243,41 +1258,61 @@ TEST_F(MultiprocessMessagePipeTest, WriteCloseSendPeer) { END_CHILD() } -DEFINE_TEST_CLIENT_TEST_WITH_PIPE(BootstrapMessagePipeAsyncClient, +DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MessagePipeStatusChangeInTransitClient, MultiprocessMessagePipeTest, parent) { - // 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()); + // 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)); + + // 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(); + } - // Ensure that we can read and write on the new pipe. - VerifyEcho(pipe.get().value(), "goodbye"); + // 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]); } -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); - - // 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"); +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]); + + 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]); END_CHILD() } @@ -1345,7 +1380,13 @@ TEST_F(MultiprocessMessagePipeTest, NotifyBadMessage) { 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 ce094c1..b0f770d 100644 --- a/mojo/edk/system/node_channel.cc +++ b/mojo/edk/system/node_channel.cc @@ -47,6 +47,7 @@ enum class MessageType : uint32_t { #if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) PORTS_MESSAGE_FROM_RELAY, #endif + ACCEPT_PEER, }; struct Header { @@ -54,8 +55,8 @@ struct Header { uint32_t padding; }; -static_assert(sizeof(Header) % kChannelMessageAlignment == 0, - "Invalid header size."); +static_assert(IsAlignedForChannelMessage(sizeof(Header)), + "Invalid header size."); struct AcceptChildData { ports::NodeName parent_name; @@ -67,6 +68,12 @@ struct AcceptParentData { 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; @@ -153,14 +160,14 @@ bool GetMessagePayload(const void* bytes, // static scoped_refptr<NodeChannel> NodeChannel::Create( Delegate* delegate, - ScopedPlatformHandle platform_handle, + ConnectionParams connection_params, 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(platform_handle), io_task_runner, + return new NodeChannel(delegate, std::move(connection_params), io_task_runner, process_error_callback); #endif } @@ -282,6 +289,18 @@ void NodeChannel::AcceptParent(const ports::NodeName& token, 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; @@ -435,17 +454,18 @@ void NodeChannel::PortsMessageFromRelay(const ports::NodeName& source, #endif // defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) NodeChannel::NodeChannel(Delegate* delegate, - ScopedPlatformHandle platform_handle, + ConnectionParams connection_params, 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(platform_handle), io_task_runner_)) + , + channel_( + Channel::Create(this, std::move(connection_params), io_task_runner_)) #endif - { +{ } NodeChannel::~NodeChannel() { @@ -728,6 +748,16 @@ void NodeChannel::OnChannelMessage(const void* payload, #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; } @@ -771,7 +801,6 @@ void NodeChannel::ProcessPendingMessagesWithMachPorts() { 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 5a5fc8a..95dc341 100644 --- a/mojo/edk/system/node_channel.h +++ b/mojo/edk/system/node_channel.h @@ -16,6 +16,7 @@ #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" @@ -76,7 +77,10 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>, 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; @@ -87,7 +91,7 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>, static scoped_refptr<NodeChannel> Create( Delegate* delegate, - ScopedPlatformHandle platform_handle, + ConnectionParams connection_params, scoped_refptr<base::TaskRunner> io_task_runner, const ProcessErrorCallback& process_error_callback); @@ -124,6 +128,9 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>, 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, @@ -161,7 +168,7 @@ class NodeChannel : public base::RefCountedThreadSafe<NodeChannel>, std::queue<std::pair<ports::NodeName, Channel::MessagePtr>>; NodeChannel(Delegate* delegate, - ScopedPlatformHandle platform_handle, + ConnectionParams connection_params, 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 63291a5..7bdb571 100644 --- a/mojo/edk/system/node_controller.cc +++ b/mojo/edk/system/node_controller.cc @@ -18,6 +18,8 @@ #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" @@ -58,7 +60,7 @@ void RecordPeerCount(size_t count) { // 8k is the maximum number of file descriptors allowed in Chrome. UMA_HISTOGRAM_CUSTOM_COUNTS("Mojo.System.Node.ConnectedPeers", static_cast<int32_t>(count), - 0 /* min */, + 1 /* min */, 8000 /* max */, 50 /* bucket count */); } @@ -69,7 +71,7 @@ void RecordPendingChildCount(size_t count) { // 8k is the maximum number of file descriptors allowed in Chrome. UMA_HISTOGRAM_CUSTOM_COUNTS("Mojo.System.Node.PendingChildren", static_cast<int32_t>(count), - 0 /* min */, + 1 /* min */, 8000 /* max */, 50 /* bucket count */); } @@ -163,7 +165,7 @@ void NodeController::SetIOTaskRunner( void NodeController::ConnectToChild( base::ProcessHandle process_handle, - ScopedPlatformHandle platform_handle, + ConnectionParams connection_params, const std::string& child_token, const ProcessErrorCallback& process_error_callback) { // Generate the temporary remote node name here so that it can be associated @@ -194,13 +196,10 @@ void NodeController::ConnectToChild( #endif io_task_runner_->PostTask( - FROM_HERE, - base::Bind(&NodeController::ConnectToChildOnIOThread, - base::Unretained(this), - process_handle, - base::Passed(&platform_handle), - node_name, - process_error_callback)); + FROM_HERE, base::Bind(&NodeController::ConnectToChildOnIOThread, + base::Unretained(this), process_handle, + base::Passed(&connection_params), node_name, + process_error_callback)); } void NodeController::CloseChildPorts(const std::string& child_token) { @@ -227,14 +226,19 @@ void NodeController::CloseChildPorts(const std::string& child_token) { AcceptIncomingMessages(); } -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. +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. base::ElapsedTimer timer; - broker_.reset(new Broker(std::move(platform_handle))); - platform_handle = broker_->GetParentPlatformHandle(); + broker_.reset(new Broker(connection_params.TakeChannelHandle())); + ScopedPlatformHandle platform_handle = broker_->GetParentPlatformHandle(); UMA_HISTOGRAM_TIMES("Mojo.System.GetParentPlatformHandleSyncTime", timer.Elapsed()); @@ -243,21 +247,33 @@ void NodeController::ConnectToParent(ScopedPlatformHandle platform_handle) { // 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(&platform_handle))); + base::Unretained(this), base::Passed(&connection_params))); } -void NodeController::SetPortObserver( - const ports::PortRef& port, - const scoped_refptr<PortObserver>& observer) { - node_->SetUserData(port, observer); +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::ClosePort(const ports::PortRef& port) { @@ -345,7 +361,7 @@ int NodeController::MergeLocalPorts(const ports::PortRef& port0, scoped_refptr<PlatformSharedBuffer> NodeController::CreateSharedBuffer( size_t num_bytes) { -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_NACL_SFI) +#if !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 @@ -376,24 +392,40 @@ void NodeController::NotifyBadMessageFrom(const ports::NodeName& source_node, void NodeController::ConnectToChildOnIOThread( base::ProcessHandle process_handle, - ScopedPlatformHandle platform_handle, + ConnectionParams connection_params, ports::NodeName token, const ProcessErrorCallback& process_error_callback) { DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_NACL) +#if !defined(OS_MACOSX) && !defined(OS_NACL) PlatformChannelPair node_channel; + ScopedPlatformHandle server_handle = node_channel.PassServerHandle(); // BrokerHost owns itself. - 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); + 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); + } #else + CHECK(channel_ok); +#endif // defined(OS_WIN) + scoped_refptr<NodeChannel> channel = - NodeChannel::Create(this, std::move(platform_handle), io_task_runner_, + 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_, process_error_callback); -#endif +#endif // !defined(OS_MACOSX) && !defined(OS_NACL) // 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 @@ -411,7 +443,7 @@ void NodeController::ConnectToChildOnIOThread( } void NodeController::ConnectToParentOnIOThread( - ScopedPlatformHandle platform_handle) { + ConnectionParams connection_params) { DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); { @@ -422,7 +454,7 @@ void NodeController::ConnectToParentOnIOThread( // 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(platform_handle), io_task_runner_, + NodeChannel::Create(this, std::move(connection_params), 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. @@ -434,6 +466,37 @@ void NodeController::ConnectToParentOnIOThread( 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_); @@ -559,16 +622,18 @@ void NodeController::DropPeer(const ports::NodeName& name, 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) { - base::AutoLock lock(pending_port_merges_lock_); - reject_pending_merges_ = true; + if (is_parent) + CancelPendingPortMerges(); - for (const auto& port : pending_port_merges_) - ports_to_close.push_back(port.second); - pending_port_merges_.clear(); + 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 : ports_to_close) @@ -652,20 +717,50 @@ void NodeController::SendPeerMessage(const ports::NodeName& name, } void NodeController::AcceptIncomingMessages() { - { - 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_); - - while (!messages.empty()) { - node_->AcceptMessage(std::move(messages.front())); - messages.pop(); - } + // This is an impactically large value which should never be reached in + // practice. See the CHECK below for usage. + constexpr size_t kMaxAcceptedMessages = 1000000; + + 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; } + + // 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(); @@ -712,6 +807,7 @@ void NodeController::DropAllPeers() { peers_.clear(); pending_children_.clear(); pending_peer_messages_.clear(); + peer_connections_.clear(); } for (const auto& peer : all_peers) @@ -746,6 +842,7 @@ void NodeController::ForwardMessage(const ports::NodeName& node, !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)); } @@ -896,9 +993,10 @@ void NodeController::OnAddBrokerClient(const ports::NodeName& from_node, } PlatformChannelPair broker_channel; - scoped_refptr<NodeChannel> client = NodeChannel::Create( - this, broker_channel.PassServerHandle(), io_task_runner_, - ProcessErrorCallback()); + ConnectionParams connection_params(broker_channel.PassServerHandle()); + scoped_refptr<NodeChannel> client = + NodeChannel::Create(this, std::move(connection_params), io_task_runner_, + ProcessErrorCallback()); #if defined(OS_WIN) // The broker must have a working handle to the client process in order to @@ -974,8 +1072,9 @@ void NodeController::OnAcceptBrokerClient(const ports::NodeName& from_node, broker = parent; } else { DCHECK(broker_channel.is_valid()); - broker = NodeChannel::Create(this, std::move(broker_channel), - io_task_runner_, ProcessErrorCallback()); + broker = + NodeChannel::Create(this, ConnectionParams(std::move(broker_channel)), + io_task_runner_, ProcessErrorCallback()); AddPeer(broker_name, broker, true /* start_channel */); } @@ -1096,15 +1195,15 @@ void NodeController::OnIntroduce(const ports::NodeName& from_node, if (!channel_handle.is_valid()) { node_->LostConnectionToNode(name); - DLOG(ERROR) << "Could not be introduced to peer " << name; + DVLOG(1) << "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, std::move(channel_handle), io_task_runner_, - ProcessErrorCallback()); + NodeChannel::Create(this, ConnectionParams(std::move(channel_handle)), + io_task_runner_, ProcessErrorCallback()); DVLOG(1) << "Adding new peer " << name << " via parent introduction."; AddPeer(name, channel, true /* start_channel */); @@ -1219,6 +1318,46 @@ void NodeController::OnPortsMessageFromRelay(const ports::NodeName& from_node, } #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()) { @@ -1248,6 +1387,21 @@ MachPortRelay* NodeController::GetMachPortRelay() { } #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; } @@ -1261,7 +1415,8 @@ void NodeController::AttemptShutdownIfRequested() { base::AutoLock lock(shutdown_lock_); if (shutdown_callback_.is_null()) return; - if (!node_->CanShutdownCleanly(true /* allow_local_ports */)) { + if (!node_->CanShutdownCleanly( + ports::Node::ShutdownPolicy::ALLOW_LOCAL_PORTS)) { DVLOG(2) << "Unable to cleanly shut down node " << name_; return; } @@ -1276,5 +1431,29 @@ void NodeController::AttemptShutdownIfRequested() { 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 11d5f19..46a2d61 100644 --- a/mojo/edk/system/node_controller.h +++ b/mojo/edk/system/node_controller.h @@ -73,7 +73,7 @@ class NodeController : public ports::NodeDelegate, // Connects this node to a child node. This node will initiate a handshake. void ConnectToChild(base::ProcessHandle process_handle, - ScopedPlatformHandle platform_handle, + ConnectionParams connection_params, const std::string& child_token, const ProcessErrorCallback& process_error_callback); @@ -81,14 +81,23 @@ class NodeController : public ports::NodeDelegate, // |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(ScopedPlatformHandle platform_handle); + 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); // Sets a port's observer. If |observer| is null the port's current observer // is removed. void SetPortObserver(const ports::PortRef& port, - const scoped_refptr<PortObserver>& observer); + 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. @@ -139,12 +148,36 @@ class NodeController : public ports::NodeDelegate, 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, - ScopedPlatformHandle platform_handle, + ConnectionParams connection_params, ports::NodeName token, const ProcessErrorCallback& process_error_callback); - void ConnectToParentOnIOThread(ScopedPlatformHandle platform_handle); + 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); scoped_refptr<NodeChannel> GetPeerChannel(const ports::NodeName& name); scoped_refptr<NodeChannel> GetParentChannel(); @@ -206,12 +239,21 @@ class NodeController : public ports::NodeDelegate, 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. @@ -286,6 +328,8 @@ class NodeController : public ports::NodeDelegate, // 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_; @@ -303,12 +347,19 @@ class NodeController : public ports::NodeDelegate, // 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_POSIX) && !defined(OS_MACOSX) && !defined(OS_NACL_SFI) - // Broker for sync shared buffer creation (non-Mac posix-only) in children. +#if !defined(OS_MACOSX) && !defined(OS_NACL_SFI) + // Broker for sync shared buffer creation 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 ad6dc38..7a94262 100644 --- a/mojo/edk/system/platform_handle_dispatcher_unittest.cc +++ b/mojo/edk/system/platform_handle_dispatcher_unittest.cc @@ -28,7 +28,7 @@ TEST(PlatformHandleDispatcherTest, Basic) { base::FilePath unused; base::ScopedFILE fp( - CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused)); + CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &unused)); ASSERT_TRUE(fp); EXPECT_EQ(sizeof(kHelloWorld), fwrite(kHelloWorld, 1, sizeof(kHelloWorld), fp.get())); @@ -70,7 +70,7 @@ TEST(PlatformHandleDispatcherTest, Serialization) { base::FilePath unused; base::ScopedFILE fp( - CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused)); + CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &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 239b3a4..37b2548 100644 --- a/mojo/edk/system/ports/BUILD.gn +++ b/mojo/edk/system/ports/BUILD.gn @@ -10,6 +10,7 @@ source_set("ports") { "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 new file mode 100644 index 0000000..bf8fa21 --- /dev/null +++ b/mojo/edk/system/ports/message_filter.h @@ -0,0 +1,29 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_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 ef2e940..defb1b6 100644 --- a/mojo/edk/system/ports/message_queue.cc +++ b/mojo/edk/system/ports/message_queue.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "mojo/edk/system/ports/event.h" +#include "mojo/edk/system/ports/message_filter.h" namespace mojo { namespace edk { @@ -44,10 +45,9 @@ bool MessageQueue::HasNextMessage() const { return !heap_.empty() && GetSequenceNum(heap_[0]) == next_sequence_num_; } -void MessageQueue::GetNextMessageIf( - std::function<bool(const Message&)> selector, - ScopedMessage* message) { - if (!HasNextMessage() || (selector && !selector(*heap_[0].get()))) { +void MessageQueue::GetNextMessage(ScopedMessage* message, + MessageFilter* filter) { + if (!HasNextMessage() || (filter && !filter->Match(*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 d90ac1a..d9a47ed 100644 --- a/mojo/edk/system/ports/message_queue.h +++ b/mojo/edk/system/ports/message_queue.h @@ -22,6 +22,8 @@ namespace ports { 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 @@ -38,9 +40,9 @@ class MessageQueue { bool HasNextMessage() const; - // Gives ownership of the message. The selector may be null. - void GetNextMessageIf(std::function<bool(const Message&)> selector, - ScopedMessage* message); + // 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); // 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 7088cbf..ea17698 100644 --- a/mojo/edk/system/ports/name.cc +++ b/mojo/edk/system/ports/name.cc @@ -8,6 +8,10 @@ namespace mojo { 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 1082719..72e41b9 100644 --- a/mojo/edk/system/ports/name.h +++ b/mojo/edk/system/ports/name.h @@ -40,14 +40,14 @@ struct PortName : Name { PortName(uint64_t v1, uint64_t v2) : Name(v1, v2) {} }; -const PortName kInvalidPortName = {0, 0}; +extern const PortName kInvalidPortName; struct NodeName : Name { NodeName() : Name(0, 0) {} NodeName(uint64_t v1, uint64_t v2) : Name(v1, v2) {} }; -const NodeName kInvalidNodeName = {0, 0}; +extern const NodeName kInvalidNodeName; } // namespace ports } // namespace edk diff --git a/mojo/edk/system/ports/node.cc b/mojo/edk/system/ports/node.cc index 128ecdf..9c6f1fd 100644 --- a/mojo/edk/system/ports/node.cc +++ b/mojo/edk/system/ports/node.cc @@ -8,6 +8,7 @@ #include <utility> +#include "base/atomicops.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/synchronization/lock.h" @@ -64,10 +65,10 @@ Node::~Node() { DLOG(WARNING) << "Unclean shutdown for node " << name_; } -bool Node::CanShutdownCleanly(bool allow_local_ports) { +bool Node::CanShutdownCleanly(ShutdownPolicy policy) { base::AutoLock ports_lock(ports_lock_); - if (!allow_local_ports) { + if (policy == ShutdownPolicy::DONT_ALLOW_LOCAL_PORTS) { #if DCHECK_IS_ON() for (auto entry : ports_) { DVLOG(2) << "Port " << entry.first << " referencing node " @@ -78,6 +79,8 @@ bool Node::CanShutdownCleanly(bool allow_local_ports) { 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. @@ -114,8 +117,7 @@ int Node::CreateUninitializedPort(PortRef* port_ref) { PortName port_name; delegate_->GenerateRandomPortName(&port_name); - scoped_refptr<Port> port = make_scoped_refptr(new Port(kInitialSequenceNum, - kInitialSequenceNum)); + scoped_refptr<Port> port(new Port(kInitialSequenceNum, kInitialSequenceNum)); int rv = AddPortWithName(port_name, port); if (rv != OK) return rv; @@ -167,7 +169,7 @@ int Node::CreatePortPair(PortRef* port0_ref, PortRef* port1_ref) { } int Node::SetUserData(const PortRef& port_ref, - const scoped_refptr<UserData>& user_data) { + scoped_refptr<UserData> user_data) { Port* port = port_ref.port(); base::AutoLock lock(port->lock); @@ -261,16 +263,12 @@ int Node::GetStatus(const PortRef& port_ref, PortStatus* port_status) { return OK; } -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) { +int Node::GetMessage(const PortRef& port_ref, + ScopedMessage* message, + MessageFilter* filter) { *message = nullptr; - DVLOG(2) << "GetMessageIf for " << port_ref.name() << "@" << name_; + DVLOG(4) << "GetMessage for " << port_ref.name() << "@" << name_; Port* port = port_ref.port(); { @@ -286,7 +284,7 @@ int Node::GetMessageIf(const PortRef& port_ref, if (!CanAcceptMoreMessages(port)) return ERROR_PORT_PEER_CLOSED; - port->message_queue.GetNextMessageIf(std::move(selector), message); + port->message_queue.GetNextMessage(message, filter); } // Allow referenced ports to trigger PortStatusChanged calls. @@ -426,7 +424,7 @@ int Node::OnUserMessage(ScopedMessage message) { ports_buf << message->ports()[i]; } - DVLOG(2) << "AcceptMessage " << event->sequence_num + DVLOG(4) << "AcceptMessage " << event->sequence_num << " [ports=" << ports_buf.str() << "] at " << port_name << "@" << name_; #endif @@ -506,7 +504,7 @@ int Node::OnPortAccepted(const PortName& port_name) { << " pointing to " << port->peer_port_name << "@" << port->peer_node_name; - return BeginProxying(PortRef(port_name, port)); + return BeginProxying(PortRef(port_name, std::move(port))); } int Node::OnObserveProxy(const PortName& port_name, @@ -531,17 +529,6 @@ int Node::OnObserveProxy(const PortName& port_name, 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; } @@ -632,7 +619,7 @@ int Node::OnObserveProxyAck(const PortName& port_name, port->remove_proxy_on_last_message = true; port->last_sequence_num_to_receive = last_sequence_num; } - TryRemoveProxy(PortRef(port_name, port)); + TryRemoveProxy(PortRef(port_name, std::move(port))); return OK; } @@ -709,7 +696,7 @@ int Node::OnObserveClosure(const PortName& port_name, forwarded_data)); if (notify_delegate) { - PortRef port_ref(port_name, port); + PortRef port_ref(port_name, std::move(port)); delegate_->PortStatusChanged(port_ref); } return OK; @@ -784,11 +771,10 @@ int Node::OnMergePort(const PortName& port_name, return ERROR_PORT_STATE_UNEXPECTED; } -int Node::AddPortWithName(const PortName& port_name, - const scoped_refptr<Port>& port) { +int Node::AddPortWithName(const PortName& port_name, scoped_refptr<Port> port) { base::AutoLock lock(ports_lock_); - if (!ports_.insert(std::make_pair(port_name, port)).second) + if (!ports_.insert(std::make_pair(port_name, std::move(port))).second) return OOPS(ERROR_PORT_EXISTS); // Suggests a bad UUID generator. DVLOG(2) << "Created port " << port_name << "@" << name_; @@ -817,6 +803,11 @@ scoped_refptr<Port> Node::GetPort_Locked(const PortName& port_name) { 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; } @@ -1003,10 +994,10 @@ int Node::AcceptPort(const PortName& port_name, << 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 GetMessageIf). + // new port finds its way to the consumer (see GetMessage). port->message_queue.set_signalable(false); - int rv = AddPortWithName(port_name, port); + int rv = AddPortWithName(port_name, std::move(port)); if (rv != OK) return rv; @@ -1087,7 +1078,7 @@ int Node::WillSendMessage_Locked(const LockedPort& port, } #if DCHECK_IS_ON() - DVLOG(2) << "Sending message " + DVLOG(4) << "Sending message " << GetEventData<UserEventData>(*message)->sequence_num << " [ports=" << ports_buf.str() << "]" << " from " << port_name << "@" << name_ @@ -1185,7 +1176,7 @@ int Node::ForwardMessages_Locked(const LockedPort& port, for (;;) { ScopedMessage message; - port->message_queue.GetNextMessageIf(nullptr, &message); + port->message_queue.GetNextMessage(&message, nullptr); if (!message) break; diff --git a/mojo/edk/system/ports/node.h b/mojo/edk/system/ports/node.h index 3aeadca..55b8d27 100644 --- a/mojo/edk/system/ports/node.h +++ b/mojo/edk/system/ports/node.h @@ -44,10 +44,16 @@ struct PortStatus { 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(); @@ -59,9 +65,11 @@ class Node { // method may be called again after AcceptMessage to check if the Node is now // ready to be destroyed. // - // 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); + // 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); // Lookup the named port. int GetPort(const PortName& port_name, PortRef* port_ref); @@ -82,8 +90,7 @@ class Node { int CreatePortPair(PortRef* port0_ref, PortRef* port1_ref); // User data associated with the port. - int SetUserData(const PortRef& port_ref, - const scoped_refptr<UserData>& user_data); + int SetUserData(const PortRef& port_ref, scoped_refptr<UserData> user_data); int GetUserData(const PortRef& port_ref, scoped_refptr<UserData>* user_data); @@ -100,15 +107,15 @@ class Node { // 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. - 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); + // + // 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); // Sends a message from the specified port to its peer. Note that the message // notification may arrive synchronously (via PortStatusChanged() on the @@ -156,8 +163,7 @@ class Node { 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, - const scoped_refptr<Port>& port); + int AddPortWithName(const PortName& port_name, 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 bd59629..675754d 100644 --- a/mojo/edk/system/ports/port_ref.cc +++ b/mojo/edk/system/ports/port_ref.cc @@ -16,9 +16,8 @@ PortRef::~PortRef() { PortRef::PortRef() { } -PortRef::PortRef(const PortName& name, const scoped_refptr<Port>& port) - : name_(name), port_(port) { -} +PortRef::PortRef(const PortName& name, scoped_refptr<Port> port) + : name_(name), port_(std::move(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 9af4a8d..59036c3 100644 --- a/mojo/edk/system/ports/port_ref.h +++ b/mojo/edk/system/ports/port_ref.h @@ -19,7 +19,7 @@ class PortRef { public: ~PortRef(); PortRef(); - PortRef(const PortName& name, const scoped_refptr<Port>& port); + PortRef(const PortName& name, 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 200e72b..cb48b3e 100644 --- a/mojo/edk/system/ports/ports_unittest.cc +++ b/mojo/edk/system/ports/ports_unittest.cc @@ -2,6 +2,7 @@ // 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> @@ -9,9 +10,18 @@ #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" @@ -24,24 +34,8 @@ namespace test { namespace { -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)); - } +bool MessageEquals(const ScopedMessage& message, const base::StringPiece& s) { + return !strcmp(static_cast<const char*>(message->payload_bytes()), s.data()); } class TestMessage : public Message { @@ -71,132 +65,140 @@ class TestMessage : public Message { } }; -struct Task { - Task(NodeName node_name, ScopedMessage message) - : node_name(node_name), - message(std::move(message)), - priority(base::RandUint64()) { - } +class TestNode; - NodeName node_name; - ScopedMessage message; - uint64_t priority; +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; }; -struct TaskComparator { - bool operator()(const Task* a, const Task* b) { - return a->priority < b->priority; +class TestNode : 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) { } -}; -const size_t kMaxNodes = 3; + ~TestNode() override { + StopWhenIdle(); + node_thread_.Stop(); + } -std::priority_queue<Task*, std::vector<Task*>, TaskComparator> task_queue; -Node* node_map[kMaxNodes]; + const NodeName& name() const { return node_name_; } -Node* GetNode(const NodeName& name) { - return node_map[name.v1]; -} + // NOTE: Node is thread-safe. + Node& node() { return node_; } -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(); + base::WaitableEvent& idle_event() { return idle_event_; } - Node* node = GetNode(task->node_name); - if (node) - node->AcceptMessage(std::move(task->message)); + bool IsIdle() { + base::AutoLock lock(lock_); + return started_ && !dispatching_ && + (incoming_messages_.empty() || (block_on_event_ && blocked_)); + } - delete task; + void BlockOnEvent(EventType type) { + base::AutoLock lock(lock_); + blocked_event_type_ = type; + block_on_event_ = true; } -} -void PumpUntilTask(EventType type) { - while (!task_queue.empty()) { - Task* task = task_queue.top(); + void Unblock() { + base::AutoLock lock(lock_); + block_on_event_ = false; + messages_available_event_.Signal(); + } - const EventHeader* header = GetEventHeader(*task->message); - if (header->type == type) - return; + void Start(MessageRouter* router) { + router_ = router; + node_thread_.Start(); + node_thread_.task_runner()->PostTask( + FROM_HERE, + base::Bind(&TestNode::ProcessMessages, base::Unretained(this))); + } - task_queue.pop(); + void StopWhenIdle() { + base::AutoLock lock(lock_); + should_quit_ = true; + messages_available_event_.Signal(); + } - Node* node = GetNode(task->node_name); - if (node) - node->AcceptMessage(std::move(task->message)); + void WakeUp() { messages_available_event_.Signal(); } - delete task; + 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)); } -} -void DiscardPendingTasks() { - while (!task_queue.empty()) { - Task* task = task_queue.top(); - task_queue.pop(); - delete task; + 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 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()); -} + int SendStringMessageWithPort(const PortRef& port, + const std::string& s, + const PortRef& sent_port) { + return SendStringMessageWithPort(port, s, sent_port.name()); + } -const char* ToString(const ScopedMessage& message) { - return static_cast<const char*>(message->payload_bytes()); -} + void set_drop_messages(bool value) { + base::AutoLock lock(lock_); + drop_messages_ = value; + } -class TestNodeDelegate : public NodeDelegate { - public: - explicit TestNodeDelegate(const NodeName& node_name) - : node_name_(node_name), - drop_messages_(false), - read_messages_(true), - save_messages_(false) { + void set_save_messages(bool value) { + base::AutoLock lock(lock_); + save_messages_ = value; } - 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 ReadMessage(const PortRef& port, ScopedMessage* message) { + return node_.GetMessage(port, message, nullptr) == OK && *message; + } bool GetSavedMessage(ScopedMessage* message) { + base::AutoLock lock(lock_); if (saved_messages_.empty()) { message->reset(); return false; } - *message = std::move(saved_messages_.front()); + std::swap(*message, 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 { - static uint64_t next_port_name = 1; - port_name->v1 = next_port_name++; - port_name->v2 = 0; + DCHECK(router_); + router_->GeneratePortName(port_name); } void AllocMessage(size_t num_header_bytes, ScopedMessage* message) override { @@ -205,489 +207,570 @@ class TestNodeDelegate : public NodeDelegate { void ForwardMessage(const NodeName& node_name, ScopedMessage message) override { - if (drop_messages_) { - DVLOG(1) << "Dropping ForwardMessage from node " - << node_name_ << " to " << node_name; - ClosePortsInMessage(GetNode(node_name), message.get()); - return; + { + 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; + } } + + DCHECK(router_); DVLOG(1) << "ForwardMessage from node " << node_name_ << " to " << node_name; - task_queue.push(new Task(node_name, std::move(message))); + router_->ForwardMessage(this, node_name, std::move(message)); } void BroadcastMessage(ScopedMessage message) override { - 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)); - } - } + router_->BroadcastMessage(this, std::move(message)); } void PortStatusChanged(const PortRef& port) override { - DVLOG(1) << "PortStatusChanged for " << port.name() << "@" << node_name_; - if (!read_messages_) + // The port may be closed, in which case we ignore the notification. + base::AutoLock lock(lock_); + if (!save_messages_) return; - Node* node = GetNode(node_name_); + for (;;) { ScopedMessage message; - 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); + { + base::AutoUnlock unlock(lock_); + if (!ReadMessage(port, &message)) + break; + } - SendStringMessage(node, received_port, buf.str()); + saved_messages_.emplace(std::move(message)); + } + } - // Avoid leaking these ports. - node->ClosePort(received_port); - } - } + 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 SaveMessage(ScopedMessage message) { - saved_messages_.emplace(std::move(message)); + 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(); + }; } + 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 { +class PortsTest : public testing::Test, public MessageRouter { public: - void SetUp() override { - DiscardPendingTasks(); - SetNode(NodeName(0, 1), nullptr); - SetNode(NodeName(1, 1), nullptr); - SetNode(NodeName(2, 1), nullptr); + void AddNode(TestNode* node) { + { + base::AutoLock lock(lock_); + nodes_[node->name()] = node; + } + node->Start(this); + } + + 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) { - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - SetNode(node0_name, &node0); + TestNode node0(0); + AddNode(&node0); - NodeName node1_name(1, 1); - TestNodeDelegate node1_delegate(node1_name); - Node node1(node1_name, &node1_delegate); - SetNode(node1_name, &node1); + TestNode node1(1); + AddNode(&node1); - // Setup pipe between node0 and node1. PortRef x0, 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())); + CreatePortPair(&node0, &x0, &node1, &x1); - // Transfer a port from node0 to node1. PortRef a0, a1; - EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1)); - EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "hello", a1)); - - EXPECT_EQ(OK, node0.ClosePort(a0)); + 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.ClosePort(x0)); - EXPECT_EQ(OK, node1.ClosePort(x1)); + EXPECT_EQ(OK, node0.node().ClosePort(x0)); + EXPECT_EQ(OK, node1.node().ClosePort(x1)); - PumpTasks(); + WaitForIdle(); - EXPECT_TRUE(node0.CanShutdownCleanly(false)); - EXPECT_TRUE(node1.CanShutdownCleanly(false)); + EXPECT_TRUE(node0.node().CanShutdownCleanly()); + EXPECT_TRUE(node1.node().CanShutdownCleanly()); } TEST_F(PortsTest, Basic2) { - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - SetNode(node0_name, &node0); + TestNode node0(0); + AddNode(&node0); - NodeName node1_name(1, 1); - TestNodeDelegate node1_delegate(node1_name); - Node node1(node1_name, &node1_delegate); - SetNode(node1_name, &node1); + TestNode node1(1); + AddNode(&node1); - // Setup pipe between node0 and node1. PortRef x0, 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())); + CreatePortPair(&node0, &x0, &node1, &x1); PortRef b0, b1; - 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().CreatePortPair(&b0, &b1)); + EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "hello", b1)); + EXPECT_EQ(OK, node0.SendStringMessage(b0, "hello again")); - // This may cause a SendMessage(b1) failure. - EXPECT_EQ(OK, node0.ClosePort(b0)); + EXPECT_EQ(OK, node0.node().ClosePort(b0)); - EXPECT_EQ(OK, node0.ClosePort(x0)); - EXPECT_EQ(OK, node1.ClosePort(x1)); + EXPECT_EQ(OK, node0.node().ClosePort(x0)); + EXPECT_EQ(OK, node1.node().ClosePort(x1)); - PumpTasks(); + WaitForIdle(); - EXPECT_TRUE(node0.CanShutdownCleanly(false)); - EXPECT_TRUE(node1.CanShutdownCleanly(false)); + EXPECT_TRUE(node0.node().CanShutdownCleanly()); + EXPECT_TRUE(node1.node().CanShutdownCleanly()); } TEST_F(PortsTest, Basic3) { - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - SetNode(node0_name, &node0); + TestNode node0(0); + AddNode(&node0); - NodeName node1_name(1, 1); - TestNodeDelegate node1_delegate(node1_name); - Node node1(node1_name, &node1_delegate); - SetNode(node1_name, &node1); + TestNode node1(1); + AddNode(&node1); - // Setup pipe between node0 and node1. PortRef x0, 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())); + CreatePortPair(&node0, &x0, &node1, &x1); - // Transfer a port from node0 to node1. PortRef 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.node().CreatePortPair(&a0, &a1)); - // Transfer a0 as well. - EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "foo", a0)); + EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "hello", a1)); + EXPECT_EQ(OK, node0.SendStringMessage(a0, "hello again")); + + EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "foo", a0)); PortRef b0, b1; - 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().CreatePortPair(&b0, &b1)); + EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "bar", b1)); + EXPECT_EQ(OK, node0.SendStringMessage(b0, "baz")); - // This may cause a SendMessage(b1) failure. - EXPECT_EQ(OK, node0.ClosePort(b0)); + EXPECT_EQ(OK, node0.node().ClosePort(b0)); - EXPECT_EQ(OK, node0.ClosePort(x0)); - EXPECT_EQ(OK, node1.ClosePort(x1)); + EXPECT_EQ(OK, node0.node().ClosePort(x0)); + EXPECT_EQ(OK, node1.node().ClosePort(x1)); - PumpTasks(); + WaitForIdle(); - EXPECT_TRUE(node0.CanShutdownCleanly(false)); - EXPECT_TRUE(node1.CanShutdownCleanly(false)); + EXPECT_TRUE(node0.node().CanShutdownCleanly()); + EXPECT_TRUE(node1.node().CanShutdownCleanly()); } TEST_F(PortsTest, LostConnectionToNode1) { - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - SetNode(node0_name, &node0); + TestNode node0(0); + AddNode(&node0); - NodeName node1_name(1, 1); - TestNodeDelegate node1_delegate(node1_name); - Node node1(node1_name, &node1_delegate); - SetNode(node1_name, &node1); + TestNode node1(1); + AddNode(&node1); + node1.set_drop_messages(true); - // Setup pipe between node0 and node1. PortRef x0, 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 port to node1 and simulate a lost connection to node1. Dropping - // events from node1 is how we simulate the lost connection. + CreatePortPair(&node0, &x0, &node1, &x1); - node1_delegate.set_drop_messages(true); + // Transfer a port to node1 and simulate a lost connection to node1. PortRef a0, a1; - EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1)); - EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "foo", a1)); + EXPECT_EQ(OK, node0.node().CreatePortPair(&a0, &a1)); + EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "foo", a1)); - PumpTasks(); + WaitForIdle(); - EXPECT_EQ(OK, node0.LostConnectionToNode(node1_name)); + RemoveNode(&node1); - PumpTasks(); + WaitForIdle(); - EXPECT_EQ(OK, node0.ClosePort(a0)); - EXPECT_EQ(OK, node0.ClosePort(x0)); - EXPECT_EQ(OK, node1.ClosePort(x1)); + EXPECT_EQ(OK, node0.node().ClosePort(a0)); + EXPECT_EQ(OK, node0.node().ClosePort(x0)); + EXPECT_EQ(OK, node1.node().ClosePort(x1)); - PumpTasks(); + WaitForIdle(); - EXPECT_TRUE(node0.CanShutdownCleanly(false)); - EXPECT_TRUE(node1.CanShutdownCleanly(false)); + EXPECT_TRUE(node0.node().CanShutdownCleanly()); + EXPECT_TRUE(node1.node().CanShutdownCleanly()); } TEST_F(PortsTest, LostConnectionToNode2) { - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - node_map[0] = &node0; + TestNode node0(0); + AddNode(&node0); - NodeName node1_name(1, 1); - TestNodeDelegate node1_delegate(node1_name); - Node node1(node1_name, &node1_delegate); - node_map[1] = &node1; + TestNode node1(1); + AddNode(&node1); - // Setup pipe between node0 and node1. PortRef x0, 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); + CreatePortPair(&node0, &x0, &node1, &x1); PortRef a0, a1; - EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1)); - EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "take a1", a1)); + EXPECT_EQ(OK, node0.node().CreatePortPair(&a0, &a1)); + EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "take a1", a1)); - PumpTasks(); + WaitForIdle(); - node1_delegate.set_drop_messages(true); + node1.set_drop_messages(true); - EXPECT_EQ(OK, node0.LostConnectionToNode(node1_name)); + RemoveNode(&node1); - PumpTasks(); + WaitForIdle(); + // a0 should have eventually detected peer closure after node loss. ScopedMessage message; - EXPECT_EQ(ERROR_PORT_PEER_CLOSED, node0.GetMessage(a0, &message)); + EXPECT_EQ(ERROR_PORT_PEER_CLOSED, + node0.node().GetMessage(a0, &message, nullptr)); EXPECT_FALSE(message); - EXPECT_EQ(OK, node0.ClosePort(a0)); + EXPECT_EQ(OK, node0.node().ClosePort(a0)); - EXPECT_EQ(OK, node0.ClosePort(x0)); + EXPECT_EQ(OK, node0.node().ClosePort(x0)); - EXPECT_EQ(OK, node1.GetMessage(x1, &message)); + EXPECT_EQ(OK, node1.node().GetMessage(x1, &message, nullptr)); EXPECT_TRUE(message); - ClosePortsInMessage(&node1, message.get()); + node1.ClosePortsInMessage(message.get()); - EXPECT_EQ(OK, node1.ClosePort(x1)); + EXPECT_EQ(OK, node1.node().ClosePort(x1)); - PumpTasks(); + WaitForIdle(); - EXPECT_TRUE(node0.CanShutdownCleanly(false)); - EXPECT_TRUE(node1.CanShutdownCleanly(false)); + EXPECT_TRUE(node0.node().CanShutdownCleanly()); + EXPECT_TRUE(node1.node().CanShutdownCleanly()); } TEST_F(PortsTest, LostConnectionToNodeWithSecondaryProxy) { // Tests that a proxy gets cleaned up when its indirect peer lives on a lost // node. - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - node_map[0] = &node0; + TestNode node0(0); + AddNode(&node0); - NodeName node1_name(1, 1); - TestNodeDelegate node1_delegate(node1_name); - Node node1(node1_name, &node1_delegate); - node_map[1] = &node1; + TestNode node1(1); + AddNode(&node1); - 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); + TestNode node2(2); + AddNode(&node2); // Create A-B spanning nodes 0 and 1 and C-D spanning 1 and 2. PortRef A, B, C, 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())); + CreatePortPair(&node0, &A, &node1, &B); + CreatePortPair(&node1, &C, &node2, &D); // Create E-F and send F over A to node 1. PortRef E, F; - EXPECT_EQ(OK, node0.CreatePortPair(&E, &F)); - EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, ".", F)); + EXPECT_EQ(OK, node0.node().CreatePortPair(&E, &F)); + EXPECT_EQ(OK, node0.SendStringMessageWithPort(A, ".", F)); - PumpTasks(); + WaitForIdle(); ScopedMessage message; - ASSERT_TRUE(node1_delegate.GetSavedMessage(&message)); + ASSERT_TRUE(node1.ReadMessage(B, &message)); ASSERT_EQ(1u, message->num_ports()); - EXPECT_EQ(OK, node1.GetPort(message->ports()[0], &F)); + EXPECT_EQ(OK, node1.node().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. - EXPECT_EQ(OK, SendStringMessageWithPort(&node1, C, ".", F)); + // Make sure node2 stops processing events when it encounters an ObserveProxy. + node2.BlockOnEvent(EventType::kObserveProxy); + + 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())); - PumpTasks(); + // Let node2 continue processing events and wait for everyone to go idle. + node2.Unblock(); + WaitForIdle(); // Port F should be gone. - EXPECT_EQ(ERROR_PORT_UNKNOWN, node1.GetPort(F.name(), &F)); + EXPECT_EQ(ERROR_PORT_UNKNOWN, node1.node().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.GetStatus(E, &status)); + EXPECT_EQ(OK, node0.node().GetStatus(E, &status)); EXPECT_TRUE(status.peer_closed); - EXPECT_EQ(OK, node0.ClosePort(A)); - EXPECT_EQ(OK, node1.ClosePort(B)); - EXPECT_EQ(OK, node1.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, node1.node().ClosePort(C)); + EXPECT_EQ(OK, node0.node().ClosePort(E)); + + WaitForIdle(); - EXPECT_TRUE(node0.CanShutdownCleanly(false)); - EXPECT_TRUE(node1.CanShutdownCleanly(false)); + EXPECT_TRUE(node0.node().CanShutdownCleanly()); + EXPECT_TRUE(node1.node().CanShutdownCleanly()); } 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. - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - node_map[0] = &node0; + TestNode node0(0); + AddNode(&node0); - NodeName node1_name(1, 1); - TestNodeDelegate node1_delegate(node1_name); - Node node1(node1_name, &node1_delegate); - node_map[1] = &node1; + TestNode node1(1); + AddNode(&node1); - node1_delegate.set_save_messages(true); - - // Create A-B spanning nodes 0 and 1. PortRef A, 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())); + CreatePortPair(&node0, &A, &node1, &B); - // Create C-D and send D over A to node 1. PortRef C, D; - EXPECT_EQ(OK, node0.CreatePortPair(&C, &D)); - EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, ".", D)); + EXPECT_EQ(OK, node0.node().CreatePortPair(&C, &D)); + + // Send D but block node0 on an ObserveProxy event. + node0.BlockOnEvent(EventType::kObserveProxy); + EXPECT_EQ(OK, node0.SendStringMessageWithPort(A, ".", D)); - // Pump tasks until the start of port collapse for port D, which should become - // a proxy. - PumpUntilTask(EventType::kObserveProxy); + // node0 won't collapse the proxy but node1 will receive the message before + // going idle. + WaitForIdle(); ScopedMessage message; - ASSERT_TRUE(node1_delegate.GetSavedMessage(&message)); + ASSERT_TRUE(node1.ReadMessage(B, &message)); ASSERT_EQ(1u, message->num_ports()); - PortRef E; - EXPECT_EQ(OK, node1.GetPort(message->ports()[0], &E)); + EXPECT_EQ(OK, node1.node().GetPort(message->ports()[0], &E)); - EXPECT_EQ(OK, node0.LostConnectionToNode(node1_name)); - PumpTasks(); + RemoveNode(&node1); + + node0.Unblock(); + WaitForIdle(); // Port C should have detected peer closure. PortStatus status; - EXPECT_EQ(OK, node0.GetStatus(C, &status)); + EXPECT_EQ(OK, node0.node().GetStatus(C, &status)); EXPECT_TRUE(status.peer_closed); - 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_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_TRUE(node0.CanShutdownCleanly(false)); - EXPECT_TRUE(node1.CanShutdownCleanly(false)); + EXPECT_TRUE(node0.node().CanShutdownCleanly()); + EXPECT_TRUE(node1.node().CanShutdownCleanly()); } TEST_F(PortsTest, GetMessage1) { - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - node_map[0] = &node0; + TestNode node(0); + AddNode(&node); PortRef a0, a1; - EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1)); + EXPECT_EQ(OK, node.node().CreatePortPair(&a0, &a1)); ScopedMessage message; - EXPECT_EQ(OK, node0.GetMessage(a0, &message)); + EXPECT_EQ(OK, node.node().GetMessage(a0, &message, nullptr)); EXPECT_FALSE(message); - EXPECT_EQ(OK, node0.ClosePort(a1)); - - EXPECT_EQ(OK, node0.GetMessage(a0, &message)); - EXPECT_FALSE(message); + EXPECT_EQ(OK, node.node().ClosePort(a1)); - PumpTasks(); + WaitForIdle(); - EXPECT_EQ(ERROR_PORT_PEER_CLOSED, node0.GetMessage(a0, &message)); + EXPECT_EQ(ERROR_PORT_PEER_CLOSED, + node.node().GetMessage(a0, &message, nullptr)); EXPECT_FALSE(message); - EXPECT_EQ(OK, node0.ClosePort(a0)); + EXPECT_EQ(OK, node.node().ClosePort(a0)); - EXPECT_TRUE(node0.CanShutdownCleanly(false)); + WaitForIdle(); + + EXPECT_TRUE(node.node().CanShutdownCleanly()); } TEST_F(PortsTest, GetMessage2) { - 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); + TestNode node(0); + AddNode(&node); PortRef a0, a1; - EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1)); + EXPECT_EQ(OK, node.node().CreatePortPair(&a0, &a1)); - EXPECT_EQ(OK, SendStringMessage(&node0, a1, "1")); + EXPECT_EQ(OK, node.SendStringMessage(a1, "1")); ScopedMessage message; - EXPECT_EQ(OK, node0.GetMessage(a0, &message)); + EXPECT_EQ(OK, node.node().GetMessage(a0, &message, nullptr)); ASSERT_TRUE(message); - EXPECT_EQ(0, strcmp("1", ToString(message))); + EXPECT_TRUE(MessageEquals(message, "1")); - EXPECT_EQ(OK, node0.ClosePort(a0)); - EXPECT_EQ(OK, node0.ClosePort(a1)); + EXPECT_EQ(OK, node.node().ClosePort(a0)); + EXPECT_EQ(OK, node.node().ClosePort(a1)); - EXPECT_TRUE(node0.CanShutdownCleanly(false)); + EXPECT_TRUE(node.node().CanShutdownCleanly()); } TEST_F(PortsTest, GetMessage3) { - 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); + TestNode node(0); + AddNode(&node); PortRef a0, a1; - EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1)); + EXPECT_EQ(OK, node.node().CreatePortPair(&a0, &a1)); const char* kStrings[] = { "1", @@ -696,371 +779,305 @@ TEST_F(PortsTest, GetMessage3) { }; for (size_t i = 0; i < sizeof(kStrings)/sizeof(kStrings[0]); ++i) - EXPECT_EQ(OK, SendStringMessage(&node0, a1, kStrings[i])); + EXPECT_EQ(OK, node.SendStringMessage(a1, kStrings[i])); ScopedMessage message; for (size_t i = 0; i < sizeof(kStrings)/sizeof(kStrings[0]); ++i) { - EXPECT_EQ(OK, node0.GetMessage(a0, &message)); + EXPECT_EQ(OK, node.node().GetMessage(a0, &message, nullptr)); ASSERT_TRUE(message); - EXPECT_EQ(0, strcmp(kStrings[i], ToString(message))); - DVLOG(1) << "got " << kStrings[i]; + EXPECT_TRUE(MessageEquals(message, kStrings[i])); } - EXPECT_EQ(OK, node0.ClosePort(a0)); - EXPECT_EQ(OK, node0.ClosePort(a1)); + EXPECT_EQ(OK, node.node().ClosePort(a0)); + EXPECT_EQ(OK, node.node().ClosePort(a1)); - EXPECT_TRUE(node0.CanShutdownCleanly(false)); + EXPECT_TRUE(node.node().CanShutdownCleanly()); } TEST_F(PortsTest, Delegation1) { - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - SetNode(node0_name, &node0); + TestNode node0(0); + AddNode(&node0); - NodeName node1_name(1, 1); - TestNodeDelegate node1_delegate(node1_name); - Node node1(node1_name, &node1_delegate); - node_map[1] = &node1; + TestNode node1(1); + AddNode(&node1); - node0_delegate.set_save_messages(true); - node1_delegate.set_save_messages(true); - - // Setup pipe between node0 and node1. PortRef x0, 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())); + CreatePortPair(&node0, &x0, &node1, &x1); // In this test, we send a message to a port that has been moved. PortRef a0, a1; - EXPECT_EQ(OK, node0.CreatePortPair(&a0, &a1)); - - EXPECT_EQ(OK, SendStringMessageWithPort(&node0, x0, "a1", a1)); - - PumpTasks(); + EXPECT_EQ(OK, node0.node().CreatePortPair(&a0, &a1)); + EXPECT_EQ(OK, node0.SendStringMessageWithPort(x0, "a1", a1)); + WaitForIdle(); ScopedMessage message; - ASSERT_TRUE(node1_delegate.GetSavedMessage(&message)); - + ASSERT_TRUE(node1.ReadMessage(x1, &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")); - EXPECT_EQ(OK, SendStringMessageWithPort(&node1, x1, "a2", a2_name)); - - PumpTasks(); - - EXPECT_EQ(OK, SendStringMessage(&node0, a0, "hello")); - - PumpTasks(); - - ASSERT_TRUE(node0_delegate.GetSavedMessage(&message)); + WaitForIdle(); + ASSERT_TRUE(node0.ReadMessage(x0, &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.GetPort(a3_name, &a3)); - - EXPECT_EQ(0, strcmp("a2", ToString(message))); - - ASSERT_TRUE(node0_delegate.GetSavedMessage(&message)); + EXPECT_EQ(OK, node0.node().GetPort(a3_name, &a3)); + ASSERT_TRUE(node0.ReadMessage(a3, &message)); EXPECT_EQ(0u, message->num_ports()); - EXPECT_EQ(0, strcmp("hello", ToString(message))); + EXPECT_TRUE(MessageEquals(message, "hello")); - EXPECT_EQ(OK, node0.ClosePort(a0)); - EXPECT_EQ(OK, node0.ClosePort(a3)); + EXPECT_EQ(OK, node0.node().ClosePort(a0)); + EXPECT_EQ(OK, node0.node().ClosePort(a3)); - EXPECT_EQ(OK, node0.ClosePort(x0)); - EXPECT_EQ(OK, node1.ClosePort(x1)); + EXPECT_EQ(OK, node0.node().ClosePort(x0)); + EXPECT_EQ(OK, node1.node().ClosePort(x1)); - EXPECT_TRUE(node0.CanShutdownCleanly(false)); - EXPECT_TRUE(node1.CanShutdownCleanly(false)); + EXPECT_TRUE(node0.node().CanShutdownCleanly()); + EXPECT_TRUE(node1.node().CanShutdownCleanly()); } TEST_F(PortsTest, Delegation2) { - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - SetNode(node0_name, &node0); - - NodeName node1_name(1, 1); - TestNodeDelegate node1_delegate(node1_name); - Node node1(node1_name, &node1_delegate); - node_map[1] = &node1; + TestNode node0(0); + AddNode(&node0); - node0_delegate.set_save_messages(true); - node1_delegate.set_save_messages(true); + TestNode node1(1); + AddNode(&node1); - for (int i = 0; i < 10; ++i) { + for (int i = 0; i < 100; ++i) { // Setup pipe a<->b between node0 and node1. PortRef A, 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())); + CreatePortPair(&node0, &A, &node1, &B); PortRef C, D; - EXPECT_EQ(OK, node0.CreatePortPair(&C, &D)); + EXPECT_EQ(OK, node0.node().CreatePortPair(&C, &D)); PortRef E, F; - EXPECT_EQ(OK, node0.CreatePortPair(&E, &F)); + EXPECT_EQ(OK, node0.node().CreatePortPair(&E, &F)); + + node1.set_save_messages(true); // Pass D over A to B. - EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, "1", D)); + EXPECT_EQ(OK, node0.SendStringMessageWithPort(A, "1", D)); // Pass F over C to D. - EXPECT_EQ(OK, SendStringMessageWithPort(&node0, C, "1", F)); + EXPECT_EQ(OK, node0.SendStringMessageWithPort(C, "1", F)); // This message should find its way to node1. - EXPECT_EQ(OK, SendStringMessage(&node0, E, "hello")); + EXPECT_EQ(OK, node0.SendStringMessage(E, "hello")); - PumpTasks(); + WaitForIdle(); - EXPECT_EQ(OK, node0.ClosePort(C)); - EXPECT_EQ(OK, node0.ClosePort(E)); + EXPECT_EQ(OK, node0.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, node0.node().ClosePort(A)); + EXPECT_EQ(OK, node1.node().ClosePort(B)); - 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! + bool got_hello = false; + ScopedMessage message; + while (node1.GetSavedMessage(&message)) { + node1.ClosePortsInMessage(message.get()); + if (MessageEquals(message, "hello")) { + got_hello = true; break; } } - PumpTasks(); // Because ClosePort may have generated tasks. + EXPECT_TRUE(got_hello); + + WaitForIdle(); // Because closing ports may have generated tasks. } - EXPECT_TRUE(node0.CanShutdownCleanly(false)); - EXPECT_TRUE(node1.CanShutdownCleanly(false)); + EXPECT_TRUE(node0.node().CanShutdownCleanly()); + EXPECT_TRUE(node1.node().CanShutdownCleanly()); } TEST_F(PortsTest, SendUninitialized) { - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - node_map[0] = &node0; + TestNode node(0); + AddNode(&node); PortRef x0; - 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)); + 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()); } TEST_F(PortsTest, SendFailure) { - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - node_map[0] = &node0; + TestNode node(0); + AddNode(&node); - node0_delegate.set_save_messages(true); + node.set_save_messages(true); PortRef A, B; - EXPECT_EQ(OK, node0.CreatePortPair(&A, &B)); + EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B)); // Try to send A over itself. EXPECT_EQ(ERROR_PORT_CANNOT_SEND_SELF, - SendStringMessageWithPort(&node0, A, "oops", A)); + node.SendStringMessageWithPort(A, "oops", A)); // Try to send B over A. EXPECT_EQ(ERROR_PORT_CANNOT_SEND_PEER, - SendStringMessageWithPort(&node0, A, "nope", B)); + node.SendStringMessageWithPort(A, "nope", B)); // B should be closed immediately. - EXPECT_EQ(ERROR_PORT_UNKNOWN, node0.GetPort(B.name(), &B)); + EXPECT_EQ(ERROR_PORT_UNKNOWN, node.node().GetPort(B.name(), &B)); - PumpTasks(); + WaitForIdle(); // There should have been no messages accepted. ScopedMessage message; - EXPECT_FALSE(node0_delegate.GetSavedMessage(&message)); + EXPECT_FALSE(node.GetSavedMessage(&message)); - EXPECT_EQ(OK, node0.ClosePort(A)); + EXPECT_EQ(OK, node.node().ClosePort(A)); - PumpTasks(); + WaitForIdle(); - EXPECT_TRUE(node0.CanShutdownCleanly(false)); + EXPECT_TRUE(node.node().CanShutdownCleanly()); } TEST_F(PortsTest, DontLeakUnreceivedPorts) { - 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); + TestNode node(0); + AddNode(&node); - 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)); - - PumpTasks(); + PortRef A, B, C, D; + EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B)); + EXPECT_EQ(OK, node.node().CreatePortPair(&C, &D)); - EXPECT_EQ(OK, node0.ClosePort(C)); + EXPECT_EQ(OK, node.SendStringMessageWithPort(A, "foo", D)); - EXPECT_EQ(OK, node0.ClosePort(A)); - EXPECT_EQ(OK, node0.ClosePort(B)); + EXPECT_EQ(OK, node.node().ClosePort(C)); + EXPECT_EQ(OK, node.node().ClosePort(A)); + EXPECT_EQ(OK, node.node().ClosePort(B)); - PumpTasks(); + WaitForIdle(); - EXPECT_TRUE(node0.CanShutdownCleanly(false)); + EXPECT_TRUE(node.node().CanShutdownCleanly()); } TEST_F(PortsTest, AllowShutdownWithLocalPortsOpen) { - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - node_map[0] = &node0; + TestNode node(0); + AddNode(&node); - node0_delegate.set_save_messages(true); - - PortRef A, B; - EXPECT_EQ(OK, node0.CreatePortPair(&A, &B)); - - PortRef C, D; - EXPECT_EQ(OK, node0.CreatePortPair(&C, &D)); + PortRef A, B, C, D; + EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B)); + EXPECT_EQ(OK, node.node().CreatePortPair(&C, &D)); - EXPECT_EQ(OK, SendStringMessageWithPort(&node0, A, "foo", D)); + EXPECT_EQ(OK, node.SendStringMessageWithPort(A, "foo", D)); ScopedMessage message; - EXPECT_TRUE(node0_delegate.GetSavedMessage(&message)); + EXPECT_TRUE(node.ReadMessage(B, &message)); ASSERT_EQ(1u, message->num_ports()); - + EXPECT_TRUE(MessageEquals(message, "foo")); PortRef E; - ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &E)); + ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &E)); - EXPECT_TRUE(node0.CanShutdownCleanly(true)); + EXPECT_TRUE( + node.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS)); - PumpTasks(); + WaitForIdle(); - EXPECT_TRUE(node0.CanShutdownCleanly(true)); - EXPECT_FALSE(node0.CanShutdownCleanly(false)); + EXPECT_TRUE( + node.node().CanShutdownCleanly(Node::ShutdownPolicy::ALLOW_LOCAL_PORTS)); + EXPECT_FALSE(node.node().CanShutdownCleanly()); - EXPECT_EQ(OK, node0.ClosePort(A)); - EXPECT_EQ(OK, node0.ClosePort(B)); - EXPECT_EQ(OK, node0.ClosePort(C)); - EXPECT_EQ(OK, node0.ClosePort(E)); + 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)); - PumpTasks(); + WaitForIdle(); - EXPECT_TRUE(node0.CanShutdownCleanly(false)); + EXPECT_TRUE(node.node().CanShutdownCleanly()); } TEST_F(PortsTest, ProxyCollapse1) { - 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); + TestNode node(0); + AddNode(&node); PortRef A, B; - EXPECT_EQ(OK, node0.CreatePortPair(&A, &B)); + EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B)); PortRef X, Y; - EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y)); + EXPECT_EQ(OK, node.node().CreatePortPair(&X, &Y)); ScopedMessage message; // Send B and receive it as C. - EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", B)); - ASSERT_TRUE(node0_delegate.GetSavedMessage(&message)); + EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", B)); + ASSERT_TRUE(node.ReadMessage(Y, &message)); ASSERT_EQ(1u, message->num_ports()); PortRef C; - ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &C)); + ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &C)); // Send C and receive it as D. - EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", C)); - ASSERT_TRUE(node0_delegate.GetSavedMessage(&message)); + EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", C)); + ASSERT_TRUE(node.ReadMessage(Y, &message)); ASSERT_EQ(1u, message->num_ports()); PortRef D; - ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &D)); + ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &D)); // Send D and receive it as E. - EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", D)); - ASSERT_TRUE(node0_delegate.GetSavedMessage(&message)); + EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", D)); + ASSERT_TRUE(node.ReadMessage(Y, &message)); ASSERT_EQ(1u, message->num_ports()); PortRef E; - ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &E)); + ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &E)); - EXPECT_EQ(OK, node0.ClosePort(X)); - EXPECT_EQ(OK, node0.ClosePort(Y)); + EXPECT_EQ(OK, node.node().ClosePort(X)); + EXPECT_EQ(OK, node.node().ClosePort(Y)); - EXPECT_EQ(OK, node0.ClosePort(A)); - EXPECT_EQ(OK, node0.ClosePort(E)); + EXPECT_EQ(OK, node.node().ClosePort(A)); + EXPECT_EQ(OK, node.node().ClosePort(E)); - PumpTasks(); + // The node should not idle until all proxies are collapsed. + WaitForIdle(); - EXPECT_TRUE(node0.CanShutdownCleanly(false)); + EXPECT_TRUE(node.node().CanShutdownCleanly()); } TEST_F(PortsTest, ProxyCollapse2) { - 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); + TestNode node(0); + AddNode(&node); PortRef A, B; - EXPECT_EQ(OK, node0.CreatePortPair(&A, &B)); + EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B)); PortRef X, Y; - EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y)); + EXPECT_EQ(OK, node.node().CreatePortPair(&X, &Y)); ScopedMessage message; - // 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)); + // 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 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)); + EXPECT_EQ(OK, node.node().ClosePort(X)); + EXPECT_EQ(OK, node.node().ClosePort(Y)); // At this point we have a scenario with: // // D -> [B] -> C -> [A] // - // Ensure that the proxies can collapse. - - EXPECT_EQ(OK, node0.ClosePort(X)); - EXPECT_EQ(OK, node0.ClosePort(Y)); + // Ensure that the proxies can collapse. The sent ports will be closed + // eventually as a result of Y's closure. - EXPECT_EQ(OK, node0.ClosePort(C)); - EXPECT_EQ(OK, node0.ClosePort(D)); + WaitForIdle(); - PumpTasks(); - - EXPECT_TRUE(node0.CanShutdownCleanly(false)); + EXPECT_TRUE(node.node().CanShutdownCleanly()); } TEST_F(PortsTest, SendWithClosedPeer) { @@ -1068,56 +1085,47 @@ TEST_F(PortsTest, SendWithClosedPeer) { // closed, the newly created port will be aware of that peer closure, and the // proxy will eventually collapse. - 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); + TestNode node(0); + AddNode(&node); // Send a message from A to B, then close A. PortRef A, B; - EXPECT_EQ(OK, node0.CreatePortPair(&A, &B)); - EXPECT_EQ(OK, SendStringMessage(&node0, A, "hey")); - EXPECT_EQ(OK, node0.ClosePort(A)); - - PumpTasks(); + EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B)); + EXPECT_EQ(OK, node.SendStringMessage(A, "hey")); + EXPECT_EQ(OK, node.node().ClosePort(A)); // Now send B over X-Y as new port C. PortRef X, Y; - 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)); - + EXPECT_EQ(OK, node.node().CreatePortPair(&X, &Y)); + EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", B)); ScopedMessage message; - ASSERT_TRUE(node0_delegate.GetSavedMessage(&message)); + ASSERT_TRUE(node.ReadMessage(Y, &message)); ASSERT_EQ(1u, message->num_ports()); - PortRef C; - ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &C)); + ASSERT_EQ(OK, node.node().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(node0_delegate.GetSavedMessage(&message)); - EXPECT_EQ(0, strcmp("hey", ToString(message))); + ASSERT_TRUE(node.ReadMessage(C, &message)); + EXPECT_TRUE(MessageEquals(message, "hey")); PortStatus status; - EXPECT_EQ(OK, node0.GetStatus(C, &status)); + EXPECT_EQ(OK, node.node().GetStatus(C, &status)); EXPECT_FALSE(status.receiving_messages); EXPECT_FALSE(status.has_messages); EXPECT_TRUE(status.peer_closed); - node0.ClosePort(C); + node.node().ClosePort(C); + + WaitForIdle(); - EXPECT_TRUE(node0.CanShutdownCleanly(false)); + EXPECT_TRUE(node.node().CanShutdownCleanly()); } TEST_F(PortsTest, SendWithClosedPeerSent) { @@ -1126,391 +1134,342 @@ TEST_F(PortsTest, SendWithClosedPeerSent) { // eventually notified of the closure, and the dead-end proxies will // eventually be removed. - 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); + TestNode node(0); + AddNode(&node); PortRef X, Y; - EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y)); + EXPECT_EQ(OK, node.node().CreatePortPair(&X, &Y)); PortRef A, B; - EXPECT_EQ(OK, node0.CreatePortPair(&A, &B)); + EXPECT_EQ(OK, node.node().CreatePortPair(&A, &B)); ScopedMessage message; // Send A as new port C. - EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", A)); - ASSERT_TRUE(node0_delegate.GetSavedMessage(&message)); + EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", A)); + + ASSERT_TRUE(node.ReadMessage(Y, &message)); ASSERT_EQ(1u, message->num_ports()); PortRef C; - ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &C)); + ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &C)); // Send C as new port D. - EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", C)); - ASSERT_TRUE(node0_delegate.GetSavedMessage(&message)); + EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", C)); + + ASSERT_TRUE(node.ReadMessage(Y, &message)); ASSERT_EQ(1u, message->num_ports()); PortRef D; - ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &D)); - - node0_delegate.set_read_messages(false); + ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &D)); // Send a message to B through D, then close D. - EXPECT_EQ(OK, SendStringMessage(&node0, D, "hey")); - EXPECT_EQ(OK, node0.ClosePort(D)); - - PumpTasks(); + EXPECT_EQ(OK, node.SendStringMessage(D, "hey")); + EXPECT_EQ(OK, node.node().ClosePort(D)); // Now send B as new port E. - node0_delegate.set_read_messages(true); - EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", B)); + EXPECT_EQ(OK, node.SendStringMessageWithPort(X, "foo", B)); + EXPECT_EQ(OK, node.node().ClosePort(X)); - EXPECT_EQ(OK, node0.ClosePort(X)); - EXPECT_EQ(OK, node0.ClosePort(Y)); - - ASSERT_TRUE(node0_delegate.GetSavedMessage(&message)); + ASSERT_TRUE(node.ReadMessage(Y, &message)); ASSERT_EQ(1u, message->num_ports()); - PortRef E; - ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &E)); + ASSERT_EQ(OK, node.node().GetPort(message->ports()[0], &E)); + + EXPECT_EQ(OK, node.node().ClosePort(Y)); - PumpTasks(); + WaitForIdle(); // E should receive the message originally sent to B, and it should also be // aware of D's closure. - ASSERT_TRUE(node0_delegate.GetSavedMessage(&message)); - EXPECT_EQ(0, strcmp("hey", ToString(message))); + ASSERT_TRUE(node.ReadMessage(E, &message)); + EXPECT_TRUE(MessageEquals(message, "hey")); PortStatus status; - EXPECT_EQ(OK, node0.GetStatus(E, &status)); + EXPECT_EQ(OK, node.node().GetStatus(E, &status)); EXPECT_FALSE(status.receiving_messages); EXPECT_FALSE(status.has_messages); EXPECT_TRUE(status.peer_closed); - node0.ClosePort(E); + EXPECT_EQ(OK, node.node().ClosePort(E)); - PumpTasks(); + WaitForIdle(); - EXPECT_TRUE(node0.CanShutdownCleanly(false)); + EXPECT_TRUE(node.node().CanShutdownCleanly()); } TEST_F(PortsTest, MergePorts) { - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - node_map[0] = &node0; + TestNode node0(0); + AddNode(&node0); - NodeName node1_name(1, 1); - TestNodeDelegate node1_delegate(node1_name); - Node node1(node1_name, &node1_delegate); - node_map[1] = &node1; + TestNode node1(1); + AddNode(&node1); // Setup two independent port pairs, A-B on node0 and C-D on node1. PortRef A, B, 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); + EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B)); + EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D)); // Write a message on A. - EXPECT_EQ(OK, SendStringMessage(&node0, A, "hey")); - - PumpTasks(); + EXPECT_EQ(OK, node0.SendStringMessage(A, "hey")); // Initiate a merge between B and C. - EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name())); + EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name())); - PumpTasks(); + WaitForIdle(); - // Expect only two receiving ports to be left after pumping tasks. - EXPECT_TRUE(node0.CanShutdownCleanly(true)); - EXPECT_TRUE(node1.CanShutdownCleanly(true)); + // 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 D to have received the message sent on A. ScopedMessage message; - ASSERT_TRUE(node1_delegate.GetSavedMessage(&message)); - EXPECT_EQ(0, strcmp("hey", ToString(message))); + ASSERT_TRUE(node1.ReadMessage(D, &message)); + EXPECT_TRUE(MessageEquals(message, "hey")); - EXPECT_EQ(OK, node0.ClosePort(A)); - EXPECT_EQ(OK, node1.ClosePort(D)); + EXPECT_EQ(OK, node0.node().ClosePort(A)); + EXPECT_EQ(OK, node1.node().ClosePort(D)); // No more ports should be open. - EXPECT_TRUE(node0.CanShutdownCleanly(false)); - EXPECT_TRUE(node1.CanShutdownCleanly(false)); + EXPECT_TRUE(node0.node().CanShutdownCleanly()); + EXPECT_TRUE(node1.node().CanShutdownCleanly()); } TEST_F(PortsTest, MergePortWithClosedPeer1) { // This tests that the right thing happens when initiating a merge on a port // whose peer has already been closed. - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - node_map[0] = &node0; + TestNode node0(0); + AddNode(&node0); - NodeName node1_name(1, 1); - TestNodeDelegate node1_delegate(node1_name); - Node node1(node1_name, &node1_delegate); - node_map[1] = &node1; + TestNode node1(1); + AddNode(&node1); // Setup two independent port pairs, A-B on node0 and C-D on node1. PortRef A, B, 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); + EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B)); + EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D)); // Write a message on A. - EXPECT_EQ(OK, SendStringMessage(&node0, A, "hey")); - - PumpTasks(); + EXPECT_EQ(OK, node0.SendStringMessage(A, "hey")); // Close A. - EXPECT_EQ(OK, node0.ClosePort(A)); + EXPECT_EQ(OK, node0.node().ClosePort(A)); // Initiate a merge between B and C. - EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name())); + EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name())); - PumpTasks(); + WaitForIdle(); - // Expect only one receiving port to be left after pumping tasks. - EXPECT_TRUE(node0.CanShutdownCleanly(false)); - EXPECT_TRUE(node1.CanShutdownCleanly(true)); + // 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 D to have received the message sent on A. ScopedMessage message; - ASSERT_TRUE(node1_delegate.GetSavedMessage(&message)); - EXPECT_EQ(0, strcmp("hey", ToString(message))); + ASSERT_TRUE(node1.ReadMessage(D, &message)); + EXPECT_TRUE(MessageEquals(message, "hey")); - EXPECT_EQ(OK, node1.ClosePort(D)); + EXPECT_EQ(OK, node1.node().ClosePort(D)); // No more ports should be open. - EXPECT_TRUE(node0.CanShutdownCleanly(false)); - EXPECT_TRUE(node1.CanShutdownCleanly(false)); + EXPECT_TRUE(node0.node().CanShutdownCleanly()); + EXPECT_TRUE(node1.node().CanShutdownCleanly()); } TEST_F(PortsTest, MergePortWithClosedPeer2) { // This tests that the right thing happens when merging into a port whose peer // has already been closed. - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - node_map[0] = &node0; + TestNode node0(0); + AddNode(&node0); - NodeName node1_name(1, 1); - TestNodeDelegate node1_delegate(node1_name); - Node node1(node1_name, &node1_delegate); - node_map[1] = &node1; + TestNode node1(1); + AddNode(&node1); // Setup two independent port pairs, A-B on node0 and C-D on node1. PortRef A, B, 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); - - // Write a message on D. - EXPECT_EQ(OK, SendStringMessage(&node0, D, "hey")); + EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B)); + EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D)); - PumpTasks(); - - // Close D. - EXPECT_EQ(OK, node1.ClosePort(D)); + // Write a message on D and close it. + EXPECT_EQ(OK, node0.SendStringMessage(D, "hey")); + EXPECT_EQ(OK, node1.node().ClosePort(D)); // Initiate a merge between B and C. - EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name())); + EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name())); - PumpTasks(); + WaitForIdle(); - // Expect only one receiving port to be left after pumping tasks. - EXPECT_TRUE(node0.CanShutdownCleanly(true)); - EXPECT_TRUE(node1.CanShutdownCleanly(false)); + // 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 A to have received the message sent on D. ScopedMessage message; - ASSERT_TRUE(node0_delegate.GetSavedMessage(&message)); - EXPECT_EQ(0, strcmp("hey", ToString(message))); + ASSERT_TRUE(node0.ReadMessage(A, &message)); + EXPECT_TRUE(MessageEquals(message, "hey")); - EXPECT_EQ(OK, node0.ClosePort(A)); + EXPECT_EQ(OK, node0.node().ClosePort(A)); // No more ports should be open. - EXPECT_TRUE(node0.CanShutdownCleanly(false)); - EXPECT_TRUE(node1.CanShutdownCleanly(false)); + EXPECT_TRUE(node0.node().CanShutdownCleanly()); + EXPECT_TRUE(node1.node().CanShutdownCleanly()); } 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. - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - node_map[0] = &node0; + TestNode node0(0); + AddNode(&node0); - NodeName node1_name(1, 1); - TestNodeDelegate node1_delegate(node1_name); - Node node1(node1_name, &node1_delegate); - node_map[1] = &node1; + TestNode node1(1); + AddNode(&node1); // Setup two independent port pairs, A-B on node0 and C-D on node1. PortRef A, B, 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); + EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B)); + EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D)); // Close A and D. - EXPECT_EQ(OK, node0.ClosePort(A)); - EXPECT_EQ(OK, node1.ClosePort(D)); + EXPECT_EQ(OK, node0.node().ClosePort(A)); + EXPECT_EQ(OK, node1.node().ClosePort(D)); - PumpTasks(); + WaitForIdle(); // Initiate a merge between B and C. - EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name())); + EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name())); - PumpTasks(); + WaitForIdle(); // Expect everything to have gone away. - EXPECT_TRUE(node0.CanShutdownCleanly(false)); - EXPECT_TRUE(node1.CanShutdownCleanly(false)); + EXPECT_TRUE(node0.node().CanShutdownCleanly()); + EXPECT_TRUE(node1.node().CanShutdownCleanly()); } TEST_F(PortsTest, MergePortsWithMovedPeers) { - // This tests that no ports can be merged successfully even if their peers - // are moved around. + // This tests that ports can be merged successfully even if their peers are + // moved around. - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - node_map[0] = &node0; + TestNode node0(0); + AddNode(&node0); - 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); + TestNode node1(1); + AddNode(&node1); // Setup two independent port pairs, A-B on node0 and C-D on node1. PortRef A, B, C, D; - EXPECT_EQ(OK, node0.CreatePortPair(&A, &B)); - EXPECT_EQ(OK, node1.CreatePortPair(&C, &D)); + EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B)); + EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D)); // Set up another pair X-Y for moving ports on node0. PortRef X, Y; - EXPECT_EQ(OK, node0.CreatePortPair(&X, &Y)); + EXPECT_EQ(OK, node0.node().CreatePortPair(&X, &Y)); ScopedMessage message; // Move A to new port E. - EXPECT_EQ(OK, SendStringMessageWithPort(&node0, X, "foo", A)); - ASSERT_TRUE(node0_delegate.GetSavedMessage(&message)); + EXPECT_EQ(OK, node0.SendStringMessageWithPort(X, "foo", A)); + ASSERT_TRUE(node0.ReadMessage(Y, &message)); ASSERT_EQ(1u, message->num_ports()); PortRef E; - ASSERT_EQ(OK, node0.GetPort(message->ports()[0], &E)); - - EXPECT_EQ(OK, node0.ClosePort(X)); - EXPECT_EQ(OK, node0.ClosePort(Y)); + ASSERT_EQ(OK, node0.node().GetPort(message->ports()[0], &E)); - node0_delegate.set_read_messages(false); + EXPECT_EQ(OK, node0.node().ClosePort(X)); + EXPECT_EQ(OK, node0.node().ClosePort(Y)); // Write messages on E and D. - EXPECT_EQ(OK, SendStringMessage(&node0, E, "hey")); - EXPECT_EQ(OK, SendStringMessage(&node1, D, "hi")); + EXPECT_EQ(OK, node0.SendStringMessage(E, "hey")); + EXPECT_EQ(OK, node1.SendStringMessage(D, "hi")); // Initiate a merge between B and C. - EXPECT_EQ(OK, node0.MergePorts(B, node1_name, C.name())); - - node0_delegate.set_read_messages(true); - node1_delegate.set_read_messages(true); - node1_delegate.set_save_messages(true); + EXPECT_EQ(OK, node0.node().MergePorts(B, node1.name(), C.name())); - PumpTasks(); + WaitForIdle(); // Expect to receive D's message on E and E's message on D. - 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))); + ASSERT_TRUE(node0.ReadMessage(E, &message)); + EXPECT_TRUE(MessageEquals(message, "hi")); + ASSERT_TRUE(node1.ReadMessage(D, &message)); + EXPECT_TRUE(MessageEquals(message, "hey")); // Close E and D. - EXPECT_EQ(OK, node0.ClosePort(E)); - EXPECT_EQ(OK, node1.ClosePort(D)); + EXPECT_EQ(OK, node0.node().ClosePort(E)); + EXPECT_EQ(OK, node1.node().ClosePort(D)); - PumpTasks(); + WaitForIdle(); // Expect everything to have gone away. - EXPECT_TRUE(node0.CanShutdownCleanly(false)); - EXPECT_TRUE(node1.CanShutdownCleanly(false)); + EXPECT_TRUE(node0.node().CanShutdownCleanly()); + EXPECT_TRUE(node1.node().CanShutdownCleanly()); } TEST_F(PortsTest, MergePortsFailsGracefully) { // This tests that the system remains in a well-defined state if something // goes wrong during port merge. - NodeName node0_name(0, 1); - TestNodeDelegate node0_delegate(node0_name); - Node node0(node0_name, &node0_delegate); - node_map[0] = &node0; + TestNode node0(0); + AddNode(&node0); - NodeName node1_name(1, 1); - TestNodeDelegate node1_delegate(node1_name); - Node node1(node1_name, &node1_delegate); - node_map[1] = &node1; + TestNode node1(1); + AddNode(&node1); // Setup two independent port pairs, A-B on node0 and C-D on node1. PortRef A, B, 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())); + EXPECT_EQ(OK, node0.node().CreatePortPair(&A, &B)); + EXPECT_EQ(OK, node1.node().CreatePortPair(&C, &D)); - // 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.CreatePortPair(&X, &Y)); - node1_delegate.set_save_messages(true); - EXPECT_EQ(OK, SendStringMessageWithPort(&node1, X, "foo", C)); - ASSERT_TRUE(node1_delegate.GetSavedMessage(&message)); + 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)); ASSERT_EQ(1u, message->num_ports()); PortRef E; - ASSERT_EQ(OK, node1.GetPort(message->ports()[0], &E)); - EXPECT_EQ(OK, node1.ClosePort(X)); - EXPECT_EQ(OK, node1.ClosePort(Y)); + ASSERT_EQ(OK, node1.node().GetPort(message->ports()[0], &E)); - // C goes away as a result of normal proxy removal. - PumpTasks(); + EXPECT_EQ(OK, node1.node().ClosePort(X)); + EXPECT_EQ(OK, node1.node().ClosePort(Y)); - EXPECT_EQ(ERROR_PORT_UNKNOWN, node1.GetPort(C.name(), &C)); + WaitForIdle(); - // B should have been closed cleanly. - EXPECT_EQ(ERROR_PORT_UNKNOWN, node0.GetPort(B.name(), &B)); + // 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)); // Close A, D, and E. - EXPECT_EQ(OK, node0.ClosePort(A)); - EXPECT_EQ(OK, node1.ClosePort(D)); - EXPECT_EQ(OK, node1.ClosePort(E)); + EXPECT_EQ(OK, node0.node().ClosePort(A)); + EXPECT_EQ(OK, node1.node().ClosePort(D)); + EXPECT_EQ(OK, node1.node().ClosePort(E)); - PumpTasks(); + WaitForIdle(); // Expect everything to have gone away. - EXPECT_TRUE(node0.CanShutdownCleanly(false)); - EXPECT_TRUE(node1.CanShutdownCleanly(false)); + EXPECT_TRUE(node0.node().CanShutdownCleanly()); + EXPECT_TRUE(node1.node().CanShutdownCleanly()); } } // namespace test diff --git a/mojo/edk/system/request_context.cc b/mojo/edk/system/request_context.cc index 276e39f..6370ab1 100644 --- a/mojo/edk/system/request_context.cc +++ b/mojo/edk/system/request_context.cc @@ -77,12 +77,12 @@ void RequestContext::AddWatchNotifyFinalizer( const HandleSignalsState& state) { DCHECK(IsCurrent()); watch_notify_finalizers_->push_back( - WatchNotifyFinalizer(watcher, result, state)); + WatchNotifyFinalizer(std::move(watcher), result, state)); } void RequestContext::AddWatchCancelFinalizer(scoped_refptr<Watcher> watcher) { DCHECK(IsCurrent()); - watch_cancel_finalizers_->push_back(watcher); + watch_cancel_finalizers_->push_back(std::move(watcher)); } bool RequestContext::IsCurrent() const { @@ -93,8 +93,7 @@ RequestContext::WatchNotifyFinalizer::WatchNotifyFinalizer( scoped_refptr<Watcher> watcher, MojoResult result, const HandleSignalsState& state) - : watcher(watcher), result(result), state(state) { -} + : watcher(std::move(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 1648dd2..6015595 100644 --- a/mojo/edk/system/shared_buffer_dispatcher.h +++ b/mojo/edk/system/shared_buffer_dispatcher.h @@ -21,7 +21,6 @@ namespace mojo { 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 ebacc64..a15456a 100644 --- a/mojo/edk/test/BUILD.gn +++ b/mojo/edk/test/BUILD.gn @@ -9,8 +9,6 @@ static_library("test_support") { 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 5d77a11..f1032d7 100644 --- a/mojo/edk/test/mojo_test_base.cc +++ b/mojo/edk/test/mojo_test_base.cc @@ -44,14 +44,15 @@ MojoTestBase::~MojoTestBase() {} MojoTestBase::ClientController& MojoTestBase::StartClient( const std::string& client_name) { clients_.push_back(base::MakeUnique<ClientController>( - client_name, this, process_error_callback_)); + client_name, this, process_error_callback_, launch_type_)); return *clients_.back(); } MojoTestBase::ClientController::ClientController( const std::string& client_name, MojoTestBase* test, - const ProcessErrorCallback& process_error_callback) { + const ProcessErrorCallback& process_error_callback, + LaunchType launch_type) { #if !defined(OS_IOS) #if defined(OS_MACOSX) // This lock needs to be held while launching the child because the Mach port @@ -63,7 +64,7 @@ MojoTestBase::ClientController::ClientController( base::AutoLock lock(g_mach_broker->GetLock()); #endif helper_.set_process_error_callback(process_error_callback); - pipe_ = helper_.StartChild(client_name); + pipe_ = helper_.StartChild(client_name, launch_type); #if defined(OS_MACOSX) g_mach_broker->AddPlaceholderForPid(helper_.test_child().Handle()); #endif @@ -75,6 +76,12 @@ MojoTestBase::ClientController::~ClientController() { << "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 4f55145..fa5b64c 100644 --- a/mojo/edk/test/mojo_test_base.h +++ b/mojo/edk/test/mojo_test_base.h @@ -29,6 +29,8 @@ class MojoTestBase : public testing::Test { MojoTestBase(); ~MojoTestBase() override; + using LaunchType = MultiprocessTestHelper::LaunchType; + protected: using HandlerCallback = base::Callback<void(ScopedMessagePipeHandle)>; @@ -36,11 +38,13 @@ class MojoTestBase : public testing::Test { public: ClientController(const std::string& client_name, MojoTestBase* test, - const ProcessErrorCallback& process_error_callback_); + const ProcessErrorCallback& process_error_callback, + LaunchType launch_type); ~ClientController(); MojoHandle pipe() const { return pipe_.get().value(); } + void ClosePeerConnection(); int WaitForShutdown(); private: @@ -148,6 +152,8 @@ class MojoTestBase : public testing::Test { // 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; @@ -155,6 +161,8 @@ class MojoTestBase : public testing::Test { 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 82a82c8..de6e2d9 100644 --- a/mojo/edk/test/multiprocess_test_helper.cc +++ b/mojo/edk/test/multiprocess_test_helper.cc @@ -8,12 +8,14 @@ #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" @@ -22,6 +24,9 @@ #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" @@ -38,11 +43,13 @@ namespace test { namespace { const char kMojoPrimordialPipeToken[] = "mojo-primordial-pipe-token"; +const char kMojoNamedPipeName[] = "mojo-named-pipe-name"; -int RunClientFunction(std::function<int(MojoHandle)> handler) { - CHECK(!MultiprocessTestHelper::primordial_pipe_token.empty()); - ScopedMessagePipeHandle pipe = CreateChildMessagePipe( - MultiprocessTestHelper::primordial_pipe_token); +template <typename Func> +int RunClientFunction(Func handler) { + CHECK(MultiprocessTestHelper::primordial_pipe.is_valid()); + ScopedMessagePipeHandle pipe = + std::move(MultiprocessTestHelper::primordial_pipe); return handler(pipe.get().value()); } @@ -55,15 +62,17 @@ MultiprocessTestHelper::~MultiprocessTestHelper() { } ScopedMessagePipeHandle MultiprocessTestHelper::StartChild( - const std::string& test_child_name) { - return StartChildWithExtraSwitch( - test_child_name, std::string(), std::string()); + const std::string& test_child_name, + LaunchType launch_type) { + return StartChildWithExtraSwitch(test_child_name, std::string(), + std::string(), launch_type); } ScopedMessagePipeHandle MultiprocessTestHelper::StartChildWithExtraSwitch( const std::string& test_child_name, const std::string& switch_string, - const std::string& switch_value) { + const std::string& switch_value, + LaunchType launch_type) { CHECK(!test_child_name.empty()); CHECK(!test_child_.IsValid()); @@ -88,12 +97,23 @@ ScopedMessagePipeHandle MultiprocessTestHelper::StartChildWithExtraSwitch( } PlatformChannelPair channel; + NamedPlatformHandle named_pipe; HandlePassingInformation handle_passing_info; - channel.PrepareToPassClientHandleToChildProcess(&command_line, - &handle_passing_info); - - std::string pipe_token = mojo::edk::GenerateRandomToken(); - command_line.AppendSwitchASCII(kMojoPrimordialPipeToken, pipe_token); + 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); + } if (!switch_string.empty()) { CHECK(!command_line.HasSwitch(switch_string)); @@ -116,18 +136,44 @@ ScopedMessagePipeHandle MultiprocessTestHelper::StartChildWithExtraSwitch( #error "Not supported yet." #endif - std::string child_token = mojo::edk::GenerateRandomToken(); - ScopedMessagePipeHandle pipe = CreateParentMessagePipe(pipe_token, - child_token); + // 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_); + } test_child_ = base::SpawnMultiProcessTestChild(test_child_main, command_line, options); - channel.ChildProcessLaunched(); + if (launch_type == LaunchType::CHILD || launch_type == LaunchType::PEER) + 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; } @@ -135,18 +181,18 @@ int MultiprocessTestHelper::WaitForChildShutdown() { CHECK(test_child_.IsValid()); int rv = -1; -#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 + WaitForMultiprocessTestChildExit(test_child_, TestTimeouts::action_timeout(), + &rv); 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; } @@ -155,17 +201,33 @@ bool MultiprocessTestHelper::WaitForChildTestShutdown() { void MultiprocessTestHelper::ChildSetup() { CHECK(base::CommandLine::InitializedForCurrentProcess()); - primordial_pipe_token = base::CommandLine::ForCurrentProcess() - ->GetSwitchValueASCII(kMojoPrimordialPipeToken); - CHECK(!primordial_pipe_token.empty()); - + 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); #if defined(OS_MACOSX) && !defined(OS_IOS) - CHECK(base::MachPortBroker::ChildSendTaskPortToParent("mojo_test")); + CHECK(base::MachPortBroker::ChildSendTaskPortToParent("mojo_test")); #endif - - SetParentPipeHandle( - PlatformChannelPair::PassClientHandleFromParentProcess( - *base::CommandLine::ForCurrentProcess())); + 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())); + } + } } // static @@ -187,7 +249,7 @@ int MultiprocessTestHelper::RunClientTestMain( } // static -std::string MultiprocessTestHelper::primordial_pipe_token; +mojo::ScopedMessagePipeHandle MultiprocessTestHelper::primordial_pipe; } // namespace test } // namespace edk diff --git a/mojo/edk/test/multiprocess_test_helper.h b/mojo/edk/test/multiprocess_test_helper.h index 203eb11..dd9bd6a 100644 --- a/mojo/edk/test/multiprocess_test_helper.h +++ b/mojo/edk/test/multiprocess_test_helper.h @@ -19,7 +19,6 @@ namespace mojo { namespace edk { -class PlatformChannelPair; namespace test { @@ -27,13 +26,31 @@ class MultiprocessTestHelper { 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); + ScopedMessagePipeHandle StartChild( + const std::string& test_child_name, + LaunchType launch_type = LaunchType::CHILD); // Like |StartChild()|, but appends an extra switch (with ASCII value) to the // command line. (The switch must not already be present in the default @@ -41,12 +58,15 @@ class MultiprocessTestHelper { ScopedMessagePipeHandle StartChildWithExtraSwitch( const std::string& test_child_name, const std::string& switch_string, - const std::string& switch_value); + const std::string& switch_value, + LaunchType launch_type); 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 @@ -69,7 +89,7 @@ class MultiprocessTestHelper { static int RunClientTestMain(const base::Callback<void(MojoHandle)>& main); // For use (and only valid) in the child process: - static std::string primordial_pipe_token; + static mojo::ScopedMessagePipeHandle primordial_pipe; private: // Valid after |StartChild()| and before |WaitForChildShutdown()|. @@ -77,6 +97,8 @@ class MultiprocessTestHelper { 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 50c41d7..3ce3b47 100644 --- a/mojo/edk/test/run_all_perftests.cc +++ b/mojo/edk/test/run_all_perftests.cc @@ -7,24 +7,19 @@ #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); - // 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); 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 05aed91..a057825 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,10 +26,6 @@ int main(int argc, char** argv) { 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. @@ -43,11 +39,10 @@ int main(int argc, char** argv) { 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 deleted file mode 100644 index 7dc7c99..0000000 --- a/mojo/edk/test/scoped_ipc_support.cc +++ /dev/null @@ -1,62 +0,0 @@ -// 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 deleted file mode 100644 index 04173d3..0000000 --- a/mojo/edk/test/scoped_ipc_support.h +++ /dev/null @@ -1,65 +0,0 @@ -// 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 deleted file mode 100644 index ad37238..0000000 --- a/mojo/gpu/BUILD.gn +++ /dev/null @@ -1,18 +0,0 @@ -# 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 deleted file mode 100644 index 241389e..0000000 --- a/mojo/gpu/DEPS +++ /dev/null @@ -1,4 +0,0 @@ -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 deleted file mode 100644 index 93377a5..0000000 --- a/mojo/gpu/mojo_gles2_impl_autogen.cc +++ /dev/null @@ -1,1899 +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. - -// 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 deleted file mode 100644 index fd0e5bd..0000000 --- a/mojo/gpu/mojo_gles2_impl_autogen.h +++ /dev/null @@ -1,890 +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. - -// 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 deleted file mode 100644 index e1ae4af..0000000 --- a/mojo/message_pump/BUILD.gn +++ /dev/null @@ -1,24 +0,0 @@ -# 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 deleted file mode 100644 index 7f6f561..0000000 --- a/mojo/message_pump/handle_watcher.cc +++ /dev/null @@ -1,480 +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. - -#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 deleted file mode 100644 index 10056b1..0000000 --- a/mojo/message_pump/handle_watcher.h +++ /dev/null @@ -1,65 +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. - -#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 deleted file mode 100644 index 96c8495..0000000 --- a/mojo/message_pump/handle_watcher_perftest.cc +++ /dev/null @@ -1,207 +0,0 @@ -// 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 deleted file mode 100644 index fd1f49b..0000000 --- a/mojo/message_pump/handle_watcher_unittest.cc +++ /dev/null @@ -1,493 +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. - -#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 deleted file mode 100644 index b907ba3..0000000 --- a/mojo/message_pump/message_pump_mojo.cc +++ /dev/null @@ -1,448 +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. - -#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 deleted file mode 100644 index ef2f55a..0000000 --- a/mojo/message_pump/message_pump_mojo.h +++ /dev/null @@ -1,178 +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. - -#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 deleted file mode 100644 index 8beb9ba..0000000 --- a/mojo/message_pump/message_pump_mojo_handler.h +++ /dev/null @@ -1,29 +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. - -#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 deleted file mode 100644 index 1abb27c..0000000 --- a/mojo/message_pump/message_pump_mojo_unittest.cc +++ /dev/null @@ -1,193 +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. - -#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 deleted file mode 100644 index f8c1864..0000000 --- a/mojo/message_pump/mojo_message_pump_export.h +++ /dev/null @@ -1,32 +0,0 @@ -// 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 deleted file mode 100644 index ffd667e..0000000 --- a/mojo/message_pump/time_helper.cc +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/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 deleted file mode 100644 index 6079000..0000000 --- a/mojo/message_pump/time_helper.h +++ /dev/null @@ -1,34 +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_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 deleted file mode 100644 index 1ef0c4d..0000000 --- a/mojo/mojo.gyp +++ /dev/null @@ -1,18 +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. - -{ - '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 deleted file mode 100644 index a067353..0000000 --- a/mojo/mojo_base.gyp +++ /dev/null @@ -1,192 +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. - -# 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 deleted file mode 100644 index a72311c..0000000 --- a/mojo/mojo_common_unittests.isolate +++ /dev/null @@ -1,23 +0,0 @@ -# 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 deleted file mode 100644 index 5541839..0000000 --- a/mojo/mojo_edk.gyp +++ /dev/null @@ -1,233 +0,0 @@ -# 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 deleted file mode 100644 index e192462..0000000 --- a/mojo/mojo_edk_tests.gyp +++ /dev/null @@ -1,397 +0,0 @@ -# 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 deleted file mode 100644 index 81bae0b..0000000 --- a/mojo/mojo_js_unittests.isolate +++ /dev/null @@ -1,46 +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. -{ - '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 deleted file mode 100644 index 005e012..0000000 --- a/mojo/mojo_public.gyp +++ /dev/null @@ -1,310 +0,0 @@ -# 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 deleted file mode 100644 index 846bf78..0000000 --- a/mojo/mojo_public_bindings_unittests.isolate +++ /dev/null @@ -1,24 +0,0 @@ -# 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 deleted file mode 100644 index f761bf7..0000000 --- a/mojo/mojo_public_system_unittests.isolate +++ /dev/null @@ -1,23 +0,0 @@ -# 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 deleted file mode 100644 index c8c5f15..0000000 --- a/mojo/mojom_bindings_generator.gypi +++ /dev/null @@ -1,178 +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. - -{ - '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 deleted file mode 100644 index 51676e9..0000000 --- a/mojo/mojom_bindings_generator_explicit.gypi +++ /dev/null @@ -1,195 +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. - -{ - '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 deleted file mode 100644 index 9e3c400..0000000 --- a/mojo/mojom_bindings_generator_variables.gypi +++ /dev/null @@ -1,29 +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. - -{ - '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 9442479..3baf667 100644 --- a/mojo/public/BUILD.gn +++ b/mojo/public/BUILD.gn @@ -13,8 +13,8 @@ group("public") { if (is_android) { deps += [ - "java:bindings", - "java:system", + "java:bindings_java", + "java:system_java", ] } } @@ -23,7 +23,6 @@ group("sdk") { deps = [ "c/system", "cpp/bindings", - "interfaces/bindings", "js", ] } diff --git a/mojo/public/README.md b/mojo/public/README.md index a31a8a8..dd91742 100644 --- a/mojo/public/README.md +++ b/mojo/public/README.md @@ -31,7 +31,7 @@ Platform -------- The platform/ subdirectory contains any build-time requirements (e.g., static -libraries) that may be needed to produce a Mojo application for certain +libraries) that may be needed to produce a Service library 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 3ec12f6..c3b3d5f 100644 --- a/mojo/public/c/system/BUILD.gn +++ b/mojo/public/c/system/BUILD.gn @@ -25,7 +25,7 @@ component("system") { # 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 //mojo/public/mojo_application.gni. +# mojo_native_application template in //services/service_manager/public/cpp/service.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 3775e90..77d452b 100644 --- a/mojo/public/c/system/core.h +++ b/mojo/public/c/system/core.h @@ -13,7 +13,6 @@ #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 deleted file mode 100644 index 65d0837..0000000 --- a/mojo/public/c/system/main.h +++ /dev/null @@ -1,35 +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_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 ef44a87..8a54380 100644 --- a/mojo/public/c/system/tests/core_unittest.cc +++ b/mojo/public/c/system/tests/core_unittest.cc @@ -192,7 +192,8 @@ TEST(CoreTest, BasicDataPipe) { 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, + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED | + MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, state.satisfiable_signals); // The producer |hp| should be writable. @@ -229,8 +230,10 @@ TEST(CoreTest, BasicDataPipe) { &result_index, states)); EXPECT_EQ(0u, result_index); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, states[0].satisfied_signals); - EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, + 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, 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 a01e14b..fa3caa5 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 -// Poor man's gtest. +// Makeshift 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 3482d4e..7e02eeb 100644 --- a/mojo/public/c/system/types.h +++ b/mojo/public/c/system/types.h @@ -149,6 +149,11 @@ const MojoDeadline MOJO_DEADLINE_INDEFINITE = static_cast<MojoDeadline>(-1); // |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; @@ -157,11 +162,13 @@ const MojoHandleSignals MOJO_HANDLE_SIGNAL_NONE = 0; 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 75d9c13..e2abd58 100644 --- a/mojo/public/c/test_support/BUILD.gn +++ b/mojo/public/c/test_support/BUILD.gn @@ -2,7 +2,6 @@ # 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 3ec9824..5c41384 100644 --- a/mojo/public/cpp/bindings/BUILD.gn +++ b/mojo/public/cpp/bindings/BUILD.gn @@ -2,14 +2,31 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -static_library("bindings") { +interfaces_bindings_gen_dir = "$root_gen_dir/mojo/public/interfaces/bindings" + +component("bindings") { sources = [ - "array.h", + # 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_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", @@ -17,8 +34,13 @@ static_library("bindings") { "associated_interface_request.h", "binding.h", "binding_set.h", + "bindings_export.h", + "clone_traits.h", + "connection_error_callback.h", "connector.h", - "enum_traits.h", + "disconnect_reason.h", + "filter_chain.h", + "interface_data_view.h", "interface_endpoint_client.h", "interface_endpoint_controller.h", "interface_id.h", @@ -29,34 +51,35 @@ static_library("bindings") { "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", @@ -68,11 +91,8 @@ static_library("bindings") { "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", @@ -94,32 +114,35 @@ static_library("bindings") { "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", - "no_interface.h", + "native_struct_data_view.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", - "stl_converters.h", - "string.h", + "string_data_view.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 = [ @@ -131,12 +154,16 @@ static_library("bindings") { deps = [ "//base", - "//mojo/public/interfaces/bindings:bindings_cpp_sources", + "//mojo/public/interfaces/bindings:bindings__generator", + "//mojo/public/interfaces/bindings:bindings_shared__generator", ] + + defines = [ "MOJO_CPP_BINDINGS_IMPLEMENTATION" ] } source_set("struct_traits") { sources = [ + "enum_traits.h", "struct_traits.h", ] } @@ -145,16 +172,13 @@ if (!is_ios) { # 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 deleted file mode 100644 index a253da1..0000000 --- a/mojo/public/cpp/bindings/array.h +++ /dev/null @@ -1,279 +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. - -#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 new file mode 100644 index 0000000..d02a884 --- /dev/null +++ b/mojo/public/cpp/bindings/array_data_view.h @@ -0,0 +1,244 @@ +// 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 366573d..594b2e0 100644 --- a/mojo/public/cpp/bindings/array_traits.h +++ b/mojo/public/cpp/bindings/array_traits.h @@ -47,7 +47,9 @@ namespace mojo { // static void AdvanceIterator(Iterator& iterator); // // // Returns a reference to the value at the current position of -// // |iterator|. +// // |iterator|. Optionally, the ConstIterator version of GetValue can +// // return by value instead of by reference if it makes sense for the +// // type. // 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 ffcf9d5..3ff694b 100644 --- a/mojo/public/cpp/bindings/array_traits_carray.h +++ b/mojo/public/cpp/bindings/array_traits_carray.h @@ -20,6 +20,14 @@ struct CArray { }; 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; @@ -48,6 +56,21 @@ struct ArrayTraits<CArray<T>> { } }; +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 deleted file mode 100644 index 862de6b..0000000 --- a/mojo/public/cpp/bindings/array_traits_standard.h +++ /dev/null @@ -1,43 +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_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 9054a92..dec47bf 100644 --- a/mojo/public/cpp/bindings/array_traits_stl.h +++ b/mojo/public/cpp/bindings/array_traits_stl.h @@ -5,6 +5,8 @@ #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" @@ -42,13 +44,17 @@ struct ArrayTraits<std::vector<T>> { return input[index]; } - static bool Resize(std::vector<T>& input, size_t size) { + 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 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); } @@ -57,6 +63,65 @@ struct ArrayTraits<std::vector<T>> { } }; +// 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 deleted file mode 100644 index 7e773fc..0000000 --- a/mojo/public/cpp/bindings/array_traits_wtf.h +++ /dev/null @@ -1,40 +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_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 1da5009..5941166 100644 --- a/mojo/public/cpp/bindings/associated_binding.h +++ b/mojo/public/cpp/bindings/associated_binding.h @@ -6,23 +6,78 @@ #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_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/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. // @@ -33,53 +88,48 @@ namespace mojo { // 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> -class AssociatedBinding { +template <typename Interface, + typename ImplRefTraits = RawPtrImplRefTraits<Interface>> +class AssociatedBinding : public AssociatedBindingBase { 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(Interface* impl) : impl_(impl) { - stub_.set_sink(impl_); - } + explicit AssociatedBinding(ImplPointerType impl) { stub_.set_sink(impl); } // Constructs a completed associated binding of |impl|. The output |ptr_info| - // 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, + // should be sent by another interface. |impl| must outlive this object. + AssociatedBinding(ImplPointerType impl, AssociatedInterfacePtrInfo<Interface>* ptr_info, - AssociatedGroup* associated_group, scoped_refptr<base::SingleThreadTaskRunner> runner = base::ThreadTaskRunnerHandle::Get()) - : AssociatedBinding(impl) { - Bind(ptr_info, associated_group, std::move(runner)); + : AssociatedBinding(std::move(impl)) { + Bind(ptr_info, std::move(runner)); } // Constructs a completed associated binding of |impl|. |impl| must outlive // the binding. - AssociatedBinding(Interface* impl, + AssociatedBinding(ImplPointerType impl, AssociatedInterfaceRequest<Interface> request, scoped_refptr<base::SingleThreadTaskRunner> runner = base::ThreadTaskRunnerHandle::Get()) - : AssociatedBinding(impl) { + : AssociatedBinding(std::move(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 passed through the - // message pipe endpoint referred to by |associated_group| to setup the - // corresponding asssociated interface pointer. + // implementation side. The output |ptr_info| should be sent by another + // interface. void Bind(AssociatedInterfacePtrInfo<Interface>* ptr_info, - AssociatedGroup* associated_group, scoped_refptr<base::SingleThreadTaskRunner> runner = base::ThreadTaskRunnerHandle::Get()) { - AssociatedInterfaceRequest<Interface> request; - associated_group->CreateAssociatedInterface(AssociatedGroup::WILL_PASS_PTR, - ptr_info, &request); + auto request = MakeRequest(ptr_info); + ptr_info->set_version(Interface::Version_); Bind(std::move(request), std::move(runner)); } @@ -87,35 +137,10 @@ class AssociatedBinding { void Bind(AssociatedInterfaceRequest<Interface> request, scoped_refptr<base::SingleThreadTaskRunner> runner = base::ThreadTaskRunnerHandle::Get()) { - 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(); + BindImpl(request.PassHandle(), &stub_, + base::WrapUnique(new typename Interface::RequestValidator_()), + Interface::HasSyncMethods_, std::move(runner), + Interface::Version_); } // Unbinds and returns the associated interface request so it can be @@ -128,44 +153,15 @@ class AssociatedBinding { 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 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; - } + Interface* impl() { return ImplRefTraits::GetRawPointer(&stub_.sink()); } private: - 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_; + typename Interface::template Stub_<ImplRefTraits> stub_; DISALLOW_COPY_AND_ASSIGN(AssociatedBinding); }; diff --git a/mojo/public/cpp/bindings/associated_group.h b/mojo/public/cpp/bindings/associated_group.h index 836c0d6..14e78ec 100644 --- a/mojo/public/cpp/bindings/associated_group.h +++ b/mojo/public/cpp/bindings/associated_group.h @@ -5,11 +5,9 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_GROUP_H_ #define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_GROUP_H_ -#include <utility> - +#include "base/callback.h" #include "base/memory/ref_counted.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/bindings_export.h" #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" namespace mojo { @@ -17,71 +15,34 @@ namespace mojo { class AssociatedGroupController; // AssociatedGroup refers to all the interface endpoints running at one end of a -// message pipe. It is used to create associated interfaces for that message -// pipe. +// message pipe. // It is thread safe and cheap to make copies. -class AssociatedGroup { +class MOJO_CPP_BINDINGS_EXPORT 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(); - AssociatedGroup(const AssociatedGroup& other); - ~AssociatedGroup(); - - AssociatedGroup& operator=(const AssociatedGroup& other); + explicit AssociatedGroup(scoped_refptr<AssociatedGroupController> controller); - // |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); + explicit AssociatedGroup(const ScopedInterfaceEndpointHandle& handle); - if (!local.is_valid() || !remote.is_valid()) { - *ptr_info = AssociatedInterfacePtrInfo<T>(); - *request = AssociatedInterfaceRequest<T>(); - return; - } + AssociatedGroup(const AssociatedGroup& other); - if (config == WILL_PASS_PTR) { - ptr_info->set_handle(std::move(remote)); + ~AssociatedGroup(); - // 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)); + AssociatedGroup& operator=(const AssociatedGroup& other); - // The implementation is remote, we don't know about its actual version - // yet. - ptr_info->set_version(0u); - request->Bind(std::move(remote)); - } - } + // 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(); private: - friend class AssociatedGroupController; - - void CreateEndpointHandlePair( - ScopedInterfaceEndpointHandle* local_endpoint, - ScopedInterfaceEndpointHandle* remote_endpoint); - + base::Callback<AssociatedGroupController*()> controller_getter_; 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 0ab8253..d33c277 100644 --- a/mojo/public/cpp/bindings/associated_group_controller.h +++ b/mojo/public/cpp/bindings/associated_group_controller.h @@ -9,41 +9,47 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "base/memory/ref_counted_delete_on_message_loop.h" +#include "base/optional.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. -class AssociatedGroupController : - public base::RefCountedDeleteOnMessageLoop<AssociatedGroupController> { +// 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> { public: - 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; + // 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; // Creates an interface endpoint handle from a given interface ID. The handle - // is used locally. + // joins this associated group. // 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, bool is_local) = 0; + virtual void CloseEndpointHandle( + InterfaceId id, + const base::Optional<DisconnectReason>& reason) = 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 @@ -63,21 +69,23 @@ class AssociatedGroupController : // and notifies all interfaces running on this pipe. virtual void RaiseError() = 0; - std::unique_ptr<AssociatedGroup> CreateAssociatedGroup(); - protected: - friend class base::RefCountedDeleteOnMessageLoop<AssociatedGroupController>; - friend class base::DeleteHelper<AssociatedGroupController>; + friend class base::RefCountedThreadSafe<AssociatedGroupController>; - // Creates a new ScopedInterfaceEndpointHandle associated with this - // controller. + // Creates a new ScopedInterfaceEndpointHandle within this associated group. ScopedInterfaceEndpointHandle CreateScopedInterfaceEndpointHandle( - InterfaceId id, - bool is_local); + InterfaceId id); - virtual ~AssociatedGroupController(); + // 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); - DISALLOW_COPY_AND_ASSIGN(AssociatedGroupController); + virtual ~AssociatedGroupController(); }; } // namespace mojo diff --git a/mojo/public/cpp/bindings/associated_interface_ptr.h b/mojo/public/cpp/bindings/associated_interface_ptr.h index 10494ce..8e66f4e 100644 --- a/mojo/public/cpp/bindings/associated_interface_ptr.h +++ b/mojo/public/cpp/bindings/associated_interface_ptr.h @@ -6,6 +6,8 @@ #define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_ #include <stdint.h> + +#include <string> #include <utility> #include "base/callback.h" @@ -14,10 +16,12 @@ #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 { @@ -26,6 +30,9 @@ namespace mojo { template <typename Interface> class AssociatedInterfacePtr { public: + using InterfaceType = Interface; + using PtrInfoType = AssociatedInterfacePtrInfo<Interface>; + // Constructs an unbound AssociatedInterfacePtr. AssociatedInterfacePtr() {} AssociatedInterfacePtr(decltype(nullptr)) {} @@ -58,20 +65,16 @@ class AssociatedInterfacePtr { // multiple task runners to a single thread for the purposes of task // scheduling. // - // NOTE: Please see the comments of - // AssociatedGroup.CreateAssociatedInterface() about when you can use this - // object to make calls. + // 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. void Bind(AssociatedInterfacePtrInfo<Interface> info, scoped_refptr<base::SingleThreadTaskRunner> runner = base::ThreadTaskRunnerHandle::Get()) { reset(); - 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) + if (info.is_valid()) internal_state_.Bind(std::move(info), std::move(runner)); } @@ -86,9 +89,6 @@ class AssociatedInterfacePtr { // 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,6 +107,12 @@ class AssociatedInterfacePtr { 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() { @@ -114,6 +120,13 @@ class AssociatedInterfacePtr { 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(); } @@ -126,6 +139,11 @@ class AssociatedInterfacePtr { 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. @@ -141,12 +159,6 @@ class AssociatedInterfacePtr { 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_; @@ -179,30 +191,95 @@ class AssociatedInterfacePtr { DISALLOW_COPY_AND_ASSIGN(AssociatedInterfacePtr); }; -// 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. +// Creates an associated interface. The returned request is supposed to be sent +// over another interface (either associated or non-associated). // -// 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. +// 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. template <typename Interface> -AssociatedInterfaceRequest<Interface> GetProxy( +AssociatedInterfaceRequest<Interface> MakeRequest( AssociatedInterfacePtr<Interface>* ptr, - AssociatedGroup* group, scoped_refptr<base::SingleThreadTaskRunner> runner = base::ThreadTaskRunnerHandle::Get()) { - AssociatedInterfaceRequest<Interface> request; AssociatedInterfacePtrInfo<Interface> ptr_info; - group->CreateAssociatedInterface(AssociatedGroup::WILL_PASS_REQUEST, - &ptr_info, &request); - + auto request = MakeRequest(&ptr_info); 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 bfb3297..3c6ca54 100644 --- a/mojo/public/cpp/bindings/associated_interface_ptr_info.h +++ b/mojo/public/cpp/bindings/associated_interface_ptr_info.h @@ -20,6 +20,7 @@ template <typename Interface> 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 30fcd16..c37636c 100644 --- a/mojo/public/cpp/bindings/associated_interface_request.h +++ b/mojo/public/cpp/bindings/associated_interface_request.h @@ -5,6 +5,7 @@ #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" @@ -64,6 +65,10 @@ class AssociatedInterfaceRequest { 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 8c9ee2f..1da331b 100644 --- a/mojo/public/cpp/bindings/binding.h +++ b/mojo/public/cpp/bindings/binding.h @@ -5,6 +5,7 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_BINDING_H_ #define MOJO_PUBLIC_CPP_BINDINGS_BINDING_H_ +#include <string> #include <utility> #include "base/callback_forward.h" @@ -12,15 +13,17 @@ #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 AssociatedGroup; +class MessageReceiver; // Represents the binding of an interface implementation to a message pipe. // When the |Binding| object is destroyed, the binding between the message pipe @@ -63,21 +66,24 @@ class AssociatedGroup; // 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> +template <typename Interface, + typename ImplRefTraits = RawPtrImplRefTraits<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(Interface* impl) : internal_state_(impl) {} + explicit Binding(ImplPointerType impl) : internal_state_(std::move(impl)) {} // Constructs a completed binding of message pipe |handle| to implementation // |impl|. Does not take ownership of |impl|, which must outlive the binding. - Binding(Interface* impl, + Binding(ImplPointerType impl, ScopedMessagePipeHandle handle, scoped_refptr<base::SingleThreadTaskRunner> runner = base::ThreadTaskRunnerHandle::Get()) - : Binding(impl) { + : Binding(std::move(impl)) { Bind(std::move(handle), std::move(runner)); } @@ -86,22 +92,22 @@ class Binding { // 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(Interface* impl, + Binding(ImplPointerType impl, InterfacePtr<Interface>* ptr, scoped_refptr<base::SingleThreadTaskRunner> runner = base::ThreadTaskRunnerHandle::Get()) - : Binding(impl) { + : Binding(std::move(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(Interface* impl, + Binding(ImplPointerType impl, InterfaceRequest<Interface> request, scoped_refptr<base::SingleThreadTaskRunner> runner = base::ThreadTaskRunnerHandle::Get()) - : Binding(impl) { + : Binding(std::move(impl)) { Bind(request.PassMessagePipe(), std::move(runner)); } @@ -152,6 +158,14 @@ class Binding { 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(); @@ -189,6 +203,11 @@ class Binding { // 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 @@ -217,6 +236,12 @@ class Binding { 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(); } @@ -231,20 +256,17 @@ class Binding { // transferred to the caller. MessagePipeHandle handle() const { return internal_state_.handle(); } - // 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(); - } + // 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(); } // Exposed for testing, should not generally be used. void EnableTestingMode() { internal_state_.EnableTestingMode(); } private: - internal::BindingState<Interface, Interface::PassesAssociatedKinds_> - internal_state_; + internal::BindingState<Interface, ImplRefTraits> 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 b1baca6..919f9c0 100644 --- a/mojo/public/cpp/bindings/binding_set.h +++ b/mojo/public/cpp/bindings/binding_set.h @@ -5,111 +5,263 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_ #define MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_ -#include <algorithm> +#include <string> #include <utility> -#include <vector> #include "base/bind.h" #include "base/callback.h" #include "base/macros.h" -#include "base/memory/weak_ptr.h" +#include "base/memory/ptr_util.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 { -// 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 { +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 { public: - BindingSet() {} - ~BindingSet() { CloseAllBindings(); } + 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() {} void set_connection_error_handler(const base::Closure& error_handler) { error_handler_ = error_handler; + error_with_reason_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()); + void set_connection_error_with_reason_handler( + const ConnectionErrorWithReasonCallback& error_handler) { + error_with_reason_handler_ = error_handler; + error_handler_.Reset(); } - // 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; + // 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; } - void CloseAllBindings() { - for (const auto& it : bindings_) { - if (it) { - it->Close(); - delete it.get(); - } - } - bindings_.clear(); + // 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); + } + + // 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: - class Element { + friend class Entry; + + class Entry { public: - 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))); + 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() {} + void FlushForTesting() { binding_.FlushForTesting(); } - void set_connection_error_handler(const base::Closure& error_handler) { - error_handler_ = error_handler; - } + private: + class DispatchFilter : public MessageReceiver { + public: + explicit DispatchFilter(Entry* entry) : entry_(entry) {} + ~DispatchFilter() override {} - base::WeakPtr<Element> GetWeakPtr() { - return weak_ptr_factory_.GetWeakPtr(); - } + private: + // MessageReceiver: + bool Accept(Message* message) override { + entry_->WillDispatch(); + return true; + } - void Close() { binding_.Close(); } + Entry* entry_; - void OnConnectionError() { - base::Closure error_handler = error_handler_; - delete this; - if (!error_handler.is_null()) - error_handler.Run(); + DISALLOW_COPY_AND_ASSIGN(DispatchFilter); + }; + + void WillDispatch() { + DCHECK(ContextTraits::SupportsContext()); + binding_set_->SetDispatchContext(&context_); } - private: - Binding<Interface> binding_; - base::Closure error_handler_; - base::WeakPtrFactory<Element> weak_ptr_factory_; + void OnConnectionError(uint32_t custom_reason, + const std::string& description) { + if (ContextTraits::SupportsContext()) + WillDispatch(); + binding_set_->OnConnectionError(binding_id_, custom_reason, description); + } - DISALLOW_COPY_AND_ASSIGN(Element); + BindingType binding_; + BindingSetBase* const binding_set_; + const BindingId binding_id_; + Context const context_; + + DISALLOW_COPY_AND_ASSIGN(Entry); }; - 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()); + 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); 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_; - std::vector<base::WeakPtr<Element>> bindings_; + 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; - DISALLOW_COPY_AND_ASSIGN(BindingSet); + DISALLOW_COPY_AND_ASSIGN(BindingSetBase); }; +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 new file mode 100644 index 0000000..9fd7a27 --- /dev/null +++ b/mojo/public/cpp/bindings/bindings_export.h @@ -0,0 +1,34 @@ +// 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 new file mode 100644 index 0000000..203ab34 --- /dev/null +++ b/mojo/public/cpp/bindings/clone_traits.h @@ -0,0 +1,86 @@ +// 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 new file mode 100644 index 0000000..306e99e --- /dev/null +++ b/mojo/public/cpp/bindings/connection_error_callback.h @@ -0,0 +1,21 @@ +// 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 d14ad17..01e9236 100644 --- a/mojo/public/cpp/bindings/connector.h +++ b/mojo/public/cpp/bindings/connector.h @@ -8,10 +8,13 @@ #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" @@ -33,7 +36,8 @@ namespace mojo { // - 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 Connector : public MessageReceiver { +class MOJO_CPP_BINDINGS_EXPORT Connector + : NON_EXPORTED_BASE(public MessageReceiver) { public: enum ConnectorConfig { // Connector::Accept() is only called from a single thread. @@ -138,6 +142,7 @@ class Connector : public MessageReceiver { // 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; } @@ -146,6 +151,10 @@ class Connector : public MessageReceiver { 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); @@ -155,7 +164,8 @@ class Connector : public MessageReceiver { void WaitToReadMore(); - // Returns false if |this| was destroyed during message dispatch. + // Returns false if it is impossible to receive more messages in the future. + // |this| may have been destroyed in that case. WARN_UNUSED_RESULT bool ReadSingleMessage(MojoResult* read_result); // |this| can be destroyed during message dispatch. @@ -175,31 +185,40 @@ class Connector : public MessageReceiver { base::Closure connection_error_handler_; ScopedMessagePipeHandle message_pipe_; - MessageReceiver* incoming_receiver_; + MessageReceiver* incoming_receiver_ = nullptr; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - Watcher handle_watcher_; + std::unique_ptr<Watcher> handle_watcher_; - bool error_; - bool drop_writes_; - bool enforce_errors_from_incoming_receiver_; + bool error_ = false; + bool drop_writes_ = false; + bool enforce_errors_from_incoming_receiver_ = true; - bool paused_; + bool paused_ = false; // If sending messages is allowed from multiple threads, |lock_| is used to // protect modifications to |message_pipe_| and |drop_writes_|. - std::unique_ptr<base::Lock> lock_; + base::Optional<base::Lock> lock_; std::unique_ptr<SyncHandleWatcher> sync_watcher_; - bool allow_woken_up_by_others_; + bool allow_woken_up_by_others_ = false; // If non-zero, currently the control flow is inside the sync handle watcher // callback. - size_t sync_handle_watcher_callback_count_; + size_t sync_handle_watcher_callback_count_ = 0; 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 new file mode 100644 index 0000000..c04e8ad --- /dev/null +++ b/mojo/public/cpp/bindings/disconnect_reason.h @@ -0,0 +1,25 @@ +// 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/lib/filter_chain.h b/mojo/public/cpp/bindings/filter_chain.h index 447be3d..1262f39 100644 --- a/mojo/public/cpp/bindings/lib/filter_chain.h +++ b/mojo/public/cpp/bindings/filter_chain.h @@ -2,20 +2,22 @@ // 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_ +#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" -#include "mojo/public/cpp/bindings/message_filter.h" namespace mojo { -namespace internal { -class FilterChain { +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. @@ -23,25 +25,22 @@ class FilterChain { FilterChain(FilterChain&& other); FilterChain& operator=(FilterChain&& other); - ~FilterChain(); + ~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); - // 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(); + // MessageReceiver: + bool Accept(Message* message) override; private: - // Owned by this object. - // TODO(dcheng): Use unique_ptr. - std::vector<MessageFilter*> filters_; + std::vector<std::unique_ptr<MessageReceiver>> filters_; MessageReceiver* sink_; @@ -50,17 +49,13 @@ class 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); + Append(base::MakeUnique<FilterType>(std::forward<Args>(args)...)); } template <> inline void FilterChain::Append<PassThroughFilter>() { } -} // namespace internal } // namespace mojo -#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_ +#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 new file mode 100644 index 0000000..ef12254 --- /dev/null +++ b/mojo/public/cpp/bindings/interface_data_view.h @@ -0,0 +1,25 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#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 9dc40a2..0aea756 100644 --- a/mojo/public/cpp/bindings/interface_endpoint_client.h +++ b/mojo/public/cpp/bindings/interface_endpoint_client.h @@ -11,34 +11,42 @@ #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 InterfaceEndpointClient : public MessageReceiverWithResponder { +class MOJO_CPP_BINDINGS_EXPORT InterfaceEndpointClient + : NON_EXPORTED_BASE(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<MessageFilter> payload_validator, + std::unique_ptr<MessageReceiver> payload_validator, bool expect_sync_requests, - scoped_refptr<base::SingleThreadTaskRunner> runner); + scoped_refptr<base::SingleThreadTaskRunner> runner, + uint32_t interface_version); ~InterfaceEndpointClient() override; // Sets the error handler to receive notifications when an error is @@ -46,6 +54,14 @@ class InterfaceEndpointClient : public MessageReceiverWithResponder { 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. @@ -60,11 +76,11 @@ class InterfaceEndpointClient : public MessageReceiverWithResponder { return !async_responders_.empty() || !sync_responses_.empty(); } - AssociatedGroupController* group_controller() const { - return handle_.group_controller(); - } AssociatedGroup* associated_group(); - uint32_t interface_id() const; + + // Adds a MessageReceiver which can filter a message after validation but + // before dispatch. + void AddFilter(std::unique_ptr<MessageReceiver> filter); // After this call the object is in an invalid state and shouldn't be reused. ScopedInterfaceEndpointHandle PassHandle(); @@ -73,7 +89,11 @@ class InterfaceEndpointClient : public MessageReceiverWithResponder { // 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; @@ -83,7 +103,14 @@ class InterfaceEndpointClient : public MessageReceiverWithResponder { // NOTE: |message| must have passed message header validation. bool HandleIncomingMessage(Message* message); - void NotifyError(); + 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(); private: // Maps from the id of a response to the MessageReceiver that handles the @@ -96,7 +123,7 @@ class InterfaceEndpointClient : public MessageReceiverWithResponder { explicit SyncResponseInfo(bool* in_response_received); ~SyncResponseInfo(); - std::unique_ptr<Message> response; + Message response; // Points to a stack-allocated variable. bool* response_received; @@ -123,26 +150,37 @@ class InterfaceEndpointClient : public MessageReceiverWithResponder { 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_; + InterfaceEndpointController* controller_ = nullptr; - MessageReceiverWithResponderStatus* const incoming_receiver_; - std::unique_ptr<MessageFilter> payload_validator_; + MessageReceiverWithResponderStatus* const incoming_receiver_ = nullptr; HandleIncomingMessageThunk thunk_; + FilterChain filters_; AsyncResponderMap async_responders_; SyncResponseMap sync_responses_; - uint64_t next_request_id_; + uint64_t next_request_id_ = 1; base::Closure error_handler_; - bool encountered_error_; + ConnectionErrorWithReasonCallback error_with_reason_handler_; + bool encountered_error_ = false; 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 edcb9bf..e88be74 100644 --- a/mojo/public/cpp/bindings/interface_ptr.h +++ b/mojo/public/cpp/bindings/interface_ptr.h @@ -6,6 +6,8 @@ #define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_ #include <stdint.h> + +#include <string> #include <utility> #include "base/callback_forward.h" @@ -14,13 +16,12 @@ #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 @@ -37,6 +38,9 @@ class AssociatedGroup; template <typename Interface> class InterfacePtr { public: + using InterfaceType = Interface; + using PtrInfoType = InterfacePtrInfo<Interface>; + // Constructs an unbound InterfacePtr. InterfacePtr() {} InterfacePtr(decltype(nullptr)) {} @@ -114,6 +118,12 @@ class InterfacePtr { 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() { @@ -121,6 +131,13 @@ class InterfacePtr { 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(); @@ -140,6 +157,11 @@ class InterfacePtr { 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). @@ -162,14 +184,6 @@ class InterfacePtr { 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; @@ -180,8 +194,7 @@ class InterfacePtr { } // DO NOT USE. Exposed only for internal use and for testing. - internal::InterfacePtrState<Interface, Interface::PassesAssociatedKinds_>* - internal_state() { + internal::InterfacePtrState<Interface>* internal_state() { return &internal_state_; } @@ -189,9 +202,7 @@ class InterfacePtr { // implicitly convertible to a real bool (which is dangerous). private: // TODO(dcheng): Use an explicit conversion operator. - typedef internal::InterfacePtrState<Interface, - Interface::PassesAssociatedKinds_> - InterfacePtr::*Testable; + typedef internal::InterfacePtrState<Interface> InterfacePtr::*Testable; public: operator Testable() const { @@ -207,8 +218,7 @@ class InterfacePtr { template <typename T> bool operator!=(const InterfacePtr<T>& other) const = delete; - typedef internal::InterfacePtrState<Interface, - Interface::PassesAssociatedKinds_> State; + typedef internal::InterfacePtrState<Interface> 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 d4b2046..09a2682 100644 --- a/mojo/public/cpp/bindings/interface_ptr_set.h +++ b/mojo/public/cpp/bindings/interface_ptr_set.h @@ -16,6 +16,10 @@ 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: @@ -55,7 +59,13 @@ class PtrSet { ~Element() {} - void Close() { ptr_.reset(); } + 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); + } Interface* get() { return ptr_.get(); } diff --git a/mojo/public/cpp/bindings/interface_request.h b/mojo/public/cpp/bindings/interface_request.h index fc23aec..29d8836 100644 --- a/mojo/public/cpp/bindings/interface_request.h +++ b/mojo/public/cpp/bindings/interface_request.h @@ -5,12 +5,17 @@ #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 { @@ -28,6 +33,19 @@ class InterfaceRequest { 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_); @@ -64,6 +82,20 @@ class InterfaceRequest { 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_; @@ -103,9 +135,9 @@ InterfaceRequest<Interface> MakeRequest(ScopedMessagePipeHandle handle) { // // DatabasePtr database = ...; // Connect to database. // TablePtr table; -// database->OpenTable(GetProxy(&table)); +// database->OpenTable(MakeRequest(&table)); // -// Upon return from GetProxy, |table| is ready to have methods called on it. +// Upon return from MakeRequest, |table| is ready to have methods called on it. // // Example #2: Registering a local implementation with a remote service. // ===================================================================== @@ -119,19 +151,16 @@ InterfaceRequest<Interface> MakeRequest(ScopedMessagePipeHandle handle) { // // CollectorPtr collector = ...; // Connect to Collector. // SourcePtr source; -// InterfaceRequest<Source> source_request = GetProxy(&source); +// InterfaceRequest<Source> source_request(&source); // collector->RegisterSource(std::move(source)); // CreateSource(std::move(source_request)); // Create implementation locally. // template <typename Interface> -InterfaceRequest<Interface> GetProxy( +InterfaceRequest<Interface> MakeRequest( 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)); - return MakeRequest<Interface>(std::move(pipe.handle1)); + return InterfaceRequest<Interface>(ptr, runner); } // 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 ba6d16e..eecfcfb 100644 --- a/mojo/public/cpp/bindings/lib/array_internal.h +++ b/mojo/public/cpp/bindings/lib/array_internal.h @@ -13,6 +13,7 @@ #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" @@ -28,13 +29,13 @@ namespace internal { template <typename K, typename V> class Map_Data; -std::string MakeMessageWithArrayIndex(const char* message, - size_t size, - size_t index); +MOJO_CPP_BINDINGS_EXPORT std::string +MakeMessageWithArrayIndex(const char* message, size_t size, size_t index); -std::string MakeMessageWithExpectedArraySize(const char* message, - size_t size, - size_t expected_size); +MOJO_CPP_BINDINGS_EXPORT std::string MakeMessageWithExpectedArraySize( + const char* message, + size_t size, + size_t expected_size); template <typename T> struct ArrayDataTraits { @@ -67,7 +68,7 @@ template <> struct ArrayDataTraits<bool> { // Helper class to emulate a reference to a bool, used for direct element // access. - class BitRef { + class MOJO_CPP_BINDINGS_EXPORT BitRef { public: ~BitRef(); BitRef& operator=(bool value); @@ -109,7 +110,7 @@ struct ArrayDataTraits<bool> { // // TODO(yzshen): Validation code should be organzied in a way similar to // Serializer<>, or merged into it. It should be templatized with the mojo -// wrapper type instead of the data type, that way we can use MojomTypeTraits +// data view 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> @@ -262,7 +263,7 @@ class Array_Data { T, IsUnionDataType<T>::value, std::is_same<T, AssociatedInterface_Data>::value || - std::is_same<T, AssociatedInterfaceRequest_Data>::value || + std::is_same<T, AssociatedEndpointHandle_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 5db27a5..d2f8ecf 100644 --- a/mojo/public/cpp/bindings/lib/array_serialization.h +++ b/mojo/public/cpp/bindings/lib/array_serialization.h @@ -14,12 +14,11 @@ #include <vector> #include "base/logging.h" -#include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/bindings/array_data_view.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 { @@ -46,7 +45,7 @@ class ArrayIterator<Traits, MaybeConstUserType, true> { using GetNextResult = decltype(Traits::GetValue(std::declval<IteratorType&>())); GetNextResult GetNext() { - auto& value = Traits::GetValue(iter_); + GetNextResult value = Traits::GetValue(iter_); Traits::AdvanceIterator(iter_); return value; } @@ -287,13 +286,19 @@ struct ArraySerializer< 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) { - return sizeof(Data) + - Align(input->GetSize() * sizeof(typename Data::Element)); + 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)); } static void SerializeElements(UserTypeIterator* input, @@ -306,7 +311,8 @@ struct ArraySerializer< size_t size = input->GetSize(); for (size_t i = 0; i < size; ++i) { - Serialize<Element>(input->GetNext(), &output->at(i), context); + typename UserTypeIterator::GetNextResult next = input->GetNext(); + Serialize<Element>(next, &output->at(i), context); static const ValidationError kError = BelongsTo<Element, @@ -361,8 +367,10 @@ struct ArraySerializer<MojomType, 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) - size += PrepareToSerialize<Element>(input->GetNext(), context); + for (size_t i = 0; i < element_count; ++i) { + typename UserTypeIterator::GetNextResult next = input->GetNext(); + size += PrepareToSerialize<Element>(next, context); + } return size; } @@ -374,7 +382,8 @@ struct ArraySerializer<MojomType, size_t size = input->GetSize(); for (size_t i = 0; i < size; ++i) { DataElementPtr data_ptr; - SerializeCaller<Element>::Run(input->GetNext(), buf, &data_ptr, + typename UserTypeIterator::GetNextResult next = input->GetNext(); + SerializeCaller<Element>::Run(next, buf, &data_ptr, validate_params->element_validate_params, context); output->at(i).Set(data_ptr); @@ -444,10 +453,6 @@ struct ArraySerializer< 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(); @@ -455,7 +460,8 @@ struct ArraySerializer< 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. - size += PrepareToSerialize<Element>(input->GetNext(), false, context); + typename UserTypeIterator::GetNextResult next = input->GetNext(); + size += PrepareToSerialize<Element>(next, false, context); } return size; } @@ -468,7 +474,8 @@ struct ArraySerializer< size_t size = input->GetSize(); for (size_t i = 0; i < size; ++i) { typename Data::Element* result = output->storage() + i; - Serialize<Element>(input->GetNext(), buf, &result, true, context); + typename UserTypeIterator::GetNextResult next = input->GetNext(); + Serialize<Element>(next, buf, &result, true, context); MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( !validate_params->element_is_nullable && output->at(i).is_null(), VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, @@ -492,13 +499,13 @@ struct ArraySerializer< }; template <typename Element, typename MaybeConstUserType> -struct Serializer<Array<Element>, MaybeConstUserType> { +struct Serializer<ArrayDataView<Element>, MaybeConstUserType> { using UserType = typename std::remove_const<MaybeConstUserType>::type; using Traits = ArrayTraits<UserType>; - using Impl = ArraySerializer<Array<Element>, + using Impl = ArraySerializer<ArrayDataView<Element>, MaybeConstUserType, ArrayIterator<Traits, MaybeConstUserType>>; - using Data = typename MojomTypeTraits<Array<Element>>::Data; + using Data = typename MojomTypeTraits<ArrayDataView<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 a9c53b5..3e95eeb 100644 --- a/mojo/public/cpp/bindings/lib/associated_group.cc +++ b/mojo/public/cpp/bindings/lib/associated_group.cc @@ -8,28 +8,27 @@ namespace mojo { -AssociatedGroup::AssociatedGroup() {} +AssociatedGroup::AssociatedGroup() = default; -AssociatedGroup::AssociatedGroup(const AssociatedGroup& other) - : controller_(other.controller_) {} +AssociatedGroup::AssociatedGroup( + scoped_refptr<AssociatedGroupController> controller) + : controller_(std::move(controller)) {} -AssociatedGroup::~AssociatedGroup() {} +AssociatedGroup::AssociatedGroup(const ScopedInterfaceEndpointHandle& handle) + : controller_getter_(handle.CreateGroupControllerGetter()) {} -AssociatedGroup& AssociatedGroup::operator=(const AssociatedGroup& other) { - if (this == &other) - return *this; +AssociatedGroup::AssociatedGroup(const AssociatedGroup& other) = default; - controller_ = other.controller_; - return *this; -} +AssociatedGroup::~AssociatedGroup() = default; + +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(); - controller_->CreateEndpointHandlePair(local_endpoint, remote_endpoint); + return controller_getter_.Run(); } } // 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 42db9b3..f4a9aa2 100644 --- a/mojo/public/cpp/bindings/lib/associated_group_controller.cc +++ b/mojo/public/cpp/bindings/lib/associated_group_controller.cc @@ -8,25 +8,17 @@ namespace mojo { -AssociatedGroupController::AssociatedGroupController( - scoped_refptr<base::SingleThreadTaskRunner> task_runner) - : base::RefCountedDeleteOnMessageLoop<AssociatedGroupController>( - task_runner) {} - AssociatedGroupController::~AssociatedGroupController() {} -std::unique_ptr<AssociatedGroup> -AssociatedGroupController::CreateAssociatedGroup() { - std::unique_ptr<AssociatedGroup> group(new AssociatedGroup); - group->controller_ = this; - return group; +ScopedInterfaceEndpointHandle +AssociatedGroupController::CreateScopedInterfaceEndpointHandle(InterfaceId id) { + return ScopedInterfaceEndpointHandle(id, this); } -ScopedInterfaceEndpointHandle -AssociatedGroupController::CreateScopedInterfaceEndpointHandle( - InterfaceId id, - bool is_local) { - return ScopedInterfaceEndpointHandle(id, is_local, this); +bool AssociatedGroupController::NotifyAssociation( + ScopedInterfaceEndpointHandle* handle_to_send, + InterfaceId id) { + return handle_to_send->NotifyAssociation(id, 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 c7f74fb..72f7960 100644 --- a/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h +++ b/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h @@ -9,6 +9,7 @@ #include <algorithm> // For |std::swap()|. #include <memory> +#include <string> #include <utility> #include "base/bind.h" @@ -18,11 +19,10 @@ #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,18 +46,12 @@ class AssociatedInterfacePtrState { 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) { - // 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)); + // 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)); } void RequireVersion(uint32_t version) { @@ -65,9 +59,13 @@ class AssociatedInterfacePtrState { return; version_ = version; - // Do a static cast in case the interface contains methods with the same - // name. - static_cast<ControlMessageProxy*>(proxy_.get())->RequireVersion(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); } void Swap(AssociatedInterfacePtrState* other) { @@ -85,13 +83,13 @@ class AssociatedInterfacePtrState { 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))); + std::move(runner), 0u)); 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 @@ -114,6 +112,12 @@ class AssociatedInterfacePtrState { 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(); @@ -123,6 +127,13 @@ class AssociatedInterfacePtrState { 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.cc b/mojo/public/cpp/bindings/lib/binding_state.cc new file mode 100644 index 0000000..b34cb47 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/binding_state.cc @@ -0,0 +1,90 @@ +// 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/binding_state.h" + +namespace mojo { +namespace internal { + +BindingStateBase::BindingStateBase() = default; + +BindingStateBase::~BindingStateBase() = default; + +void BindingStateBase::AddFilter(std::unique_ptr<MessageReceiver> filter) { + DCHECK(endpoint_client_); + endpoint_client_->AddFilter(std::move(filter)); +} + +bool BindingStateBase::HasAssociatedInterfaces() const { + return router_ ? router_->HasAssociatedEndpoints() : false; +} + +void BindingStateBase::PauseIncomingMethodCallProcessing() { + DCHECK(router_); + router_->PauseIncomingMethodCallProcessing(); +} +void BindingStateBase::ResumeIncomingMethodCallProcessing() { + DCHECK(router_); + router_->ResumeIncomingMethodCallProcessing(); +} + +bool BindingStateBase::WaitForIncomingMethodCall(MojoDeadline deadline) { + DCHECK(router_); + return router_->WaitForIncomingMessage(deadline); +} + +void BindingStateBase::Close() { + if (!router_) + return; + + endpoint_client_.reset(); + router_->CloseMessagePipe(); + router_ = nullptr; +} + +void BindingStateBase::CloseWithReason(uint32_t custom_reason, + const std::string& description) { + if (endpoint_client_) + endpoint_client_->CloseWithReason(custom_reason, description); + + Close(); +} + +void BindingStateBase::FlushForTesting() { + endpoint_client_->FlushForTesting(); +} + +void BindingStateBase::EnableTestingMode() { + DCHECK(is_bound()); + router_->EnableTestingMode(); +} + +void BindingStateBase::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) { + DCHECK(!router_); + + MultiplexRouter::Config config = + passes_associated_kinds + ? MultiplexRouter::MULTI_INTERFACE + : (has_sync_methods + ? MultiplexRouter::SINGLE_INTERFACE_WITH_SYNC_METHODS + : MultiplexRouter::SINGLE_INTERFACE); + router_ = new MultiplexRouter(std::move(handle), config, false, runner); + router_->SetMasterInterfaceName(interface_name); + + endpoint_client_.reset(new InterfaceEndpointClient( + router_->CreateLocalEndpointHandle(kMasterInterfaceId), stub, + std::move(request_validator), has_sync_methods, std::move(runner), + interface_version)); +} + +} // namesapce internal +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/binding_state.h b/mojo/public/cpp/bindings/lib/binding_state.h index c8d3e83..0b0dbee 100644 --- a/mojo/public/cpp/bindings/lib/binding_state.h +++ b/mojo/public/cpp/bindings/lib/binding_state.h @@ -6,6 +6,7 @@ #define MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDING_STATE_H_ #include <memory> +#include <string> #include <utility> #include "base/bind.h" @@ -15,15 +16,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/associated_group.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/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" @@ -31,76 +32,34 @@ namespace mojo { namespace internal { -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> { +class MOJO_CPP_BINDINGS_EXPORT BindingStateBase { public: - explicit BindingState(Interface* impl) : impl_(impl) { - stub_.set_sink(impl_); - } + BindingStateBase(); + ~BindingStateBase(); - ~BindingState() { Close(); } + void AddFilter(std::unique_ptr<MessageReceiver> filter); - void Bind(ScopedMessagePipeHandle handle, - scoped_refptr<base::SingleThreadTaskRunner> runner) { - DCHECK(!router_); - internal::FilterChain filters; - filters.Append<MessageHeaderValidator>(Interface::Name_); - filters.Append<typename Interface::RequestValidator_>(); - - 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; } + bool HasAssociatedInterfaces() const; - void PauseIncomingMethodCallProcessing() { - DCHECK(router_); - router_->PauseIncomingMethodCallProcessing(); - } - void ResumeIncomingMethodCallProcessing() { - DCHECK(router_); - router_->ResumeIncomingMethodCallProcessing(); - } + void PauseIncomingMethodCallProcessing(); + void ResumeIncomingMethodCallProcessing(); bool WaitForIncomingMethodCall( - MojoDeadline deadline = MOJO_DEADLINE_INDEFINITE) { - DCHECK(router_); - return router_->WaitForIncomingMessage(deadline); - } - - void Close() { - if (!router_) - return; - - router_->CloseMessagePipe(); - DestroyRouter(); - } + MojoDeadline deadline = MOJO_DEADLINE_INDEFINITE); - InterfaceRequest<Interface> Unbind() { - InterfaceRequest<Interface> request = - MakeRequest<Interface>(router_->PassMessagePipe()); - DestroyRouter(); - return std::move(request); - } + void Close(); + void CloseWithReason(uint32_t custom_reason, const std::string& description); void set_connection_error_handler(const base::Closure& error_handler) { DCHECK(is_bound()); - connection_error_handler_ = error_handler; + endpoint_client_->set_connection_error_handler(error_handler); } - Interface* impl() { return impl_; } + void set_connection_error_with_reason_handler( + const ConnectionErrorWithReasonCallback& error_handler) { + DCHECK(is_bound()); + endpoint_client_->set_connection_error_with_reason_handler(error_handler); + } bool is_bound() const { return !!router_; } @@ -109,90 +68,42 @@ class BindingState<Interface, false> { return router_->handle(); } - AssociatedGroup* associated_group() { return nullptr; } + void FlushForTesting(); - void EnableTestingMode() { - DCHECK(is_bound()); - router_->EnableTestingMode(); - } + void EnableTestingMode(); - private: - void DestroyRouter() { - router_->set_connection_error_handler(base::Closure()); - delete router_; - router_ = nullptr; - connection_error_handler_.Reset(); - } - - void RunConnectionErrorHandler() { - if (!connection_error_handler_.is_null()) - connection_error_handler_.Run(); - } + 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); - internal::Router* router_ = nullptr; - typename Interface::Stub_ stub_; - Interface* impl_; - base::Closure connection_error_handler_; - - DISALLOW_COPY_AND_ASSIGN(BindingState); + scoped_refptr<internal::MultiplexRouter> router_; + std::unique_ptr<InterfaceEndpointClient> endpoint_client_; }; -// 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> { +template <typename Interface, typename ImplRefTraits> +class BindingState : public BindingStateBase { public: - explicit BindingState(Interface* impl) : impl_(impl) { - stub_.set_sink(impl_); + using ImplPointerType = typename ImplRefTraits::PointerType; + + explicit BindingState(ImplPointerType impl) { + stub_.set_sink(std::move(impl)); } ~BindingState() { Close(); } void Bind(ScopedMessagePipeHandle handle, scoped_refptr<base::SingleThreadTaskRunner> runner) { - 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(); + BindingStateBase::BindInternal( + std::move(handle), runner, Interface::Name_, + base::MakeUnique<typename Interface::RequestValidator_>(), + Interface::PassesAssociatedKinds_, Interface::HasSyncMethods_, &stub_, + Interface::Version_); } InterfaceRequest<Interface> Unbind() { @@ -200,45 +111,13 @@ class BindingState<Interface, true> { InterfaceRequest<Interface> request = MakeRequest<Interface>(router_->PassMessagePipe()); router_ = nullptr; - connection_error_handler_.Reset(); return request; } - 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(); - } + Interface* impl() { return ImplRefTraits::GetRawPointer(&stub_.sink()); } private: - 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_; + typename Interface::template Stub_<ImplRefTraits> stub_; 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 deleted file mode 100644 index a3bdb1f..0000000 --- a/mojo/public/cpp/bindings/lib/bindings_internal.cc +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/public/cpp/bindings/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 b37d872..631daec 100644 --- a/mojo/public/cpp/bindings/lib/bindings_internal.h +++ b/mojo/public/cpp/bindings/lib/bindings_internal.h @@ -17,30 +17,26 @@ namespace mojo { template <typename T> -class Array; +class ArrayDataView; template <typename T> -class AssociatedInterfacePtrInfo; +class AssociatedInterfacePtrInfoDataView; template <typename T> -class AssociatedInterfaceRequest; +class AssociatedInterfaceRequestDataView; template <typename T> -class InterfacePtr; +class InterfacePtrDataView; template <typename T> -class InterfaceRequest; +class InterfaceRequestDataView; template <typename K, typename V> -class Map; +class MapDataView; -class String; +class NativeStructDataView; -template <typename T> -class StructPtr; - -template <typename T> -class InlinedStructPtr; +class StringDataView; namespace internal { @@ -58,12 +54,17 @@ class Array_Data; template <typename K, typename V> class Map_Data; +class NativeStruct_Data; + using String_Data = Array_Data<char>; -size_t Align(size_t size); -char* AlignPointer(char* ptr); +inline size_t Align(size_t size) { + return (size + 7) & ~0x7; +} -bool IsAligned(const void* ptr); +inline bool IsAligned(const void* ptr) { + return !(reinterpret_cast<uintptr_t>(ptr) & 0x7); +} // 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 @@ -73,7 +74,19 @@ bool IsAligned(const void* ptr); // // A null pointer is encoded as an offset value of 0. // -void EncodePointer(const void* ptr, uint64_t* offset); +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); +} + // Note: This function doesn't validate the encoded pointer value. inline const void* DecodePointer(const uint64_t* offset) { if (!*offset) @@ -111,6 +124,8 @@ struct Pointer { }; 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) {} @@ -127,19 +142,24 @@ struct Interface_Data { }; 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 { - InterfaceId interface_id; + AssociatedEndpointHandle_Data handle; 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> @@ -203,7 +223,7 @@ struct MojomTypeTraits { }; template <typename T> -struct MojomTypeTraits<Array<T>, false> { +struct MojomTypeTraits<ArrayDataView<T>, false> { using Data = Array_Data<typename MojomTypeTraits<T>::DataAsArrayElement>; using DataAsArrayElement = Pointer<Data>; @@ -211,7 +231,7 @@ struct MojomTypeTraits<Array<T>, false> { }; template <typename T> -struct MojomTypeTraits<AssociatedInterfacePtrInfo<T>, false> { +struct MojomTypeTraits<AssociatedInterfacePtrInfoDataView<T>, false> { using Data = AssociatedInterface_Data; using DataAsArrayElement = Data; @@ -220,8 +240,8 @@ struct MojomTypeTraits<AssociatedInterfacePtrInfo<T>, false> { }; template <typename T> -struct MojomTypeTraits<AssociatedInterfaceRequest<T>, false> { - using Data = AssociatedInterfaceRequest_Data; +struct MojomTypeTraits<AssociatedInterfaceRequestDataView<T>, false> { + using Data = AssociatedEndpointHandle_Data; using DataAsArrayElement = Data; static const MojomTypeCategory category = @@ -253,7 +273,7 @@ struct MojomTypeTraits<ScopedHandleBase<T>, false> { }; template <typename T> -struct MojomTypeTraits<InterfacePtr<T>, false> { +struct MojomTypeTraits<InterfacePtrDataView<T>, false> { using Data = Interface_Data; using DataAsArrayElement = Data; @@ -261,7 +281,7 @@ struct MojomTypeTraits<InterfacePtr<T>, false> { }; template <typename T> -struct MojomTypeTraits<InterfaceRequest<T>, false> { +struct MojomTypeTraits<InterfaceRequestDataView<T>, false> { using Data = Handle_Data; using DataAsArrayElement = Data; @@ -270,7 +290,7 @@ struct MojomTypeTraits<InterfaceRequest<T>, false> { }; template <typename K, typename V> -struct MojomTypeTraits<Map<K, V>, false> { +struct MojomTypeTraits<MapDataView<K, V>, false> { using Data = Map_Data<typename MojomTypeTraits<K>::DataAsArrayElement, typename MojomTypeTraits<V>::DataAsArrayElement>; using DataAsArrayElement = Pointer<Data>; @@ -279,37 +299,19 @@ struct MojomTypeTraits<Map<K, V>, false> { }; template <> -struct MojomTypeTraits<String, false> { - using Data = String_Data; +struct MojomTypeTraits<NativeStructDataView, false> { + using Data = internal::NativeStruct_Data; using DataAsArrayElement = Pointer<Data>; - static const MojomTypeCategory category = MojomTypeCategory::STRING; + static const MojomTypeCategory category = MojomTypeCategory::STRUCT; }; -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 <> +struct MojomTypeTraits<StringDataView, false> { + using Data = String_Data; + using DataAsArrayElement = Pointer<Data>; -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; + static const MojomTypeCategory category = MojomTypeCategory::STRING; }; template <typename T, MojomTypeCategory categories> diff --git a/mojo/public/cpp/bindings/lib/buffer.h b/mojo/public/cpp/bindings/lib/buffer.h index c3b570e..213a445 100644 --- a/mojo/public/cpp/bindings/lib/buffer.h +++ b/mojo/public/cpp/bindings/lib/buffer.h @@ -7,15 +7,61 @@ #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 a way to allocate memory. Allocations are 8-byte aligned and -// zero-initialized. Allocations remain valid for the lifetime of the Buffer. +// 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. class Buffer { public: - virtual ~Buffer() {} - virtual void* Allocate(size_t num_bytes) = 0; + 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); }; } // namespace internal diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc index 1bb38f0..4426def 100644 --- a/mojo/public/cpp/bindings/lib/connector.cc +++ b/mojo/public/cpp/bindings/lib/connector.cc @@ -12,52 +12,20 @@ #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. @@ -65,25 +33,34 @@ Connector::Connector(ScopedMessagePipeHandle message_pipe, } Connector::~Connector() { - DCHECK(thread_checker_.CalledOnValidThread()); + { + // 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() { - DCHECK(thread_checker_.CalledOnValidThread()); - - CancelWait(); - MayAutoLock locker(lock_.get()); - message_pipe_.reset(); + // Throw away the returned message pipe. + PassMessagePipe(); } ScopedMessagePipeHandle Connector::PassMessagePipe() { DCHECK(thread_checker_.CalledOnValidThread()); CancelWait(); - MayAutoLock locker(lock_.get()); - return std::move(message_pipe_); + 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; } void Connector::RaiseError() { @@ -143,7 +120,7 @@ bool Connector::Accept(Message* message) { if (error_) return false; - MayAutoLock locker(lock_.get()); + internal::MayAutoLock locker(&lock_); if (!message_pipe_.is_valid() || drop_writes_) return true; @@ -204,6 +181,13 @@ bool Connector::SyncWatch(const bool* should_stop) { 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); } @@ -214,8 +198,10 @@ void Connector::OnSyncHandleWatcherHandleReady(MojoResult result) { sync_handle_watcher_callback_count_++; OnHandleReadyInternal(result); // At this point, this object might have been deleted. - if (weak_self) + if (weak_self) { + DCHECK_LT(0u, sync_handle_watcher_callback_count_); sync_handle_watcher_callback_count_--; + } } void Connector::OnHandleReadyInternal(MojoResult result) { @@ -231,12 +217,14 @@ void Connector::OnHandleReadyInternal(MojoResult result) { void Connector::WaitToReadMore() { CHECK(!paused_); - DCHECK(!handle_watcher_.IsWatching()); + DCHECK(!handle_watcher_); - MojoResult rv = handle_watcher_.Start( + 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( 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 @@ -257,8 +245,8 @@ bool Connector::ReadSingleMessage(MojoResult* read_result) { bool receiver_result = false; - // Detect if |this| was destroyed during message dispatch. Allow for the - // possibility of re-entering ReadMore() through message dispatch. + // Detect if |this| was destroyed or the message pipe was closed/transferred + // during message dispatch. base::WeakPtr<Connector> weak_self = weak_self_; Message message; @@ -292,9 +280,11 @@ void Connector::ReadAllAvailableMessages() { while (!error_) { MojoResult rv; - // Return immediately if |this| was destroyed. Do not touch any members! - if (!ReadSingleMessage(&rv)) + if (!ReadSingleMessage(&rv)) { + // Return immediately without touching any members. |this| may have been + // destroyed. return; + } if (paused_) return; @@ -305,7 +295,7 @@ void Connector::ReadAllAvailableMessages() { } void Connector::CancelWait() { - handle_watcher_.Cancel(); + handle_watcher_.reset(); sync_watcher_.reset(); } @@ -325,7 +315,7 @@ void Connector::HandleError(bool force_pipe_reset, bool force_async_handler) { if (force_pipe_reset) { CancelWait(); - MayAutoLock locker(lock_.get()); + internal::MayAutoLock locker(&lock_); 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 9f44e88..c90aada 100644 --- a/mojo/public/cpp/bindings/lib/control_message_handler.cc +++ b/mojo/public/cpp/bindings/lib/control_message_handler.cc @@ -11,15 +11,53 @@ #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 == kRunMessageId || - message->header()->name == kRunOrClosePipeMessageId; + return message->header()->name == interface_control::kRunMessageId || + message->header()->name == interface_control::kRunOrClosePipeMessageId; } ControlMessageHandler::ControlMessageHandler(uint32_t interface_version) @@ -30,7 +68,10 @@ ControlMessageHandler::~ControlMessageHandler() { } bool ControlMessageHandler::Accept(Message* message) { - if (message->header()->name == kRunOrClosePipeMessageId) + if (!ValidateControlRequestWithoutResponse(message)) + return false; + + if (message->header()->name == interface_control::kRunOrClosePipeMessageId) return RunOrClosePipe(message); NOTREACHED(); @@ -40,7 +81,10 @@ bool ControlMessageHandler::Accept(Message* message) { bool ControlMessageHandler::AcceptWithResponder( Message* message, MessageReceiverWithStatus* responder) { - if (message->header()->name == kRunMessageId) + if (!ValidateControlRequestWithResponse(message)) + return false; + + if (message->header()->name == interface_control::kRunMessageId) return Run(message, responder); NOTREACHED(); @@ -49,20 +93,37 @@ bool ControlMessageHandler::AcceptWithResponder( bool ControlMessageHandler::Run(Message* message, MessageReceiverWithStatus* responder) { - 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_; - - size_t size = PrepareToSerialize<RunResponseMessageParamsPtr>( - response_params_ptr, &context_); - ResponseMessageBuilder builder(kRunMessageId, size, message->request_id()); - - RunResponseMessageParams_Data* response_params = nullptr; - Serialize<RunResponseMessageParamsPtr>(response_params_ptr, builder.buffer(), - &response_params, &context_); + 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(); + } + + 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()); + + interface_control::internal::RunResponseMessageParams_Data* response_params = + nullptr; + Serialize<interface_control::RunResponseMessageParamsDataView>( + response_params_ptr, builder.buffer(), &response_params, &context_); bool ok = responder->Accept(builder.message()); ALLOW_UNUSED_LOCAL(ok); delete responder; @@ -71,13 +132,18 @@ bool ControlMessageHandler::Run(Message* message, } bool ControlMessageHandler::RunOrClosePipe(Message* message) { - RunOrClosePipeMessageParams_Data* params = - reinterpret_cast<RunOrClosePipeMessageParams_Data*>( + interface_control::internal::RunOrClosePipeMessageParams_Data* params = + reinterpret_cast< + interface_control::internal::RunOrClosePipeMessageParams_Data*>( message->mutable_payload()); - RunOrClosePipeMessageParamsPtr params_ptr; - Deserialize<RunOrClosePipeMessageParamsPtr>(params, ¶ms_ptr, &context_); + 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; - return interface_version_ >= params_ptr->require_version->version; + return false; } } // 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 13b5aa6..3c385e4 100644 --- a/mojo/public/cpp/bindings/lib/control_message_handler.h +++ b/mojo/public/cpp/bindings/lib/control_message_handler.h @@ -7,7 +7,9 @@ #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" @@ -15,7 +17,8 @@ namespace mojo { namespace internal { // Handlers for request messages defined in interface_control_messages.mojom. -class ControlMessageHandler : public MessageReceiverWithResponderStatus { +class MOJO_CPP_BINDINGS_EXPORT ControlMessageHandler + : NON_EXPORTED_BASE(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 7af409d..23de991 100644 --- a/mojo/public/cpp/bindings/lib/control_message_proxy.cc +++ b/mojo/public/cpp/bindings/lib/control_message_proxy.cc @@ -9,9 +9,12 @@ #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" @@ -20,11 +23,28 @@ namespace internal { namespace { -using RunCallback = base::Callback<void(QueryVersionResultPtr)>; +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)>; class RunResponseForwardToCallback : public MessageReceiver { public: - RunResponseForwardToCallback(const RunCallback& callback) + explicit RunResponseForwardToCallback(const RunCallback& callback) : callback_(callback) {} bool Accept(Message* message) override; @@ -34,59 +54,83 @@ class RunResponseForwardToCallback : public MessageReceiver { }; bool RunResponseForwardToCallback::Accept(Message* message) { - RunResponseMessageParams_Data* params = - reinterpret_cast<RunResponseMessageParams_Data*>( + if (!ValidateControlResponse(message)) + return false; + + interface_control::internal::RunResponseMessageParams_Data* params = + reinterpret_cast< + interface_control::internal::RunResponseMessageParams_Data*>( message->mutable_payload()); - RunResponseMessageParamsPtr params_ptr; + interface_control::RunResponseMessageParamsPtr params_ptr; SerializationContext context; - Deserialize<RunResponseMessageParamsPtr>(params, ¶ms_ptr, &context); + Deserialize<interface_control::RunResponseMessageParamsDataView>( + params, ¶ms_ptr, &context); - callback_.Run(std::move(params_ptr->query_version_result)); + callback_.Run(std::move(params_ptr)); return true; } void SendRunMessage(MessageReceiverWithResponder* receiver, - 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); - - size_t size = PrepareToSerialize<RunMessageParamsPtr>(params_ptr, context); - RequestMessageBuilder builder(kRunMessageId, size); - - RunMessageParams_Data* params = nullptr; - Serialize<RunMessageParamsPtr>(params_ptr, builder.buffer(), ¶ms, - context); + interface_control::RunInputPtr input_ptr, + const RunCallback& callback) { + SerializationContext context; + + 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); + + interface_control::internal::RunMessageParams_Data* params = nullptr; + Serialize<interface_control::RunMessageParamsDataView>( + params_ptr, builder.buffer(), ¶ms, &context); MessageReceiver* responder = new RunResponseForwardToCallback(callback); if (!receiver->AcceptWithResponder(builder.message(), responder)) delete responder; } -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); - - size_t size = - PrepareToSerialize<RunOrClosePipeMessageParamsPtr>(params_ptr, context); - MessageBuilder builder(kRunOrClosePipeMessageId, size); - - RunOrClosePipeMessageParams_Data* params = nullptr; - Serialize<RunOrClosePipeMessageParamsPtr>(params_ptr, builder.buffer(), - ¶ms, context); - bool ok = receiver->Accept(builder.message()); +Message ConstructRunOrClosePipeMessage( + interface_control::RunOrClosePipeInputPtr input_ptr) { + SerializationContext context; + + auto params_ptr = interface_control::RunOrClosePipeMessageParams::New(); + params_ptr->input = std::move(input_ptr); + + 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); ALLOW_UNUSED_LOCAL(ok); } -void RunVersionCallback(const base::Callback<void(uint32_t)>& callback, - QueryVersionResultPtr query_version_result) { - callback.Run(query_version_result->version); +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(); } } // namespace @@ -95,16 +139,49 @@ ControlMessageProxy::ControlMessageProxy(MessageReceiverWithResponder* receiver) : receiver_(receiver) { } +ControlMessageProxy::~ControlMessageProxy() = default; + void ControlMessageProxy::QueryVersion( const base::Callback<void(uint32_t)>& callback) { - SendRunMessage(receiver_, QueryVersion::New(), - base::Bind(&RunVersionCallback, callback), &context_); + 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)); } void ControlMessageProxy::RequireVersion(uint32_t version) { - RequireVersionPtr require_version(RequireVersion::New()); + auto require_version = interface_control::RequireVersion::New(); require_version->version = version; - SendRunOrClosePipeMessage(receiver_, std::move(require_version), &context_); + 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(); } } // 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 5ec6ddc..2f9314e 100644 --- a/mojo/public/cpp/bindings/lib/control_message_proxy.h +++ b/mojo/public/cpp/bindings/lib/control_message_proxy.h @@ -7,8 +7,9 @@ #include <stdint.h> -#include "base/callback_forward.h" +#include "base/callback.h" #include "base/macros.h" +#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/lib/serialization_context.h" namespace mojo { @@ -18,18 +19,26 @@ class MessageReceiverWithResponder; namespace internal { // Proxy for request messages defined in interface_control_messages.mojom. -class ControlMessageProxy { +class MOJO_CPP_BINDINGS_EXPORT 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); - protected: + void FlushForTesting(); + void OnConnectionError(); + + private: + void RunFlushForTestingClosure(); + // Not owned. MessageReceiverWithResponder* receiver_; - SerializationContext context_; + bool encountered_error_ = false; + + base::Closure run_loop_quit_closure_; DISALLOW_COPY_AND_ASSIGN(ControlMessageProxy); }; diff --git a/mojo/public/cpp/bindings/lib/clone_equals_util.h b/mojo/public/cpp/bindings/lib/equals_traits.h index f7bd898..53c7dce 100644 --- a/mojo/public/cpp/bindings/lib/clone_equals_util.h +++ b/mojo/public/cpp/bindings/lib/equals_traits.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_PUBLIC_CPP_BINDINGS_LIB_CLONE_EQUALS_UTIL_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CLONE_EQUALS_UTIL_H_ +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_EQUALS_TRAITS_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_EQUALS_TRAITS_H_ #include <type_traits> #include <unordered_map> @@ -16,73 +16,6 @@ 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)); @@ -158,4 +91,4 @@ bool Equals(const T& a, const T& b) { } // namespace internal } // namespace mojo -#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_CLONE_EQUALS_UTIL_H_ +#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 899bac1..5d919fe 100644 --- a/mojo/public/cpp/bindings/lib/filter_chain.cc +++ b/mojo/public/cpp/bindings/lib/filter_chain.cc @@ -2,14 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/public/cpp/bindings/lib/filter_chain.h" +#include "mojo/public/cpp/bindings/filter_chain.h" #include <algorithm> #include "base/logging.h" namespace mojo { -namespace internal { FilterChain::FilterChain(MessageReceiver* sink) : sink_(sink) { } @@ -26,24 +25,23 @@ FilterChain& FilterChain::operator=(FilterChain&& other) { } 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); } -MessageReceiver* FilterChain::GetHead() { +bool FilterChain::Accept(Message* message) { DCHECK(sink_); - return filters_.empty() ? sink_ : filters_.front(); + for (auto& filter : filters_) + if (!filter->Accept(message)) + return false; + return sink_->Accept(message); +} + +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/fixed_buffer.cc b/mojo/public/cpp/bindings/lib/fixed_buffer.cc index 50b8a21..725a193 100644 --- a/mojo/public/cpp/bindings/lib/fixed_buffer.cc +++ b/mojo/public/cpp/bindings/lib/fixed_buffer.cc @@ -4,56 +4,25 @@ #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. - ptr_ = static_cast<char*>(calloc(size_, 1)); + void* ptr = calloc(size, 1); + Initialize(ptr, size); } FixedBufferForTesting::~FixedBufferForTesting() { - free(ptr_); + free(data()); } void* FixedBufferForTesting::Leak() { - char* ptr = ptr_; - ptr_ = nullptr; - cursor_ = 0; - size_ = 0; + void* ptr = data(); + Initialize(nullptr, 0); return ptr; } diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.h b/mojo/public/cpp/bindings/lib/fixed_buffer.h index 9a5704b..070b0c8 100644 --- a/mojo/public/cpp/bindings/lib/fixed_buffer.h +++ b/mojo/public/cpp/bindings/lib/fixed_buffer.h @@ -7,62 +7,21 @@ #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 { -// 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 { +// 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) { public: explicit FixedBufferForTesting(size_t size); - ~FixedBufferForTesting() override; + ~FixedBufferForTesting(); // 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 344c2ca..14ed21f 100644 --- a/mojo/public/cpp/bindings/lib/handle_interface_serialization.h +++ b/mojo/public/cpp/bindings/lib/handle_interface_serialization.h @@ -5,9 +5,12 @@ #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" @@ -18,52 +21,98 @@ namespace mojo { namespace internal { -template <typename T> -struct Serializer<AssociatedInterfacePtrInfo<T>, +template <typename Base, typename T> +struct Serializer<AssociatedInterfacePtrInfoDataView<Base>, 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().is_local()); - DCHECK_EQ(input.handle().group_controller(), - context->group_controller.get()); + 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; + } output->version = input.version(); - output->interface_id = input.PassHandle().release(); } static bool Deserialize(AssociatedInterface_Data* input, AssociatedInterfacePtrInfo<T>* output, SerializationContext* context) { - output->set_handle(context->group_controller->CreateLocalEndpointHandle( - FetchAndReset(&input->interface_id))); + 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_version(input->version); return true; } }; -template <typename T> -struct Serializer<AssociatedInterfaceRequest<T>, +template <typename Base, typename T> +struct Serializer<AssociatedInterfaceRequestDataView<Base>, 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, - AssociatedInterfaceRequest_Data* output, + AssociatedEndpointHandle_Data* output, SerializationContext* context) { - 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(); + 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; + } } - static bool Deserialize(AssociatedInterfaceRequest_Data* input, + static bool Deserialize(AssociatedEndpointHandle_Data* input, AssociatedInterfaceRequest<T>* output, SerializationContext* context) { - output->Bind(context->group_controller->CreateLocalEndpointHandle( - FetchAndReset(&input->interface_id))); + 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()); + } return true; } }; -template <typename T> -struct Serializer<InterfacePtr<T>, InterfacePtr<T>> { +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; + } + static void Serialize(InterfacePtr<T>& input, Interface_Data* output, SerializationContext* context) { @@ -82,8 +131,15 @@ struct Serializer<InterfacePtr<T>, InterfacePtr<T>> { } }; -template <typename T> -struct Serializer<InterfaceRequest<T>, InterfaceRequest<T>> { +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; + } + static void Serialize(InterfaceRequest<T>& input, Handle_Data* output, SerializationContext* context) { @@ -100,6 +156,11 @@ struct Serializer<InterfaceRequest<T>, InterfaceRequest<T>> { 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 new file mode 100644 index 0000000..93280d6 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/hash_util.h @@ -0,0 +1,84 @@ +// 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 e1f388a..3eca5a1 100644 --- a/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc +++ b/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc @@ -10,6 +10,7 @@ #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" @@ -17,6 +18,7 @@ #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 { @@ -45,7 +47,7 @@ class ResponderThunk : public MessageReceiverWithStatus { task_runner_(std::move(runner)) {} ~ResponderThunk() override { if (!accept_was_invoked_) { - // The Mojo application handled a message that was expecting a response + // The Service 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 @@ -132,47 +134,47 @@ bool InterfaceEndpointClient::HandleIncomingMessageThunk::Accept( InterfaceEndpointClient::InterfaceEndpointClient( ScopedInterfaceEndpointHandle handle, MessageReceiverWithResponderStatus* receiver, - std::unique_ptr<MessageFilter> payload_validator, + std::unique_ptr<MessageReceiver> payload_validator, bool expect_sync_requests, - scoped_refptr<base::SingleThreadTaskRunner> runner) - : handle_(std::move(handle)), + scoped_refptr<base::SingleThreadTaskRunner> runner, + uint32_t interface_version) + : expect_sync_requests_(expect_sync_requests), + handle_(std::move(handle)), incoming_receiver_(receiver), - payload_validator_(std::move(payload_validator)), thunk_(this), - next_request_id_(1), - encountered_error_(false), + filters_(&thunk_), 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. - payload_validator_->set_sink(&thunk_); + if (payload_validator) + filters_.Append(std::move(payload_validator)); - controller_ = handle_.group_controller()->AttachEndpointClient( - handle_, this, task_runner_); - if (expect_sync_requests) - controller_->AllowWokenUpBySyncWatchOnSameThread(); + if (handle_.pending_association()) { + handle_.SetAssociationEventHandler(base::Bind( + &InterfaceEndpointClient::OnAssociationEvent, base::Unretained(this))); + } else { + InitControllerIfNecessary(); + } } InterfaceEndpointClient::~InterfaceEndpointClient() { DCHECK(thread_checker_.CalledOnValidThread()); - handle_.group_controller()->DetachEndpointClient(handle_); + if (controller_) + handle_.group_controller()->DetachEndpointClient(handle_); } AssociatedGroup* InterfaceEndpointClient::associated_group() { if (!associated_group_) - associated_group_ = handle_.group_controller()->CreateAssociatedGroup(); + associated_group_ = base::MakeUnique<AssociatedGroup>(handle_); 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()); @@ -180,38 +182,73 @@ ScopedInterfaceEndpointHandle InterfaceEndpointClient::PassHandle() { if (!handle_.is_valid()) return ScopedInterfaceEndpointHandle(); - controller_ = nullptr; - handle_.group_controller()->DetachEndpointClient(handle_); + handle_.SetAssociationEventHandler( + ScopedInterfaceEndpointHandle::AssociationEventCallback()); + + if (controller_) { + 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()); - handle_.group_controller()->RaiseError(); + 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); } 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) @@ -234,20 +271,18 @@ bool InterfaceEndpointClient::AcceptWithResponder(Message* message, 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)))); + request_id, base::MakeUnique<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(ContainsKey(sync_responses_, request_id)); + DCHECK(base::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())); - } + if (response_received) + ignore_result(sync_responder->Accept(&iter->second->response)); sync_responses_.erase(iter); } @@ -257,30 +292,97 @@ bool InterfaceEndpointClient::AcceptWithResponder(Message* message, bool InterfaceEndpointClient::HandleIncomingMessage(Message* message) { DCHECK(thread_checker_.CalledOnValidThread()); - - return payload_validator_->Accept(message); + return filters_.Accept(message); } -void InterfaceEndpointClient::NotifyError() { +void InterfaceEndpointClient::NotifyError( + const base::Optional<DisconnectReason>& reason) { DCHECK(thread_checker_.CalledOnValidThread()); if (encountered_error_) return; encountered_error_ = true; - if (!error_handler_.is_null()) - error_handler_.Run(); + + // 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())); + } } bool InterfaceEndpointClient::HandleValidatedMessage(Message* message) { DCHECK_EQ(handle_.id(), message->interface_id()); - if (message->has_flag(Message::kFlagExpectsResponse)) { - if (!incoming_receiver_) - return false; + 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)) { MessageReceiverWithStatus* responder = new ResponderThunk(weak_ptr_factory_.GetWeakPtr(), task_runner_); - bool ok = incoming_receiver_->AcceptWithResponder(message, responder); + bool ok = false; + if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) { + ok = control_message_handler_.AcceptWithResponder(message, responder); + } else { + ok = incoming_receiver_->AcceptWithResponder(message, responder); + } if (!ok) delete responder; return ok; @@ -291,8 +393,7 @@ bool InterfaceEndpointClient::HandleValidatedMessage(Message* message) { auto it = sync_responses_.find(request_id); if (it == sync_responses_.end()) return false; - it->second->response.reset(new Message()); - message->MoveTo(it->second->response.get()); + it->second->response = std::move(*message); *it->second->response_received = true; return true; } @@ -304,8 +405,8 @@ bool InterfaceEndpointClient::HandleValidatedMessage(Message* message) { async_responders_.erase(it); return responder->Accept(message); } else { - if (!incoming_receiver_) - return false; + if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) + return control_message_handler_.Accept(message); 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 584933e..8f5b4ff 100644 --- a/mojo/public/cpp/bindings/lib/interface_ptr_state.h +++ b/mojo/public/cpp/bindings/lib/interface_ptr_state.h @@ -9,6 +9,7 @@ #include <algorithm> // For |std::swap()|. #include <memory> +#include <string> #include <utility> #include "base/bind.h" @@ -19,174 +20,20 @@ #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<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> { +class InterfacePtrState { public: InterfacePtrState() : version_(0u) {} @@ -209,13 +56,10 @@ class InterfacePtrState<Interface, true> { 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_.get())->QueryVersion( - base::Bind(&InterfacePtrState::OnQueryVersion, base::Unretained(this), - callback)); + // 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)); } void RequireVersion(uint32_t version) { @@ -225,9 +69,17 @@ class InterfacePtrState<Interface, true> { return; version_ = version; - // Do a static cast in case the interface contains methods with the same - // name. - static_cast<ControlMessageProxy*>(proxy_.get())->RequireVersion(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); } void Swap(InterfacePtrState* other) { @@ -280,6 +132,14 @@ class InterfacePtrState<Interface, true> { 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(); @@ -295,6 +155,17 @@ class InterfacePtrState<Interface, true> { 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_; @@ -309,15 +180,22 @@ class InterfacePtrState<Interface, true> { if (!handle_.is_valid()) return; - router_ = new MultiplexRouter(true, std::move(handle_), runner_); + 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_->SetMasterInterfaceName(Interface::Name_); endpoint_client_.reset(new InterfaceEndpointClient( router_->CreateLocalEndpointHandle(kMasterInterfaceId), nullptr, base::WrapUnique(new typename Interface::ResponseValidator_()), false, - std::move(runner_))); + std::move(runner_), + // The version is only queried from the client so the value passed here + // will not be used. + 0u)); 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 c28b835..718a763 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.h" +#include "mojo/public/cpp/bindings/array_data_view.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.h" +#include "mojo/public/cpp/bindings/map_data_view.h" namespace mojo { namespace internal { @@ -46,12 +46,15 @@ class MapKeyReader : public MapReaderBase<MaybeConstUserType> { public: using Base = MapReaderBase<MaybeConstUserType>; using Traits = typename Base::Traits; + using MaybeConstIterator = typename Base::MaybeConstIterator; explicit MapKeyReader(MaybeConstUserType& input) : Base(input) {} ~MapKeyReader() {} - const typename Traits::Key& GetNext() { - const typename Traits::Key& key = Traits::GetKey(this->iter_); + using GetNextResult = + decltype(Traits::GetKey(std::declval<MaybeConstIterator&>())); + GetNextResult GetNext() { + GetNextResult key = Traits::GetKey(this->iter_); Traits::AdvanceIterator(this->iter_); return key; } @@ -78,17 +81,17 @@ class MapValueReader : public MapReaderBase<MaybeConstUserType> { }; template <typename Key, typename Value, typename MaybeConstUserType> -struct Serializer<Map<Key, Value>, MaybeConstUserType> { +struct Serializer<MapDataView<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<Map<Key, Value>>::Data; - using KeyArraySerializer = ArraySerializer<Array<Key>, + using Data = typename MojomTypeTraits<MapDataView<Key, Value>>::Data; + using KeyArraySerializer = ArraySerializer<ArrayDataView<Key>, std::vector<UserKey>, MapKeyReader<MaybeConstUserType>>; using ValueArraySerializer = - ArraySerializer<Array<Value>, + ArraySerializer<ArrayDataView<Value>, std::vector<UserValue>, MapValueReader<MaybeConstUserType>>; @@ -122,8 +125,8 @@ struct Serializer<Map<Key, Value>, MaybeConstUserType> { auto result = Data::New(buf); if (result) { - auto keys_ptr = - MojomTypeTraits<Array<Key>>::Data::New(Traits::GetSize(input), buf); + auto keys_ptr = MojomTypeTraits<ArrayDataView<Key>>::Data::New( + Traits::GetSize(input), buf); if (keys_ptr) { MapKeyReader<MaybeConstUserType> key_reader(input); KeyArraySerializer::SerializeElements( @@ -132,8 +135,8 @@ struct Serializer<Map<Key, Value>, MaybeConstUserType> { result->keys.Set(keys_ptr); } - auto values_ptr = - MojomTypeTraits<Array<Value>>::Data::New(Traits::GetSize(input), buf); + auto values_ptr = MojomTypeTraits<ArrayDataView<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 new file mode 100644 index 0000000..06091fe --- /dev/null +++ b/mojo/public/cpp/bindings/lib/may_auto_lock.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_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 939e064..e5f3808 100644 --- a/mojo/public/cpp/bindings/lib/message.cc +++ b/mojo/public/cpp/bindings/lib/message.cc @@ -11,18 +11,58 @@ #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)); @@ -36,19 +76,52 @@ void Message::InitializeFromMojoMessage(ScopedMessageHandle message, handles_.swap(*handles); } -void Message::MoveTo(Message* destination) { - DCHECK(this != destination); +const uint8_t* Message::payload() const { + if (version() < 2) + return data() + header()->num_bytes; - // No copy needed. - std::swap(destination->buffer_, buffer_); - std::swap(destination->handles_, handles_); + return static_cast<const uint8_t*>(header_v2()->payload.Get()); +} - CloseHandles(); - handles_.clear(); - buffer_.reset(); +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; } 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(); @@ -80,6 +153,7 @@ ScopedMessageHandle Message::TakeMojoMessage() { } void Message::NotifyBadMessage(const std::string& error) { + DCHECK(buffer_); buffer_->NotifyBadMessage(error); } @@ -91,6 +165,88 @@ void Message::CloseHandles() { } } +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; @@ -122,4 +278,55 @@ MojoResult ReadMessage(MessagePipeHandle handle, Message* message) { 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 af79cfd..cc12ef6 100644 --- a/mojo/public/cpp/bindings/lib/message_buffer.cc +++ b/mojo/public/cpp/bindings/lib/message_buffer.cc @@ -13,54 +13,35 @@ namespace internal { 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); - if (capacity == 0) { - buffer_ = nullptr; - } else { - rv = GetMessageBuffer(message_.get(), &buffer_); + void* buffer = nullptr; + if (capacity != 0) { + 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; - if (num_bytes == 0) { - buffer_ = nullptr; - } else { - MojoResult rv = GetMessageBuffer(message_.get(), &buffer_); + void* buffer = nullptr; + if (num_bytes != 0) { + 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 0382131..96d5140 100644 --- a/mojo/public/cpp/bindings/lib/message_buffer.h +++ b/mojo/public/cpp/bindings/lib/message_buffer.h @@ -5,7 +5,6 @@ #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> @@ -17,7 +16,7 @@ namespace mojo { namespace internal { -// A fixed-size Buffer implementation using a Mojo message object for storage. +// A fixed-size Buffer using a Mojo message object for storage. class MessageBuffer : public Buffer { public: // Initializes this buffer to carry a fixed byte capacity and no handles. @@ -26,24 +25,14 @@ class MessageBuffer : public Buffer { // Initializes this buffer from an existing Mojo MessageHandle. MessageBuffer(ScopedMessageHandle message, uint32_t num_bytes); - ~MessageBuffer() override; - - void* data() const { return buffer_; } - uint32_t data_num_bytes() const { return data_num_bytes_; } - - // Buffer: - void* Allocate(size_t delta) override; + ~MessageBuffer(); 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 4ffa180..6806a73 100644 --- a/mojo/public/cpp/bindings/lib/message_builder.cc +++ b/mojo/public/cpp/bindings/lib/message_builder.cc @@ -4,11 +4,10 @@ #include "mojo/public/cpp/bindings/lib/message_builder.h" -#include <stddef.h> -#include <stdint.h> - -#include "mojo/public/cpp/bindings/lib/serialization_util.h" -#include "mojo/public/cpp/bindings/message.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" namespace mojo { namespace internal { @@ -19,38 +18,52 @@ void Allocate(Buffer* buf, Header** header) { (*header)->num_bytes = sizeof(Header); } -MessageBuilder::MessageBuilder(uint32_t name, size_t payload_size) { - InitializeMessage(sizeof(MessageHeader) + payload_size); +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))); + + 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; + MessageHeader* header; + Allocate(message_.buffer(), &header); + header->version = 0; + header->name = name; + header->flags = flags; + } } 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 a5a050f..8a4d5c4 100644 --- a/mojo/public/cpp/bindings/lib/message_builder.h +++ b/mojo/public/cpp/bindings/lib/message_builder.h @@ -8,24 +8,30 @@ #include <stddef.h> #include <stdint.h> -#include "mojo/public/cpp/bindings/lib/message_internal.h" +#include "base/macros.h" +#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/message.h" namespace mojo { + class Message; namespace internal { -class MessageBuilder { +class Buffer; + +class MOJO_CPP_BINDINGS_EXPORT MessageBuilder { public: - MessageBuilder(uint32_t name, size_t payload_size); + MessageBuilder(uint32_t name, + uint32_t flags, + size_t payload_size, + size_t payload_interface_id_count); ~MessageBuilder(); Buffer* buffer() { return message_.buffer(); } Message* message() { return &message_; } - protected: - MessageBuilder(); + private: void InitializeMessage(size_t size); Message message_; @@ -33,51 +39,6 @@ class MessageBuilder { 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 deleted file mode 100644 index b09f40d..0000000 --- a/mojo/public/cpp/bindings/lib/message_filter.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/public/cpp/bindings/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 10f7774..9f8c627 100644 --- a/mojo/public/cpp/bindings/lib/message_header_validator.cc +++ b/mojo/public/cpp/bindings/lib/message_header_validator.cc @@ -4,6 +4,8 @@ #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" @@ -11,40 +13,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: - if (header->version == 0) { - if (header->num_bytes != sizeof(internal::MessageHeader)) { - internal::ReportValidationError( - validation_context, - internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); - return false; + 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; } - } 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; - } - } + internal::ReportValidationError( + validation_context, + internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; + } while (false); // Validate flags (allow unknown bits): // These flags require a RequestID. - if (header->version < 1 && ((header->flags & Message::kFlagExpectsResponse) || - (header->flags & Message::kFlagIsResponse))) { + constexpr uint32_t kRequestIdFlags = + Message::kFlagExpectsResponse | Message::kFlagIsResponse; + if (header->version == 0 && (header->flags & kRequestIdFlags)) { internal::ReportValidationError( validation_context, internal::VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID); @@ -52,25 +54,60 @@ bool IsValidMessageHeader(const internal::MessageHeader* header, } // These flags are mutually exclusive. - if ((header->flags & Message::kFlagExpectsResponse) && - (header->flags & Message::kFlagIsResponse)) { + if ((header->flags & kRequestIdFlags) == kRequestIdFlags) { 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(MessageReceiver* sink) - : MessageHeaderValidator("MessageHeaderValidator", sink) {} +MessageHeaderValidator::MessageHeaderValidator() + : MessageHeaderValidator("MessageHeaderValidator") {} -MessageHeaderValidator::MessageHeaderValidator(const std::string& description, - MessageReceiver* sink) - : MessageFilter(sink), description_(description) { +MessageHeaderValidator::MessageHeaderValidator(const std::string& description) + : description_(description) { } void MessageHeaderValidator::SetDescription(const std::string& description) { @@ -78,10 +115,10 @@ void MessageHeaderValidator::SetDescription(const std::string& description) { } bool MessageHeaderValidator::Accept(Message* message) { - // Pass 0 as number of handles because we don't expect any in the header, even - // if |message| contains handles. + // Pass 0 as number of handles and associated endpoint 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, message, description_); + message->data(), message->data_num_bytes(), 0, 0, message, description_); if (!internal::ValidateStructHeaderAndClaimMemory(message->data(), &validation_context)) @@ -90,7 +127,7 @@ bool MessageHeaderValidator::Accept(Message* message) { if (!IsValidMessageHeader(message->header(), &validation_context)) return false; - return sink_->Accept(message); + return true; } } // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/message_internal.h b/mojo/public/cpp/bindings/lib/message_internal.h index 63edffd..6693198 100644 --- a/mojo/public/cpp/bindings/lib/message_internal.h +++ b/mojo/public/cpp/bindings/lib/message_internal.h @@ -7,11 +7,22 @@ #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 { @@ -27,16 +38,44 @@ struct MessageHeader : internal::StructHeader { }; static_assert(sizeof(MessageHeader) == 24, "Bad sizeof(MessageHeader)"); -struct MessageHeaderWithRequestID : MessageHeader { +struct MessageHeaderV1 : 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(MessageHeaderWithRequestID) == 32, - "Bad sizeof(MessageHeaderWithRequestID)"); +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)"); #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 dcfbab1..2da459a 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,6 +36,7 @@ class MultiplexRouter::InterfaceEndpoint id_(id), closed_(false), peer_closed_(false), + handle_created_(false), client_(nullptr), event_signalled_(false) {} @@ -50,16 +51,31 @@ class MultiplexRouter::InterfaceEndpoint bool closed() const { return closed_; } void set_closed() { - router_->lock_.AssertAcquired(); + router_->AssertLockAcquired(); closed_ = true; } bool peer_closed() const { return peer_closed_; } void set_peer_closed() { - router_->lock_.AssertAcquired(); + router_->AssertLockAcquired(); 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(); } @@ -68,7 +84,7 @@ class MultiplexRouter::InterfaceEndpoint void AttachClient(InterfaceEndpointClient* client, scoped_refptr<base::SingleThreadTaskRunner> runner) { - router_->lock_.AssertAcquired(); + router_->AssertLockAcquired(); DCHECK(!client_); DCHECK(!closed_); DCHECK(runner->BelongsToCurrentThread()); @@ -80,7 +96,7 @@ class MultiplexRouter::InterfaceEndpoint // This method must be called on the same thread as the corresponding // AttachClient() call. void DetachClient() { - router_->lock_.AssertAcquired(); + router_->AssertLockAcquired(); DCHECK(client_); DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(!closed_); @@ -91,18 +107,36 @@ class MultiplexRouter::InterfaceEndpoint } void SignalSyncMessageEvent() { - router_->lock_.AssertAcquired(); + router_->AssertLockAcquired(); 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 @@ -132,7 +166,7 @@ class MultiplexRouter::InterfaceEndpoint friend class base::RefCounted<InterfaceEndpoint>; ~InterfaceEndpoint() override { - router_->lock_.AssertAcquired(); + router_->AssertLockAcquired(); DCHECK(!client_); DCHECK(closed_); @@ -142,26 +176,23 @@ class MultiplexRouter::InterfaceEndpoint 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_); - bool more_to_process = router_->ProcessFirstSyncMessageForEndpoint(id_); + MayAutoLock locker(&router_->lock_); + scoped_refptr<InterfaceEndpoint> self_protector(this); - if (!more_to_process) - ResetSyncMessageSignal(); + bool more_to_process = router_->ProcessFirstSyncMessageForEndpoint(id_); - // 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 (!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_) { // 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. @@ -175,12 +206,21 @@ class MultiplexRouter::InterfaceEndpoint return; { - base::AutoLock locker(router_->lock_); - EnsureEventMessagePipeExists(); - - auto iter = router_->sync_message_tasks_.find(id_); - if (iter != router_->sync_message_tasks_.end() && !iter->second.empty()) - SignalSyncMessageEvent(); + MayAutoLock locker(&router_->lock_); + + 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(); + } + } } sync_watcher_.reset(new SyncHandleWatcher( @@ -188,31 +228,6 @@ class MultiplexRouter::InterfaceEndpoint 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. @@ -227,6 +242,12 @@ class MultiplexRouter::InterfaceEndpoint // 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. @@ -250,13 +271,53 @@ class MultiplexRouter::InterfaceEndpoint 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(Message* message) { + static std::unique_ptr<Task> CreateMessageTask( + MessageWrapper message_wrapper) { Task* task = new Task(MESSAGE); - task->message.reset(new Message); - message->MoveTo(task->message.get()); + task->message_wrapper = std::move(message_wrapper); return base::WrapUnique(task); } static std::unique_ptr<Task> CreateNotifyErrorTask( @@ -271,7 +332,7 @@ struct MultiplexRouter::Task { bool IsMessageTask() const { return type == MESSAGE; } bool IsNotifyErrorTask() const { return type == NOTIFY_ERROR; } - std::unique_ptr<Message> message; + MessageWrapper message_wrapper; scoped_refptr<InterfaceEndpoint> endpoint_to_notify; enum Type { MESSAGE, NOTIFY_ERROR }; @@ -279,36 +340,56 @@ struct MultiplexRouter::Task { private: explicit Task(Type in_type) : type(in_type) {} + + DISALLOW_COPY_AND_ASSIGN(Task); }; MultiplexRouter::MultiplexRouter( - bool set_interface_id_namesapce_bit, ScopedMessagePipeHandle message_pipe, + Config config, + bool set_interface_id_namesapce_bit, scoped_refptr<base::SingleThreadTaskRunner> runner) - : AssociatedGroupController(base::ThreadTaskRunnerHandle::Get()), - set_interface_id_namespace_bit_(set_interface_id_namesapce_bit), - header_validator_(this), + : set_interface_id_namespace_bit_(set_interface_id_namesapce_bit), + task_runner_(runner), + header_validator_(nullptr), + filters_(this), connector_(std::move(message_pipe), - Connector::MULTI_THREADED_SEND, + config == MULTI_INTERFACE ? Connector::MULTI_THREADED_SEND + : Connector::SINGLE_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) { - // 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_); + 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_); 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() { - base::AutoLock locker(lock_); + MayAutoLock locker(&lock_); sync_message_tasks_.clear(); tasks_.clear(); @@ -319,40 +400,66 @@ MultiplexRouter::~MultiplexRouter() { // because it may remove the corresponding value from the map. ++iter; - DCHECK(endpoint->closed()); - UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED); + 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(endpoints_.empty()); } -void MultiplexRouter::SetMasterInterfaceName(const std::string& name) { +void MultiplexRouter::SetMasterInterfaceName(const char* name) { DCHECK(thread_checker_.CalledOnValidThread()); - header_validator_.SetDescription(name + " [master] MessageHeaderValidator"); + header_validator_->SetDescription( + std::string(name) + " [master] MessageHeaderValidator"); control_message_handler_.SetDescription( - name + " [master] PipeControlMessageHandler"); + std::string(name) + " [master] PipeControlMessageHandler"); + connector_.SetWatcherHeapProfilerTag(name); } -void MultiplexRouter::CreateEndpointHandlePair( - ScopedInterfaceEndpointHandle* local_endpoint, - ScopedInterfaceEndpointHandle* remote_endpoint) { - base::AutoLock locker(lock_); +InterfaceId MultiplexRouter::AssociateInterface( + ScopedInterfaceEndpointHandle handle_to_send) { + if (!handle_to_send.pending_association()) + return kInvalidInterfaceId; + uint32_t id = 0; - 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); + { + 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)); + + InterfaceEndpoint* endpoint = new InterfaceEndpoint(this, id); + endpoints_[id] = endpoint; + if (encountered_error_) + UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED); + endpoint->set_handle_created(); + } + + 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); + } - *local_endpoint = CreateScopedInterfaceEndpointHandle(id, true); - *remote_endpoint = CreateScopedInterfaceEndpointHandle(id, false); + control_message_proxy_.NotifyPeerEndpointClosed( + id, handle_to_send.disconnect_reason()); + } + return id; } ScopedInterfaceEndpointHandle MultiplexRouter::CreateLocalEndpointHandle( @@ -360,10 +467,12 @@ ScopedInterfaceEndpointHandle MultiplexRouter::CreateLocalEndpointHandle( if (!IsValidInterfaceId(id)) return ScopedInterfaceEndpointHandle(); - base::AutoLock locker(lock_); + MayAutoLock 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 { @@ -371,34 +480,32 @@ ScopedInterfaceEndpointHandle MultiplexRouter::CreateLocalEndpointHandle( // notification that the peer endpoint has closed. CHECK(!endpoint->closed()); CHECK(endpoint->peer_closed()); + + if (endpoint->handle_created()) + return ScopedInterfaceEndpointHandle(); } - return CreateScopedInterfaceEndpointHandle(id, true); + + endpoint->set_handle_created(); + return CreateScopedInterfaceEndpointHandle(id); } -void MultiplexRouter::CloseEndpointHandle(InterfaceId id, bool is_local) { +void MultiplexRouter::CloseEndpointHandle( + InterfaceId id, + const base::Optional<DisconnectReason>& reason) { if (!IsValidInterfaceId(id)) return; - 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)); + MayAutoLock locker(&lock_); + DCHECK(base::ContainsKey(endpoints_, id)); InterfaceEndpoint* endpoint = endpoints_[id].get(); DCHECK(!endpoint->client()); DCHECK(!endpoint->closed()); UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED); - if (!IsMasterInterfaceId(id)) - control_message_proxy_.NotifyPeerEndpointClosed(id); + if (!IsMasterInterfaceId(id) || reason) { + MayAutoUnlock unlocker(&lock_); + control_message_proxy_.NotifyPeerEndpointClosed(id, reason); + } ProcessTasks(NO_DIRECT_CLIENT_CALLS, nullptr); } @@ -412,8 +519,8 @@ InterfaceEndpointController* MultiplexRouter::AttachEndpointClient( DCHECK(IsValidInterfaceId(id)); DCHECK(client); - base::AutoLock locker(lock_); - DCHECK(ContainsKey(endpoints_, id)); + MayAutoLock locker(&lock_); + DCHECK(base::ContainsKey(endpoints_, id)); InterfaceEndpoint* endpoint = endpoints_[id].get(); endpoint->AttachClient(client, std::move(runner)); @@ -431,8 +538,8 @@ void MultiplexRouter::DetachEndpointClient( DCHECK(IsValidInterfaceId(id)); - base::AutoLock locker(lock_); - DCHECK(ContainsKey(endpoints_, id)); + MayAutoLock locker(&lock_); + DCHECK(base::ContainsKey(endpoints_, id)); InterfaceEndpoint* endpoint = endpoints_[id].get(); endpoint->DetachClient(); @@ -456,21 +563,51 @@ void MultiplexRouter::CloseMessagePipe() { 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()); - base::AutoLock locker(lock_); + MayAutoLock locker(&lock_); if (endpoints_.size() > 1) return true; if (endpoints_.size() == 0) return false; - return !ContainsKey(endpoints_, kMasterInterfaceId); + return !base::ContainsKey(endpoints_, kMasterInterfaceId); } void MultiplexRouter::EnableTestingMode() { DCHECK(thread_checker_.CalledOnValidThread()); - base::AutoLock locker(lock_); + MayAutoLock locker(&lock_); testing_mode_ = true; connector_.set_enforce_errors_from_incoming_receiver(false); @@ -479,8 +616,13 @@ void MultiplexRouter::EnableTestingMode() { bool MultiplexRouter::Accept(Message* message) { DCHECK(thread_checker_.CalledOnValidThread()); + if (!message->DeserializeAssociatedEndpointHandles(this)) + return false; + scoped_refptr<MultiplexRouter> protector(this); - base::AutoLock locker(lock_); + MayAutoLock locker(&lock_); + + DCHECK(!paused_); ClientCallBehavior client_call_behavior = connector_.during_sync_handle_watcher_callback() @@ -494,15 +636,16 @@ bool MultiplexRouter::Accept(Message* message) { 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(message)); + tasks_.push_back( + Task::CreateMessageTask(MessageWrapper(this, std::move(*message)))); Task* task = tasks_.back().get(); - if (task->message->has_flag(Message::kFlagIsSync)) { - InterfaceId id = task->message->interface_id(); + if (task->message_wrapper.value().has_flag(Message::kFlagIsSync)) { + InterfaceId id = task->message_wrapper.value().interface_id(); sync_message_tasks_[id].push_back(task); - auto iter = endpoints_.find(id); - if (iter != endpoints_.end()) - iter->second->SignalSyncMessageEvent(); + InterfaceEndpoint* endpoint = FindEndpoint(id); + if (endpoint) + endpoint->SignalSyncMessageEvent(); } } else if (!tasks_.empty()) { // Processing the message may result in new tasks (for error notification) @@ -516,14 +659,17 @@ bool MultiplexRouter::Accept(Message* message) { return true; } -bool MultiplexRouter::OnPeerAssociatedEndpointClosed(InterfaceId id) { - lock_.AssertAcquired(); - - if (IsMasterInterfaceId(id)) - return false; +bool MultiplexRouter::OnPeerAssociatedEndpointClosed( + InterfaceId id, + const base::Optional<DisconnectReason>& reason) { + DCHECK(!IsMasterInterfaceId(id) || reason); + MayAutoLock locker(&lock_); 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, @@ -541,26 +687,11 @@ bool MultiplexRouter::OnPeerAssociatedEndpointClosed(InterfaceId id) { 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); - base::AutoLock locker(lock_); + MayAutoLock locker(&lock_); encountered_error_ = true; @@ -585,20 +716,21 @@ void MultiplexRouter::OnPipeConnectionError() { void MultiplexRouter::ProcessTasks( ClientCallBehavior client_call_behavior, base::SingleThreadTaskRunner* current_task_runner) { - lock_.AssertAcquired(); + AssertLockAcquired(); if (posted_to_process_tasks_) return; - while (!tasks_.empty()) { + while (!tasks_.empty() && !paused_) { std::unique_ptr<Task> task(std::move(tasks_.front())); tasks_.pop_front(); InterfaceId id = kInvalidInterfaceId; - bool sync_message = task->IsMessageTask() && task->message && - task->message->has_flag(Message::kFlagIsSync); + bool sync_message = + task->IsMessageTask() && !task->message_wrapper.value().IsNull() && + task->message_wrapper.value().has_flag(Message::kFlagIsSync); if (sync_message) { - id = task->message->interface_id(); + id = task->message_wrapper.value().interface_id(); auto& sync_message_queue = sync_message_tasks_[id]; DCHECK_EQ(task.get(), sync_message_queue.front()); sync_message_queue.pop_front(); @@ -608,8 +740,8 @@ void MultiplexRouter::ProcessTasks( task->IsNotifyErrorTask() ? ProcessNotifyErrorTask(task.get(), client_call_behavior, current_task_runner) - : ProcessIncomingMessage(task->message.get(), client_call_behavior, - current_task_runner); + : ProcessIncomingMessage(&task->message_wrapper.value(), + client_call_behavior, current_task_runner); if (!processed) { if (sync_message) { @@ -629,21 +761,25 @@ void MultiplexRouter::ProcessTasks( } bool MultiplexRouter::ProcessFirstSyncMessageForEndpoint(InterfaceId id) { - lock_.AssertAcquired(); + AssertLockAcquired(); 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()); - std::unique_ptr<Message> message(std::move(task->message)); + MessageWrapper message_wrapper = std::move(task->message_wrapper); - // Note: after this call, |task| and |iter| may be invalidated. + // Note: after this call, |task| and |iter| may be invalidated. bool processed = ProcessIncomingMessage( - message.get(), ALLOW_DIRECT_CLIENT_CALLS_FOR_SYNC_MESSAGES, nullptr); + &message_wrapper.value(), ALLOW_DIRECT_CLIENT_CALLS_FOR_SYNC_MESSAGES, + nullptr); DCHECK(processed); iter = sync_message_tasks_.find(id); @@ -663,7 +799,9 @@ bool MultiplexRouter::ProcessNotifyErrorTask( ClientCallBehavior client_call_behavior, base::SingleThreadTaskRunner* current_task_runner) { DCHECK(!current_task_runner || current_task_runner->BelongsToCurrentThread()); - lock_.AssertAcquired(); + DCHECK(!paused_); + + AssertLockAcquired(); InterfaceEndpoint* endpoint = task->endpoint_to_notify.get(); if (!endpoint->client()) return true; @@ -677,14 +815,17 @@ bool MultiplexRouter::ProcessNotifyErrorTask( 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(). - base::AutoUnlock unlocker(lock_); - client->NotifyError(); + MayAutoUnlock unlocker(&lock_); + client->NotifyError(disconnect_reason); } return true; } @@ -694,47 +835,35 @@ bool MultiplexRouter::ProcessIncomingMessage( ClientCallBehavior client_call_behavior, base::SingleThreadTaskRunner* current_task_runner) { DCHECK(!current_task_runner || current_task_runner->BelongsToCurrentThread()); - lock_.AssertAcquired(); + DCHECK(!paused_); + DCHECK(message); + AssertLockAcquired(); - if (!message) { + if (message->IsNull()) { // This is a sync message and has been processed during sync handle // watching. return true; } if (PipeControlMessageHandler::IsPipeControlMessage(message)) { - if (!control_message_handler_.Accept(message)) + bool result = false; + + { + MayAutoUnlock unlocker(&lock_); + result = control_message_handler_.Accept(message); + } + + if (!result) RaiseErrorInNonTestingMode(); + return true; } InterfaceId id = message->interface_id(); DCHECK(IsValidInterfaceId(id)); - 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()) + InterfaceEndpoint* endpoint = FindEndpoint(id); + if (!endpoint || endpoint->closed()) return true; if (!endpoint->client()) { @@ -768,7 +897,7 @@ bool MultiplexRouter::ProcessIncomingMessage( // // It is safe to call into |client| without the lock. Because |client| is // always accessed on the same thread, including DetachEndpointClient(). - base::AutoUnlock unlocker(lock_); + MayAutoUnlock unlocker(&lock_); result = client->HandleIncomingMessage(message); } if (!result) @@ -779,7 +908,7 @@ bool MultiplexRouter::ProcessIncomingMessage( void MultiplexRouter::MaybePostToProcessTasks( base::SingleThreadTaskRunner* task_runner) { - lock_.AssertAcquired(); + AssertLockAcquired(); if (posted_to_process_tasks_) return; @@ -792,7 +921,7 @@ void MultiplexRouter::MaybePostToProcessTasks( 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. - base::AutoLock locker(lock_); + MayAutoLock locker(&lock_); posted_to_process_tasks_ = false; scoped_refptr<base::SingleThreadTaskRunner> runner( std::move(posted_to_task_runner_)); @@ -802,23 +931,20 @@ void MultiplexRouter::LockAndCallProcessTasks() { void MultiplexRouter::UpdateEndpointStateMayRemove( InterfaceEndpoint* endpoint, EndpointStateUpdateType type) { - 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 (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(); } if (endpoint->closed() && endpoint->peer_closed()) endpoints_.erase(endpoint->id()); } void MultiplexRouter::RaiseErrorInNonTestingMode() { - lock_.AssertAcquired(); + AssertLockAcquired(); if (!testing_mode_) RaiseError(); } @@ -826,24 +952,35 @@ void MultiplexRouter::RaiseErrorInNonTestingMode() { MultiplexRouter::InterfaceEndpoint* MultiplexRouter::FindOrInsertEndpoint( InterfaceId id, bool* inserted) { - lock_.AssertAcquired(); + AssertLockAcquired(); // Either |inserted| is nullptr or it points to a boolean initialized as // false. DCHECK(!inserted || !*inserted); - auto iter = endpoints_.find(id); - InterfaceEndpoint* endpoint; - if (iter == endpoints_.end()) { + InterfaceEndpoint* endpoint = FindEndpoint(id); + if (!endpoint) { 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 dc66e8e..cac138b 100644 --- a/mojo/public/cpp/bindings/lib/multiplex_router.h +++ b/mojo/public/cpp/bindings/lib/multiplex_router.h @@ -12,15 +12,19 @@ #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" @@ -34,8 +38,6 @@ class SingleThreadTaskRunner; namespace mojo { -class AssociatedGroup; - namespace internal { // MultiplexRouter supports routing messages for multiple interfaces over a @@ -47,31 +49,51 @@ namespace internal { // 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. -class MultiplexRouter - : public MessageReceiver, +// +// 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), public AssociatedGroupController, - public PipeControlMessageHandlerDelegate { + NON_EXPORTED_BASE(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(bool set_interface_id_namespace_bit, - ScopedMessagePipeHandle message_pipe, + MultiplexRouter(ScopedMessagePipeHandle message_pipe, + Config config, + bool set_interface_id_namespace_bit, scoped_refptr<base::SingleThreadTaskRunner> runner); // Sets the master interface name for this router. Only used when reporting // message header or control message validation errors. - void SetMasterInterfaceName(const std::string& name); + // |name| must be a string literal. + void SetMasterInterfaceName(const char* name); // --------------------------------------------------------------------------- // The following public methods are safe to call from any threads. // AssociatedGroupController implementation: - void CreateEndpointHandlePair( - ScopedInterfaceEndpointHandle* local_endpoint, - ScopedInterfaceEndpointHandle* remote_endpoint) override; + InterfaceId AssociateInterface( + ScopedInterfaceEndpointHandle handle_to_send) override; ScopedInterfaceEndpointHandle CreateLocalEndpointHandle( InterfaceId id) override; - void CloseEndpointHandle(InterfaceId id, bool is_local) override; + void CloseEndpointHandle( + InterfaceId id, + const base::Optional<DisconnectReason>& reason) override; InterfaceEndpointController* AttachEndpointClient( const ScopedInterfaceEndpointHandle& handle, InterfaceEndpointClient* endpoint_client, @@ -102,14 +124,8 @@ class MultiplexRouter } // See Binding for details of pause/resume. - void PauseIncomingMethodCallProcessing() { - DCHECK(thread_checker_.CalledOnValidThread()); - connector_.PauseIncomingMethodCallProcessing(); - } - void ResumeIncomingMethodCallProcessing() { - DCHECK(thread_checker_.CalledOnValidThread()); - connector_.ResumeIncomingMethodCallProcessing(); - } + void PauseIncomingMethodCallProcessing(); + void ResumeIncomingMethodCallProcessing(); // Whether there are any associated interfaces running currently. bool HasAssociatedEndpoints() const; @@ -131,8 +147,13 @@ class MultiplexRouter return connector_.handle(); } + bool SimulateReceivingMessageForTesting(Message* message) { + return filters_.Accept(message); + } + private: class InterfaceEndpoint; + class MessageWrapper; struct Task; ~MultiplexRouter() override; @@ -141,8 +162,9 @@ class MultiplexRouter bool Accept(Message* message) override; // PipeControlMessageHandlerDelegate implementation: - bool OnPeerAssociatedEndpointClosed(InterfaceId id) override; - bool OnAssociatedEndpointClosedBeforeSent(InterfaceId id) override; + bool OnPeerAssociatedEndpointClosed( + InterfaceId id, + const base::Optional<DisconnectReason>& reason) override; void OnPipeConnectionError(); @@ -202,19 +224,30 @@ class MultiplexRouter 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_; - MessageHeaderValidator header_validator_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + + // Owned by |filters_| below. + MessageHeaderValidator* header_validator_; + + FilterChain filters_; Connector connector_; base::ThreadChecker thread_checker_; // Protects the following members. - mutable base::Lock lock_; + // Not set in Config::SINGLE_INTERFACE* mode. + mutable base::Optional<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_; @@ -229,6 +262,8 @@ class MultiplexRouter 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 837b75a..7b1a1a6 100644 --- a/mojo/public/cpp/bindings/lib/native_struct.cc +++ b/mojo/public/cpp/bindings/lib/native_struct.cc @@ -4,27 +4,31 @@ #include "mojo/public/cpp/bindings/native_struct.h" +#include "mojo/public/cpp/bindings/lib/hash_util.h" + namespace mojo { // static NativeStructPtr NativeStruct::New() { - NativeStructPtr rv; - internal::StructHelper<NativeStruct>::Initialize(&rv); - return rv; + return NativeStructPtr(base::in_place); } -NativeStruct::NativeStruct() : data(nullptr) {} +NativeStruct::NativeStruct() {} NativeStruct::~NativeStruct() {} NativeStructPtr NativeStruct::Clone() const { NativeStructPtr rv(New()); - rv->data = data.Clone(); + rv->data = data; return rv; } bool NativeStruct::Equals(const NativeStruct& other) const { - return data.Equals(other.data); + return data == other.data; +} + +size_t NativeStruct::Hash(size_t seed) const { + return internal::Hash(seed, 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 5c58774..1c7cd81 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 NativeStruct_Data { +class MOJO_CPP_BINDINGS_EXPORT 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 ac06059..fa0dbf3 100644 --- a/mojo/public/cpp/bindings/lib/native_struct_serialization.cc +++ b/mojo/public/cpp/bindings/lib/native_struct_serialization.cc @@ -15,7 +15,8 @@ size_t UnmappedNativeStructSerializerImpl::PrepareToSerialize( SerializationContext* context) { if (!input) return 0; - return internal::PrepareToSerialize<Array<uint8_t>>(input->data, context); + return internal::PrepareToSerialize<ArrayDataView<uint8_t>>(input->data, + context); } // static @@ -31,8 +32,8 @@ void UnmappedNativeStructSerializerImpl::Serialize( Array_Data<uint8_t>* data = nullptr; const ContainerValidateParams params(0, false, nullptr); - internal::Serialize<Array<uint8_t>>(input->data, buffer, &data, ¶ms, - context); + internal::Serialize<ArrayDataView<uint8_t>>(input->data, buffer, &data, + ¶ms, context); *output = reinterpret_cast<NativeStruct_Data*>(data); } @@ -44,7 +45,8 @@ bool UnmappedNativeStructSerializerImpl::Deserialize( Array_Data<uint8_t>* data = reinterpret_cast<Array_Data<uint8_t>*>(input); NativeStructPtr result(NativeStruct::New()); - if (!internal::Deserialize<Array<uint8_t>>(data, &result->data, context)) { + if (!internal::Deserialize<ArrayDataView<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 e64b862..457435b 100644 --- a/mojo/public/cpp/bindings/lib/native_struct_serialization.h +++ b/mojo/public/cpp/bindings/lib/native_struct_serialization.h @@ -13,12 +13,14 @@ #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 { @@ -102,7 +104,7 @@ struct NativeStructSerializerImpl { } }; -struct UnmappedNativeStructSerializerImpl { +struct MOJO_CPP_BINDINGS_EXPORT UnmappedNativeStructSerializerImpl { static size_t PrepareToSerialize(const NativeStructPtr& input, SerializationContext* context); static void Serialize(const NativeStructPtr& input, @@ -123,7 +125,7 @@ struct NativeStructSerializerImpl<const NativeStructPtr> : public UnmappedNativeStructSerializerImpl {}; template <typename MaybeConstUserType> -struct Serializer<NativeStructPtr, MaybeConstUserType> +struct Serializer<NativeStructDataView, 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 deleted file mode 100644 index 9e0945c..0000000 --- a/mojo/public/cpp/bindings/lib/no_interface.cc +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/public/cpp/bindings/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 7ee9f8a..d451c05 100644 --- a/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc +++ b/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc @@ -5,8 +5,10 @@ #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" @@ -41,8 +43,9 @@ bool PipeControlMessageHandler::Accept(Message* message) { } bool PipeControlMessageHandler::Validate(Message* message) { - internal::ValidationContext validation_context( - message->data(), message->data_num_bytes(), 0, message, description_); + internal::ValidationContext validation_context(message->payload(), + message->payload_num_bytes(), + 0, 0, message, description_); if (message->name() == pipe_control::kRunOrClosePipeMessageId) { if (!internal::ValidateMessageIsRequestWithoutResponse( @@ -58,22 +61,25 @@ bool PipeControlMessageHandler::Validate(Message* message) { } 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::RunOrClosePipeMessageParamsPtr>( - params, ¶ms_ptr, &context_); + internal::Deserialize<pipe_control::RunOrClosePipeMessageParamsDataView>( + params, ¶ms_ptr, &context); if (params_ptr->input->is_peer_associated_endpoint_closed_event()) { - 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); + 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); } 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 55ee64b..701108e 100644 --- a/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc +++ b/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc @@ -11,33 +11,28 @@ #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 { -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); +Message ConstructRunOrClosePipeMessage( + pipe_control::RunOrClosePipeInputPtr input_ptr) { + internal::SerializationContext context; - size_t size = - internal::PrepareToSerialize< - pipe_control::RunOrClosePipeMessageParamsPtr>(params_ptr, context); - internal::MessageBuilder builder(pipe_control::kRunOrClosePipeMessageId, - size); + 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); pipe_control::internal::RunOrClosePipeMessageParams_Data* params = nullptr; - internal::Serialize<pipe_control::RunOrClosePipeMessageParamsPtr>( - params_ptr, builder.buffer(), ¶ms, context); + internal::Serialize<pipe_control::RunOrClosePipeMessageParamsDataView>( + params_ptr, builder.buffer(), ¶ms, &context); builder.message()->set_interface_id(kInvalidInterfaceId); - 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); + return std::move(*builder.message()); } } // namespace @@ -45,30 +40,30 @@ void SendRunOrClosePipeMessage(MessageReceiver* receiver, PipeControlMessageProxy::PipeControlMessageProxy(MessageReceiver* receiver) : receiver_(receiver) {} -void PipeControlMessageProxy::NotifyPeerEndpointClosed(InterfaceId id) { - DCHECK(!IsMasterInterfaceId(id)); - pipe_control::PeerAssociatedEndpointClosedEventPtr event( - pipe_control::PeerAssociatedEndpointClosedEvent::New()); - event->id = id; - - pipe_control::RunOrClosePipeInputPtr input( - pipe_control::RunOrClosePipeInput::New()); - input->set_peer_associated_endpoint_closed_event(std::move(event)); - - SendRunOrClosePipeMessage(receiver_, std::move(input), &context_); +void PipeControlMessageProxy::NotifyPeerEndpointClosed( + InterfaceId id, + const base::Optional<DisconnectReason>& reason) { + Message message(ConstructPeerEndpointClosedMessage(id, reason)); + bool ok = receiver_->Accept(&message); + ALLOW_UNUSED_LOCAL(ok); } -void PipeControlMessageProxy::NotifyEndpointClosedBeforeSent(InterfaceId id) { - DCHECK(!IsMasterInterfaceId(id)); - pipe_control::AssociatedEndpointClosedBeforeSentEventPtr event( - pipe_control::AssociatedEndpointClosedBeforeSentEvent::New()); +// static +Message PipeControlMessageProxy::ConstructPeerEndpointClosedMessage( + InterfaceId id, + const base::Optional<DisconnectReason>& reason) { + auto 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; + } - pipe_control::RunOrClosePipeInputPtr input( - pipe_control::RunOrClosePipeInput::New()); - input->set_associated_endpoint_closed_before_sent_event(std::move(event)); + auto input = pipe_control::RunOrClosePipeInput::New(); + input->set_peer_associated_endpoint_closed_event(std::move(event)); - SendRunOrClosePipeMessage(receiver_, std::move(input), &context_); + return ConstructRunOrClosePipeMessage(std::move(input)); } } // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/router.cc b/mojo/public/cpp/bindings/lib/router.cc deleted file mode 100644 index 8c1b77d..0000000 --- a/mojo/public/cpp/bindings/lib/router.cc +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/public/cpp/bindings/lib/router.h" - -#include <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 deleted file mode 100644 index 6dbe08d..0000000 --- a/mojo/public/cpp/bindings/lib/router.h +++ /dev/null @@ -1,177 +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_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 f54c3f7..c134507 100644 --- a/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc +++ b/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc @@ -4,69 +4,379 @@ #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() - : ScopedInterfaceEndpointHandle(kInvalidInterfaceId, true, nullptr) {} + : state_(new State) {} ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle( ScopedInterfaceEndpointHandle&& other) - : id_(other.id_), is_local_(other.is_local_) { - group_controller_.swap(other.group_controller_); - other.id_ = kInvalidInterfaceId; + : state_(new State) { + state_.swap(other.state_); } ScopedInterfaceEndpointHandle::~ScopedInterfaceEndpointHandle() { - reset(); + state_->Close(base::nullopt); } ScopedInterfaceEndpointHandle& ScopedInterfaceEndpointHandle::operator=( ScopedInterfaceEndpointHandle&& other) { reset(); - swap(other); - + state_.swap(other.state_); return *this; } -void ScopedInterfaceEndpointHandle::reset() { - if (!IsValidInterfaceId(id_)) - return; +bool ScopedInterfaceEndpointHandle::is_valid() const { + return state_->is_valid(); +} + +bool ScopedInterfaceEndpointHandle::pending_association() const { + return state_->pending_association(); +} - group_controller_->CloseEndpointHandle(id_, is_local_); +InterfaceId ScopedInterfaceEndpointHandle::id() const { + return state_->id(); +} - id_ = kInvalidInterfaceId; - is_local_ = true; - group_controller_ = nullptr; +AssociatedGroupController* ScopedInterfaceEndpointHandle::group_controller() + const { + return state_->group_controller(); } -void ScopedInterfaceEndpointHandle::swap(ScopedInterfaceEndpointHandle& other) { - using std::swap; - swap(other.id_, id_); - swap(other.is_local_, is_local_); - swap(other.group_controller_, group_controller_); +const base::Optional<DisconnectReason>& +ScopedInterfaceEndpointHandle::disconnect_reason() const { + return state_->disconnect_reason(); } -InterfaceId ScopedInterfaceEndpointHandle::release() { - InterfaceId result = id_; +void ScopedInterfaceEndpointHandle::SetAssociationEventHandler( + AssociationEventCallback handler) { + state_->SetAssociationEventHandler(std::move(handler)); +} - id_ = kInvalidInterfaceId; - is_local_ = true; - group_controller_ = nullptr; +void ScopedInterfaceEndpointHandle::reset() { + ResetInternal(base::nullopt); +} - return result; +void ScopedInterfaceEndpointHandle::ResetWithReason( + uint32_t custom_reason, + const std::string& description) { + ResetInternal(DisconnectReason(custom_reason, description)); } ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle( InterfaceId id, - bool is_local, scoped_refptr<AssociatedGroupController> group_controller) - : id_(id), - is_local_(is_local), - group_controller_(std::move(group_controller)) { - DCHECK(!IsValidInterfaceId(id) || 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_); } } // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/serialization.h b/mojo/public/cpp/bindings/lib/serialization.h index 6d7dd8e..359b02b 100644 --- a/mojo/public/cpp/bindings/lib/serialization.h +++ b/mojo/public/cpp/bindings/lib/serialization.h @@ -8,19 +8,16 @@ #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/fixed_buffer.h" +#include "mojo/public/cpp/bindings/lib/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" @@ -44,7 +41,7 @@ DataArrayType StructSerializeImpl(UserType* input) { 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 mojo::Array (backed by std::vector). + // practice, this should never happen for std::vector. bool need_copy = !IsAligned(result_buffer); if (need_copy) { @@ -53,9 +50,9 @@ DataArrayType StructSerializeImpl(UserType* input) { DCHECK(IsAligned(result_buffer)); } - FixedBuffer buffer; + Buffer buffer; buffer.Initialize(result_buffer, size); - typename MojomType::Struct::Data_* data = nullptr; + typename MojomTypeTraits<MojomType>::Data* data = nullptr; Serialize<MojomType>(*input, &buffer, &data, &context); if (need_copy) { @@ -70,13 +67,11 @@ template <typename MojomType, typename DataArrayType, typename UserType> bool StructDeserializeImpl(const DataArrayType& input, UserType* output) { static_assert(BelongsTo<MojomType, MojomTypeCategory::STRUCT>::value, "Unexpected type."); - using DataType = typename MojomType::Struct::Data_; - - if (input.is_null()) - return false; + using DataType = typename MojomTypeTraits<MojomType>::Data; + // TODO(sammc): Use DataArrayType::empty() once WTF::Vector::empty() exists. void* input_buffer = - input.empty() + input.size() == 0 ? nullptr : const_cast<void*>(reinterpret_cast<const void*>(&input.front())); @@ -89,7 +84,7 @@ bool StructDeserializeImpl(const DataArrayType& input, UserType* output) { memcpy(input_buffer, &input.front(), input.size()); } - ValidationContext validation_context(input_buffer, input.size(), 0); + ValidationContext validation_context(input_buffer, input.size(), 0, 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 7fd80be..e2fd5c6 100644 --- a/mojo/public/cpp/bindings/lib/serialization_context.cc +++ b/mojo/public/cpp/bindings/lib/serialization_context.cc @@ -7,7 +7,6 @@ #include <limits> #include "base/logging.h" -#include "mojo/public/cpp/bindings/associated_group_controller.h" #include "mojo/public/cpp/system/core.h" namespace mojo { @@ -50,10 +49,6 @@ void SerializedHandleVector::Swap(std::vector<mojo::Handle>* other) { 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 64d2a1a..a34fe3d 100644 --- a/mojo/public/cpp/bindings/lib/serialization_context.h +++ b/mojo/public/cpp/bindings/lib/serialization_context.h @@ -12,18 +12,16 @@ #include <vector> #include "base/macros.h" -#include "base/memory/ref_counted.h" +#include "mojo/public/cpp/bindings/bindings_export.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 SerializedHandleVector { +class MOJO_CPP_BINDINGS_EXPORT SerializedHandleVector { public: SerializedHandleVector(); ~SerializedHandleVector(); @@ -54,21 +52,23 @@ class SerializedHandleVector { }; // Context information for serialization/deserialization routines. -struct SerializationContext { +struct MOJO_CPP_BINDINGS_EXPORT 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 5bed126..55c9982 100644 --- a/mojo/public/cpp/bindings/lib/serialization_forward.h +++ b/mojo/public/cpp/bindings/lib/serialization_forward.h @@ -12,6 +12,7 @@ #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 5e65891..6e0c758 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.h" +#include "mojo/public/cpp/bindings/string_data_view.h" #include "mojo/public/cpp/bindings/string_traits.h" namespace mojo { namespace internal { template <typename MaybeConstUserType> -struct Serializer<String, MaybeConstUserType> { +struct Serializer<StringDataView, MaybeConstUserType> { using UserType = typename std::remove_const<MaybeConstUserType>::type; using Traits = StringTraits<UserType>; @@ -60,7 +60,7 @@ struct Serializer<String, MaybeConstUserType> { SerializationContext* context) { if (!input) return CallSetToNullIfExists<Traits>(output); - return Traits::Read(StringDataView(input), output); + return Traits::Read(StringDataView(input, context), output); } }; diff --git a/mojo/public/cpp/bindings/lib/string_traits_wtf.cc b/mojo/public/cpp/bindings/lib/string_traits_wtf.cc index 19fa907..203f6f5 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 @@ namespace { struct UTF8AdaptorInfo { explicit UTF8AdaptorInfo(const WTF::String& input) : utf8_adaptor(input) { #if DCHECK_IS_ON() - original_size_in_bytes = static_cast<size_t>(input.sizeInBytes()); + original_size_in_bytes = input.charactersSizeInBytes(); #endif } @@ -34,8 +34,7 @@ UTF8AdaptorInfo* ToAdaptor(const WTF::String& input, void* context) { UTF8AdaptorInfo* adaptor = static_cast<UTF8AdaptorInfo*>(context); #if DCHECK_IS_ON() - DCHECK_EQ(adaptor->original_size_in_bytes, - static_cast<size_t>(input.sizeInBytes())); + DCHECK_EQ(adaptor->original_size_in_bytes, input.charactersSizeInBytes()); #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 3d864af..585a8f0 100644 --- a/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc +++ b/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc @@ -6,6 +6,7 @@ #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" @@ -37,7 +38,7 @@ class SyncCallSettings { size_t scoped_allow_count_ = 0; }; -base::LazyInstance<base::ThreadLocalPointer<SyncCallSettings>> +base::LazyInstance<base::ThreadLocalPointer<SyncCallSettings>>::DestructorAtExit g_sync_call_settings = LAZY_INSTANCE_INITIALIZER; // static @@ -45,6 +46,7 @@ SyncCallSettings* SyncCallSettings::current() { 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 f6372d9..5ae763b 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>> +base::LazyInstance<base::ThreadLocalPointer<SyncHandleRegistry>>::Leaky g_current_sync_handle_watcher = LAZY_INSTANCE_INITIALIZER; } // namespace @@ -34,7 +34,7 @@ bool SyncHandleRegistry::RegisterHandle(const Handle& handle, const HandleCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); - if (ContainsKey(handles_, handle)) + if (base::ContainsKey(handles_, handle)) return false; MojoResult result = MojoAddHandle(wait_set_handle_.get().value(), @@ -48,7 +48,7 @@ bool SyncHandleRegistry::RegisterHandle(const Handle& handle, void SyncHandleRegistry::UnregisterHandle(const Handle& handle) { DCHECK(thread_checker_.CalledOnValidThread()); - if (!ContainsKey(handles_, handle)) + if (!base::ContainsKey(handles_, handle)) return; MojoResult result = @@ -107,6 +107,19 @@ SyncHandleRegistry::SyncHandleRegistry() { 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 deleted file mode 100644 index d6b8c38..0000000 --- a/mojo/public/cpp/bindings/lib/sync_handle_registry.h +++ /dev/null @@ -1,67 +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_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 a95e07e..ad0a364 100644 --- a/mojo/public/cpp/bindings/lib/validation_context.cc +++ b/mojo/public/cpp/bindings/lib/validation_context.cc @@ -4,13 +4,7 @@ #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 { @@ -18,69 +12,39 @@ namespace internal { 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) + const base::StringPiece& description, + int stack_depth) : 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)) { + 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. 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 6045ca8..ed6c654 100644 --- a/mojo/public/cpp/bindings/lib/validation_context.h +++ b/mojo/public/cpp/bindings/lib/validation_context.h @@ -8,23 +8,28 @@ #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 ValidationContext { +class MOJO_CPP_BINDINGS_EXPORT 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 @@ -33,8 +38,10 @@ class ValidationContext { 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 = ""); + const base::StringPiece& description = "", + int stack_depth = 0); ~ValidationContext(); @@ -43,24 +50,97 @@ class ValidationContext { // 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); + 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; + } // 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); + 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; + } // 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; + 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; + } Message* message() const { return message_; } const base::StringPiece& description() const { return description_; } private: - bool InternalIsValidRange(uintptr_t begin, uintptr_t end) const; + bool InternalIsValidRange(uintptr_t begin, uintptr_t end) const { + return end > begin && begin >= data_begin_ && end <= data_end_; + } Message* const message_; const base::StringPiece description_; @@ -73,6 +153,13 @@ class ValidationContext { 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 90652de..904f5e4 100644 --- a/mojo/public/cpp/bindings/lib/validation_errors.cc +++ b/mojo/public/cpp/bindings/lib/validation_errors.cc @@ -14,6 +14,7 @@ namespace { ValidationErrorObserverForTesting* g_validation_error_observer = nullptr; SerializationWarningObserverForTesting* g_serialization_warning_observer = nullptr; +bool g_suppress_logging = false; } // namespace @@ -55,6 +56,8 @@ const char* ValidationErrorToString(ValidationError error) { 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"; @@ -69,8 +72,10 @@ void ReportValidationError(ValidationContext* context, } if (description) { - LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error) << " (" - << description << ")"; + if (!g_suppress_logging) { + LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error) + << " (" << description << ")"; + } if (context->message()) { context->message()->NotifyBadMessage( base::StringPrintf("Validation failed for %s [%s (%s)]", @@ -78,7 +83,8 @@ void ReportValidationError(ValidationContext* context, ValidationErrorToString(error), description)); } } else { - LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error); + if (!g_suppress_logging) + LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error); if (context->message()) { context->message()->NotifyBadMessage( base::StringPrintf("Validation failed for %s [%s]", @@ -88,6 +94,25 @@ void ReportValidationError(ValidationContext* context, } } +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 ec0aa27..122418d 100644 --- a/mojo/public/cpp/bindings/lib/validation_errors.h +++ b/mojo/public/cpp/bindings/lib/validation_errors.h @@ -8,9 +8,13 @@ #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 { @@ -67,17 +71,41 @@ enum ValidationError { // 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, }; -const char* ValidationErrorToString(ValidationError error); +MOJO_CPP_BINDINGS_EXPORT const char* ValidationErrorToString( + ValidationError error); + +MOJO_CPP_BINDINGS_EXPORT void ReportValidationError( + ValidationContext* context, + ValidationError error, + const char* description = nullptr); -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); +}; // Only used by validation tests and when there is only one thread doing message // validation. -class ValidationErrorObserverForTesting { +class MOJO_CPP_BINDINGS_EXPORT ValidationErrorObserverForTesting { public: explicit ValidationErrorObserverForTesting(const base::Closure& callback); ~ValidationErrorObserverForTesting(); @@ -99,11 +127,11 @@ class ValidationErrorObserverForTesting { // // The function returns true if the error is recorded (by a // SerializationWarningObserverForTesting object), false otherwise. -bool ReportSerializationWarning(ValidationError error); +MOJO_CPP_BINDINGS_EXPORT bool ReportSerializationWarning(ValidationError error); // Only used by serialization tests and when there is only one thread doing // message serialization. -class SerializationWarningObserverForTesting { +class MOJO_CPP_BINDINGS_EXPORT 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 9e63521..7614df5 100644 --- a/mojo/public/cpp/bindings/lib/validation_util.cc +++ b/mojo/public/cpp/bindings/lib/validation_util.cc @@ -16,16 +16,6 @@ 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)) { @@ -56,20 +46,17 @@ bool ValidateStructHeaderAndClaimMemory(const void* data, return true; } -bool ValidateUnionHeaderAndClaimMemory(const void* data, - bool inlined, - ValidationContext* validation_context) { +bool ValidateNonInlinedUnionHeaderAndClaimMemory( + const void* data, + ValidationContext* validation_context) { if (!IsAligned(data)) { ReportValidationError(validation_context, VALIDATION_ERROR_MISALIGNED_OBJECT); return false; } - // 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)) { + if (!validation_context->ClaimMemory(data, kUnionDataSize) || + *static_cast<const uint32_t*>(data) != kUnionDataSize) { ReportValidationError(validation_context, VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE); return false; @@ -113,41 +100,12 @@ bool ValidateMessageIsResponse(const Message* message, return true; } -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 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); + return input.handle.is_valid(); } -bool IsHandleOrInterfaceValid(const AssociatedInterfaceRequest_Data& input) { - return IsValidInterfaceId(input.interface_id); +bool IsHandleOrInterfaceValid(const AssociatedEndpointHandle_Data& input) { + return input.is_valid(); } bool IsHandleOrInterfaceValid(const Interface_Data& input) { @@ -172,7 +130,7 @@ bool ValidateHandleOrInterfaceNonNullable( } bool ValidateHandleOrInterfaceNonNullable( - const AssociatedInterfaceRequest_Data& input, + const AssociatedEndpointHandle_Data& input, const char* error_message, ValidationContext* validation_context) { if (IsHandleOrInterfaceValid(input)) @@ -212,7 +170,7 @@ bool ValidateHandleOrInterfaceNonNullable( bool ValidateHandleOrInterface(const AssociatedInterface_Data& input, ValidationContext* validation_context) { - if (!IsMasterInterfaceId(input.interface_id)) + if (validation_context->ClaimAssociatedEndpointHandle(input.handle)) return true; ReportValidationError(validation_context, @@ -220,9 +178,9 @@ bool ValidateHandleOrInterface(const AssociatedInterface_Data& input, return false; } -bool ValidateHandleOrInterface(const AssociatedInterfaceRequest_Data& input, +bool ValidateHandleOrInterface(const AssociatedEndpointHandle_Data& input, ValidationContext* validation_context) { - if (!IsMasterInterfaceId(input.interface_id)) + if (validation_context->ClaimAssociatedEndpointHandle(input)) 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 c883392..ea5a991 100644 --- a/mojo/public/cpp/bindings/lib/validation_util.h +++ b/mojo/public/cpp/bindings/lib/validation_util.h @@ -7,6 +7,7 @@ #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" @@ -19,7 +20,15 @@ namespace internal { // Checks whether decoding the pointer will overflow and produce a pointer // smaller than |offset|. -bool ValidateEncodedPointer(const uint64_t* 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)); +} template <typename T> bool ValidatePointer(const Pointer<T>& input, @@ -38,30 +47,32 @@ bool ValidatePointer(const Pointer<T>& input, // |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. -bool ValidateStructHeaderAndClaimMemory(const void* data, - ValidationContext* validation_context); +MOJO_CPP_BINDINGS_EXPORT bool ValidateStructHeaderAndClaimMemory( + const void* data, + ValidationContext* validation_context); // Validates that |data| contains a valid union header, in terms of alignment -// 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); +// 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); // Validates that the message is a request which doesn't expect a response. -bool ValidateMessageIsRequestWithoutResponse( +MOJO_CPP_BINDINGS_EXPORT bool ValidateMessageIsRequestWithoutResponse( const Message* message, ValidationContext* validation_context); // Validates that the message is a request expecting a response. -bool ValidateMessageIsRequestExpectingResponse( +MOJO_CPP_BINDINGS_EXPORT bool ValidateMessageIsRequestExpectingResponse( const Message* message, ValidationContext* validation_context); // Validates that the message is a response. -bool ValidateMessageIsResponse(const Message* message, - ValidationContext* validation_context); +MOJO_CPP_BINDINGS_EXPORT bool ValidateMessageIsResponse( + const Message* message, + ValidationContext* validation_context); // Validates that the message payload is a valid struct of type ParamsType. template <typename ParamsType> @@ -70,13 +81,6 @@ bool ValidateMessagePayload(const Message* message, 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> @@ -105,24 +109,28 @@ bool ValidateInlinedUnionNonNullable(const T& input, return false; } -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 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 ValidateHandleOrInterfaceNonNullable( +MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable( const AssociatedInterface_Data& input, const char* error_message, ValidationContext* validation_context); -bool ValidateHandleOrInterfaceNonNullable( - const AssociatedInterfaceRequest_Data& input, +MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable( + const AssociatedEndpointHandle_Data& input, const char* error_message, ValidationContext* validation_context); -bool ValidateHandleOrInterfaceNonNullable( +MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable( const Interface_Data& input, const char* error_message, ValidationContext* validation_context); -bool ValidateHandleOrInterfaceNonNullable( +MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable( const Handle_Data& input, const char* error_message, ValidationContext* validation_context); @@ -131,6 +139,12 @@ template <typename T> 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); } @@ -138,6 +152,12 @@ bool ValidateContainer(const Pointer<T>& input, 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); } @@ -145,24 +165,40 @@ bool ValidateStruct(const Pointer<T>& input, 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); } -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); +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); } // 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 edbf27b..cb24bc4 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/lib/clone_equals_util.h" +#include "mojo/public/cpp/bindings/clone_traits.h" +#include "mojo/public/cpp/bindings/lib/equals_traits.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 @@ struct CloneTraits<WTF::Vector<T>, false> { WTF::Vector<T> result; result.reserveCapacity(input.size()); for (const auto& element : input) - result.append(internal::Clone(element)); + result.push_back(mojo::Clone(element)); return result; } @@ -34,11 +34,13 @@ struct CloneTraits<WTF::HashMap<K, V>, false> { WTF::HashMap<K, V> result; auto input_end = input.end(); for (auto it = input.begin(); it != input_end; ++it) - result.add(internal::Clone(it->key), internal::Clone(it->value)); + result.add(mojo::Clone(it->key), mojo::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 132e19c..0f112b9 100644 --- a/mojo/public/cpp/bindings/lib/wtf_serialization.h +++ b/mojo/public/cpp/bindings/lib/wtf_serialization.h @@ -5,9 +5,7 @@ #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 d4c7952..c1ba075 100644 --- a/mojo/public/cpp/bindings/map.h +++ b/mojo/public/cpp/bindings/map.h @@ -5,299 +5,37 @@ #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 { -// 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; - - // Map keys cannot be move only classes. - static_assert(!internal::IsMoveOnlyType<Key>::value, - "Map keys cannot be move only types."); - - using Iterator = typename std::map<Key, Value>::iterator; - using ConstIterator = typename std::map<Key, Value>::const_iterator; - - // 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; - } -}; +// 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()); +} + +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())); +} + +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()); +} + +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())); +} } // namespace mojo diff --git a/mojo/public/cpp/bindings/map_data_view.h b/mojo/public/cpp/bindings/map_data_view.h new file mode 100644 index 0000000..a65bb9e --- /dev/null +++ b/mojo/public/cpp/bindings/map_data_view.h @@ -0,0 +1,63 @@ +// 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 01dd66d..5c0d8b2 100644 --- a/mojo/public/cpp/bindings/map_traits.h +++ b/mojo/public/cpp/bindings/map_traits.h @@ -37,13 +37,13 @@ namespace mojo { // static const V& GetValue(CustomConstIterator& iterator); // // // Returning false results in deserialization failure and causes the -// // message pipe receiving it to be disconnected. +// // 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> // static bool Insert(CustomMap<K, V>& input, -// const K& key, -// V&& value); -// static bool Insert(CustomMap<K, V>& input, -// const K& key, -// const V& value); +// IK&& key, +// IV&& 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 deleted file mode 100644 index 0c76890..0000000 --- a/mojo/public/cpp/bindings/map_traits_standard.h +++ /dev/null @@ -1,53 +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_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 ff79a20..83a4399 100644 --- a/mojo/public/cpp/bindings/map_traits_stl.h +++ b/mojo/public/cpp/bindings/map_traits_stl.h @@ -94,14 +94,10 @@ struct MapTraits<std::unordered_map<K, V>> { static V& GetValue(Iterator& iterator) { return iterator->second; } static const V& GetValue(ConstIterator& iterator) { return iterator->second; } - 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)); + 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))); return true; } diff --git a/mojo/public/cpp/bindings/map_traits_wtf.h b/mojo/public/cpp/bindings/map_traits_wtf.h deleted file mode 100644 index 805e4c9..0000000 --- a/mojo/public/cpp/bindings/map_traits_wtf.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_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 4392201..dd68b36 100644 --- a/mojo/public/cpp/bindings/map_traits_wtf_hash_map.h +++ b/mojo/public/cpp/bindings/map_traits_wtf_hash_map.h @@ -46,20 +46,13 @@ struct MapTraits<WTF::HashMap<K, V>> { static V& GetValue(Iterator& iterator) { return iterator->value; } static const V& GetValue(ConstIterator& iterator) { return iterator->value; } - static bool Insert(WTF::HashMap<K, V>& input, const K& key, V&& value) { + template <typename IK, typename IV> + static bool Insert(WTF::HashMap<K, V>& input, IK&& key, IV&& value) { if (!WTF::HashMap<K, V>::isValidKey(key)) { - LOG(ERROR) << "The key value is disallowed by WTF::HashMap: " << key; + LOG(ERROR) << "The key value is disallowed by WTF::HashMap"; return false; } - 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); + input.insert(std::forward<IK>(key), std::forward<IV>(value)); return true; } diff --git a/mojo/public/cpp/bindings/message.h b/mojo/public/cpp/bindings/message.h index e758432..65d6cec 100644 --- a/mojo/public/cpp/bindings/message.h +++ b/mojo/public/cpp/bindings/message.h @@ -13,26 +13,46 @@ #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 Message { +class MOJO_CPP_BINDINGS_EXPORT 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); @@ -41,10 +61,9 @@ class Message { uint32_t num_bytes, std::vector<Handle>* handles); - // Transfers data and handles to |destination|. - void MoveTo(Message* destination); - - uint32_t data_num_bytes() const { return buffer_->data_num_bytes(); } + uint32_t data_num_bytes() const { + return static_cast<uint32_t>(buffer_->size()); + } // Access the raw bytes of the message. const uint8_t* data() const { @@ -57,12 +76,30 @@ class Message { const internal::MessageHeader* header() const { return static_cast<const internal::MessageHeader*>(buffer_->data()); } - internal::MessageHeader* header() { - return const_cast<internal::MessageHeader*>( - static_cast<const Message*>(this)->header()); + return static_cast<internal::MessageHeader*>(buffer_->data()); + } + + 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; } @@ -70,32 +107,32 @@ class Message { bool has_flag(uint32_t flag) const { return !!(header()->flags & flag); } // Access the request_id field (if present). - 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; - } + uint64_t request_id() const { return header_v1()->request_id; } void set_request_id(uint64_t request_id) { - DCHECK(has_request_id()); - static_cast<internal::MessageHeaderWithRequestID*>(header()) - ->request_id = request_id; + header_v1()->request_id = request_id; } // Access the payload. - const uint8_t* payload() const { return data() + header()->num_bytes; } + const uint8_t* payload() const; uint8_t* mutable_payload() { return const_cast<uint8_t*>(payload()); } - 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); - } + uint32_t payload_num_bytes() const; + + uint32_t payload_num_interface_ids() const; + const uint32_t* payload_interface_ids() const; // 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(); } @@ -108,11 +145,22 @@ class Message { // 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); }; @@ -186,6 +234,57 @@ class MessageReceiverWithResponderStatus : public MessageReceiver { 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 @@ -195,6 +294,22 @@ class MessageReceiverWithResponderStatus : public MessageReceiver { // 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 deleted file mode 100644 index 638c53b..0000000 --- a/mojo/public/cpp/bindings/message_filter.h +++ /dev/null @@ -1,38 +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_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 3bcbd0a..50c19db 100644 --- a/mojo/public/cpp/bindings/message_header_validator.h +++ b/mojo/public/cpp/bindings/message_header_validator.h @@ -5,16 +5,17 @@ #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 MessageHeaderValidator : public MessageFilter { +class MOJO_CPP_BINDINGS_EXPORT MessageHeaderValidator + : NON_EXPORTED_BASE(public MessageReceiver) { public: - explicit MessageHeaderValidator(MessageReceiver* sink = nullptr); - MessageHeaderValidator(const std::string& description, - MessageReceiver* sink = nullptr); + MessageHeaderValidator(); + explicit MessageHeaderValidator(const std::string& description); // 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 882c970..ac27250 100644 --- a/mojo/public/cpp/bindings/native_struct.h +++ b/mojo/public/cpp/bindings/native_struct.h @@ -5,7 +5,10 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_H_ #define MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_H_ -#include "mojo/public/cpp/bindings/array.h" +#include <vector> + +#include "base/optional.h" +#include "mojo/public/cpp/bindings/bindings_export.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" @@ -17,7 +20,7 @@ using NativeStructPtr = StructPtr<NativeStruct>; // Native-only structs correspond to "[Native] struct Foo;" definitions in // mojom. -class NativeStruct { +class MOJO_CPP_BINDINGS_EXPORT NativeStruct { public: using Data_ = internal::NativeStruct_Data; @@ -38,8 +41,9 @@ class NativeStruct { NativeStructPtr Clone() const; bool Equals(const NativeStruct& other) const; + size_t Hash(size_t seed) const; - Array<uint8_t> data; + base::Optional<std::vector<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 new file mode 100644 index 0000000..613bd7a --- /dev/null +++ b/mojo/public/cpp/bindings/native_struct_data_view.h @@ -0,0 +1,36 @@ +// 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 deleted file mode 100644 index d8915cd..0000000 --- a/mojo/public/cpp/bindings/no_interface.h +++ /dev/null @@ -1,52 +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_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 b387b06..a5c04da 100644 --- a/mojo/public/cpp/bindings/pipe_control_message_handler.h +++ b/mojo/public/cpp/bindings/pipe_control_message_handler.h @@ -5,9 +5,11 @@ #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/interface_id.h" -#include "mojo/public/cpp/bindings/lib/serialization_context.h" +#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/message.h" namespace mojo { @@ -15,7 +17,8 @@ namespace mojo { class PipeControlMessageHandlerDelegate; // Handler for messages defined in pipe_control_messages.mojom. -class PipeControlMessageHandler : public MessageReceiver { +class MOJO_CPP_BINDINGS_EXPORT PipeControlMessageHandler + : NON_EXPORTED_BASE(public MessageReceiver) { public: explicit PipeControlMessageHandler( PipeControlMessageHandlerDelegate* delegate); @@ -43,7 +46,6 @@ class PipeControlMessageHandler : public MessageReceiver { 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 06f0695..16fd918 100644 --- a/mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h +++ b/mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h @@ -5,6 +5,8 @@ #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 { @@ -14,8 +16,9 @@ class PipeControlMessageHandlerDelegate { // 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) = 0; - virtual bool OnAssociatedEndpointClosedBeforeSent(InterfaceId id) = 0; + virtual bool OnPeerAssociatedEndpointClosed( + InterfaceId id, + const base::Optional<DisconnectReason>& reason) = 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 7f3e006..52c408f 100644 --- a/mojo/public/cpp/bindings/pipe_control_message_proxy.h +++ b/mojo/public/cpp/bindings/pipe_control_message_proxy.h @@ -6,26 +6,36 @@ #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. -class PipeControlMessageProxy { +// +// NOTE: This object may be used from multiple threads. +class MOJO_CPP_BINDINGS_EXPORT PipeControlMessageProxy { public: - // Doesn't take ownership of |receiver|. It must outlive this object. + // Doesn't take ownership of |receiver|. If This PipeControlMessageProxy will + // be used from multiple threads, |receiver| must be thread-safe. explicit PipeControlMessageProxy(MessageReceiver* receiver); - void NotifyPeerEndpointClosed(InterfaceId id); - void NotifyEndpointClosedBeforeSent(InterfaceId id); + void NotifyPeerEndpointClosed(InterfaceId id, + const base::Optional<DisconnectReason>& reason); + + static Message ConstructPeerEndpointClosedMessage( + InterfaceId id, + const base::Optional<DisconnectReason>& reason); 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 new file mode 100644 index 0000000..4d40cdf --- /dev/null +++ b/mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h @@ -0,0 +1,22 @@ +// 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 1f45b0c..16527cf 100644 --- a/mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h +++ b/mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h @@ -5,8 +5,16 @@ #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 { @@ -15,8 +23,16 @@ class AssociatedGroupController; // ScopedInterfaceEndpointHandle refers to one end of an interface, either the // implementation side or the client side. -class ScopedInterfaceEndpointHandle { +// Threading: At any given time, a ScopedInterfaceEndpointHandle should only +// be accessed from a single thread. +class MOJO_CPP_BINDINGS_EXPORT 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(); @@ -27,39 +43,77 @@ class ScopedInterfaceEndpointHandle { ScopedInterfaceEndpointHandle& operator=( ScopedInterfaceEndpointHandle&& other); - bool is_valid() const { return IsValidInterfaceId(id_); } + bool is_valid() const; - bool is_local() const { return is_local_; } + // Returns true if the interface hasn't associated with a message pipe. + bool pending_association() const; - void reset(); - void swap(ScopedInterfaceEndpointHandle& other); + // Returns kInvalidInterfaceId when in pending association state or the handle + // is invalid. + InterfaceId id() const; - // DO NOT USE METHODS BELOW THIS LINE. These are for internal use and testing. + // Returns null when in pending association state or the handle is invalid. + AssociatedGroupController* group_controller() const; - InterfaceId id() const { return id_; } + // 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; - AssociatedGroupController* group_controller() const { - return group_controller_.get(); - } + 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 + }; - // Releases the handle without closing it. - InterfaceId release(); + 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); + + void reset(); + void ResetWithReason(uint32_t custom_reason, const std::string& description); private: friend class AssociatedGroupController; + friend class AssociatedGroup; + + class State; - // 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. + // Used by AssociatedGroupController. ScopedInterfaceEndpointHandle( InterfaceId id, - bool is_local, scoped_refptr<AssociatedGroupController> group_controller); - 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_; DISALLOW_COPY_AND_ASSIGN(ScopedInterfaceEndpointHandle); }; diff --git a/mojo/public/cpp/bindings/stl_converters.h b/mojo/public/cpp/bindings/stl_converters.h deleted file mode 100644 index 92b2924..0000000 --- a/mojo/public/cpp/bindings/stl_converters.h +++ /dev/null @@ -1,245 +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_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 deleted file mode 100644 index 7cfd713..0000000 --- a/mojo/public/cpp/bindings/string.h +++ /dev/null @@ -1,196 +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_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 new file mode 100644 index 0000000..2b091b4 --- /dev/null +++ b/mojo/public/cpp/bindings/string_data_view.h @@ -0,0 +1,34 @@ +// 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 a6ade6f..7d3075a 100644 --- a/mojo/public/cpp/bindings/string_traits.h +++ b/mojo/public/cpp/bindings/string_traits.h @@ -5,26 +5,10 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_H_ #define MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_H_ -#include "base/logging.h" -#include "mojo/public/cpp/bindings/lib/array_internal.h" +#include "mojo/public/cpp/bindings/string_data_view.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. // @@ -40,6 +24,7 @@ class StringDataView { // 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 deleted file mode 100644 index 9b78d24..0000000 --- a/mojo/public/cpp/bindings/string_traits_standard.h +++ /dev/null @@ -1,31 +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_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 5a08908..f96973a 100644 --- a/mojo/public/cpp/bindings/string_traits_string16.h +++ b/mojo/public/cpp/bindings/string_traits_string16.h @@ -6,12 +6,13 @@ #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 StringTraits<base::string16> { +struct MOJO_CPP_BINDINGS_EXPORT 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 7fb7eea..f4b4a06 100644 --- a/mojo/public/cpp/bindings/strong_binding.h +++ b/mojo/public/cpp/bindings/strong_binding.h @@ -5,122 +5,121 @@ #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. Deleting the -// connector also closes the pipe. -// -// Example of an implementation that is always bound strongly to a pipe +// 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. // -// class StronglyBound : public Foo { -// public: -// explicit StronglyBound(InterfaceRequest<Foo> request) -// : binding_(this, std::move(request)) {} +// To use, call StrongBinding<T>::Create() (see below) or the helper +// MakeStrongBinding function: // -// // Foo implementation here +// mojo::MakeStrongBinding(base::MakeUnique<FooImpl>(), +// std::move(foo_request)); // -// 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: - explicit StrongBinding(Interface* impl) : binding_(impl) {} - - StrongBinding(Interface* impl, ScopedMessagePipeHandle handle) - : StrongBinding(impl) { - Bind(std::move(handle)); + // 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(); } - StrongBinding(Interface* impl, InterfacePtr<Interface>* ptr) - : StrongBinding(impl) { - Bind(ptr); + // Note: The error handler must not delete the interface implementation. + // + // This method may only be called after this StrongBinding has been bound to a + // message pipe. + void set_connection_error_handler(const base::Closure& error_handler) { + DCHECK(binding_.is_bound()); + connection_error_handler_ = error_handler; + connection_error_with_reason_handler_.Reset(); } - StrongBinding(Interface* impl, InterfaceRequest<Interface> request) - : StrongBinding(impl) { - Bind(std::move(request)); + 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(); } - ~StrongBinding() {} + // Forces the binding to close. This destroys the StrongBinding instance. + void Close() { delete this; } - 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))); - } + Interface* impl() { return impl_.get(); } - void Bind(InterfacePtr<Interface>* ptr) { - DCHECK(!binding_.is_bound()); - binding_.Bind(ptr); - binding_.set_connection_error_handler( - base::Bind(&StrongBinding::OnConnectionError, base::Unretained(this))); - } + // 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(); } - void Bind(InterfaceRequest<Interface> request) { - DCHECK(!binding_.is_bound()); - binding_.Bind(std::move(request)); - binding_.set_connection_error_handler( + 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))); } - bool WaitForIncomingMethodCall() { - return binding_.WaitForIncomingMethodCall(); - } - - // Note: The error handler must not delete the interface implementation. - // - // This method may only be called after this StrongBinding has been bound to a - // message pipe. - void set_connection_error_handler(const base::Closure& error_handler) { - DCHECK(binding_.is_bound()); - connection_error_handler_ = error_handler; - } - - Interface* impl() { return binding_.impl(); } - // Exposed for testing, should not generally be used. - internal::Router* internal_router() { return binding_.internal_router(); } + ~StrongBinding() {} - void OnConnectionError() { + void OnConnectionError(uint32_t custom_reason, + const std::string& description) { if (!connection_error_handler_.is_null()) connection_error_handler_.Run(); - delete binding_.impl(); + else if (!connection_error_with_reason_handler_.is_null()) + connection_error_with_reason_handler_.Run(custom_reason, description); + Close(); } - private: + std::unique_ptr<Interface> impl_; 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 92f2728..b135312 100644 --- a/mojo/public/cpp/bindings/struct_ptr.h +++ b/mojo/public/cpp/bindings/struct_ptr.h @@ -5,23 +5,26 @@ #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 StructHelper { - public: - template <typename Ptr> - static void Initialize(Ptr* ptr) { - ptr->Initialize(); - } -}; +class StructPtrWTFHelper; + +template <typename Struct> +class InlinedStructPtrWTFHelper; } // namespace internal @@ -31,35 +34,34 @@ class StructPtr { public: using Struct = S; - StructPtr() : ptr_(nullptr) {} - StructPtr(decltype(nullptr)) : ptr_(nullptr) {} + StructPtr() = default; + StructPtr(decltype(nullptr)) {} - ~StructPtr() { delete ptr_; } + ~StructPtr() = default; StructPtr& operator=(decltype(nullptr)) { reset(); return *this; } - StructPtr(StructPtr&& other) : ptr_(nullptr) { Take(&other); } + StructPtr(StructPtr&& other) { 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() { - if (ptr_) { - delete ptr_; - ptr_ = nullptr; - } - } + void reset() { ptr_.reset(); } - bool is_null() const { return ptr_ == nullptr; } + bool is_null() const { return !ptr_; } Struct& operator*() const { DCHECK(ptr_); @@ -67,9 +69,9 @@ class StructPtr { } Struct* operator->() const { DCHECK(ptr_); - return ptr_; + return ptr_.get(); } - Struct* get() const { return ptr_; } + Struct* get() const { return ptr_.get(); } void Swap(StructPtr* other) { std::swap(ptr_, other->ptr_); } @@ -78,52 +80,52 @@ class StructPtr { // 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_); } - private: - // TODO(dcheng): Use an explicit conversion operator. - typedef Struct* StructPtr::*Testable; + // 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); + } - public: - operator Testable() const { return ptr_ ? &StructPtr::ptr_ : 0; } + explicit operator bool() const { return !is_null(); } private: - 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(); - } - + friend class internal::StructPtrWTFHelper<Struct>; void Take(StructPtr* other) { reset(); Swap(other); } - Struct* ptr_; + std::unique_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() : is_null_(true) {} - InlinedStructPtr(decltype(nullptr)) : is_null_(true) {} + InlinedStructPtr() : state_(NIL) {} + InlinedStructPtr(decltype(nullptr)) : state_(NIL) {} ~InlinedStructPtr() {} @@ -132,79 +134,150 @@ class InlinedStructPtr { return *this; } - InlinedStructPtr(InlinedStructPtr&& other) : is_null_(true) { Take(&other); } + InlinedStructPtr(InlinedStructPtr&& other) : state_(NIL) { 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() { - is_null_ = true; + state_ = NIL; value_. ~Struct(); new (&value_) Struct(); } - bool is_null() const { return is_null_; } + bool is_null() const { return state_ == NIL; } Struct& operator*() const { - DCHECK(!is_null_); + DCHECK(state_ == VALID); return value_; } Struct* operator->() const { - DCHECK(!is_null_); + DCHECK(state_ == VALID); return &value_; } Struct* get() const { return &value_; } void Swap(InlinedStructPtr* other) { std::swap(value_, other->value_); - std::swap(is_null_, other->is_null_); + std::swap(state_, other->state_); } 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_); } - private: - // TODO(dcheng): Use an explicit conversion operator. - typedef Struct InlinedStructPtr::*Testable; + // 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); + } - public: - operator Testable() const { return is_null_ ? 0 : &InlinedStructPtr::value_; } + explicit operator bool() const { return !is_null(); } private: - 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; } - + friend class internal::InlinedStructPtrWTFHelper<Struct>; void Take(InlinedStructPtr* other) { reset(); Swap(other); } + enum State { + VALID, + NIL, + DELETED, // For use in WTF::HashMap only + }; + mutable Struct value_; - bool is_null_; + State state_; 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 0f0bea7..6cc070f 100644 --- a/mojo/public/cpp/bindings/struct_traits.h +++ b/mojo/public/cpp/bindings/struct_traits.h @@ -8,7 +8,11 @@ namespace mojo { // This must be specialized for any type |T| to be serialized/deserialized as -// a mojom struct of type |MojomType|. +// 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). // // Each specialization needs to implement a few things: // 1. Static getters for each field in the Mojom type. These should be @@ -20,19 +24,21 @@ namespace mojo { // from |input|. // // Serializable form of a field: -// Value or reference of the same type used in |MojomType|, or the -// following alternatives: +// Value or reference of the same type used in the generated stuct +// wrapper type, or the following alternatives: // - string: // Value or reference of any type that has a StringTraits defined. -// Supported by default: base::StringPiece, std::string. +// Supported by default: base::StringPiece, std::string, +// WTF::String (in blink). // // - array: // Value or reference of any type that has an ArrayTraits defined. -// Supported by default: std::vector, WTF::Vector (in blink), CArray. +// Supported by default: std::vector, CArray, WTF::Vector (in blink) // // - map: // Value or reference of any type that has a MapTraits defined. -// Supported by default: std::map. +// Supported by default: std::map, std::unordered_map, +// WTF::HashMap (in blink). // // - struct: // Value or reference of any type that has a StructTraits defined. @@ -40,6 +46,10 @@ namespace mojo { // - 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 @@ -49,13 +59,13 @@ namespace mojo { // Getters for fields of other types are called once. // // 2. A static Read() method to set the contents of a |T| instance from a -// |MojomType|DataView (e.g., if |MojomType| is test::Example, the data -// view will be test::ExampleDataView). +// DataViewType. // -// static bool Read(|MojomType|DataView data, T* output); +// static bool Read(DataViewType data, T* output); // -// The generated |MojomType|DataView type provides a convenient, -// inexpensive view of a serialized struct's field data. +// The generated DataViewType provides a convenient, inexpensive view of a +// serialized struct's field data. The caller guarantees that +// |!data.is_null()|. // // Returning false indicates invalid incoming data and causes the message // pipe receiving it to be disconnected. Therefore, you can do custom @@ -111,9 +121,12 @@ namespace mojo { // 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(pass_by_value)" ] -// - if another type U's StructTraits has a getter for T, it needs to return -// non-const reference/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. // // EXAMPLE: // @@ -128,7 +141,7 @@ namespace mojo { // // StructTraits for Foo: // template <> -// struct StructTraits<Foo, CustomFoo> { +// struct StructTraits<FooDataView, CustomFoo> { // // Optional methods dealing with null: // static bool IsNull(const CustomFoo& input); // static void SetToNull(CustomFoo* output); @@ -144,7 +157,7 @@ namespace mojo { // static bool Read(FooDataView data, CustomFoo* output); // }; // -template <typename MojomType, typename T> +template <typename DataViewType, 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 e8fc1c4..39a77a8 100644 --- a/mojo/public/cpp/bindings/sync_call_restrictions.h +++ b/mojo/public/cpp/bindings/sync_call_restrictions.h @@ -7,6 +7,7 @@ #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 @@ -14,8 +15,12 @@ #define ENABLE_SYNC_CALL_RESTRICTIONS 0 #endif +namespace leveldb { +class LevelDBMojoProxy; +} + namespace ui { -class GpuService; +class Gpu; } namespace views { @@ -36,7 +41,7 @@ namespace mojo { // 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 SyncCallRestrictions { +class MOJO_CPP_BINDINGS_EXPORT SyncCallRestrictions { public: #if ENABLE_SYNC_CALL_RESTRICTIONS // Checks whether the current thread is allowed to make sync calls, and causes @@ -50,7 +55,9 @@ class SyncCallRestrictions { private: // DO NOT ADD ANY OTHER FRIEND STATEMENTS, talk to mojo/OWNERS first. // BEGIN ALLOWED USAGE. - friend class ui::GpuService; // http://crbug.com/620058 + friend class ui::Gpu; // http://crbug.com/620058 + // LevelDBMojoProxy makes same-process sync calls from the DB thread. + friend class leveldb::LevelDBMojoProxy; // 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 6c0701e..b5415af 100644 --- a/mojo/public/cpp/bindings/sync_handle_registry.h +++ b/mojo/public/cpp/bindings/sync_handle_registry.h @@ -11,6 +11,7 @@ #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 { @@ -19,7 +20,8 @@ namespace mojo { // be watched together. // // This class is not thread safe. -class SyncHandleRegistry : public base::RefCounted<SyncHandleRegistry> { +class MOJO_CPP_BINDINGS_EXPORT 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 36b796b..eff73dd 100644 --- a/mojo/public/cpp/bindings/sync_handle_watcher.h +++ b/mojo/public/cpp/bindings/sync_handle_watcher.h @@ -8,6 +8,7 @@ #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" @@ -25,7 +26,7 @@ namespace mojo { // associated endpoints on different threads. // // This class is not thread safe. -class SyncHandleWatcher { +class MOJO_CPP_BINDINGS_EXPORT 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 new file mode 100644 index 0000000..bab6d22 --- /dev/null +++ b/mojo/public/cpp/bindings/thread_safe_interface_ptr.h @@ -0,0 +1,278 @@ +// 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 1446ab3..395eeb4 100644 --- a/mojo/public/cpp/bindings/type_converter.h +++ b/mojo/public/cpp/bindings/type_converter.h @@ -7,8 +7,15 @@ #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 @@ -74,6 +81,9 @@ namespace mojo { 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> @@ -81,6 +91,18 @@ struct TypeConverter<T, T> { 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 new file mode 100644 index 0000000..292ee58 --- /dev/null +++ b/mojo/public/cpp/bindings/union_traits.h @@ -0,0 +1,39 @@ +// 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 deleted file mode 100644 index 46d9a69..0000000 --- a/mojo/public/cpp/bindings/wtf_array.h +++ /dev/null @@ -1,197 +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_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 deleted file mode 100644 index 0aba959..0000000 --- a/mojo/public/cpp/bindings/wtf_map.h +++ /dev/null @@ -1,200 +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_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 8dcec71..0dc7af9 100644 --- a/mojo/public/cpp/system/BUILD.gn +++ b/mojo/public/cpp/system/BUILD.gn @@ -2,7 +2,26 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -source_set("system") { +# 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" + sources = [ "buffer.cc", "buffer.h", @@ -14,6 +33,7 @@ source_set("system") { "message_pipe.h", "platform_handle.cc", "platform_handle.h", + "system_export.h", "watcher.cc", "watcher.h", ] @@ -22,4 +42,9 @@ source_set("system") { "//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 449c6ce..1ae923c 100644 --- a/mojo/public/cpp/system/buffer.h +++ b/mojo/public/cpp/system/buffer.h @@ -20,6 +20,7 @@ #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 { @@ -41,7 +42,8 @@ typedef ScopedHandleBase<SharedBufferHandle> ScopedSharedBufferHandle; // A strongly-typed representation of a |MojoHandle| referring to a shared // buffer. -class SharedBufferHandle : public Handle { +class MOJO_CPP_SYSTEM_EXPORT SharedBufferHandle + : NON_EXPORTED_BASE(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 e4a6088..42e4aba 100644 --- a/mojo/public/cpp/system/platform_handle.cc +++ b/mojo/public/cpp/system/platform_handle.cc @@ -4,6 +4,11 @@ #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 { @@ -61,6 +66,13 @@ ScopedSharedBufferHandle WrapSharedMemoryHandle( 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; @@ -91,6 +103,8 @@ MojoResult UnwrapSharedMemoryHandle(ScopedSharedBufferHandle handle, 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); @@ -126,4 +140,39 @@ MojoResult UnwrapSharedMemoryHandle(ScopedSharedBufferHandle handle, 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 2a81734..801264e 100644 --- a/mojo/public/cpp/system/platform_handle.h +++ b/mojo/public/cpp/system/platform_handle.h @@ -22,6 +22,7 @@ #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> @@ -50,15 +51,18 @@ const MojoPlatformHandleType kPlatformSharedBufferHandleType = #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, @@ -66,10 +70,22 @@ ScopedSharedBufferHandle WrapSharedMemoryHandle( // Unwraps a base::SharedMemoryHandle from a Mojo handle. The caller assumes // responsibility for the lifetime of the SharedMemoryHandle. -MojoResult UnwrapSharedMemoryHandle(ScopedSharedBufferHandle handle, - base::SharedMemoryHandle* memory_handle, - size_t* size, - bool* read_only); +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) } // namespace mojo diff --git a/mojo/public/cpp/system/system_export.h b/mojo/public/cpp/system/system_export.h new file mode 100644 index 0000000..c9bb140 --- /dev/null +++ b/mojo/public/cpp/system/system_export.h @@ -0,0 +1,34 @@ +// 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 d9319fb..55dcf40 100644 --- a/mojo/public/cpp/system/watcher.cc +++ b/mojo/public/cpp/system/watcher.cc @@ -7,49 +7,17 @@ #include "base/bind.h" #include "base/location.h" #include "base/macros.h" -#include "base/message_loop/message_loop.h" +#include "base/trace_event/heap_profiler.h" #include "mojo/public/c/system/functions.h" namespace mojo { -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) +Watcher::Watcher(const tracked_objects::Location& from_here, + 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(); @@ -72,7 +40,6 @@ MojoResult Watcher::Start(Handle handle, DCHECK(!IsWatching()); DCHECK(!callback.is_null()); - message_loop_observer_.reset(new MessageLoopObserver(this)); callback_ = callback; handle_ = handle; MojoResult result = MojoWatch(handle_.value(), signals, @@ -81,7 +48,6 @@ MojoResult Watcher::Start(Handle handle, 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; @@ -99,7 +65,6 @@ void Watcher::Cancel() { 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); @@ -112,14 +77,15 @@ void Watcher::OnHandleReady(MojoResult result) { 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()) + if (!callback.is_null()) { + TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION event(heap_profiler_tag_); callback.Run(result); + } } // static @@ -133,6 +99,7 @@ void Watcher::CallOnHandleReady(uintptr_t context, // 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 82f3e81..236788b 100644 --- a/mojo/public/cpp/system/watcher.h +++ b/mojo/public/cpp/system/watcher.h @@ -5,8 +5,6 @@ #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" @@ -16,13 +14,14 @@ #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 Watcher { +class MOJO_CPP_SYSTEM_EXPORT 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 @@ -35,15 +34,11 @@ class Watcher { // // |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)>; - explicit Watcher(scoped_refptr<base::SingleThreadTaskRunner> runner = - base::ThreadTaskRunnerHandle::Get()); + Watcher(const tracked_objects::Location& from_here, + scoped_refptr<base::SingleThreadTaskRunner> runner = + base::ThreadTaskRunnerHandle::Get()); // NOTE: This destructor automatically calls |Cancel()| if the Watcher is // still active. @@ -80,10 +75,13 @@ class Watcher { Handle handle() const { return handle_; } ReadyCallback ready_callback() const { return callback_; } - private: - class MessageLoopObserver; - friend class MessageLoopObserver; + // 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: void OnHandleReady(MojoResult result); static void CallOnHandleReady(uintptr_t context, @@ -100,8 +98,6 @@ class Watcher { // 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. @@ -115,6 +111,10 @@ class Watcher { // 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 3d33b4a..efa1712 100644 --- a/mojo/public/cpp/test_support/BUILD.gn +++ b/mojo/public/cpp/test_support/BUILD.gn @@ -2,7 +2,6 @@ # 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 654145b..fb11ec2 100644 --- a/mojo/public/interfaces/BUILD.gn +++ b/mojo/public/interfaces/BUILD.gn @@ -4,8 +4,6 @@ group("interfaces") { deps = [ - "application", "bindings", - "network", ] } diff --git a/mojo/public/interfaces/bindings/BUILD.gn b/mojo/public/interfaces/bindings/BUILD.gn index c7421b9..eab75c1 100644 --- a/mojo/public/interfaces/bindings/BUILD.gn +++ b/mojo/public/interfaces/bindings/BUILD.gn @@ -5,8 +5,13 @@ 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 2143c06..0a19042 100644 --- a/mojo/public/interfaces/bindings/interface_control_messages.mojom +++ b/mojo/public/interfaces/bindings/interface_control_messages.mojom @@ -2,12 +2,11 @@ // 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"] -module mojo; +[JavaPackage="org.chromium.mojo.bindings.interfacecontrol"] +module mojo.interface_control; -// 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. +// For each user-defined interface, some control functions are provided by the +// interface endpoints at both sides. //////////////////////////////////////////////////////////////////////////////// // Run@0xFFFFFFFF(RunInput input) => (RunOutput? output); @@ -15,75 +14,54 @@ module mojo; // 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 { - // 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; + RunInput input; +}; +union RunInput { QueryVersion query_version; + FlushForTesting flush_for_testing; }; struct RunResponseMessageParams { - // 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. + RunOutput? output; +}; +union RunOutput { 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 { - // 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. + RunOrClosePipeInput input; +}; +union RunOrClosePipeInput { 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 c743ffe..c1453fc 100644 --- a/mojo/public/interfaces/bindings/pipe_control_messages.mojom +++ b/mojo/public/interfaces/bindings/pipe_control_messages.mojom @@ -25,26 +25,21 @@ struct RunOrClosePipeMessageParams { union RunOrClosePipeInput { PeerAssociatedEndpointClosedEvent peer_associated_endpoint_closed_event; - 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 only used for associated interfaces. When a master interface -// is closed, the message pipe is shutdown directly. -struct PeerAssociatedEndpointClosedEvent { - // The interface ID. - uint32 id; +// A user-defined reason about why the interface is disconnected. +struct DisconnectReason { + uint32 custom_reason; + string description; }; -// 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. +// An event to notify that an interface endpoint set up at the message sender +// side has been closed. // -// This event is only used for associated interfaces. -struct AssociatedEndpointClosedBeforeSentEvent { +// This event is omitted if the endpoint belongs to the master interface and +// there is no disconnect reason specified. +struct PeerAssociatedEndpointClosedEvent { // The interface ID. uint32 id; + DisconnectReason? disconnect_reason; }; - diff --git a/mojo/public/interfaces/bindings/tests/BUILD.gn b/mojo/public/interfaces/bindings/tests/BUILD.gn index 0a48076..9b10db0 100644 --- a/mojo/public/interfaces/bindings/tests/BUILD.gn +++ b/mojo/public/interfaces/bindings/tests/BUILD.gn @@ -17,7 +17,9 @@ mojom("test_interfaces") { "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", @@ -27,8 +29,59 @@ mojom("test_interfaces") { ":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" ] + } +} - use_new_wrapper_types = true +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" ] + } } mojom("test_mojom_import") { @@ -36,7 +89,6 @@ mojom("test_mojom_import") { sources = [ "sample_import.mojom", ] - use_new_wrapper_types = true } mojom("test_mojom_import_wrapper") { @@ -62,7 +114,6 @@ mojom("test_mojom_import2") { ":test_mojom_import", ":test_mojom_import_wrapper_wrapper", ] - use_new_wrapper_types = true } mojom("test_struct_traits_interfaces") { @@ -70,7 +121,6 @@ mojom("test_struct_traits_interfaces") { sources = [ "struct_with_traits.mojom", ] - use_new_wrapper_types = true } mojom("test_interfaces_experimental") { @@ -78,7 +128,6 @@ mojom("test_interfaces_experimental") { sources = [ "test_unions.mojom", ] - use_new_wrapper_types = true } mojom("test_associated_interfaces") { @@ -89,7 +138,10 @@ mojom("test_associated_interfaces") { "test_associated_interfaces.mojom", "validation_test_associated_interfaces.mojom", ] - use_new_wrapper_types = true + + public_deps = [ + ":test_interfaces", + ] } mojom("versioning_test_service_interfaces") { @@ -97,7 +149,6 @@ mojom("versioning_test_service_interfaces") { sources = [ "versioning_test_service.mojom", ] - use_new_wrapper_types = true } mojom("versioning_test_client_interfaces") { @@ -105,7 +156,6 @@ mojom("versioning_test_client_interfaces") { sources = [ "versioning_test_client.mojom", ] - use_new_wrapper_types = true } mojom("test_wtf_types") { @@ -114,7 +164,6 @@ mojom("test_wtf_types") { 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 deleted file mode 100644 index b288878..0000000 --- a/mojo/public/interfaces/bindings/tests/data/message_data +++ /dev/null @@ -1,25 +0,0 @@ -// 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 efac7ed..b797fea 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,13 +1,26 @@ [dist4]message_header // num_bytes -[u4]0 // version +[u4]2 // version [u4]0 // interface ID [u4]0 // name [u4]0 // flags [u4]0 // padding +[u8]0 // request_id +[dist8]payload +[dist8]payload_interface_ids [anchr]message_header +[anchr]payload [dist4]method0_params // num_bytes [u4]0 // version -[u4]4 // associated interface pointer: interface ID +[u4]1 // associated interface pointer: interface ID index [u4]1 // associated interface pointer: version [anchr]method0_params + +[anchr]payload_interface_ids +[dist4]interface_id_array // num_bytes +[u4]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 dd16401..b785ed1 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,14 +1,25 @@ [dist4]message_header // num_bytes -[u4]0 // version +[u4]2 // version [u4]0 // interface ID [u4]0 // name [u4]0 // flags [u4]0 // padding +[u8]0 // request_id +[dist8]payload +[dist8]payload_interface_ids [anchr]message_header +[anchr]payload [dist4]method0_params // num_bytes [u4]0 // version -[u4]0xFFFFFFFF // associated interface pointer: unexpected invalid - // interface ID +[u4]0xFFFFFFFF // associated interface pointer: Unexpected invalid + // interface ID index. [u4]1 // associated interface pointer: version [anchr]method0_params + +[anchr]payload_interface_ids +[dist4]interface_id_array // num_bytes +[u4]2 // num_elements +[u4]3 +[u4]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 7ebbe07..efa2162 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,13 +1,26 @@ [dist4]message_header // num_bytes -[u4]0 // version +[u4]2 // 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]4 // associated interface request +[u4]1 // associated interface request: interface ID index [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 d74a306..5a66aad 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,14 +1,27 @@ [dist4]message_header // num_bytes -[u4]0 // version +[u4]2 // 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 +[u4]0xFFFFFFFF // associated interface request: Unexpected invalid + // interface ID index. [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 deleted file mode 100644 index 511cd0d..0000000 --- a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.data +++ /dev/null @@ -1,15 +0,0 @@ -[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 deleted file mode 100644 index 420b421..0000000 --- a/mojo/public/interfaces/bindings/tests/data/validation/associated_conformance_mthd2_master_interface_id.expected +++ /dev/null @@ -1 +0,0 @@ -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 da22db1..13df01e 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,11 +1,15 @@ [dist4]message_header // num_bytes -[u4]0 // version +[u4]2 // version [u4]0 // interface ID [u4]3 // name [u4]0 // flags [u4]0 // padding +[u8]0 // request_id +[dist8]payload +[dist8]payload_interface_ids [anchr]message_header +[anchr]payload [dist4]method3_params // num_bytes [u4]0 // version [dist8]param0_ptr // param0 @@ -14,8 +18,18 @@ [anchr]param0_ptr [dist4]associated_interface_array // num_bytes [u4]2 // num_elements -[u4]4 // interface ID +[u4]2 // interface ID index [u4]14 // version -[u4]5 // interface ID +[u4]3 // interface ID index [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 788cefa..2e163be 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,11 +1,15 @@ [dist4]message_header // num_bytes -[u4]0 // version +[u4]2 // version [u4]0 // interface ID [u4]3 // name [u4]0 // flags [u4]0 // padding +[u8]0 // request_id +[dist8]payload +[dist8]payload_interface_ids [anchr]message_header +[anchr]payload [dist4]method3_params // num_bytes [u4]0 // version [dist8]param0_ptr // param0 @@ -14,8 +18,19 @@ [anchr]param0_ptr [dist4]associated_interface_array // num_bytes [u4]2 // num_elements -[u4]0xFFFFFFFF // unexpected invalid interface ID +[u4]2 // interface ID index [u4]14 // version -[u4]5 // interface ID +[u4]0xFFFFFFFF // interface ID index: Unexpected invalid + // value. [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 1cd3484..0a46e0a 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 833c76b..4ecc4d9 100644 --- a/mojo/public/interfaces/bindings/tests/rect.mojom +++ b/mojo/public/interfaces/bindings/tests/rect.mojom @@ -12,11 +12,20 @@ struct Rect { int32 height; }; -// A copy of Rect that can be typemapped. Arrays of Rect are currently used, -// which do not support typemapping. +// A copy of Rect that is typemapped differently in the chromium and blink +// variants. 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 b1b7437..b50409e 100644 --- a/mojo/public/interfaces/bindings/tests/struct_with_traits.mojom +++ b/mojo/public/interfaces/bindings/tests/struct_with_traits.mojom @@ -23,6 +23,7 @@ struct StructWithTraits { 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; @@ -33,28 +34,50 @@ struct StructWithTraitsContainer { StructWithTraits f_struct; }; -struct PassByValueStructWithTraits { +// Maps to a pass-by-value trivial struct. +struct TrivialStructWithTraits { + int32 value; +}; + +// Maps to a move-only struct. +struct MoveOnlyStructWithTraits { handle f_handle; }; -// The custom type for PassByValueStructWithTraits is not clonable. Test that +// The custom type for MoveOnlyStructWithTraits is not clonable. Test that // this container can compile as long as Clone() is not used. -struct PassByValueStructWithTraitsContainer { - PassByValueStructWithTraits f_struct; +struct MoveOnlyStructWithTraitsContainer { + MoveOnlyStructWithTraits f_struct; }; -struct StructWithTraitsForUniquePtrTest { +struct StructWithTraitsForUniquePtr { int32 f_int32; }; +union UnionWithTraits { + int32 f_int32; + NestedStructWithTraits f_struct; +}; + interface TraitsTestService { EchoStructWithTraits(StructWithTraits s) => (StructWithTraits passed); - EchoPassByValueStructWithTraits(PassByValueStructWithTraits s) => - (PassByValueStructWithTraits passed); + EchoTrivialStructWithTraits(TrivialStructWithTraits s) => + (TrivialStructWithTraits passed); + + EchoMoveOnlyStructWithTraits(MoveOnlyStructWithTraits s) => + (MoveOnlyStructWithTraits passed); + + EchoNullableMoveOnlyStructWithTraits(MoveOnlyStructWithTraits? s) => + (MoveOnlyStructWithTraits? passed); EchoEnumWithTraits(EnumWithTraits e) => (EnumWithTraits passed); - EchoStructWithTraitsForUniquePtrTest(StructWithTraitsForUniquePtrTest e) => ( - StructWithTraitsForUniquePtrTest passed); + EchoStructWithTraitsForUniquePtr(StructWithTraitsForUniquePtr e) => ( + StructWithTraitsForUniquePtr passed); + + EchoNullableStructWithTraitsForUniquePtr(StructWithTraitsForUniquePtr? e) => ( + StructWithTraitsForUniquePtr? passed); + + EchoUnionWithTraits(UnionWithTraits u) => (UnionWithTraits passed); }; diff --git a/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom b/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom index 534cfd8..adc4e7e 100644 --- a/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom +++ b/mojo/public/interfaces/bindings/tests/test_associated_interfaces.mojom @@ -4,6 +4,8 @@ module mojo.test; +import "mojo/public/interfaces/bindings/tests/ping_service.mojom"; + interface FooInterface {}; struct StructContainsAssociated { @@ -42,3 +44,11 @@ interface IntegerSenderConnection { 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 462d512..272e603 100644 --- a/mojo/public/interfaces/bindings/tests/test_constants.mojom +++ b/mojo/public/interfaces/bindings/tests/test_constants.mojom @@ -42,12 +42,16 @@ const float kFloatInfinity = float.INFINITY; 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 46c6f69..3df4318 100644 --- a/mojo/public/interfaces/bindings/tests/test_native_types.mojom +++ b/mojo/public/interfaces/bindings/tests/test_native_types.mojom @@ -34,4 +34,5 @@ interface PicklePasser { 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 2709d49..03a0a20 100644 --- a/mojo/public/interfaces/bindings/tests/test_structs.mojom +++ b/mojo/public/interfaces/bindings/tests/test_structs.mojom @@ -126,6 +126,8 @@ struct MapKeyTypes { 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 @@ -358,6 +360,16 @@ struct MultiVersionStructV7 { 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. @@ -380,3 +392,23 @@ struct ContainsOther { 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 2fdbea8..183f184 100644 --- a/mojo/public/interfaces/bindings/tests/test_wtf_types.mojom +++ b/mojo/public/interfaces/bindings/tests/test_wtf_types.mojom @@ -25,13 +25,26 @@ union TestWTFCodeGeneration2 { }; 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 c46c0a5..ab69045 100644 --- a/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom +++ b/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom @@ -56,6 +56,18 @@ enum EnumB { 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 @@ -84,6 +96,11 @@ interface ConformanceTestInterface { 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 { @@ -111,3 +128,8 @@ struct StructWithEnum { 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 e33faaa..0780641 100644 --- a/mojo/public/java/BUILD.gn +++ b/mojo/public/java/BUILD.gn @@ -4,9 +4,8 @@ import("//build/config/android/rules.gni") -android_library("system") { +android_library("system_java") { 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", @@ -20,10 +19,11 @@ android_library("system") { "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") { +android_library("bindings_java") { java_files = [ "bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java", "bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java", @@ -56,7 +56,7 @@ android_library("bindings") { ] deps = [ - ":system", + ":system_java", "//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 6146316..f77399d 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 @@ public class BindingsHelper { /** * Returns an {@link AsyncWaiter} to use with the given handle, or |null| if none if available. */ - static AsyncWaiter getDefaultAsyncWaiterForHandle(Handle handle) { + static Watcher getWatcherForHandle(Handle handle) { if (handle.getCore() != null) { - return handle.getCore().getDefaultAsyncWaiter(); + return handle.getCore().getWatcher(); } 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 3686bcf..2aa5ea6 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 @@ public class Connector implements MessageReceiver, HandleOwner<MessagePipeHandle /** * The callback that is notified when the state of the owned handle changes. */ - private final AsyncWaiterCallback mAsyncWaiterCallback = new AsyncWaiterCallback(); + private final WatcherCallback mWatcherCallback = new WatcherCallback(); /** * The owned message pipe. @@ -35,9 +35,9 @@ public class Connector implements MessageReceiver, HandleOwner<MessagePipeHandle private final MessagePipeHandle mMessagePipeHandle; /** - * A waiter which is notified when a new message is available on the owned message pipe. + * A watcher which is notified when a new message is available on the owned message pipe. */ - private final AsyncWaiter mAsyncWaiter; + private final Watcher mWatcher; /** * The {@link MessageReceiver} to which received messages are sent. @@ -45,11 +45,6 @@ public class Connector implements MessageReceiver, HandleOwner<MessagePipeHandle 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; @@ -59,17 +54,16 @@ public class Connector implements MessageReceiver, HandleOwner<MessagePipeHandle * {@link AsyncWaiter} from the {@link Core} implementation of |messagePipeHandle|. */ public Connector(MessagePipeHandle messagePipeHandle) { - this(messagePipeHandle, BindingsHelper.getDefaultAsyncWaiterForHandle(messagePipeHandle)); + this(messagePipeHandle, BindingsHelper.getWatcherForHandle(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, AsyncWaiter asyncWaiter) { - mCancellable = null; + public Connector(MessagePipeHandle messagePipeHandle, Watcher watcher) { mMessagePipeHandle = messagePipeHandle; - mAsyncWaiter = asyncWaiter; + mWatcher = watcher; } /** @@ -91,8 +85,7 @@ public class Connector implements MessageReceiver, HandleOwner<MessagePipeHandle * Start listening for incoming messages. */ public void start() { - assert mCancellable == null; - registerAsyncWaiterForRead(); + mWatcher.start(mMessagePipeHandle, Core.HandleSignals.READABLE, mWatcherCallback); } /** @@ -140,32 +133,21 @@ public class Connector implements MessageReceiver, HandleOwner<MessagePipeHandle } } - private class AsyncWaiterCallback implements AsyncWaiter.Callback { - + private class WatcherCallback implements Watcher.Callback { /** - * @see org.chromium.mojo.system.AsyncWaiter.Callback#onResult(int) + * @see org.chromium.mojo.system.Watcher.Callback#onResult(int) */ @Override public void onResult(int 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); + Connector.this.onWatcherResult(result); } } /** - * @see org.chromium.mojo.system.AsyncWaiter.Callback#onResult(int) + * @see org.chromium.mojo.system.Watcher.Callback#onResult(int) */ - private void onAsyncWaiterResult(int result) { - mCancellable = null; + private void onWatcherResult(int result) { if (result == MojoResult.OK) { readOutstandingMessages(); } else { @@ -175,26 +157,12 @@ public class Connector implements MessageReceiver, HandleOwner<MessagePipeHandle 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() { @@ -207,18 +175,14 @@ public class Connector implements MessageReceiver, HandleOwner<MessagePipeHandle return; } } while (result.getValue()); - if (result.getMojoResult() == MojoResult.SHOULD_WAIT) { - registerAsyncWaiterForRead(); - } else { + if (result.getMojoResult() != MojoResult.SHOULD_WAIT) { onError(new MojoException(result.getMojoResult())); } } private void cancelIfActive() { - if (mCancellable != null) { - mCancellable.cancel(); - mCancellable = null; - } + mWatcher.cancel(); + mWatcher.destroy(); } /** 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 755fb01..64ff1c0 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,11 +29,15 @@ public class Decoder { /** * Minimal value for the next handle to deserialize. */ - private int mMinNextClaimedHandle = 0; + private int mMinNextClaimedHandle; /** * Minimal value of the start of the next memory to claim. */ - private long mMinNextMemory = 0; + private long mMinNextMemory; + /** + * The current nesting level when decoding. + */ + private long mStackDepth; /** * The maximal memory accessible. @@ -46,11 +50,17 @@ public class Decoder { 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) { @@ -79,6 +89,17 @@ public class Decoder { } mMinNextMemory = BindingsHelper.align(end); } + + public void increaseStackDepth() { + ++mStackDepth; + if (mStackDepth >= MAX_RECURSION_DEPTH) { + throw new DeserializationException("Recursion depth limit exceeded."); + } + } + + public void decreaseStackDepth() { + --mStackDepth; + } } /** @@ -744,4 +765,12 @@ public class Decoder { 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 c621d9b..bb49cbc 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,8 +4,6 @@ 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; @@ -13,6 +11,8 @@ import org.chromium.mojo.system.MojoException; 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,32 +58,23 @@ class ExecutorFactory { */ private final Object mLock; /** - * The {@link AsyncWaiter} to get notified of new message availability on |mReadHandle|. + * The {@link Watcher} to get notified of new message availability on |mReadHandle|. */ - private final AsyncWaiter mWaiter; + private final Watcher mWatcher; /** * Constructor. */ public PipedExecutor(Core core) { - mWaiter = core.getDefaultAsyncWaiter(); - assert mWaiter != null; + mWatcher = core.getWatcher(); + assert mWatcher != null; mLock = new Object(); Pair<MessagePipeHandle, MessagePipeHandle> handles = core.createMessagePipe( new MessagePipeHandle.CreateOptions()); mReadHandle = handles.first; mWriteHandle = handles.second; mPendingActions = new ArrayList<Runnable>(); - 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); + mWatcher.start(mReadHandle, Core.HandleSignals.READABLE, this); } /** @@ -99,14 +90,6 @@ class ExecutorFactory { } /** - * @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() { @@ -114,6 +97,8 @@ class ExecutorFactory { mWriteHandle.close(); mPendingActions.clear(); } + mWatcher.cancel(); + mWatcher.destroy(); mReadHandle.close(); } @@ -126,7 +111,6 @@ class ExecutorFactory { 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 90fdb3a..385eb40 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,6 +6,14 @@ package org.chromium.mojo.bindings; 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; @@ -99,12 +107,12 @@ public interface Interface extends ConnectionErrorHandler, Closeable { /** * The {@link ConnectionErrorHandler} that will be notified of errors. */ - private ConnectionErrorHandler mErrorHandler = null; + private ConnectionErrorHandler mErrorHandler; /** * The currently known version of the interface. */ - private int mVersion = 0; + private int mVersion; /** * Constructor. @@ -186,16 +194,19 @@ public interface Interface extends ConnectionErrorHandler, Closeable { @Override public void queryVersion(final Callback1<Integer> callback) { RunMessageParams message = new RunMessageParams(); - message.reserved0 = 16; - message.reserved1 = 0; - message.queryVersion = new QueryVersion(); + message.input = new RunInput(); + message.input.setQueryVersion(new QueryVersion()); InterfaceControlMessagesHelper.sendRunMessage(getCore(), mMessageReceiver, message, new Callback1<RunResponseMessageParams>() { @Override public void call(RunResponseMessageParams response) { - mVersion = response.queryVersionResult.version; - try { + if (response.output != null + && response.output.which() + == RunOutput.Tag.QueryVersionResult) { + mVersion = response.output.getQueryVersionResult().version; + } + try { callback.call(mVersion); } catch (RuntimeException e) { // TODO(lhchavez): Remove this hack. See b/28986534 for details. @@ -216,10 +227,9 @@ public interface Interface extends ConnectionErrorHandler, Closeable { } mVersion = version; RunOrClosePipeMessageParams message = new RunOrClosePipeMessageParams(); - message.reserved0 = 16; - message.reserved1 = 0; - message.requireVersion = new RequireVersion(); - message.requireVersion.version = version; + message.input = new RunOrClosePipeInput(); + message.input.setRequireVersion(new RequireVersion()); + message.input.getRequireVersion().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 939fb93..51f543d 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,6 +7,14 @@ package org.chromium.mojo.bindings; 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; /** @@ -64,11 +72,16 @@ public class InterfaceControlMessagesHelper { */ 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.reserved0 = 16; - response.reserved1 = 0; - response.queryVersionResult = new QueryVersionResult(); - response.queryVersionResult.version = manager.getVersion(); + 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; + } return responder.accept(response.serializeWithHeader( core, new MessageHeader(InterfaceControlMessagesConstants.RUN_MESSAGE_ID, @@ -84,6 +97,9 @@ public class InterfaceControlMessagesHelper { Manager<I, P> manager, ServiceMessage message) { Message payload = message.getPayload(); RunOrClosePipeMessageParams query = RunOrClosePipeMessageParams.deserialize(payload); - return query.requireVersion.version <= manager.getVersion(); + if (query.input.which() == RunOrClosePipeInput.Tag.RequireVersion) { + return query.input.getRequireVersion().version <= manager.getVersion(); + } + return false; } } 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 0d270cc..996c457 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 @@ public class Message { /** * This message interpreted as a message for a mojo service with an appropriate header. */ - private ServiceMessage mWithHeader = null; + private ServiceMessage mWithHeader; /** * 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 16b29b4..a278cc5 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 @@ package org.chromium.mojo.bindings; 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 @@ public class RouterImpl implements Router { * {@link MessageReceiver} used to return responses to the caller. */ class ResponderThunk implements MessageReceiver { - private boolean mAcceptWasInvoked = false; + private boolean mAcceptWasInvoked; /** * @see @@ -109,23 +109,23 @@ public class RouterImpl implements Router { private final Executor mExecutor; /** - * Constructor that will use the default {@link AsyncWaiter}. + * Constructor that will use the default {@link Watcher}. * * @param messagePipeHandle The {@link MessagePipeHandle} to route message for. */ public RouterImpl(MessagePipeHandle messagePipeHandle) { - this(messagePipeHandle, BindingsHelper.getDefaultAsyncWaiterForHandle(messagePipeHandle)); + this(messagePipeHandle, BindingsHelper.getWatcherForHandle(messagePipeHandle)); } /** * Constructor. * * @param messagePipeHandle The {@link MessagePipeHandle} to route message for. - * @param asyncWaiter the {@link AsyncWaiter} to use to get notification of new messages on the + * @param watcher the {@link Watcher} to use to get notification of new messages on the * handle. */ - public RouterImpl(MessagePipeHandle messagePipeHandle, AsyncWaiter asyncWaiter) { - mConnector = new Connector(messagePipeHandle, asyncWaiter); + public RouterImpl(MessagePipeHandle messagePipeHandle, Watcher watcher) { + mConnector = new Connector(messagePipeHandle, watcher); mConnector.setIncomingMessageReceiver(new HandleIncomingMessageThunk()); Core core = messagePipeHandle.getCore(); if (core != null) { @@ -171,23 +171,20 @@ public class RouterImpl implements Router { assert messageWithHeader.getHeader().hasFlag(MessageHeader.MESSAGE_EXPECTS_RESPONSE_FLAG); // Compute a request id for being able to route the response. - // 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); + 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); return true; } @@ -230,15 +227,11 @@ public class RouterImpl implements Router { return false; } else if (header.hasFlag(MessageHeader.MESSAGE_IS_RESPONSE_FLAG)) { long requestId = header.getRequestId(); - 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); + MessageReceiver responder = mResponders.get(requestId); + if (responder == null) { + return false; } + 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 85cc97c..14f4e1e 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,6 +6,8 @@ package org.chromium.mojo.bindings; import org.chromium.mojo.system.Core; +import java.nio.ByteBuffer; + /** * Base class for all mojo structs. */ @@ -49,6 +51,23 @@ public abstract class Struct { } /** + * 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 deleted file mode 100644 index 474de4b..0000000 --- a/mojo/public/java/system/src/org/chromium/mojo/system/AsyncWaiter.java +++ /dev/null @@ -1,53 +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. - -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 ba0e5c6..e5c6d08 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 interface Core { public UntypedHandle acquireNativeHandle(int handle); /** - * Returns a default implementation of {@link AsyncWaiter}. + * Returns an implementation of {@link Watcher}. */ - public AsyncWaiter getDefaultAsyncWaiter(); + public Watcher getWatcher(); /** * 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 new file mode 100644 index 0000000..9c70161 --- /dev/null +++ b/mojo/public/java/system/src/org/chromium/mojo/system/Watcher.java @@ -0,0 +1,38 @@ +// 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 eda7e04..0fae4b4 100644 --- a/mojo/public/js/BUILD.gn +++ b/mojo/public/js/BUILD.gn @@ -2,6 +2,8 @@ # 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", @@ -11,34 +13,43 @@ source_set("js") { 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 2fdcae3..f3e40d2 100644 --- a/mojo/public/js/bindings.js +++ b/mojo/public/js/bindings.js @@ -3,115 +3,283 @@ // found in the LICENSE file. define("mojo/public/js/bindings", [ - "mojo/public/js/router", "mojo/public/js/core", -], function(router, core) { - - var Router = router.Router; + "mojo/public/js/lib/control_message_proxy", + "mojo/public/js/interface_types", + "mojo/public/js/router", +], function(core, controlMessageProxy, types, router) { - 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; + function makeRequest(interfacePtr) { + var pipe = core.createMessagePipe(); + interfacePtr.ptr.bind(new types.InterfacePtrInfo(pipe.handle0, 0)); + return new types.InterfaceRequest(pipe.handle1); } - // TODO(hansmuller): remove then after 'Client=' has been removed from Mojom. - ProxyProperties.prototype.getLocalDelegate = function() { - return this.local && StubBindings(this.local).delegate; - } + // --------------------------------------------------------------------------- - // 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"); - } + // 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; - ProxyProperties.prototype.close = function() { - this.connection.close(); - } + this.interfaceType_ = interfaceType; + this.router_ = null; + this.proxy_ = null; - // Public stub class properties that are managed at runtime by the JS - // bindings. See StubBindings below. - function StubProperties(delegate) { - this.delegate = delegate; - } + // |router_| is lazily initialized. |handle_| is valid between bind() and + // the initialization of |router_|. + this.handle_ = null; + this.controlMessageProxy_ = null; - StubProperties.prototype.close = function() { - this.connection.close(); + if (ptrInfoOrHandle) + this.bind(ptrInfoOrHandle); } - // The base class for generated proxy classes. - function ProxyBase(receiver) { - this[kProxyProperties] = new ProxyProperties(receiver); + InterfacePtrController.prototype.bind = function(ptrInfoOrHandle) { + this.reset(); - // TODO(hansmuller): Temporary, for Chrome backwards compatibility. - if (receiver instanceof Router) - this.receiver_ = receiver; - } + if (ptrInfoOrHandle instanceof types.InterfacePtrInfo) { + this.version = ptrInfoOrHandle.version; + this.handle_ = ptrInfoOrHandle.handle; + } else { + this.handle_ = ptrInfoOrHandle; + } + }; - // The base class for generated stub classes. - function StubBase(delegate) { - this[kStubProperties] = new StubProperties(delegate); - } + InterfacePtrController.prototype.isBound = function() { + return this.router_ !== null || this.handle_ !== null; + }; - // TODO(hansmuller): remove everything except the connection property doc - // after 'Client=' has been removed from Mojom. + // 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; - // 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. - // - // 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. + this.proxy_ = null; + } + if (this.handle_) { + core.close(this.handle_); + this.handle_ = null; + } + }; + + InterfacePtrController.prototype.setConnectionErrorHandler + = function(callback) { + if (!this.isBound()) + throw new Error("Cannot set connection error handler if not bound."); + + this.configureProxyIfNecessary_(); + this.router_.setErrorHandler(callback); + }; + + InterfacePtrController.prototype.passInterface = function() { + var result; + if (this.router_) { + // TODO(yzshen): Fix Router interface to support extracting handle. + result = new types.InterfacePtrInfo( + this.router_.connector_.handle_, this.version); + this.router_.connector_.handle_ = null; + } else { + // This also handles the case when this object is not bound. + result = new types.InterfacePtrInfo(this.handle_, this.version); + this.handle_ = null; + } + + this.reset(); + return result; + }; + + InterfacePtrController.prototype.getProxy = function() { + this.configureProxyIfNecessary_(); + return this.proxy_; + }; + + InterfacePtrController.prototype.enableTestingMode = function() { + this.configureProxyIfNecessary_(); + return this.router_.enableTestingMode(); + }; + + InterfacePtrController.prototype.configureProxyIfNecessary_ = function() { + if (!this.handle_) + return; + + this.router_ = new router.Router(this.handle_); + this.handle_ = null; + this.router_ .setPayloadValidators([this.interfaceType_.validateResponse]); + + this.controlMessageProxy_ = new + controlMessageProxy.ControlMessageProxy(this.router_); + + this.proxy_ = new this.interfaceType_.proxyClass(this.router_); + }; + + InterfacePtrController.prototype.queryVersion = function() { + function onQueryVersion(version) { + this.version = version; + return version; + } + + this.configureProxyIfNecessary_(); + return this.controlMessageProxy_.queryVersion().then( + onQueryVersion.bind(this)); + }; + + InterfacePtrController.prototype.requireVersion = function(version) { + this.configureProxyIfNecessary_(); + + if (this.version >= version) { + return; + } + this.version = version; + this.controlMessageProxy_.requireVersion(version); + }; + + // --------------------------------------------------------------------------- + + // |request| could be omitted and passed into bind() later. // - // ProxyBindings(proxy).local - The "local" stub object whose delegate - // implements the proxy's Mojo client interface. + // Example: // - // 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|. + // // FooImpl implements mojom.Foo. + // function FooImpl() { ... } + // FooImpl.prototype.fooMethod1 = function() { ... } + // FooImpl.prototype.fooMethod2 = function() { ... } // - // ProxyBindings(proxy).getLocalDelegate() - Returns the implementation - // delegate of the proxy's client stub object. This is just shorthand - // for |StubBindings(ProxyBindings(proxy).local).delegate|. + // 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; - function ProxyBindings(proxy) { - return (proxy instanceof ProxyBase) ? proxy[kProxyProperties] : proxy; + if (requestOrHandle) + this.bind(requestOrHandle); } - // TODO(hansmuller): remove the remote doc after 'Client=' has been - // removed from Mojom. + Binding.prototype.isBound = function() { + return this.router_ !== null; + }; - // 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. + Binding.prototype.createInterfacePtrAndBind = function() { + var ptr = new this.interfaceType_.ptrClass(); + // TODO(yzshen): Set the version of the interface pointer. + this.bind(makeRequest(ptr)); + return ptr; + } + + Binding.prototype.bind = function(requestOrHandle) { + this.close(); + + var handle = requestOrHandle instanceof types.InterfaceRequest ? + requestOrHandle.handle : requestOrHandle; + if (!core.isHandle(handle)) + return; + + this.stub_ = new this.interfaceType_.stubClass(this.impl_); + this.router_ = new router.Router(handle, this.interfaceType_.kVersion); + this.router_.setIncomingReceiver(this.stub_); + this.router_ .setPayloadValidators([this.interfaceType_.validateRequest]); + }; + + Binding.prototype.close = function() { + if (!this.isBound()) + return; + + this.router_.close(); + this.router_ = null; + this.stub_ = null; + }; + + Binding.prototype.setConnectionErrorHandler + = function(callback) { + if (!this.isBound()) + throw new Error("Cannot set connection error handler if not bound."); + this.router_.setErrorHandler(callback); + }; + + Binding.prototype.unbind = function() { + if (!this.isBound()) + return new types.InterfaceRequest(null); + + var result = new types.InterfaceRequest(this.router_.connector_.handle_); + this.router_.connector_.handle_ = null; + this.close(); + return result; + }; + + Binding.prototype.enableTestingMode = function() { + return this.router_.enableTestingMode(); + }; - function StubBindings(stub) { - return stub instanceof StubBase ? stub[kStubProperties] : stub; + // --------------------------------------------------------------------------- + + 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.EmptyProxy = ProxyBase; - exports.EmptyStub = StubBase; - exports.ProxyBase = ProxyBase; - exports.ProxyBindings = ProxyBindings; - exports.StubBase = StubBase; - exports.StubBindings = StubBindings; + exports.InterfacePtrInfo = types.InterfacePtrInfo; + exports.InterfaceRequest = types.InterfaceRequest; + exports.makeRequest = makeRequest; + exports.InterfacePtrController = InterfacePtrController; + exports.Binding = Binding; + exports.BindingSet = BindingSet; + return exports; }); diff --git a/mojo/public/js/codec.js b/mojo/public/js/codec.js index 4003b51..ff5d31a 100644 --- a/mojo/public/js/codec.js +++ b/mojo/public/js/codec.js @@ -3,9 +3,10 @@ // found in the LICENSE file. define("mojo/public/js/codec", [ - "mojo/public/js/unicode", "mojo/public/js/buffer", -], function(unicode, buffer) { + "mojo/public/js/interface_types", + "mojo/public/js/unicode", +], function(buffer, types, unicode) { var kErrorUnsigned = "Passing negative value to unsigned"; var kErrorArray = "Passing non Array for array type"; @@ -303,8 +304,12 @@ define("mojo/public/js/codec", [ }; Encoder.prototype.encodeHandle = function(handle) { - this.handles.push(handle); - this.writeUint32(this.handles.length - 1); + if (handle) { + this.handles.push(handle); + this.writeUint32(this.handles.length - 1); + } else { + this.writeUint32(kEncodedInvalidHandleValue); + } }; Encoder.prototype.encodeString = function(val) { @@ -711,6 +716,20 @@ define("mojo/public/js/codec", [ 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; } @@ -788,33 +807,54 @@ define("mojo/public/js/codec", [ NullableHandle.encode = Handle.encode; - function Interface() { + function Interface(cls) { + this.cls = cls; } - Interface.encodedSize = 8; + Interface.prototype.encodedSize = 8; - Interface.decode = function(decoder) { - var handle = decoder.decodeHandle(); - // Ignore the version field for now. - decoder.readUint32(); + 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; + }; - 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); + } + + NullableInterface.prototype = Object.create(Interface.prototype); + + function InterfaceRequest() { + } + + InterfaceRequest.encodedSize = 4; + + InterfaceRequest.decode = function(decoder) { + return new types.InterfaceRequest(decoder.decodeHandle()); + }; + + InterfaceRequest.encode = function(encoder, val) { + encoder.encodeHandle(val ? val.handle : null); }; - function NullableInterface() { + function NullableInterfaceRequest() { } - NullableInterface.encodedSize = Interface.encodedSize; + NullableInterfaceRequest.encodedSize = InterfaceRequest.encodedSize; - NullableInterface.decode = Interface.decode; + NullableInterfaceRequest.decode = InterfaceRequest.decode; - NullableInterface.encode = Interface.encode; + NullableInterfaceRequest.encode = InterfaceRequest.encode; function MapOf(keyClass, valueClass) { this.keyClass = keyClass; @@ -863,6 +903,7 @@ define("mojo/public/js/codec", [ exports.Float = Float; exports.Double = Double; exports.String = String; + exports.Enum = Enum; exports.NullableString = NullableString; exports.PointerTo = PointerTo; exports.NullablePointerTo = NullablePointerTo; @@ -873,6 +914,8 @@ define("mojo/public/js/codec", [ 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 deleted file mode 100644 index b610d9a..0000000 --- a/mojo/public/js/codec_unittests.js +++ /dev/null @@ -1,296 +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. - -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 deleted file mode 100644 index 3f7e839..0000000 --- a/mojo/public/js/connection.js +++ /dev/null @@ -1,176 +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. - -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 674f36b..ee16be8 100644 --- a/mojo/public/js/connector.js +++ b/mojo/public/js/connector.js @@ -82,6 +82,12 @@ define("mojo/public/js/connector", [ 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_, @@ -98,29 +104,12 @@ define("mojo/public/js/connector", [ } 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 d29f5cb..58cf274 100644 --- a/mojo/public/js/constants.cc +++ b/mojo/public/js/constants.cc @@ -9,10 +9,15 @@ namespace mojo { 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 kUnicodeModuleName[] = "mojo/public/js/unicode"; +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 kValidatorModuleName[] = "mojo/public/js/validator"; - } // namespace mojo diff --git a/mojo/public/js/constants.h b/mojo/public/js/constants.h index de75a90..9d32d20 100644 --- a/mojo/public/js/constants.h +++ b/mojo/public/js/constants.h @@ -11,10 +11,13 @@ namespace mojo { extern const char kBindingsModuleName[]; extern const char kBufferModuleName[]; extern const char kCodecModuleName[]; -extern const char kConnectionModuleName[]; extern const char kConnectorModuleName[]; -extern const char kUnicodeModuleName[]; +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 kValidatorModuleName[]; } // namespace mojo diff --git a/mojo/public/js/core.js b/mojo/public/js/core.js index b89a956..ef480ee 100644 --- a/mojo/public/js/core.js +++ b/mojo/public/js/core.js @@ -114,6 +114,27 @@ var READ_DATA_FLAG_QUERY; 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. @@ -236,3 +257,57 @@ function readData(handle, flags) { [native code] } * @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 deleted file mode 100644 index 12364dc..0000000 --- a/mojo/public/js/core_unittests.js +++ /dev/null @@ -1,198 +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. - -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 e3db0a6..e94c5eb 100644 --- a/mojo/public/js/router.js +++ b/mojo/public/js/router.js @@ -3,17 +3,20 @@ // 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(codec, core, connector, validator) { +], function(console, codec, core, connector, controlMessageHandler, validator) { var Connector = connector.Connector; var MessageReader = codec.MessageReader; var Validator = validator.Validator; + var ControlMessageHandler = controlMessageHandler.ControlMessageHandler; - function Router(handle, connectorFactory) { + function Router(handle, interface_version, connectorFactory) { if (!core.isHandle(handle)) throw new Error("Router constructor: Not a handle"); if (connectorFactory === undefined) @@ -24,6 +27,12 @@ define("mojo/public/js/router", [ 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), @@ -36,6 +45,7 @@ define("mojo/public/js/router", [ Router.prototype.close = function() { this.completers_.clear(); // Drop any responders. this.connector_.close(); + this.testingController_ = null; }; Router.prototype.accept = function(message) { @@ -81,6 +91,11 @@ define("mojo/public/js/router", [ 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); @@ -95,8 +110,17 @@ define("mojo/public/js/router", [ }; Router.prototype.handleValidIncomingMessage_ = function(message) { + if (this.testingController_) + return; + if (message.expectsResponse()) { - if (this.incomingReceiver_) { + if (controlMessageHandler.isControlMessage(message)) { + if (this.controlMessageHandler_) { + this.controlMessageHandler_.acceptWithResponder(message, this); + } else { + this.close(); + } + } else if (this.incomingReceiver_) { this.incomingReceiver_.acceptWithResponder(message, this); } else { // If we receive a request expecting a response when the client is not @@ -107,17 +131,39 @@ define("mojo/public/js/router", [ var reader = new MessageReader(message); var requestID = reader.requestID; var completer = this.completers_.get(requestID); - this.completers_.delete(requestID); - completer.resolve(message); + if (completer) { + this.completers_.delete(requestID); + completer.resolve(message); + } else { + console.log("Unexpected response with request ID: " + requestID); + } } else { - if (this.incomingReceiver_) + if (controlMessageHandler.isControlMessage(message)) { + if (this.controlMessageHandler_) { + var ok = this.controlMessageHandler_.accept(message); + if (ok) return; + } + this.close(); + } else if (this.incomingReceiver_) { this.incomingReceiver_.accept(message); + } } - } + }; Router.prototype.handleInvalidIncomingMessage_ = function(message, error) { - this.close(); - } + if (!this.testingController_) { + // TODO(yzshen): Consider notifying the embedder. + // TODO(yzshen): This should also trigger connection error handler. + // Consider making accept() return a boolean and let the connector deal + // with this, as the C++ code does. + console.log("Invalid message: " + validator.validationError[error]); + + this.close(); + return; + } + + this.testingController_.onInvalidIncomingMessage(error); + }; Router.prototype.handleConnectionError_ = function(result) { this.completers_.forEach(function(value) { @@ -128,25 +174,30 @@ define("mojo/public/js/router", [ this.close(); }; - // The TestRouter subclass is only intended to be used in unit tests. - // It defeats valid message handling and delgates invalid message handling. + // The RouterTestingController is used in unit tests. It defeats valid message + // handling and delgates invalid message handling. - function TestRouter(handle, connectorFactory) { - Router.call(this, handle, connectorFactory); + function RouterTestingController(connector) { + this.connector_ = connector; + this.invalidMessageHandler_ = null; } - TestRouter.prototype = Object.create(Router.prototype); + RouterTestingController.prototype.waitForNextMessage = function() { + this.connector_.waitForNextMessageForTesting(); + }; - TestRouter.prototype.handleValidIncomingMessage_ = function() { + RouterTestingController.prototype.setInvalidIncomingMessageHandler = + function(callback) { + this.invalidMessageHandler_ = callback; }; - TestRouter.prototype.handleInvalidIncomingMessage_ = - function(message, error) { - this.validationErrorHandler(error); - }; + RouterTestingController.prototype.onInvalidIncomingMessage = + function(error) { + if (this.invalidMessageHandler_) + this.invalidMessageHandler_(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 deleted file mode 100644 index 691d51b..0000000 --- a/mojo/public/js/struct_unittests.js +++ /dev/null @@ -1,279 +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. - -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 deleted file mode 100644 index f5a57f9..0000000 --- a/mojo/public/js/test/validation_test_input_parser.js +++ /dev/null @@ -1,299 +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. - -// 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 cfe5037..49ab5c9 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 Mojo application implemented in JS +// This module provides a way for a Service 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 deleted file mode 100644 index 5dcda7d..0000000 --- a/mojo/public/js/union_unittests.js +++ /dev/null @@ -1,184 +0,0 @@ -// 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 deleted file mode 100644 index 817d42c..0000000 --- a/mojo/public/js/validation_unittests.js +++ /dev/null @@ -1,349 +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. - -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 cbf7521..fee742d 100644 --- a/mojo/public/js/validator.js +++ b/mojo/public/js/validator.js @@ -24,10 +24,15 @@ define("mojo/public/js/validator", [ '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; } @@ -37,12 +42,18 @@ define("mojo/public/js/validator", [ } function isInterfaceClass(cls) { - return cls === codec.Interface || cls === codec.NullableInterface; + return cls instanceof codec.Interface; + } + + function isInterfaceRequestClass(cls) { + return cls === codec.InterfaceRequest || + cls === codec.NullableInterfaceRequest; } function isNullable(type) { return type === codec.NullableString || type === codec.NullableHandle || type === codec.NullableInterface || + type === codec.NullableInterfaceRequest || type instanceof codec.NullableArrayOf || type instanceof codec.NullablePointerTo; } @@ -76,7 +87,7 @@ define("mojo/public/js/validator", [ return false; return true; - } + }; Validator.prototype.claimRange = function(start, numBytes) { if (this.isValidRange(start, numBytes)) { @@ -84,7 +95,7 @@ define("mojo/public/js/validator", [ return true; } return false; - } + }; Validator.prototype.claimHandle = function(index) { if (index === codec.kEncodedInvalidHandleValue) @@ -96,6 +107,13 @@ define("mojo/public/js/validator", [ // 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) { @@ -107,15 +125,19 @@ define("mojo/public/js/validator", [ 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, minVersion) { + Validator.prototype.validateStructHeader = function(offset, minNumBytes) { if (!codec.isAligned(offset)) return validationError.MISALIGNED_OBJECT; @@ -123,20 +145,44 @@ define("mojo/public/js/validator", [ return validationError.ILLEGAL_MEMORY_RANGE; var numBytes = this.message.buffer.getUint32(offset); - var version = this.message.buffer.getUint32(offset + 4); - // Backward compatibility is not yet supported. - if (numBytes < minNumBytes || version < minVersion) + if (numBytes < minNumBytes) return validationError.UNEXPECTED_STRUCT_HEADER; if (!this.claimRange(offset, numBytes)) return validationError.ILLEGAL_MEMORY_RANGE; return validationError.NONE; - } + }; + + Validator.prototype.validateStructVersion = function(offset, versionSizes) { + var numBytes = this.message.buffer.getUint32(offset); + var version = this.message.buffer.getUint32(offset + 4); + + if (version <= versionSizes[versionSizes.length - 1].version) { + // Scan in reverse order to optimize for more recent versionSizes. + for (var i = versionSizes.length - 1; i >= 0; --i) { + if (version >= versionSizes[i].version) { + if (numBytes == versionSizes[i].numBytes) + break; + return validationError.UNEXPECTED_STRUCT_HEADER; + } + } + } else if (numBytes < versionSizes[versionSizes.length-1].numBytes) { + return validationError.UNEXPECTED_STRUCT_HEADER; + } + + return validationError.NONE; + }; + + Validator.prototype.isFieldInStructVersion = function(offset, fieldVersion) { + var structVersion = this.message.buffer.getUint32(offset + 4); + return fieldVersion <= structVersion; + }; Validator.prototype.validateMessageHeader = function() { - var err = this.validateStructHeader(0, codec.kMessageHeaderSize, 0); + + var err = this.validateStructHeader(0, codec.kMessageHeaderSize); if (err != validationError.NONE) return err; @@ -162,7 +208,28 @@ define("mojo/public/js/validator", [ 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 @@ -173,7 +240,7 @@ define("mojo/public/js/validator", [ 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); @@ -196,7 +263,7 @@ define("mojo/public/js/validator", [ return this.validateArray(arrayOffset, elementSize, elementType, expectedDimensionSizes, currentDimension); - } + }; Validator.prototype.validateStructPointer = function( offset, structClass, nullable) { @@ -209,7 +276,7 @@ define("mojo/public/js/validator", [ validationError.NONE : validationError.UNEXPECTED_NULL_POINTER; return structClass.validate(this, structOffset); - } + }; Validator.prototype.validateUnion = function( offset, unionClass, nullable) { @@ -220,7 +287,7 @@ define("mojo/public/js/validator", [ } return unionClass.validate(this, offset); - } + }; Validator.prototype.validateNestedUnion = function( offset, unionClass, nullable) { @@ -233,7 +300,7 @@ define("mojo/public/js/validator", [ validationError.NONE : validationError.UNEXPECTED_NULL_UNION; return this.validateUnion(unionOffset, unionClass, nullable); - } + }; // This method assumes that the array at arrayPointerOffset has // been validated. @@ -241,7 +308,7 @@ define("mojo/public/js/validator", [ 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) { @@ -256,7 +323,7 @@ define("mojo/public/js/validator", [ validationError.NONE : validationError.UNEXPECTED_NULL_POINTER; var mapEncodedSize = codec.kStructHeaderSize + codec.kMapStructPayloadSize; - var err = this.validateStructHeader(structOffset, mapEncodedSize, 0); + var err = this.validateStructHeader(structOffset, mapEncodedSize); if (err !== validationError.NONE) return err; @@ -289,12 +356,12 @@ define("mojo/public/js/validator", [ 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 @@ -337,6 +404,9 @@ define("mojo/public/js/validator", [ 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); @@ -347,9 +417,12 @@ define("mojo/public/js/validator", [ 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 @@ -365,11 +438,11 @@ define("mojo/public/js/validator", [ return err; } return validationError.NONE; - } + }; Validator.prototype.validateInterfaceElements = function(offset, numElements, nullable) { - var elementSize = codec.Interface.encodedSize; + var elementSize = codec.Interface.prototype.encodedSize; for (var i = 0; i < numElements; i++) { var elementOffset = offset + i * elementSize; var err = this.validateInterface(elementOffset, nullable); @@ -377,7 +450,19 @@ define("mojo/public/js/validator", [ 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 = @@ -393,7 +478,7 @@ define("mojo/public/js/validator", [ return err; } return validationError.NONE; - } + }; Validator.prototype.validateStructElements = function(offset, numElements, structClass, nullable) { @@ -406,7 +491,19 @@ define("mojo/public/js/validator", [ 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 deleted file mode 100644 index 28c8a8d..0000000 --- a/mojo/public/mojo_application.gni +++ /dev/null @@ -1,270 +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. - -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 deleted file mode 100644 index 6184417..0000000 --- a/mojo/public/mojo_application_manifest.gni +++ /dev/null @@ -1,139 +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. - -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 deleted file mode 100644 index 9b89abb..0000000 --- a/mojo/public/mojo_application_manifest.gypi +++ /dev/null @@ -1,56 +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. - -{ - '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 deleted file mode 100644 index 3165a8d..0000000 --- a/mojo/public/mojo_constants.gni +++ /dev/null @@ -1,8 +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. - -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 eeea5c5..153d110 100644 --- a/mojo/public/tools/bindings/BUILD.gn +++ b/mojo/public/tools/bindings/BUILD.gn @@ -16,7 +16,9 @@ action("precompile_templates") { "$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-internal.h.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.cc.tmpl", "$mojom_generator_root/generators/cpp_templates/module.h.tmpl", "$mojom_generator_root/generators/cpp_templates/struct_data_view_declaration.tmpl", @@ -25,11 +27,15 @@ action("precompile_templates") { "$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_serialization_definition.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/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_serialization_definition.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/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 deleted file mode 100644 index 0f00114..0000000 --- a/mojo/public/tools/bindings/bindings.gyp +++ /dev/null @@ -1,83 +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. - -{ - '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 ef19cc3..bb0fc43 100644 --- a/mojo/public/tools/bindings/blink_bindings_configuration.gni +++ b/mojo/public/tools/bindings/blink_bindings_configuration.gni @@ -9,21 +9,25 @@ for_blink = true _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 += [ read_file(typemap, "scope") ] + typemaps += [ { + filename = typemap + config = read_file(typemap, "scope") + } ] } -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", -] +blacklist = [] diff --git a/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/mojo/public/tools/bindings/chromium_bindings_configuration.gni index 44c2ae6..831157f 100644 --- a/mojo/public/tools/bindings/chromium_bindings_configuration.gni +++ b/mojo/public/tools/bindings/chromium_bindings_configuration.gni @@ -3,28 +3,78 @@ # found in the LICENSE file. _typemap_imports = [ + "//ash/public/interfaces/typemaps.gni", "//cc/ipc/typemaps.gni", - "//device/bluetooth/public/interfaces/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", "//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", ] -_typemaps = [] +_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 += [ read_file(typemap, "scope") ] + 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") + } ] } diff --git a/mojo/public/tools/bindings/generate_type_mappings.py b/mojo/public/tools/bindings/generate_type_mappings.py index e2fbf31..824f804 100755 --- a/mojo/public/tools/bindings/generate_type_mappings.py +++ b/mojo/public/tools/bindings/generate_type_mappings.py @@ -97,8 +97,9 @@ def ParseTypemap(typemap): mojom_type = match_result.group(1) native_type = match_result.group(2) - # The only attribute supported currently is "move_only". - move_only = match_result.group(3) and match_result.group(3) == "move_only" + attributes = [] + if match_result.group(3): + attributes = match_result.group(3).split(',') assert mojom_type not in result, ( "Cannot map multiple native types (%s, %s) to the same mojom type: %s" % @@ -106,7 +107,11 @@ def ParseTypemap(typemap): result[mojom_type] = { 'typename': native_type, - 'move_only': move_only, + '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, '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 0a446a7..f0d503e 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl @@ -1,22 +1,55 @@ {#--- Macro for enum definition, and the declaration of associated functions. ---#} + {%- macro enum_decl(enum) %} -enum class {{enum.name}} : int32_t { -{%- for field in enum.fields %} -{%- if field.value %} +{%- 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 %} {{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) %} -struct {{enum.name}}_Data { +{%- set enum_name = enum|get_name_for_kind(flatten_nested_kind=True) %} +struct {{enum_name}}_Data { public: - static bool const kIsExtensible = {% if enum.extensible %}true{% else %}false{% endif %}; + static bool constexpr kIsExtensible = {% if enum.extensible %}true{% else %}false{% endif %}; static bool IsKnownValue(int32_t value) { {%- if enum.fields %} @@ -42,42 +75,57 @@ struct {{enum.name}}_Data { }; {%- endmacro %} -{#--- 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_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}}> {}; +{%- endmacro %} -{%- 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); +{%- 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)); } -} -{%- endmacro %} + 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 %} +template <> +struct DefaultHash<{{enum_name}}> { + using Hash = {{hash_fn_name}}; +}; -{%- macro enum_hash(enum) %} template <> -struct hash<{{enum|get_qualified_name_for_kind}}> - : public mojo::internal::EnumHashImpl<{{enum|get_qualified_name_for_kind}}> {}; +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 {%- 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 e42128d..d7d0e5d 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,4 +1,5 @@ -{%- set mojom_type = enum|get_qualified_name_for_kind %} +{%- set mojom_type = enum|get_qualified_name_for_kind( + flatten_nested_kind=True) %} 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 0249ef3..7f64974 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl @@ -1,5 +1,7 @@ {%- import "interface_macros.tmpl" as interface_macros %} class {{interface.name}}Proxy; + +template <typename ImplRefTraits> class {{interface.name}}Stub; class {{interface.name}}RequestValidator; @@ -7,15 +9,18 @@ class {{interface.name}}RequestValidator; class {{interface.name}}ResponseValidator; {%- endif %} -class {{interface.name}} { +class {{export_attribute}} {{interface.name}} + : public {{interface.name}}InterfaceBase { public: static const char Name_[]; - 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 %}; + 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 %}; using Proxy_ = {{interface.name}}Proxy; - using Stub_ = {{interface.name}}Stub; + + template <typename ImplRefTraits> + using Stub_ = {{interface.name}}Stub<ImplRefTraits>; using RequestValidator_ = {{interface.name}}RequestValidator; {%- if interface|has_callbacks %} @@ -24,23 +29,21 @@ class {{interface.name}} { 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 %} -{%- if enum|is_native_only_kind %} - using {{enum.name}} = mojo::NativeEnum; -{%- else %} - {{enum_decl(enum)|indent(2)}} -{%- endif %} + using {{enum.name}} = {{enum|get_name_for_kind(flatten_nested_kind=True)}}; {%- endfor %} {#--- Constants #} {%- for constant in interface.constants %} -{%- 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 %} + static {{constant|format_constant_declaration(nested=True)}}; {%- endfor %} {#--- Methods #} @@ -55,8 +58,8 @@ class {{interface.name}} { {%- endif %} using {{method.name}}Callback = {{interface_macros.declare_callback(method, - for_blink, use_new_wrapper_types)}}; + for_blink, use_once_callback)}}; {%- endif %} - virtual void {{method.name}}({{interface_macros.declare_request_params("", method)}}) = 0; + virtual void {{method.name}}({{interface_macros.declare_request_params("", method, use_once_callback)}}) = 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 4017fc5..a23b107 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl @@ -5,24 +5,22 @@ {%- set proxy_name = interface.name ~ "Proxy" %} {%- set namespace_as_string = "%s"|format(namespace|replace(".","::")) %} -{%- macro alloc_params(struct, params, message, serialization_context, - description) %} - ({{serialization_context}})->handles.Swap(({{message}})->mutable_handles()); +{%- 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()); 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) { - mojo::internal::ValidationContext validation_context( - {{message}}->data(), {{message}}->data_num_bytes(), - {{message}}->handles()->size(), {{message}}, + ReportValidationErrorForMessage( + {{message}}, + mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED, "{{description}} deserializer"); - ReportValidationError( - &validation_context, - mojo::internal::VALIDATION_ERROR_DESERIALIZATION_FAILED); return false; } {%- endmacro %} @@ -41,18 +39,17 @@ std::move(p_{{param.name}}) 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_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}}; +{%- if constant.kind|is_string_kind %} +const char {{interface.name}}::{{constant.name}}[] = {{constant|constant_value}}; {%- endif %} {%- endfor %} @@ -74,12 +71,11 @@ class {{class_name}}_{{method.name}}_HandleSyncResponse : 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 %}) - : serialization_context_(std::move(group_controller)), result_(result) + : result_(result) {%- for param in method.response_parameters -%} , out_{{param.name}}_(out_{{param.name}}) {%- endfor %} { @@ -87,7 +83,6 @@ class {{class_name}}_{{method.name}}_HandleSyncResponse } 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}}_; @@ -100,13 +95,14 @@ bool {{class_name}}_{{method.name}}_HandleSyncResponse::Accept( reinterpret_cast<internal::{{class_name}}_{{method.name}}_ResponseParams_Data*>( message->mutable_payload()); - {{alloc_params(method.response_param_struct, "params", "message", - "&serialization_context_", - "{{class_name}}::{{method.name}} response")}} +{%- set desc = class_name~"::"~method.name~" response" %} + {{alloc_params(method.response_param_struct, "params", "message", desc)}} {%- for param in method.response_parameters %} *out_{{param.name}}_ = std::move(p_{{param.name}}); {%- endfor %} + mojo::internal::SyncMessageResponseSetup::SetCurrentSyncResponseMessage( + message); *result_ = true; return true; } @@ -116,15 +112,16 @@ class {{class_name}}_{{method.name}}_ForwardToCallback : public mojo::MessageReceiver { public: {{class_name}}_{{method.name}}_ForwardToCallback( - const {{class_name}}::{{method.name}}Callback& callback, - scoped_refptr<mojo::AssociatedGroupController> group_controller) - : callback_(callback), - serialization_context_(std::move(group_controller)) { +{%- if use_once_callback %} + {{class_name}}::{{method.name}}Callback callback +{%- else %} + const {{class_name}}::{{method.name}}Callback& callback +{%- endif %} + ) : callback_(std::move(callback)) { } 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( @@ -133,18 +130,19 @@ bool {{class_name}}_{{method.name}}_ForwardToCallback::Accept( reinterpret_cast<internal::{{class_name}}_{{method.name}}_ResponseParams_Data*>( message->mutable_payload()); - {{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)}}); +{%- 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)}}); + } return true; } {%- endif %} {%- endfor %} {{proxy_name}}::{{proxy_name}}(mojo::MessageReceiverWithResponder* receiver) - : ControlMessageProxy(receiver) { + : receiver_(receiver) { } {#--- Proxy definitions #} @@ -158,19 +156,22 @@ bool {{class_name}}_{{method.name}}_ForwardToCallback::Accept( {%- 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::RequestMessageBuilder builder({{message_name}}, size, - mojo::Message::kFlagIsSync); + mojo::internal::MessageBuilder builder( + {{message_name}}, + mojo::Message::kFlagIsSync | mojo::Message::kFlagExpectsResponse, + size, serialization_context.associated_endpoint_count); {{build_message(params_struct, "param_%s", params_description, - "&serialization_context_")}} + "&serialization_context")}} bool result = false; mojo::MessageReceiver* responder = new {{class_name}}_{{method.name}}_HandleSyncResponse( - serialization_context_.group_controller, &result + &result {%- for param in method.response_parameters -%} , param_{{param.name}} {%- endfor %}); @@ -181,23 +182,26 @@ bool {{proxy_name}}::{{method.name}}( {%- endif %} void {{proxy_name}}::{{method.name}}( - {{interface_macros.declare_request_params("in_", method)}}) { + {{interface_macros.declare_request_params("in_", method, use_once_callback)}}) { + mojo::internal::SerializationContext serialization_context; {{struct_macros.get_serialized_size(params_struct, "in_%s", - "&serialization_context_")}} + "&serialization_context")}} {%- if method.response_parameters != None %} - mojo::internal::RequestMessageBuilder builder({{message_name}}, size); + constexpr uint32_t kFlags = mojo::Message::kFlagExpectsResponse; {%- else %} - mojo::internal::MessageBuilder builder({{message_name}}, size); + constexpr uint32_t kFlags = 0; {%- 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( - callback, serialization_context_.group_controller); + new {{class_name}}_{{method.name}}_ForwardToCallback(std::move(callback)); if (!receiver_->AcceptWithResponder(builder.message(), responder)) delete responder; {%- else %} @@ -217,42 +221,23 @@ void {{proxy_name}}::{{method.name}}( {%- 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 - : public base::RefCountedThreadSafe< - {{class_name}}_{{method.name}}_ProxyToResponder> { +class {{class_name}}_{{method.name}}_ProxyToResponder { public: static {{class_name}}::{{method.name}}Callback CreateCallback( uint64_t request_id, bool is_sync, - 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); + mojo::MessageReceiverWithStatus* responder) { + std::unique_ptr<{{class_name}}_{{method.name}}_ProxyToResponder> proxy( + new {{class_name}}_{{method.name}}_ProxyToResponder( + request_id, is_sync, responder)); return base::Bind(&{{class_name}}_{{method.name}}_ProxyToResponder::Run, - 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)) { + base::Passed(&proxy)); } ~{{class_name}}_{{method.name}}_ProxyToResponder() { #if DCHECK_IS_ON() if (responder_) { - // Is the Mojo application destroying the callback without running it + // Is the Service 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."); @@ -263,31 +248,43 @@ class {{class_name}}_{{method.name}}_ProxyToResponder 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, - use_new_wrapper_types)}}); + "in_", method.response_parameters, for_blink)}}); 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, - use_new_wrapper_types)}}) { + "in_", method.response_parameters, for_blink)}}) { + mojo::internal::SerializationContext serialization_context; {{struct_macros.get_serialized_size(response_params_struct, "in_%s", - "&serialization_context_")}} - mojo::internal::ResponseMessageBuilder builder( - {{message_name}}, size, request_id_, - is_sync_ ? mojo::Message::kFlagIsSync : 0); + "&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_); + {{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 @@ -299,18 +296,12 @@ void {{class_name}}_{{method.name}}_ProxyToResponder::Run( {%- endif -%} {%- endfor %} -{{class_name}}Stub::{{class_name}}Stub() - : sink_(nullptr), - control_message_handler_({{interface.name}}::Version_) { -} - -{{class_name}}Stub::~{{interface.name}}Stub() {} - -{#--- Stub definition #} +{#--- StubDispatch definition #} -bool {{class_name}}Stub::Accept(mojo::Message* message) { - if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) - return control_message_handler_.Accept(message); +// static +bool {{class_name}}StubDispatch::Accept( + {{interface.name}}* impl, + mojo::Message* message) { {%- if interface.methods %} switch (message->header()->name) { {%- for method in interface.methods %} @@ -320,13 +311,14 @@ bool {{class_name}}Stub::Accept(mojo::Message* message) { reinterpret_cast<internal::{{class_name}}_{{method.name}}_Params_Data*>( message->mutable_payload()); - {{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_); +{%- 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); TRACE_EVENT0("mojom", "{{class_name}}::{{method.name}}"); - sink_->{{method.name}}({{pass_params(method.parameters)}}); + mojo::internal::MessageDispatchContext context(message); + impl->{{method.name}}({{pass_params(method.parameters)}}); return true; {%- else %} break; @@ -338,10 +330,11 @@ bool {{class_name}}Stub::Accept(mojo::Message* message) { return false; } -bool {{class_name}}Stub::AcceptWithResponder( - mojo::Message* message, mojo::MessageReceiverWithStatus* responder) { - if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) - return control_message_handler_.AcceptWithResponder(message, responder); +// static +bool {{class_name}}StubDispatch::AcceptWithResponder( + {{interface.name}}* impl, + mojo::Message* message, + mojo::MessageReceiverWithStatus* responder) { {%- if interface.methods %} switch (message->header()->name) { {%- for method in interface.methods %} @@ -351,20 +344,19 @@ bool {{class_name}}Stub::AcceptWithResponder( reinterpret_cast<internal::{{class_name}}_{{method.name}}_Params_Data*>( message->mutable_payload()); - {{alloc_params(method.param_struct, "params", "message", - "&serialization_context_", "{{class_name}}::{{method.name}}")| - indent(4)}} +{%- set desc = class_name~"::"~method.name %} + {{alloc_params(method.param_struct, "params", "message", desc)| + indent(4)}} {{class_name}}::{{method.name}}Callback callback = {{class_name}}_{{method.name}}_ProxyToResponder::CreateCallback( message->request_id(), - message->has_flag(mojo::Message::kFlagIsSync), - responder, - serialization_context_.group_controller); - // A null |sink_| means no implementation was bound. - assert(sink_); + message->has_flag(mojo::Message::kFlagIsSync), responder); + // A null |impl| means no implementation was bound. + assert(impl); TRACE_EVENT0("mojom", "{{class_name}}::{{method.name}}"); - sink_->{{method.name}}( -{%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}callback); + mojo::internal::MessageDispatchContext context(message); + impl->{{method.name}}( +{%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}std::move(callback)); return true; {%- else %} break; @@ -378,22 +370,14 @@ bool {{class_name}}Stub::AcceptWithResponder( {#--- Request validator definitions #} -{{class_name}}RequestValidator::{{class_name}}RequestValidator( - mojo::MessageReceiver* sink) : MessageFilter(sink) { -} - bool {{class_name}}RequestValidator::Accept(mojo::Message* message) { - assert(sink_); + if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) + return true; mojo::internal::ValidationContext validation_context( - 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); - } + message->payload(), message->payload_num_bytes(), + message->handles()->size(), message->payload_num_interface_ids(), message, + "{{class_name}} RequestValidator"); switch (message->header()->name) { {%- for method in interface.methods %} @@ -414,7 +398,7 @@ bool {{class_name}}RequestValidator::Accept(mojo::Message* message) { message, &validation_context)) { return false; } - return sink_->Accept(message); + return true; } {%- endfor %} default: @@ -430,22 +414,14 @@ bool {{class_name}}RequestValidator::Accept(mojo::Message* message) { {#--- Response validator definitions #} {% if interface|has_callbacks %} -{{class_name}}ResponseValidator::{{class_name}}ResponseValidator( - mojo::MessageReceiver* sink) : MessageFilter(sink) { -} - bool {{class_name}}ResponseValidator::Accept(mojo::Message* message) { - assert(sink_); + if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) + return true; mojo::internal::ValidationContext validation_context( - 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); - } + message->payload(), message->payload_num_bytes(), + message->handles()->size(), message->payload_num_interface_ids(), message, + "{{class_name}} ResponseValidator"); if (!mojo::internal::ValidateMessageIsResponse(message, &validation_context)) return false; @@ -457,7 +433,7 @@ bool {{class_name}}ResponseValidator::Accept(mojo::Message* message) { message, &validation_context)) { return false; } - return sink_->Accept(message); + return true; } {%- 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 4bec4c6..8649273 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_macros.tmpl @@ -5,39 +5,35 @@ {%- endfor %} {%- endmacro %} -{%- macro declare_responder_params(prefix, parameters, for_blink, use_new_wrapper_types) %} +{%- macro declare_responder_params(prefix, parameters, for_blink) %} {%- 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_new_wrapper_types) -%} +{%- macro declare_callback(method, for_blink, use_once_callback) -%} +{%- if use_once_callback -%} +base::OnceCallback<void( +{%- else -%} 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) -%} +{%- macro declare_request_params(prefix, method, use_once_callback) -%} {{declare_params(prefix, method.parameters)}} {%- if method.response_parameters != None -%} -{%- if method.parameters %}, {% endif -%} +{%- if method.parameters %}, {% endif -%} +{%- if use_once_callback -%} +{{method.name}}Callback callback +{%- else -%} 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 477116b..0a158ec 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,7 +1,6 @@ {%- import "interface_macros.tmpl" as interface_macros %} -class {{interface.name}}Proxy - : public {{interface.name}}, - public mojo::internal::ControlMessageProxy { +class {{export_attribute}} {{interface.name}}Proxy + : public {{interface.name}} { public: explicit {{interface.name}}Proxy(mojo::MessageReceiverWithResponder* receiver); @@ -9,13 +8,9 @@ class {{interface.name}}Proxy {%- if method.sync %} bool {{method.name}}({{interface_macros.declare_sync_method_params("", method)}}) override; {%- endif %} - void {{method.name}}({{interface_macros.declare_request_params("", method)}}) override; + void {{method.name}}({{interface_macros.declare_request_params("", method, use_once_callback)}}) override; {%- endfor %} - mojo::internal::SerializationContext* serialization_context() { - return &serialization_context_; - } - private: - mojo::internal::SerializationContext serialization_context_; + mojo::MessageReceiverWithResponder* receiver_; }; 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 29917ea..a00d148 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,6 +1,4 @@ -class {{interface.name}}RequestValidator : public mojo::MessageFilter { +class {{export_attribute}} {{interface.name}}RequestValidator : public NON_EXPORTED_BASE(mojo::MessageReceiver) { 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 5893bfd..e2caa02 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,6 +1,4 @@ -class {{interface.name}}ResponseValidator : public mojo::MessageFilter { +class {{export_attribute}} {{interface.name}}ResponseValidator : public NON_EXPORTED_BASE(mojo::MessageReceiver) { 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 30b5de7..9f01348 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,19 +1,40 @@ -class {{interface.name}}Stub : public mojo::MessageReceiverWithResponderStatus { +class {{export_attribute}} {{interface.name}}StubDispatch { public: - {{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_; + 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); } - bool Accept(mojo::Message* message) override; - bool AcceptWithResponder(mojo::Message* message, - mojo::MessageReceiverWithStatus* responder) override; + 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); + } private: - {{interface.name}}* sink_; - mojo::internal::SerializationContext serialization_context_; - mojo::internal::ControlMessageHandler control_message_handler_; + ImplPointerType sink_; }; 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 deleted file mode 100644 index 5256e75..0000000 --- a/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl +++ /dev/null @@ -1,126 +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. - -{%- 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 new file mode 100644 index 0000000..964b254 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl @@ -0,0 +1,96 @@ +// 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 new file mode 100644 index 0000000..645bb69 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/module-shared.cc.tmpl @@ -0,0 +1,64 @@ +// 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 new file mode 100644 index 0000000..dd13466 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl @@ -0,0 +1,212 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +{%- 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 efb9db6..2c66a85 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl @@ -26,13 +26,11 @@ #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 %} @@ -52,75 +50,16 @@ namespace {{variant}} { {#--- Constants #} {%- for constant in module.constants %} -{%- if not constant.kind|is_integral_kind %} -const {{constant.kind|cpp_pod_type}} {{constant.name}} = {{constant|constant_value}}; +{%- if constant.kind|is_string_kind %} +const char {{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_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}}; +{%- if constant.kind|is_string_kind %} +const char {{struct.name}}::{{constant.name}}[] = {{constant|constant_value}}; {%- endif %} {%- endfor %} {%- endfor %} @@ -129,7 +68,6 @@ const {{constant.kind|cpp_pod_type}} {{struct.name}}::{{constant.name}} = {{cons {%- for struct in structs %} {%- if not struct|is_native_only_kind %} {%- include "wrapper_class_definition.tmpl" %} -{%- include "struct_data_view_definition.tmpl" %} {%- endif %} {%- endfor %} @@ -155,18 +93,17 @@ namespace mojo { {#--- Struct Serialization Helpers -#} {% for struct in structs %} {%- if not struct|is_native_only_kind %} -{% include "struct_serialization_definition.tmpl" %} +{% include "struct_traits_definition.tmpl" %} {%- endif %} {%- endfor %} {#--- Union Serialization Helpers #} {%- for union in unions %} -{%- include "union_serialization_definition.tmpl" %} +{%- include "union_traits_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 c4f3e07..acdad5e 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl @@ -12,11 +12,6 @@ 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}} { @@ -40,30 +35,30 @@ namespace {{variant}} { #include <stdint.h> -#include <functional> -#include <ostream> +#include <limits> #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/map.h" -#include "mojo/public/cpp/bindings/message_filter.h" -#include "mojo/public/cpp/bindings/native_enum.h" +#include "mojo/public/cpp/bindings/lib/union_accessor.h" #include "mojo/public/cpp/bindings/native_struct.h" -#include "mojo/public/cpp/bindings/no_interface.h" +#include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h" #include "mojo/public/cpp/bindings/struct_ptr.h" #include "mojo/public/cpp/bindings/struct_traits.h" -#include "{{variant_path}}-internal.h" +#include "mojo/public/cpp/bindings/thread_safe_interface_ptr.h" +#include "mojo/public/cpp/bindings/union_traits.h" +#include "{{module.path}}-shared.h" {%- for import in imports %} {%- if variant %} #include "{{"%s-%s.h"|format(import.module.path, variant)}}" @@ -72,11 +67,13 @@ namespace {{variant}} { {%- endif %} {%- endfor %} {%- if not for_blink %} -#include "mojo/public/cpp/bindings/array.h" -#include "mojo/public/cpp/bindings/string.h" +#include <string> +#include <vector> {%- else %} -#include "mojo/public/cpp/bindings/wtf_array.h" -#include "mojo/public/cpp/bindings/wtf_map.h" +{# 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 "third_party/WebKit/Source/wtf/Optional.h" #include "third_party/WebKit/Source/wtf/text/WTFString.h" {%- endif %} @@ -85,42 +82,32 @@ namespace {{variant}} { #include "{{header}}" {%- endfor %} -{#--- 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 { +{%- if export_header %} +#include "{{export_header}}" +{%- endif %} -{%- for enum in enums %} +{#--- 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(enum)}} +{{enum_hash_blink(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 %} -{#- 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 %} +{{constant|format_constant_declaration}}; {%- endfor %} {#--- Interface Forward Declarations -#} @@ -128,9 +115,13 @@ extern const {{constant.kind|cpp_pod_type}} {{constant.name}}; 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 = @@ -144,7 +135,6 @@ using {{struct.name}} = mojo::NativeStruct; 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 %} @@ -166,27 +156,6 @@ typedef mojo::StructPtr<{{union.name}}> {{union.name}}Ptr; {#--- 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 -#} @@ -216,7 +185,6 @@ namespace std { {% 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 %} @@ -231,7 +199,6 @@ namespace std { {% 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 %} @@ -243,67 +210,23 @@ namespace std { {%- 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_serialization_declaration.tmpl" %} +{% include "struct_traits_declaration.tmpl" %} {%- endif %} {%- endfor %} {#--- Union Serialization Helpers -#} {% if unions %} {%- for union in unions %} -{% include "union_serialization_declaration.tmpl" %} +{% include "union_traits_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 7c34939..96e0d61 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,29 +1,56 @@ class {{struct.name}}DataView { public: + {{struct.name}}DataView() {} + {{struct.name}}DataView( internal::{{struct.name}}_Data* data, - mojo::internal::SerializationContext* context); + 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_; } {%- 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 %} +{%- 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); + + 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 %} + 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> - 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 %} + 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, value, context_); + pointer, output, context_); } {%- elif kind|is_enum_kind %} template <typename UserType> - bool Read{{name|under_to_camel}}(UserType* value) const { + WARN_UNUSED_RESULT bool Read{{name|under_to_camel}}(UserType* output) const { {%- if pf.min_version != 0 %} auto data_value = data_->header_.version >= {{pf.min_version}} ? data_->{{name}} : 0; @@ -31,23 +58,61 @@ class {{struct.name}}DataView { auto data_value = data_->{{name}}; {%- endif %} return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>( - data_value, value); + data_value, output); } - {{kind|get_qualified_name_for_kind}} {{name}}() const; + {{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}}); + } -{%- elif kind|is_union_kind %} - bool Read{{name|under_to_camel}}({{kind|cpp_wrapper_type}}* value); +{%- 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_any_handle_or_interface_kind %} - {{kind|cpp_wrapper_type}} Take{{name|under_to_camel}}(); +{%- 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; + } {%- else %} - {{kind|cpp_wrapper_type}} {{name}}() const; + {{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}}; + } + {%- endif %} {%- endfor %} private: - internal::{{struct.name}}_Data* data_; - mojo::internal::SerializationContext* context_; + internal::{{struct.name}}_Data* data_ = nullptr; +{%- if struct|requires_context_for_data_view %} + mojo::internal::SerializationContext* context_ = nullptr; +{%- endif %} }; 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 2be92b3..95311dc 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,59 +1,29 @@ -{{struct.name}}DataView::{{struct.name}}DataView( - internal::{{struct.name}}_Data* data, - mojo::internal::SerializationContext* context) - : data_(data), context_(context) { - DCHECK(data_); -} - {%- 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 %} - if (data_->header_.version < {{pf.min_version}}) - return {{kind|get_qualified_name_for_kind}}{}; -{%- endif %} - return static_cast<{{kind|get_qualified_name_for_kind}}>(data_->{{name}}); -} +{%- set kind = pf.field.kind %} +{%- set name = pf.field.name %} -{%- elif kind|is_union_kind %} -bool {{struct.name}}DataView::Read{{name|under_to_camel}}( - {{kind|cpp_wrapper_type}}* value) { +{%- 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; + auto pointer = data_->header_.version >= {{pf.min_version}} + ? &data_->{{name}} : nullptr; {%- else %} - auto pointer = &data_->{{name}}; + auto pointer = &data_->{{name}}; {%- endif %} - return mojo::internal::Deserialize<{{kind|unmapped_type_for_serializer}}>( - pointer, value, context_); + *output = {{kind|cpp_data_view_type}}(pointer, 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; +{%- elif kind|is_object_kind %} +inline void {{struct.name}}DataView::Get{{name|under_to_camel}}DataView( + {{kind|cpp_data_view_type}}* output) { {%- 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}}{}; + auto pointer = data_->header_.version >= {{pf.min_version}} + ? data_->{{name}}.Get() : nullptr; +{%- else %} + auto pointer = data_->{{name}}.Get(); {%- endif %} - return data_->{{name}}; + *output = {{kind|cpp_data_view_type}}(pointer, context_); } {%- 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 dd87be7..156f774 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_declaration.tmpl @@ -2,21 +2,13 @@ class {{class_name}} { public: - static {{class_name}}* New(mojo::internal::Buffer* buf); + 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); -{% 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 %} @@ -46,7 +38,8 @@ class {{class_name}} { {%- endif %} private: - {{class_name}}(); + {{class_name}}() : header_({sizeof(*this), {{struct.versions[-1].version}}}) { + } ~{{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 a2e5708..60dca40 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl @@ -2,11 +2,6 @@ {%- 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) { @@ -20,7 +15,7 @@ bool {{class_name}}::Validate( // the message comes from an older version. const {{class_name}}* object = static_cast<const {{class_name}}*>(data); - static const struct { + static constexpr struct { uint32_t version; uint32_t num_bytes; } kVersionSizes[] = { @@ -73,7 +68,3 @@ bool {{class_name}}::Validate( 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 c59f317..bb5fb9c 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl @@ -19,7 +19,8 @@ {%- 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 %} +{%- for pf in struct.packed.packed_fields_in_ordinal_order + if pf.field.kind|is_object_kind or pf.field.kind|is_associated_kind %} {%- set name = pf.field.name -%} {%- set kind = pf.field.kind -%} {%- set original_input_field = input_field_pattern|format(name) %} @@ -148,8 +149,11 @@ {%- 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_or_interface_kind %} +{%- elif kind|is_any_handle_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 aede1a7..835178b 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,37 +1,13 @@ {%- import "struct_macros.tmpl" as struct_macros %} -{%- set mojom_type = struct|get_qualified_name_for_kind %} +{%- set data_view = struct|get_qualified_name_for_kind ~ "DataView" %} {%- 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<{{mojom_type}}Ptr, MaybeConstUserType> { +struct Serializer<{{data_view}}, MaybeConstUserType> { using UserType = typename std::remove_const<MaybeConstUserType>::type; - using Traits = StructTraits<{{mojom_type}}, UserType>; + using Traits = StructTraits<{{data_view}}, UserType>; static size_t PrepareToSerialize(MaybeConstUserType& input, SerializationContext* context) { @@ -61,7 +37,7 @@ struct Serializer<{{mojom_type}}Ptr, MaybeConstUserType> { {{struct_macros.serialize( struct, struct.name ~ " struct", "CallWithContext(Traits::%s, input, custom_context)", "result", - "buffer", "context", True)|indent(4)}} + "buffer", "context", True)|indent(2)}} *output = result; CustomContextHelper<Traits>::TearDown(input, custom_context); @@ -73,7 +49,7 @@ struct Serializer<{{mojom_type}}Ptr, MaybeConstUserType> { if (!input) return CallSetToNullIfExists<Traits>(output); - {{mojom_type}}DataView data_view(input, context); + {{data_view}} data_view(input, context); return Traits::Read(data_view, output); } }; 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 new file mode 100644 index 0000000..1b7cf89 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_declaration.tmpl @@ -0,0 +1,32 @@ +{%- 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/struct_serialization_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_definition.tmpl index 7421abc..f84337f 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_traits_definition.tmpl @@ -2,8 +2,8 @@ {%- set mojom_type = struct|get_qualified_name_for_kind %} // static -bool StructTraits<{{mojom_type}}, {{mojom_type}}Ptr>::Read( - {{mojom_type}}DataView input, +bool StructTraits<{{mojom_type}}::DataView, {{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/union_data_view_declaration.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl new file mode 100644 index 0000000..2a9e02d --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_declaration.tmpl @@ -0,0 +1,91 @@ +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 new file mode 100644 index 0000000..6da9280 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_data_view_definition.tmpl @@ -0,0 +1,12 @@ +{%- 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 be2db4c..005ba76 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl @@ -6,20 +6,27 @@ class {{class_name}} { public: // Used to identify Mojom Union Data Classes. typedef void MojomUnionDataType; - static {{class_name}}* New(mojo::internal::Buffer* buf); - {{class_name}}(); - // Do nothing in the destructor since it won't be called. + + {{class_name}}() {} + // Do nothing in the destructor since it won't be called when it is a + // non-inlined union. ~{{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; - } + bool is_null() const { return size == 0; } - void set_null(); + void set_null() { + size = 0U; + tag = static_cast<{{enum_name}}>(0); + data.unknown = 0U; + } enum class {{enum_name}} : uint32_t { {% for field in union.fields %} 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 fc6eddf..af5ea9f 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl @@ -3,24 +3,31 @@ {%- 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) + if (!data) { + DCHECK(!inlined); return true; + } - if (!ValidateUnionHeaderAndClaimMemory(data, inlined, validation_context)) + // 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)) { 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}}: { @@ -38,13 +45,3 @@ bool {{class_name}}::Validate( } } } - -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 bab9045..b589ae9 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,37 +1,141 @@ -{%- set mojom_type = union|get_qualified_name_for_kind %} +{%- set data_view = union|get_qualified_name_for_kind ~ "DataView" %} {%- set data_type = union|get_qualified_name_for_kind(internal=True) %} namespace internal { -template <typename MojomType> -struct UnionSerializerImpl; +template <typename MaybeConstUserType> +struct Serializer<{{data_view}}, MaybeConstUserType> { + using UserType = typename std::remove_const<MaybeConstUserType>::type; + using Traits = UnionTraits<{{data_view}}, UserType>; -template <> -struct UnionSerializerImpl<{{mojom_type}}Ptr> { - static size_t PrepareToSerialize({{mojom_type}}Ptr& input, + static size_t PrepareToSerialize(MaybeConstUserType& input, bool inlined, - SerializationContext* context); + SerializationContext* context) { + size_t size = inlined ? 0 : sizeof({{data_type}}); + + if (CallIsNullIfExists<Traits>(input)) + return size; + + void* custom_context = CustomContextHelper<Traits>::SetUp(input, context); + ALLOW_UNUSED_LOCAL(custom_context); - static void Serialize({{mojom_type}}Ptr& input, + 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, Buffer* buffer, {{data_type}}** output, bool inlined, - SerializationContext* context); + SerializationContext* context) { + if (CallIsNullIfExists<Traits>(input)) { + if (inlined) + (*output)->set_null(); + else + *output = nullptr; + return; + } - static bool Deserialize({{data_type}}* input, - {{mojom_type}}Ptr* output, - SerializationContext* context); -}; + void* custom_context = CustomContextHelper<Traits>::GetNext(context); -template <typename MaybeConstUserType> -struct Serializer<{{mojom_type}}Ptr, MaybeConstUserType> - : public UnionSerializerImpl<{{mojom_type}}Ptr> { - using UserType = typename std::remove_const<MaybeConstUserType>::type; + 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); + } + + static bool Deserialize({{data_type}}* input, + UserType* output, + SerializationContext* context) { + if (!input || input->is_null()) + return CallSetToNullIfExists<Traits>(output); - 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."); + {{data_view}} data_view(input, context); + return Traits::Read(data_view, output); + } }; } // 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 deleted file mode 100644 index 5a11ff7..0000000 --- a/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl +++ /dev/null @@ -1,164 +0,0 @@ -{%- 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 new file mode 100644 index 0000000..4933e57 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_traits_declaration.tmpl @@ -0,0 +1,24 @@ +{%- 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 new file mode 100644 index 0000000..cde3f95 --- /dev/null +++ b/mojo/public/tools/bindings/generators/cpp_templates/union_traits_definition.tmpl @@ -0,0 +1,47 @@ +{%- 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 367be6d..a50a585 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)}} + if (!{{field.kind|get_qualified_name_for_kind(internal=True,flatten_nested_kind=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 98ae23c..9e6e46f 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,29 +1,24 @@ -{% from "enum_macros.tmpl" import enum_decl -%} - -class {{struct.name}} { +class {{export_attribute}} {{struct.name}} { public: using DataView = {{struct.name}}DataView; using Data_ = internal::{{struct.name}}_Data; {#--- Enums #} {%- for enum in struct.enums -%} -{%- if enum|is_native_only_kind %} - using {{enum.name}} = mojo::NativeEnum; -{%- else %} - {{enum_decl(enum)|indent(2)}} -{%- endif %} + using {{enum.name}} = {{enum|get_name_for_kind(flatten_nested_kind=True)}}; {%- endfor %} {#--- Constants #} {%- for constant in struct.constants %} -{%- 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 %} + static {{constant|format_constant_declaration(nested=True)}}; {%- endfor %} - static {{struct.name}}Ptr New(); + template <typename... Args> + static {{struct.name}}Ptr New(Args&&... args) { + return {{struct.name}}Ptr( + base::in_place, + std::forward<Args>(args)...); + } template <typename U> static {{struct.name}}Ptr From(const U& u) { @@ -35,7 +30,15 @@ class {{struct.name}} { return mojo::TypeConverter<U, {{struct.name}}>::Convert(*this); } - {{struct.name}}(); +{% 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}}(); // Clone() is a template so it is only instantiated if it is used. Thus, the @@ -52,20 +55,24 @@ class {{struct.name}} { T, {{struct.name}}>::value>::type* = nullptr> bool Equals(const T& other) const; -{%- set serialization_result_type = "mojo::WTFArray<uint8_t>" - if for_blink else "mojo::Array<uint8_t>" %} +{%- 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>" %} template <typename UserType> static {{serialization_result_type}} Serialize(UserType* input) { return mojo::internal::StructSerializeImpl< - {{struct.name}}Ptr, {{serialization_result_type}}>(input); + {{struct.name}}::DataView, {{serialization_result_type}}>(input); } template <typename UserType> static bool Deserialize(const {{serialization_result_type}}& input, UserType* output) { return mojo::internal::StructDeserializeImpl< - {{struct.name}}Ptr, {{serialization_result_type}}>( + {{struct.name}}::DataView, {{serialization_result_type}}>( input, output); } @@ -75,5 +82,10 @@ class {{struct.name}} { {%- 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 0bb1cda..e75543f 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,15 +1,33 @@ -// static -{{struct.name}}Ptr {{struct.name}}::New() { - {{struct.name}}Ptr rv; - mojo::internal::StructHelper<{{struct.name}}>::Initialize(&rv); - return rv; -} - -{{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 %} { -} +{% 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; -{{struct.name}}::~{{struct.name}}() { +{%- 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; } +{%- endif %} 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 f4aa314..feb8615 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,12 +1,11 @@ template <typename StructPtrType> {{struct.name}}Ptr {{struct.name}}::Clone() const { - // Use StructPtrType to prevent the compiler from trying to compile this - // without being asked. - StructPtrType rv(New()); + return New( {%- for field in struct.fields %} - rv->{{field.name}} = mojo::internal::Clone({{field.name}}); + mojo::Clone({{field.name}}) +{%- if not loop.last -%},{%- endif %} {%- 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 f62bc72..8b7cf9e 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,5 +1,6 @@ -class {{union.name}} { +class {{export_attribute}} {{union.name}} { public: + using DataView = {{union.name}}DataView; using Data_ = internal::{{union.name}}_Data; using Tag = Data_::{{union.name}}_Tag; @@ -32,13 +33,27 @@ class {{union.name}} { 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; - {{field.kind|cpp_union_getter_return_type}} get_{{field.name}}() const; + 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 %} + } + 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 85cc4e6..b9e416a 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,8 +1,6 @@ // static {{union.name}}Ptr {{union.name}}::New() { - {{union.name}}Ptr rv; - mojo::internal::StructHelper<{{union.name}}>::Initialize(&rv); - return rv; + return {{union.name}}Ptr(base::in_place); } {{union.name}}::{{union.name}}() { @@ -16,20 +14,6 @@ } {% 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 %} @@ -79,3 +63,23 @@ void {{union.name}}::DestroyActive() { {%- 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 d3371d9..4c4851f 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 @@ template <typename UnionPtrType> 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::internal::Clone(*data_.{{field.name}})); + rv->set_{{field.name}}(mojo::Clone(*data_.{{field.name}})); {%- else %} - rv->set_{{field.name}}(mojo::internal::Clone(data_.{{field.name}})); + rv->set_{{field.name}}(mojo::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 5cf9a68..4c0823c 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 @@ org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE {%- 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 @@ if ({{variable}} == null) { {{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 @@ if (decoder{{level+1}} == null) { 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 and not field.kind|is_interface_request_kind %} +{%- elif field.kind|is_any_handle_kind %} {{field|name}} = org.chromium.mojo.system.InvalidHandle.INSTANCE; -{% endif %} -{% endfor %} +{%- endif %} +{%- endfor %} } public {{struct|name}}() { @@ -182,36 +182,55 @@ if ({{variable}} != null) { 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; } - 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)}} + 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(); } -{% 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 %} } /** @@ -225,13 +244,13 @@ if ({{variable}} != null) { 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; } @@ -242,28 +261,28 @@ if ({{variable}} != null) { 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_; @@ -272,7 +291,7 @@ public final class {{union|name}} extends org.chromium.mojo.bindings.Union { 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. @@ -289,7 +308,7 @@ public final class {{union|name}} extends org.chromium.mojo.bindings.Union { assert mTag_ == Tag.{{field|ucc}}; return m{{field|ucc}}; } -{% endfor %} +{%- endfor %} @Override @@ -297,20 +316,20 @@ public final class {{union|name}} extends org.chromium.mojo.bindings.Union { 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; } @@ -328,20 +347,20 @@ public final class {{union|name}} extends org.chromium.mojo.bindings.Union { } {{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; } @@ -364,10 +383,10 @@ public final class {{union|name}} extends org.chromium.mojo.bindings.Union { 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; } @@ -383,12 +402,12 @@ public final class {{union|name}} extends org.chromium.mojo.bindings.Union { 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; } @@ -396,4 +415,4 @@ public final class {{union|name}} extends org.chromium.mojo.bindings.Union { 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 3928e0c..c7dcbbc 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 @@ try { } switch(header.getType()) { {% if with_response %} - case org.chromium.mojo.bindings.InterfaceControlMessagesConstants.RUN_MESSAGE_ID: + case org.chromium.mojo.bindings.interfacecontrol.InterfaceControlMessagesConstants.RUN_MESSAGE_ID: return org.chromium.mojo.bindings.InterfaceControlMessagesHelper.handleRun( getCore(), {{interface|name}}_Internal.MANAGER, messageWithHeader, receiver); {% else %} - case org.chromium.mojo.bindings.InterfaceControlMessagesConstants.RUN_OR_CLOSE_PIPE_MESSAGE_ID: + case org.chromium.mojo.bindings.interfacecontrol.InterfaceControlMessagesConstants.RUN_OR_CLOSE_PIPE_MESSAGE_ID: return org.chromium.mojo.bindings.InterfaceControlMessagesHelper.handleRunOrClosePipe( {{interface|name}}_Internal.MANAGER, messageWithHeader); {% endif %} @@ -113,12 +113,7 @@ try { {% else %} {{request_struct|name}}.deserialize(messageWithHeader.getPayload()); {% 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); - } + 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 %}); 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 4ae0a9b..019b1b6 100644 --- a/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl +++ b/mojo/public/tools/bindings/generators/js_templates/enum_definition.tmpl @@ -1,13 +1,33 @@ {%- 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 %} -{%- endfor %} + 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; + }; {%- 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 1b5cafa..54e2d4e 100644 --- a/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl +++ b/mojo/public/tools/bindings/generators/js_templates/interface_definition.tmpl @@ -2,12 +2,21 @@ 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) { - bindings.ProxyBase.call(this, receiver); + this.receiver_ = 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 %} @@ -15,7 +24,7 @@ ) { var params = new {{interface.name}}_{{method.name}}_Params(); {%- for parameter in method.parameters %} - params.{{parameter.name}} = {{parameter|js_proxy_method_parameter_value}}; + params.{{parameter.name}} = {{parameter.name}}; {%- endfor %} {%- if method.response_parameters == None %} @@ -47,15 +56,13 @@ {%- endfor %} function {{interface.name}}Stub(delegate) { - bindings.StubBase.call(this, delegate); + this.delegate_ = 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 {{delegate_expr}} && {{delegate_expr}}.{{js_method_name}} && {{delegate_expr}}.{{js_method_name}}({{method.parameters|map('js_stub_method_parameter_value')|join(', ')}}); + return this.delegate_ && this.delegate_.{{js_method_name}} && this.delegate_.{{js_method_name}}({{method.parameters|map(attribute='name')|join(', ')}}); } {%- endfor %} @@ -162,6 +169,8 @@ params.{{parameter.name}}{% if not loop.last %}, {% endif -%} 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 6d7a1a2..3ce4ab6 100644 --- a/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl +++ b/mojo/public/tools/bindings/generators/js_templates/module.amd.tmpl @@ -3,15 +3,19 @@ // 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(bindings, codec, connection, core, validator +], function( +{%- if module.path != "mojo/public/interfaces/bindings/interface_control_messages.mojom" -%} +bindings, {% endif -%} +codec, 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 86ea2ce..ddfef72 100644 --- a/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl +++ b/mojo/public/tools/bindings/generators/js_templates/module_definition.tmpl @@ -40,6 +40,7 @@ {%- 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 ca80d67..e823e46 100644 --- a/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl +++ b/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl @@ -34,16 +34,40 @@ {{struct.name}}.validate = function(messageValidator, offset) { var err; - err = messageValidator.validateStructHeader(offset, {{struct.name}}.encodedSize, {{struct.versions[-1].version}}); + err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize); 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 %} -{%- for packed_field in struct.packed.packed_fields %} +{%- set last_checked_version = 0 %} +{%- for packed_field in struct.packed.packed_fields_in_ordinal_order %} {%- 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; @@ -59,7 +83,8 @@ var numberOfBytes = decoder.readUint32(); var version = decoder.readUint32(); {%- for byte in struct.bytes %} -{%- if byte.packed_fields|length > 1 %} +{%- if byte.packed_fields|length >= 1 and + byte.packed_fields[0].field|is_bool_field %} packed = decoder.readUint8(); {%- for packed_field in byte.packed_fields %} val.{{packed_field.field.name}} = (packed >> {{packed_field.bit}}) & 1 ? true : false; @@ -82,7 +107,8 @@ encoder.writeUint32({{struct.versions[-1].version}}); {%- for byte in struct.bytes %} -{%- if byte.packed_fields|length > 1 %} +{%- if byte.packed_fields|length >= 1 and + byte.packed_fields[0].field|is_bool_field %} 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 3c903bc..4823feb 100644 --- a/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl +++ b/mojo/public/tools/bindings/generators/js_templates/union_definition.tmpl @@ -89,7 +89,11 @@ Object.defineProperty({{union.name}}.prototype, "{{field.name}}", { 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 %} } @@ -111,7 +115,11 @@ Object.defineProperty({{union.name}}.prototype, "{{field.name}}", { 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 7a39749..d4e15a7 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 @@ if (err !== validator.validationError.NONE) {%- macro _validate_field(field, offset, name) %} {%- if field|is_string_pointer_field %} // validate {{name}} -err = messageValidator.validateStringPointer({{offset}}, {{field|validate_string_params}}) +err = messageValidator.validateStringPointer({{offset}}, {{field|validate_nullable_params}}) {{_check_err()}} {%- elif field|is_array_pointer_field %} // validate {{name}} @@ -22,11 +22,19 @@ err = messageValidator.validateMapPointer({{offset}}, {{field|validate_map_param {{_check_err()}} {%- elif field|is_interface_field %} // validate {{name}} -err = messageValidator.validateInterface({{offset}}, {{field|validate_interface_params}}); +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}}) {{_check_err()}} {%- elif field|is_handle_field %} // validate {{name}} -err = messageValidator.validateHandle({{offset}}, {{field|validate_handle_params}}) +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}}); {{_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 38b219c..38d222b 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 @@ _kind_to_cpp_literal_suffix = { # 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,22 +50,40 @@ class _NameFormatter(object): self._variant = variant def Format(self, separator, prefixed=False, internal=False, - include_variant=False, add_same_module_namespaces=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.""" + parts = [] if self._ShouldIncludeNamespace(add_same_module_namespaces): if prefixed: parts.append("") parts.extend(self._GetNamespace()) - if include_variant and self._variant: + if include_variant and self._variant and not internal: parts.append(self._variant) - parts.extend(self._GetName(internal)) + parts.extend(self._GetName(internal, flatten_nested_kind)) return separator.join(parts) - def FormatForCpp(self, add_same_module_namespaces=False, internal=False): + def FormatForCpp(self, add_same_module_namespaces=False, internal=False, + flatten_nested_kind=False): return self.Format( "::", prefixed=True, add_same_module_namespaces=add_same_module_namespaces, - internal=internal, include_variant=True) + internal=internal, include_variant=True, + flatten_nested_kind=flatten_nested_kind) def FormatForMojom(self): return self.Format(".", add_same_module_namespaces=True) @@ -74,24 +92,32 @@ class _NameFormatter(object): if not internal: return token.name if (mojom.IsStructKind(token) or mojom.IsUnionKind(token) or - mojom.IsInterfaceKind(token) or mojom.IsEnumKind(token)): + mojom.IsEnumKind(token)): return token.name + "_Data" return token.name - def _GetName(self, internal): - 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 = [] if internal: - name.append("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 + if self._token.parent_kind: - 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 + name_parts.append(self._MapKindName(self._token.parent_kind, internal)) + name_parts.append(self._MapKindName(self._token, internal)) + return name_parts def _ShouldIncludeNamespace(self, add_same_module_namespaces): return add_same_module_namespaces or self._token.imported_from @@ -116,22 +142,30 @@ def DefaultValue(field): 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): - return _NameFormatter(kind, _variant).FormatForCpp(internal=internal) - -def GetQualifiedNameForKind(kind, internal=False): +def GetNameForKind(kind, internal=False, flatten_nested_kind=False, + add_same_module_namespaces=False): return _NameFormatter(kind, _variant).FormatForCpp( - internal=internal, add_same_module_namespaces=True) + 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" + def GetFullMojomNameForKind(kind): return _NameFormatter(kind, _variant).FormatForMojom() @@ -144,60 +178,128 @@ def IsNativeOnlyKind(kind): 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 GetCppWrapperType(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 + if IsTypemappedKind(kind): - return GetNativeTypeName(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 if mojom.IsEnumKind(kind): - return GetNameForKind(kind) + return GetNameForKind( + kind, add_same_module_namespaces=add_same_module_namespaces) if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind): - return "%sPtr" % GetNameForKind(kind) + return "%sPtr" % GetNameForKind( + kind, add_same_module_namespaces=add_same_module_namespaces) if mojom.IsArrayKind(kind): - 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) + 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) if mojom.IsMapKind(kind): - 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)) + 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)) if mojom.IsInterfaceKind(kind): - return "%sPtr" % GetNameForKind(kind) + return "%sPtr" % GetNameForKind( + kind, add_same_module_namespaces=add_same_module_namespaces) if mojom.IsInterfaceRequestKind(kind): - return "%sRequest" % GetNameForKind(kind.kind) + return "%sRequest" % GetNameForKind( + kind.kind, add_same_module_namespaces=add_same_module_namespaces) if mojom.IsAssociatedInterfaceKind(kind): - return "%sAssociatedPtrInfo" % GetNameForKind(kind.kind) + return "%sAssociatedPtrInfo" % GetNameForKind( + kind.kind, add_same_module_namespaces=add_same_module_namespaces) if mojom.IsAssociatedInterfaceRequestKind(kind): - return "%sAssociatedRequest" % GetNameForKind(kind.kind) + return "%sAssociatedRequest" % GetNameForKind( + kind.kind, add_same_module_namespaces=add_same_module_namespaces) if mojom.IsStringKind(kind): if _for_blink: return "WTF::String" - if not _use_new_wrapper_types: - return "mojo::String" - return ("base::Optional<std::string>" if mojom.IsNullableKind(kind) else - "std::string") + type_name = "std::string" + return _AddOptional(type_name) if mojom.IsNullableKind(kind) else type_name if mojom.IsGenericHandleKind(kind): return "mojo::ScopedHandle" if mojom.IsDataPipeConsumerKind(kind): @@ -220,15 +322,22 @@ def IsMoveOnlyKind(kind): if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind): return True if mojom.IsArrayKind(kind): - return IsMoveOnlyKind(kind.kind) if _use_new_wrapper_types else True + return IsMoveOnlyKind(kind.kind) if mojom.IsMapKind(kind): - return IsMoveOnlyKind(kind.value_kind) if _use_new_wrapper_types else True + return IsMoveOnlyKind(kind.value_kind) 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) + return ((not mojom.IsReferenceKind(kind)) or IsMoveOnlyKind(kind) or + IsCopyablePassByValue(kind)) def GetCppWrapperParamType(kind): cpp_wrapper_type = GetCppWrapperType(kind) @@ -254,7 +363,7 @@ def GetCppFieldType(kind): if mojom.IsAssociatedInterfaceKind(kind): return "mojo::internal::AssociatedInterface_Data" if mojom.IsAssociatedInterfaceRequestKind(kind): - return "mojo::internal::AssociatedInterfaceRequest_Data" + return "mojo::internal::AssociatedEndpointHandle_Data" if mojom.IsEnumKind(kind): return "int32_t" if mojom.IsStringKind(kind): @@ -273,27 +382,47 @@ def GetUnionGetterReturnType(kind): return "%s&" % GetCppWrapperType(kind) return GetCppWrapperType(kind) -def GetUnmappedTypeForSerializer(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) + if mojom.IsEnumKind(kind): - return GetQualifiedNameForKind(kind) + return _GetName(kind) if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind): - return "%sPtr" % GetQualifiedNameForKind(kind) + return "%sDataView" % _GetName(kind) if mojom.IsArrayKind(kind): - return "mojo::Array<%s>" % GetUnmappedTypeForSerializer(kind.kind) + return "mojo::ArrayDataView<%s>" % GetCppDataViewType(kind.kind, qualified) if mojom.IsMapKind(kind): - return "mojo::Map<%s, %s>" % ( - GetUnmappedTypeForSerializer(kind.key_kind), - GetUnmappedTypeForSerializer(kind.value_kind)) + return ("mojo::MapDataView<%s, %s>" % ( + GetCppDataViewType(kind.key_kind, qualified), + GetCppDataViewType(kind.value_kind, qualified))) + if mojom.IsStringKind(kind): + return "mojo::StringDataView" if mojom.IsInterfaceKind(kind): - return "%sPtr" % GetQualifiedNameForKind(kind) + return "%sPtrDataView" % _GetName(kind) if mojom.IsInterfaceRequestKind(kind): - return "%sRequest" % GetQualifiedNameForKind(kind.kind) + return "%sRequestDataView" % _GetName(kind.kind) if mojom.IsAssociatedInterfaceKind(kind): - return "%sAssociatedPtrInfo" % GetQualifiedNameForKind(kind.kind) + return "%sAssociatedPtrInfoDataView" % _GetName(kind.kind) if mojom.IsAssociatedInterfaceRequestKind(kind): - return "%sAssociatedRequest" % GetQualifiedNameForKind(kind.kind) - if mojom.IsStringKind(kind): - return "mojo::String" + return "%sAssociatedRequestDataView" % _GetName(kind.kind) if mojom.IsGenericHandleKind(kind): return "mojo::ScopedHandle" if mojom.IsDataPipeConsumerKind(kind): @@ -306,18 +435,26 @@ def GetUnmappedTypeForSerializer(kind): 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 _NameFormatter(token, _variant).FormatForCpp() + return GetNameForKind(token, flatten_nested_kind=True) if isinstance(token, mojom.BuiltinValue): - 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 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 (kind is not None and mojom.IsFloatKind(kind)): return token if token.isdigit() else token + "f"; @@ -342,6 +479,12 @@ def TranslateConstants(token, kind): 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: @@ -351,11 +494,69 @@ def ShouldInlineStruct(struct): 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 @@ -378,7 +579,8 @@ def GetContainerValidateParamsCtorArgs(kind): element_validate_params = GetNewContainerValidateParams(kind.kind) if mojom.IsEnumKind(kind.kind): enum_validate_func = ("%s::Validate" % - GetQualifiedNameForKind(kind.kind, internal=True)) + GetQualifiedNameForKind(kind.kind, internal=True, + flatten_nested_kind=True)) else: enum_validate_func = "nullptr" @@ -403,57 +605,143 @@ def GetNewContainerValidateParams(kind): 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_size": lambda ps: ps.GetTotalSize() + _HEADER_SIZE, + "struct_constructors": GetStructConstructors, "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 entry in self.typemap.itervalues(): - extra_headers.update(entry.get("traits_headers", [])) - return list(extra_headers) + 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 def GetExtraPublicHeaders(self): - extra_headers = set() - for entry in self.typemap.itervalues(): - extra_headers.update(entry.get("public_headers", [])) - return list(extra_headers) + 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 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, @@ -461,14 +749,17 @@ class Generator(generator.Generator): "imports": self.module.imports, "kinds": self.module.kinds, "enums": self.module.enums, - "structs": self.GetStructs(), + "all_enums": all_enums, + "structs": structs, "unions": self.GetUnions(), - "interfaces": self.GetInterfaces(), + "interfaces": interfaces, "variant": self.variant, "extra_traits_headers": self.GetExtraTraitsHeaders(), "extra_public_headers": self.GetExtraPublicHeaders(), "for_blink": self.for_blink, - "use_new_wrapper_types": self.use_new_wrapper_types, + "use_once_callback": self.use_once_callback, + "export_attribute": self.export_attribute, + "export_header": self.export_header, } @staticmethod @@ -483,27 +774,45 @@ class Generator(generator.Generator): 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): - 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))) + 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))) diff --git a/mojo/public/tools/bindings/generators/mojom_java_generator.py b/mojo/public/tools/bindings/generators/mojom_java_generator.py index 481efbc..9265b3a 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 contextlib import os import re import shutil +import sys import tempfile -import zipfile from jinja2 import contextfilter @@ -20,6 +20,12 @@ import mojom.generate.generator as generator 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' @@ -219,8 +225,8 @@ def GetPackage(module): return ParseStringAttribute(module.attributes['JavaPackage']) # Default package. if module.namespace: - return 'org.chromium.mojom.' + module.namespace - return 'org.chromium.mojom' + return 'org.chromium.' + module.namespace + return 'org.chromium' def GetNameForKind(context, kind): def _GetNameHierachy(kind): @@ -397,14 +403,6 @@ def TempDir(): 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 = { @@ -533,7 +531,7 @@ class Generator(generator.Generator): with TempDir() as temp_java_root: self.output_dir = os.path.join(temp_java_root, package_path) self.DoGenerateFiles(); - ZipContentInto(temp_java_root, zip_filename) + build_utils.ZipDir(zip_filename, temp_java_root) 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 e9a4883..0eedb31 100644 --- a/mojo/public/tools/bindings/generators/mojom_js_generator.py +++ b/mojo/public/tools/bindings/generators/mojom_js_generator.py @@ -37,9 +37,13 @@ _kind_to_javascript_default_value = { def JavaScriptType(kind): + name = [] if kind.imported_from: - return kind.imported_from["unique_name"] + "." + kind.name - return kind.name + name.append(kind.imported_from["unique_name"]) + if kind.parent_kind: + name.append(kind.parent_kind.name) + name.append(kind.name) + return ".".join(name) def JavaScriptDefaultValue(field): @@ -58,9 +62,10 @@ def JavaScriptDefaultValue(field): return "null" if mojom.IsMapKind(field.kind): return "null" - if mojom.IsInterfaceKind(field.kind) or \ - mojom.IsInterfaceRequestKind(field.kind): - return _kind_to_javascript_default_value[mojom.MSGPIPE] + if mojom.IsInterfaceKind(field.kind): + return "new %sPtr()" % JavaScriptType(field.kind) + if mojom.IsInterfaceRequestKind(field.kind): + return "new bindings.InterfaceRequest()" if mojom.IsAssociatedKind(field.kind): return "null" if mojom.IsEnumKind(field.kind): @@ -120,16 +125,19 @@ def CodecType(kind): element_type = ElementCodecType(kind.kind) return "new codec.%s(%s%s)" % (array_type, element_type, array_length) if mojom.IsInterfaceKind(kind): - return "codec.%s" % ("NullableInterface" if mojom.IsNullableKind(kind) - else "Interface") + return "new codec.%s(%sPtr)" % ( + "NullableInterface" if mojom.IsNullableKind(kind) else "Interface", + JavaScriptType(kind)) if mojom.IsInterfaceRequestKind(kind): - return CodecType(mojom.MSGPIPE) + return "codec.%s" % ( + "NullableInterfaceRequest" if mojom.IsNullableKind(kind) + else "InterfaceRequest") if mojom.IsAssociatedInterfaceKind(kind): return "codec.AssociatedInterfaceNotSupported" if mojom.IsAssociatedInterfaceRequestKind(kind): return "codec.AssociatedInterfaceRequestNotSupported" if mojom.IsEnumKind(kind): - return _kind_to_codec_type[mojom.INT32] + return "new codec.Enum(%s)" % JavaScriptType(kind) if mojom.IsMapKind(kind): map_type = "NullableMapOf" if mojom.IsNullableKind(kind) else "MapOf" key_type = ElementCodecType(kind.key_kind) @@ -141,9 +149,10 @@ def CodecType(kind): 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.IsInterfaceKind(kind) or mojom.IsAssociatedKind(kind)): + mojom.IsAnyInterfaceKind(kind)): return "decodeStruct(%s)" % CodecType(kind) if mojom.IsStructKind(kind): return "decodeStructPointer(%s)" % JavaScriptType(kind) @@ -156,8 +165,6 @@ def JavaScriptDecodeSnippet(kind): 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) @@ -165,7 +172,7 @@ def JavaScriptDecodeSnippet(kind): def JavaScriptEncodeSnippet(kind): if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or - mojom.IsInterfaceKind(kind) or mojom.IsAssociatedKind(kind)): + mojom.IsAnyInterfaceKind(kind)): return "encodeStruct(%s, " % CodecType(kind) if mojom.IsUnionKind(kind): return "encodeStruct(%s, " % JavaScriptType(kind) @@ -178,8 +185,6 @@ def JavaScriptEncodeSnippet(kind): 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) @@ -228,6 +233,9 @@ def JavaScriptValidateArrayParams(field): expected_dimension_sizes) +def JavaScriptValidateEnumParams(field): + return JavaScriptType(field.kind) + def JavaScriptValidateStructParams(field): nullable = JavaScriptNullableParam(field) struct_type = JavaScriptType(field.kind) @@ -248,42 +256,6 @@ def JavaScriptValidateMapParams(field): (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: @@ -316,6 +288,9 @@ def ExpressionToText(value): def IsArrayPointerField(field): return mojom.IsArrayKind(field.kind) +def IsEnumField(field): + return mojom.IsEnumKind(field.kind) + def IsStringPointerField(field): return mojom.IsStringKind(field.kind) @@ -331,38 +306,55 @@ def IsHandleField(field): 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 = { - "default_value": JavaScriptDefaultValue, - "payload_size": JavaScriptPayloadSize, "decode_snippet": JavaScriptDecodeSnippet, + "default_value": JavaScriptDefaultValue, "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_handle_field": IsHandleField, + "is_interface_field": IsInterfaceField, + "is_interface_request_field": IsInterfaceRequestField, "is_map_pointer_field": IsMapPointerField, - "is_struct_pointer_field": IsStructPointerField, + "is_object_field": IsObjectField, "is_string_pointer_field": IsStringPointerField, + "is_struct_pointer_field": IsStructPointerField, "is_union_field": IsUnionField, - "is_handle_field": IsHandleField, - "is_interface_field": IsInterfaceField, "js_type": JavaScriptType, - "js_proxy_method_parameter_value": JavaScriptProxyMethodParameterValue, - "js_stub_method_parameter_value": JavaScriptStubMethodParameterValue, + "payload_size": JavaScriptPayloadSize, "stylize_method": generator.StudlyCapsToCamel, + "union_decode_snippet": JavaScriptUnionDecodeSnippet, + "union_encode_snippet": JavaScriptUnionEncodeSnippet, "validate_array_params": JavaScriptValidateArrayParams, - "validate_handle_params": JavaScriptValidateHandleParams, - "validate_interface_params": JavaScriptValidateInterfaceParams, + "validate_enum_params": JavaScriptValidateEnumParams, "validate_map_params": JavaScriptValidateMapParams, - "validate_string_params": JavaScriptValidateStringParams, + "validate_nullable_params": JavaScriptNullableParam, "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 deleted file mode 100755 index 4c6f597..0000000 --- a/mojo/public/tools/bindings/generators/run_cpp_generator.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/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 d6f4f17..2466636 100644 --- a/mojo/public/tools/bindings/mojom.gni +++ b/mojo/public/tools/bindings/mojom.gni @@ -2,6 +2,18 @@ # 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 = [ @@ -12,42 +24,79 @@ mojom_generator_sources = [ "$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 (!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) { +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. - read_file(typemap.mojom, "") + 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 = [] + }, + ] } -# 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. +# 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. # # Parameters: # @@ -70,20 +119,78 @@ foreach(configuration, _bindings_configurations) { # # visibility (optional) # -# 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. +# 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. # Default value is false. -# 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. +# 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. 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)) { @@ -107,8 +214,89 @@ template("mojom") { } } + # 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 @@ -119,9 +307,6 @@ template("mojom") { 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 = [] @@ -134,7 +319,6 @@ template("mojom") { 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)) { @@ -155,10 +339,23 @@ template("mojom") { foreach(source, enabled_sources) { # TODO(sammc): Use a map instead of a linear scan when GN supports maps. foreach(typemap, bindings_configuration.typemaps) { - if (get_path_info(source, "abspath") == typemap.mojom) { + _typemap_config = { + } + _typemap_config = typemap.config + if (get_path_info(source, "abspath") == _typemap_config.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) { @@ -178,28 +375,7 @@ template("mojom") { ] outputs = generator_cpp_outputs + generator_java_outputs + generator_js_outputs - 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), - ] - } - } + args = common_generator_args if (cpp_only) { args += [ @@ -219,6 +395,14 @@ template("mojom") { 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", @@ -228,11 +412,27 @@ template("mojom") { 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_new_wrapper_types) && - invoker.use_new_wrapper_types) { - args += [ "--use_new_wrapper_types" ] + if (defined(invoker.use_once_callback) && invoker.use_once_callback) { + args += [ "--use_once_callback" ] } } } @@ -272,27 +472,39 @@ template("mojom") { # line length limitations. typemap_description = [] foreach(typemap, active_typemaps) { + _typemap_config = { + } + _typemap_config = typemap.config typemap_description += [ "--start-typemap" ] - if (defined(typemap.public_headers)) { - foreach(value, typemap.public_headers) { + if (defined(_typemap_config.public_headers)) { + foreach(value, _typemap_config.public_headers) { typemap_description += [ "public_headers=$value" ] } } - if (defined(typemap.traits_headers)) { - foreach(value, typemap.traits_headers) { + if (defined(_typemap_config.traits_headers)) { + foreach(value, _typemap_config.traits_headers) { typemap_description += [ "traits_headers=$value" ] } } - foreach(value, typemap.type_mappings) { + foreach(value, _typemap_config.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(invoker.visibility)) { + if (defined(bindings_configuration.for_blink) && + bindings_configuration.for_blink && + defined(invoker.visibility_blink)) { + visibility = invoker.visibility_blink + } else if (defined(invoker.visibility)) { visibility = invoker.visibility } if (defined(invoker.testonly)) { @@ -301,69 +513,81 @@ template("mojom") { if (defined(invoker.sources) && !defined(bindings_configuration.variant)) { data = process_file_template(enabled_sources, generator_js_outputs) } - - 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) { + defines = [] 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", ] - if (enabled_sources != []) { - deps += [ ":$generator_target_name" ] - } public_deps = [ + ":$shared_cpp_sources_target_name", "//base", + "//mojo/public/cpp/bindings", ] + if (enabled_sources != []) { + public_deps += [ ":$generator_target_name" ] + } foreach(d, all_deps) { # Resolve the name, so that a target //mojo/something becomes - # //mojo/something:something and we can append cpp_sources_suffix to + # //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}_${cpp_sources_suffix}" ] + 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 + } } foreach(typemap, active_typemaps) { - if (defined(typemap.public_headers)) { - sources += typemap.public_headers + _typemap_config = { + } + _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.deps)) { - deps += typemap.deps + if (defined(_typemap_config.deps)) { + deps += _typemap_config.deps } } if (defined(bindings_configuration.for_blink) && @@ -405,8 +629,8 @@ template("mojom") { android_library(java_target_name) { deps = [ "//base:base_java", - "//mojo/public/java:bindings", - "//mojo/public/java:system", + "//mojo/public/java:bindings_java", + "//mojo/public/java:system_java", ] 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 12196ba..3a0b6fc 100755 --- a/mojo/public/tools/bindings/mojom_bindings_generator.py +++ b/mojo/public/tools/bindings/mojom_bindings_generator.py @@ -37,10 +37,9 @@ sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), from mojom.error import Error import mojom.fileutil as fileutil -from mojom.generate.data import OrderedModuleFromData +from mojom.generate import translate from mojom.generate import template_expander from mojom.parse.parser import Parse -from mojom.parse.translate import Translate _BUILTIN_GENERATORS = { @@ -102,6 +101,12 @@ def FindImportFile(rel_dir, file_name, search_rel_dirs): 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 = {} @@ -136,20 +141,18 @@ class MojomProcessor(object): 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. - for import_data in mojom['imports']: + imports = {} + for parsed_imp in tree.import_list: rel_import_file = FindImportFile( RelativePath(dirname, rel_filename.source_root), - import_data['filename'], args.import_directories) - import_data['module'] = self._GenerateModule( + parsed_imp.import_filename, args.import_directories) + imports[parsed_imp.import_filename] = self._GenerateModule( args, remaining_args, generator_modules, rel_import_file) - module = OrderedModuleFromData(mojom) + module = translate.OrderedModule(tree, name, imports) # Set the path as relative to the source root. module.path = rel_filename.relative_path() @@ -163,7 +166,10 @@ class MojomProcessor(object): module, args.output_dir, typemap=self._typemap.get(language, {}), variant=args.variant, bytecode_path=args.bytecode_path, for_blink=args.for_blink, - use_new_wrapper_types=args.use_new_wrapper_types) + 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) filtered_args = [] if hasattr(generator_module, 'GENERATOR_PREFIX'): prefix = '--' + generator_module.GENERATOR_PREFIX + '_' @@ -190,7 +196,7 @@ class MojomProcessor(object): with open(rel_filename.path) as f: source = f.read() except IOError as e: - print "%s: Error: %s" % (e.rel_filename.path, e.strerror) + \ + print "%s: Error: %s" % (rel_filename.path, e.strerror) + \ MakeImportStackMessage(imported_filename_stack + [rel_filename.path]) sys.exit(1) @@ -230,6 +236,12 @@ def _Generate(args, remaining_args): 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 @@ -258,9 +270,6 @@ def main(): 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", @@ -286,9 +295,25 @@ def main(): help="Use WTF types as generated types for mojo " "string/array/map.") generate_parser.add_argument( - "--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.") + "--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.") 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 deleted file mode 100755 index 267bd80..0000000 --- a/mojo/public/tools/bindings/mojom_list_outputs.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/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 deleted file mode 100644 index 9ceb2e9..0000000 --- a/mojo/public/tools/bindings/pylib/mojom/generate/data.py +++ /dev/null @@ -1,530 +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. - -# 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 deleted file mode 100644 index 096554c..0000000 --- a/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py +++ /dev/null @@ -1,86 +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. - -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 ccb8363..e4ab373 100644 --- a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py +++ b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py @@ -37,15 +37,19 @@ class Generator(object): # Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all # files to stdout. def __init__(self, module, output_dir=None, typemap=None, variant=None, - bytecode_path=None, for_blink=False, - use_new_wrapper_types=False): + bytecode_path=None, for_blink=False, use_once_callback=False, + export_attribute=None, export_header=None, + generate_non_variant_code=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_new_wrapper_types = use_new_wrapper_types + 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 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 2775b6c..3a5f188 100644 --- a/mojo/public/tools/bindings/pylib/mojom/generate/module.py +++ b/mojo/public/tools/bindings/pylib/mojom/generate/module.py @@ -294,6 +294,20 @@ class UnionField(Field): pass 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') @@ -425,14 +439,9 @@ class Map(ReferenceKind): ']') 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 IsInterfaceKind(key_kind): + if IsAnyInterfaceKind(key_kind): raise Exception("Interfaces cannot be keys in maps.") if IsArrayKind(key_kind): raise Exception("Arrays cannot be keys in maps.") @@ -681,6 +690,10 @@ def IsFloatKind(kind): 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 @@ -771,22 +784,24 @@ def IsPointerKind(kind): IsMapKind(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. +# Please note that it doesn't include any interface kind. def IsAnyHandleKind(kind): return (IsGenericHandleKind(kind) or IsDataPipeConsumerKind(kind) or IsDataPipeProducerKind(kind) or IsMessagePipeKind(kind) or - IsSharedBufferKind(kind) or - IsInterfaceRequestKind(kind)) + IsSharedBufferKind(kind)) -def IsAnyHandleOrInterfaceKind(kind): - return (IsAnyHandleKind(kind) or IsInterfaceKind(kind) or +def IsAnyInterfaceKind(kind): + return (IsInterfaceKind(kind) or IsInterfaceRequestKind(kind) or IsAssociatedKind(kind)) +def IsAnyHandleOrInterfaceKind(kind): + return IsAnyHandleKind(kind) or IsAnyInterfaceKind(kind) + + def IsAssociatedKind(kind): return (IsAssociatedInterfaceKind(kind) or IsAssociatedInterfaceRequestKind(kind)) @@ -838,3 +853,39 @@ def HasSyncMethods(interface): 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 9ea6cf8..66f8954 100644 --- a/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py +++ b/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py @@ -2,8 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# Based on: -# http://src.chromium.org/viewvc/blink/trunk/Source/build/scripts/template_expander.py +# Based on third_party/WebKit/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 new file mode 100644 index 0000000..ffad744 --- /dev/null +++ b/mojo/public/tools/bindings/pylib/mojom/generate/translate.py @@ -0,0 +1,639 @@ +# 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 5cf20fe..868fb45 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 @@ class Parser(object): p[0] = ast.Ordinal(value, filename=self.filename, lineno=p.lineno(1)) def p_enum_1(self, p): - """enum : attribute_section ENUM NAME LBRACE nonempty_enum_value_list \ + """enum : attribute_section ENUM NAME LBRACE enum_value_list \ RBRACE SEMI | attribute_section ENUM NAME LBRACE nonempty_enum_value_list \ COMMA RBRACE SEMI""" @@ -358,6 +358,14 @@ class Parser(object): 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 deleted file mode 100644 index 66e8443..0000000 --- a/mojo/public/tools/bindings/pylib/mojom/parse/translate.py +++ /dev/null @@ -1,236 +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. - -"""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 0d4960f..adc9cb1 100755 --- a/mojo/public/tools/gn/zip.py +++ b/mojo/public/tools/gn/zip.py @@ -20,25 +20,30 @@ sys.path.append(os.path.join(os.path.dirname(__file__), "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) - outfile.write(f, file_name) + build_utils.AddToZipHermetic(outfile, file_name, f) for f in link_inputs: realf = os.path.realpath(f) # Resolve symlinks. file_name = os.path.relpath(realf, base_dir) files.append(file_name) - outfile.write(realf, file_name) + build_utils.AddToZipHermetic(outfile, file_name, realf) 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) - with zf.open(f) as zff: - outfile.writestr(f, zff.read()) + build_utils.AddToZipHermetic(outfile, f, data=zf.read(f)) def main(): diff --git a/mojo/public/tools/manifest/manifest_collator.py b/mojo/public/tools/manifest/manifest_collator.py deleted file mode 100755 index 9a6d0e9..0000000 --- a/mojo/public/tools/manifest/manifest_collator.py +++ /dev/null @@ -1,104 +0,0 @@ -#!/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 deleted file mode 100755 index de70a82..0000000 --- a/mojo/public/tools/prepend.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/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()) diff --git a/third_party/catapult/devil/devil/android/perf/cache_control.py b/third_party/catapult/devil/devil/android/perf/cache_control.py index 7bd0a4e..15d536d 100644 --- a/third_party/catapult/devil/devil/android/perf/cache_control.py +++ b/third_party/catapult/devil/devil/android/perf/cache_control.py @@ -13,4 +13,3 @@ class CacheControl(object): """Drops the filesystem ram caches for performance testing.""" self._device.RunShellCommand('sync', as_root=True) self._device.WriteFile(CacheControl._DROP_CACHES, '3', as_root=True) - diff --git a/third_party/catapult/devil/devil/android/perf/thermal_throttle.py b/third_party/catapult/devil/devil/android/perf/thermal_throttle.py index 9aad4bb..2741e14 100644 --- a/third_party/catapult/devil/devil/android/perf/thermal_throttle.py +++ b/third_party/catapult/devil/devil/android/perf/thermal_throttle.py @@ -129,4 +129,3 @@ class ThermalThrottle(object): serial_number, btemp, degree_symbol) return has_been_throttled - |