From 21a249e4d9cb0b2ec6f0ff84ed5f7939ea67ac52 Mon Sep 17 00:00:00 2001 From: Luis Hector Chavez Date: Wed, 26 Jul 2017 17:38:05 +0000 Subject: Revert "libmojo: Uprev the library to r456626 from Chromium" This reverts commit 8ac9103e05b66812c25348943383f9365d1ce3e0. Reason for revert: Broke the mac_sdk Exempt-From-Owner-Approval: Fixing mac_sdk Change-Id: I0b74d1abaa66933a93fd6f82ff018e8948c1204e --- mojo/public/cpp/bindings/BUILD.gn | 68 +-- mojo/public/cpp/bindings/array.h | 279 +++++++++++ mojo/public/cpp/bindings/array_data_view.h | 244 --------- mojo/public/cpp/bindings/array_traits.h | 4 +- mojo/public/cpp/bindings/array_traits_carray.h | 23 - mojo/public/cpp/bindings/array_traits_standard.h | 43 ++ mojo/public/cpp/bindings/array_traits_stl.h | 77 +-- mojo/public/cpp/bindings/array_traits_wtf.h | 40 ++ mojo/public/cpp/bindings/associated_binding.h | 162 +++--- mojo/public/cpp/bindings/associated_group.h | 77 ++- .../cpp/bindings/associated_group_controller.h | 60 +-- .../public/cpp/bindings/associated_interface_ptr.h | 143 ++---- .../cpp/bindings/associated_interface_ptr_info.h | 1 - .../cpp/bindings/associated_interface_request.h | 5 - mojo/public/cpp/bindings/binding.h | 58 +-- mojo/public/cpp/bindings/binding_set.h | 272 +++-------- mojo/public/cpp/bindings/bindings_export.h | 34 -- mojo/public/cpp/bindings/clone_traits.h | 86 ---- .../cpp/bindings/connection_error_callback.h | 21 - mojo/public/cpp/bindings/connector.h | 41 +- mojo/public/cpp/bindings/disconnect_reason.h | 25 - mojo/public/cpp/bindings/filter_chain.h | 61 --- mojo/public/cpp/bindings/interface_data_view.h | 25 - .../cpp/bindings/interface_endpoint_client.h | 70 +-- mojo/public/cpp/bindings/interface_ptr.h | 44 +- mojo/public/cpp/bindings/interface_ptr_set.h | 12 +- mojo/public/cpp/bindings/interface_request.h | 45 +- mojo/public/cpp/bindings/lib/array_internal.h | 19 +- mojo/public/cpp/bindings/lib/array_serialization.h | 49 +- mojo/public/cpp/bindings/lib/associated_group.cc | 31 +- .../bindings/lib/associated_group_controller.cc | 22 +- .../bindings/lib/associated_interface_ptr_state.h | 49 +- mojo/public/cpp/bindings/lib/binding_state.h | 213 ++++++-- mojo/public/cpp/bindings/lib/bindings_internal.cc | 47 ++ mojo/public/cpp/bindings/lib/bindings_internal.h | 114 +++-- mojo/public/cpp/bindings/lib/buffer.h | 54 +- mojo/public/cpp/bindings/lib/clone_equals_util.h | 161 ++++++ mojo/public/cpp/bindings/lib/connector.cc | 102 ++-- .../cpp/bindings/lib/control_message_handler.cc | 112 +---- .../cpp/bindings/lib/control_message_handler.h | 5 +- .../cpp/bindings/lib/control_message_proxy.cc | 165 ++----- .../cpp/bindings/lib/control_message_proxy.h | 17 +- mojo/public/cpp/bindings/lib/equals_traits.h | 94 ---- mojo/public/cpp/bindings/lib/filter_chain.cc | 22 +- mojo/public/cpp/bindings/lib/filter_chain.h | 66 +++ mojo/public/cpp/bindings/lib/fixed_buffer.cc | 43 +- mojo/public/cpp/bindings/lib/fixed_buffer.h | 55 ++- .../bindings/lib/handle_interface_serialization.h | 105 +--- mojo/public/cpp/bindings/lib/hash_util.h | 84 ---- .../cpp/bindings/lib/interface_endpoint_client.cc | 189 ++----- mojo/public/cpp/bindings/lib/interface_ptr_state.h | 220 +++++++-- mojo/public/cpp/bindings/lib/map_serialization.h | 27 +- mojo/public/cpp/bindings/lib/may_auto_lock.h | 62 --- mojo/public/cpp/bindings/lib/message.cc | 223 +-------- mojo/public/cpp/bindings/lib/message_buffer.cc | 37 +- mojo/public/cpp/bindings/lib/message_buffer.h | 15 +- mojo/public/cpp/bindings/lib/message_builder.cc | 67 +-- mojo/public/cpp/bindings/lib/message_builder.h | 61 ++- mojo/public/cpp/bindings/lib/message_filter.cc | 23 + .../cpp/bindings/lib/message_header_validator.cc | 105 ++-- mojo/public/cpp/bindings/lib/message_internal.h | 45 +- mojo/public/cpp/bindings/lib/multiplex_router.cc | 543 ++++++++------------- mojo/public/cpp/bindings/lib/multiplex_router.h | 83 +--- mojo/public/cpp/bindings/lib/native_struct.cc | 16 +- mojo/public/cpp/bindings/lib/native_struct_data.h | 4 +- .../bindings/lib/native_struct_serialization.cc | 10 +- .../cpp/bindings/lib/native_struct_serialization.h | 6 +- mojo/public/cpp/bindings/lib/no_interface.cc | 20 + .../bindings/lib/pipe_control_message_handler.cc | 28 +- .../cpp/bindings/lib/pipe_control_message_proxy.cc | 69 +-- mojo/public/cpp/bindings/lib/router.cc | 323 ++++++++++++ mojo/public/cpp/bindings/lib/router.h | 177 +++++++ .../lib/scoped_interface_endpoint_handle.cc | 370 ++------------ mojo/public/cpp/bindings/lib/serialization.h | 21 +- .../cpp/bindings/lib/serialization_context.cc | 5 + .../cpp/bindings/lib/serialization_context.h | 22 +- .../cpp/bindings/lib/serialization_forward.h | 1 - .../public/cpp/bindings/lib/string_serialization.h | 6 +- mojo/public/cpp/bindings/lib/string_traits_wtf.cc | 5 +- .../cpp/bindings/lib/sync_call_restrictions.cc | 4 +- .../cpp/bindings/lib/sync_handle_registry.cc | 19 +- .../public/cpp/bindings/lib/sync_handle_registry.h | 67 +++ mojo/public/cpp/bindings/lib/validation_context.cc | 66 ++- mojo/public/cpp/bindings/lib/validation_context.h | 101 +--- mojo/public/cpp/bindings/lib/validation_errors.cc | 31 +- mojo/public/cpp/bindings/lib/validation_errors.h | 42 +- mojo/public/cpp/bindings/lib/validation_util.cc | 66 ++- mojo/public/cpp/bindings/lib/validation_util.h | 110 ++--- .../cpp/bindings/lib/wtf_clone_equals_util.h | 10 +- mojo/public/cpp/bindings/lib/wtf_serialization.h | 2 + mojo/public/cpp/bindings/map.h | 312 +++++++++++- mojo/public/cpp/bindings/map_data_view.h | 63 --- mojo/public/cpp/bindings/map_traits.h | 12 +- mojo/public/cpp/bindings/map_traits_standard.h | 53 ++ mojo/public/cpp/bindings/map_traits_stl.h | 12 +- mojo/public/cpp/bindings/map_traits_wtf.h | 62 +++ mojo/public/cpp/bindings/map_traits_wtf_hash_map.h | 15 +- mojo/public/cpp/bindings/message.h | 163 +------ mojo/public/cpp/bindings/message_filter.h | 38 ++ .../public/cpp/bindings/message_header_validator.h | 11 +- mojo/public/cpp/bindings/native_struct.h | 10 +- mojo/public/cpp/bindings/native_struct_data_view.h | 36 -- mojo/public/cpp/bindings/no_interface.h | 52 ++ .../cpp/bindings/pipe_control_message_handler.h | 10 +- .../pipe_control_message_handler_delegate.h | 7 +- .../cpp/bindings/pipe_control_message_proxy.h | 20 +- mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h | 22 - .../bindings/scoped_interface_endpoint_handle.h | 94 +--- mojo/public/cpp/bindings/stl_converters.h | 245 ++++++++++ mojo/public/cpp/bindings/string.h | 196 ++++++++ mojo/public/cpp/bindings/string_data_view.h | 34 -- mojo/public/cpp/bindings/string_traits.h | 19 +- mojo/public/cpp/bindings/string_traits_standard.h | 31 ++ mojo/public/cpp/bindings/string_traits_string16.h | 3 +- mojo/public/cpp/bindings/strong_binding.h | 143 +++--- mojo/public/cpp/bindings/struct_ptr.h | 203 +++----- mojo/public/cpp/bindings/struct_traits.h | 45 +- mojo/public/cpp/bindings/sync_call_restrictions.h | 13 +- mojo/public/cpp/bindings/sync_handle_registry.h | 4 +- mojo/public/cpp/bindings/sync_handle_watcher.h | 3 +- .../cpp/bindings/thread_safe_interface_ptr.h | 278 ----------- mojo/public/cpp/bindings/type_converter.h | 22 - mojo/public/cpp/bindings/union_traits.h | 39 -- mojo/public/cpp/bindings/wtf_array.h | 197 ++++++++ mojo/public/cpp/bindings/wtf_map.h | 200 ++++++++ 125 files changed, 4698 insertions(+), 4919 deletions(-) create mode 100644 mojo/public/cpp/bindings/array.h delete mode 100644 mojo/public/cpp/bindings/array_data_view.h create mode 100644 mojo/public/cpp/bindings/array_traits_standard.h create mode 100644 mojo/public/cpp/bindings/array_traits_wtf.h delete mode 100644 mojo/public/cpp/bindings/bindings_export.h delete mode 100644 mojo/public/cpp/bindings/clone_traits.h delete mode 100644 mojo/public/cpp/bindings/connection_error_callback.h delete mode 100644 mojo/public/cpp/bindings/disconnect_reason.h delete mode 100644 mojo/public/cpp/bindings/filter_chain.h delete mode 100644 mojo/public/cpp/bindings/interface_data_view.h create mode 100644 mojo/public/cpp/bindings/lib/bindings_internal.cc create mode 100644 mojo/public/cpp/bindings/lib/clone_equals_util.h delete mode 100644 mojo/public/cpp/bindings/lib/equals_traits.h create mode 100644 mojo/public/cpp/bindings/lib/filter_chain.h delete mode 100644 mojo/public/cpp/bindings/lib/hash_util.h delete mode 100644 mojo/public/cpp/bindings/lib/may_auto_lock.h create mode 100644 mojo/public/cpp/bindings/lib/message_filter.cc create mode 100644 mojo/public/cpp/bindings/lib/no_interface.cc create mode 100644 mojo/public/cpp/bindings/lib/router.cc create mode 100644 mojo/public/cpp/bindings/lib/router.h create mode 100644 mojo/public/cpp/bindings/lib/sync_handle_registry.h delete mode 100644 mojo/public/cpp/bindings/map_data_view.h create mode 100644 mojo/public/cpp/bindings/map_traits_standard.h create mode 100644 mojo/public/cpp/bindings/map_traits_wtf.h create mode 100644 mojo/public/cpp/bindings/message_filter.h delete mode 100644 mojo/public/cpp/bindings/native_struct_data_view.h create mode 100644 mojo/public/cpp/bindings/no_interface.h delete mode 100644 mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h create mode 100644 mojo/public/cpp/bindings/stl_converters.h create mode 100644 mojo/public/cpp/bindings/string.h delete mode 100644 mojo/public/cpp/bindings/string_data_view.h create mode 100644 mojo/public/cpp/bindings/string_traits_standard.h delete mode 100644 mojo/public/cpp/bindings/thread_safe_interface_ptr.h delete mode 100644 mojo/public/cpp/bindings/union_traits.h create mode 100644 mojo/public/cpp/bindings/wtf_array.h create mode 100644 mojo/public/cpp/bindings/wtf_map.h (limited to 'mojo/public/cpp/bindings') diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn index 5c41384..3ec9824 100644 --- a/mojo/public/cpp/bindings/BUILD.gn +++ b/mojo/public/cpp/bindings/BUILD.gn @@ -2,31 +2,14 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -interfaces_bindings_gen_dir = "$root_gen_dir/mojo/public/interfaces/bindings" - -component("bindings") { +static_library("bindings") { sources = [ - # Normally, targets should depend on the source_sets generated by mojom - # targets. However, the generated source_sets use portions of the bindings - # library. In order to avoid linker warnings about locally-defined imports - # in Windows components build, this target depends on the generated C++ - # files directly so that the EXPORT macro defintions match. - "$interfaces_bindings_gen_dir/interface_control_messages.mojom-shared-internal.h", - "$interfaces_bindings_gen_dir/interface_control_messages.mojom-shared.cc", - "$interfaces_bindings_gen_dir/interface_control_messages.mojom-shared.h", - "$interfaces_bindings_gen_dir/interface_control_messages.mojom.cc", - "$interfaces_bindings_gen_dir/interface_control_messages.mojom.h", - "$interfaces_bindings_gen_dir/pipe_control_messages.mojom-shared-internal.h", - "$interfaces_bindings_gen_dir/pipe_control_messages.mojom-shared.cc", - "$interfaces_bindings_gen_dir/pipe_control_messages.mojom-shared.h", - "$interfaces_bindings_gen_dir/pipe_control_messages.mojom.cc", - "$interfaces_bindings_gen_dir/pipe_control_messages.mojom.h", - "array_data_view.h", + "array.h", "array_traits.h", "array_traits_carray.h", + "array_traits_standard.h", "array_traits_stl.h", "associated_binding.h", - "associated_binding_set.h", "associated_group.h", "associated_group_controller.h", "associated_interface_ptr.h", @@ -34,13 +17,8 @@ component("bindings") { "associated_interface_request.h", "binding.h", "binding_set.h", - "bindings_export.h", - "clone_traits.h", - "connection_error_callback.h", "connector.h", - "disconnect_reason.h", - "filter_chain.h", - "interface_data_view.h", + "enum_traits.h", "interface_endpoint_client.h", "interface_endpoint_controller.h", "interface_id.h", @@ -51,35 +29,34 @@ component("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", @@ -91,8 +68,11 @@ component("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", @@ -114,35 +94,32 @@ component("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", - "native_struct_data_view.h", + "no_interface.h", "pipe_control_message_handler.h", "pipe_control_message_handler_delegate.h", "pipe_control_message_proxy.h", - "raw_ptr_impl_ref_traits.h", "scoped_interface_endpoint_handle.h", - "string_data_view.h", + "stl_converters.h", + "string.h", "string_traits.h", + "string_traits_standard.h", "string_traits_stl.h", "string_traits_string16.h", "string_traits_string_piece.h", - "strong_associated_binding.h", "strong_binding.h", - "strong_binding_set.h", "struct_ptr.h", "sync_call_restrictions.h", "sync_handle_registry.h", "sync_handle_watcher.h", - "thread_safe_interface_ptr.h", "type_converter.h", - "union_traits.h", - "unique_ptr_impl_ref_traits.h", ] public_deps = [ @@ -154,16 +131,12 @@ component("bindings") { deps = [ "//base", - "//mojo/public/interfaces/bindings:bindings__generator", - "//mojo/public/interfaces/bindings:bindings_shared__generator", + "//mojo/public/interfaces/bindings:bindings_cpp_sources", ] - - defines = [ "MOJO_CPP_BINDINGS_IMPLEMENTATION" ] } source_set("struct_traits") { sources = [ - "enum_traits.h", "struct_traits.h", ] } @@ -172,13 +145,16 @@ 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 new file mode 100644 index 0000000..a253da1 --- /dev/null +++ b/mojo/public/cpp/bindings/array.h @@ -0,0 +1,279 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_ + +#include +#include +#include +#include +#include +#include +#include + +#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 +class Array { + public: + using ConstRefType = typename std::vector::const_reference; + using RefType = typename std::vector::reference; + + using Element = T; + + using iterator = typename std::vector::iterator; + using const_iterator = typename std::vector::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& other) : vec_(other), is_null_(false) {} + + // Moves the contents of |other| into this array. + Array(std::vector&& other) : vec_(std::move(other)), is_null_(false) {} + Array(Array&& other) : is_null_(true) { Take(&other); } + + Array& operator=(std::vector&& 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 + static Array From(const U& other) { + return TypeConverter::Convert(other); + } + + // Copies the contents of this array to a new object of type |U|. + template + U To() const { + return TypeConverter::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& storage() const { return vec_; } + + // Passes the underlying storage and resets this array to null. + std::vector PassStorage() { + is_null_ = true; + return std::move(vec_); + } + + operator const std::vector&() 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* 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 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 + bool operator==(const Array& other) const = delete; + template + bool operator!=(const Array& other) const = delete; + + void Take(Array* other) { + operator=(nullptr); + Swap(other); + } + + std::vector vec_; + bool is_null_; + + DISALLOW_COPY_AND_ASSIGN(Array); +}; + +// A |TypeConverter| that will create an |Array| containing a copy of the +// contents of an |std::vector|, using |TypeConverter| to copy each +// element. The returned array will always be non-null. +template +struct TypeConverter, std::vector> { + static Array Convert(const std::vector& input) { + Array result(input.size()); + for (size_t i = 0; i < input.size(); ++i) + result[i] = TypeConverter::Convert(input[i]); + return std::move(result); + } +}; + +// A |TypeConverter| that will create an |std::vector| containing a copy of +// the contents of an |Array|, using |TypeConverter| to copy each +// element. If the input array is null, the output vector will be empty. +template +struct TypeConverter, Array> { + static std::vector Convert(const Array& input) { + std::vector result; + if (!input.is_null()) { + result.resize(input.size()); + for (size_t i = 0; i < input.size(); ++i) + result[i] = TypeConverter::Convert(input[i]); + } + return result; + } +}; + +// A |TypeConverter| that will create an |Array| containing a copy of the +// contents of an |std::set|, using |TypeConverter| to copy each +// element. The returned array will always be non-null. +template +struct TypeConverter, std::set> { + static Array Convert(const std::set& input) { + Array result; + for (auto i : input) + result.push_back(TypeConverter::Convert(i)); + return std::move(result); + } +}; + +// A |TypeConverter| that will create an |std::set| containing a copy of +// the contents of an |Array|, using |TypeConverter| to copy each +// element. If the input array is null, the output set will be empty. +template +struct TypeConverter, Array> { + static std::set Convert(const Array& input) { + std::set result; + if (!input.is_null()) { + for (size_t i = 0; i < input.size(); ++i) + result.insert(TypeConverter::Convert(input[i])); + } + return result; + } +}; + +// Less than operator to allow Arrays as keys in std maps and sets. +template +inline bool operator<(const Array& a, const Array& b) { + if (a.is_null()) + return !b.is_null(); + if (b.is_null()) + return false; + return a.storage() < b.storage(); +} + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_H_ diff --git a/mojo/public/cpp/bindings/array_data_view.h b/mojo/public/cpp/bindings/array_data_view.h deleted file mode 100644 index d02a884..0000000 --- a/mojo/public/cpp/bindings/array_data_view.h +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_ARRAY_DATA_VIEW_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_DATA_VIEW_H_ - -#include - -#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 -class ArrayDataViewImpl; - -template -class ArrayDataViewImpl< - T, - typename std::enable_if< - BelongsTo::value>::type> { - public: - using Data_ = typename MojomTypeTraits>::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 -class ArrayDataViewImpl< - T, - typename std::enable_if< - BelongsTo::value>::type> { - public: - using Data_ = typename MojomTypeTraits>::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 -class ArrayDataViewImpl< - T, - typename std::enable_if< - BelongsTo::value>::type> { - public: - static_assert(sizeof(T) == sizeof(int32_t), "Unexpected enum size"); - - using Data_ = typename MojomTypeTraits>::Data; - - ArrayDataViewImpl(Data_* data, SerializationContext* context) - : data_(data), context_(context) {} - - T operator[](size_t index) const { return static_cast(data_->at(index)); } - - const T* data() const { return reinterpret_cast(data_->storage()); } - - template - bool Read(size_t index, U* output) { - return Deserialize(data_->at(index), output); - } - - protected: - Data_* data_; - SerializationContext* context_; -}; - -template -class ArrayDataViewImpl< - T, - typename std::enable_if< - BelongsTo::value>::type> { - public: - using Data_ = typename MojomTypeTraits>::Data; - - ArrayDataViewImpl(Data_* data, SerializationContext* context) - : data_(data), context_(context) {} - - template - U Take(size_t index) { - U result; - bool ret = Deserialize(&data_->at(index), &result, context_); - DCHECK(ret); - return result; - } - - protected: - Data_* data_; - SerializationContext* context_; -}; - -template -class ArrayDataViewImpl< - T, - typename std::enable_if< - BelongsTo::value>::type> { - public: - using Data_ = typename MojomTypeTraits>::Data; - - ArrayDataViewImpl(Data_* data, SerializationContext* context) - : data_(data), context_(context) {} - - T Take(size_t index) { - T result; - bool ret = Deserialize(&data_->at(index), &result, context_); - DCHECK(ret); - return result; - } - - protected: - Data_* data_; - SerializationContext* context_; -}; - -template -class ArrayDataViewImpl::value>::type> { - public: - using Data_ = typename MojomTypeTraits>::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 - bool Read(size_t index, U* output) { - return Deserialize(data_->at(index).Get(), output, context_); - } - - protected: - Data_* data_; - SerializationContext* context_; -}; - -template -class ArrayDataViewImpl< - T, - typename std::enable_if< - BelongsTo::value>::type> { - public: - using Data_ = typename MojomTypeTraits>::Data; - - ArrayDataViewImpl(Data_* data, SerializationContext* context) - : data_(data), context_(context) {} - - void GetDataView(size_t index, T* output) { - *output = T(&data_->at(index), context_); - } - - template - bool Read(size_t index, U* output) { - return Deserialize(&data_->at(index), output, context_); - } - - protected: - Data_* data_; - SerializationContext* context_; -}; - -} // namespace internal - -template -class MapDataView; - -template -class ArrayDataView : public internal::ArrayDataViewImpl { - public: - using Element = T; - using Data_ = typename internal::ArrayDataViewImpl::Data_; - - ArrayDataView() : internal::ArrayDataViewImpl(nullptr, nullptr) {} - - ArrayDataView(Data_* data, internal::SerializationContext* context) - : internal::ArrayDataViewImpl(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 - // bool Read(size_t index, U* output); - - // Handles: - // T Take(size_t index); - - // Interfaces: - // template - // U Take(size_t index); - - // Object types: - // void GetDataView(size_t index, T* output); - // template - // bool Read(size_t index, U* output); - - private: - template - friend class MapDataView; -}; - -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_DATA_VIEW_H_ diff --git a/mojo/public/cpp/bindings/array_traits.h b/mojo/public/cpp/bindings/array_traits.h index 594b2e0..366573d 100644 --- a/mojo/public/cpp/bindings/array_traits.h +++ b/mojo/public/cpp/bindings/array_traits.h @@ -47,9 +47,7 @@ namespace mojo { // static void AdvanceIterator(Iterator& iterator); // // // Returns a reference to the value at the current position of -// // |iterator|. Optionally, the ConstIterator version of GetValue can -// // return by value instead of by reference if it makes sense for the -// // type. +// // |iterator|. // static const T& GetValue(ConstIterator& iterator); // static T& GetValue(Iterator& iterator); // diff --git a/mojo/public/cpp/bindings/array_traits_carray.h b/mojo/public/cpp/bindings/array_traits_carray.h index 3ff694b..ffcf9d5 100644 --- a/mojo/public/cpp/bindings/array_traits_carray.h +++ b/mojo/public/cpp/bindings/array_traits_carray.h @@ -19,14 +19,6 @@ struct CArray { T* data; }; -template -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 struct ArrayTraits> { using Element = T; @@ -56,21 +48,6 @@ struct ArrayTraits> { } }; -template -struct ArrayTraits> { - using Element = T; - - static bool IsNull(const ConstCArray& input) { return !input.data; } - - static size_t GetSize(const ConstCArray& input) { return input.size; } - - static const T* GetData(const ConstCArray& input) { return input.data; } - - static const T& GetAt(const ConstCArray& input, size_t index) { - return input.data[index]; - } -}; - } // namespace mojo #endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_CARRAY_H_ diff --git a/mojo/public/cpp/bindings/array_traits_standard.h b/mojo/public/cpp/bindings/array_traits_standard.h new file mode 100644 index 0000000..862de6b --- /dev/null +++ b/mojo/public/cpp/bindings/array_traits_standard.h @@ -0,0 +1,43 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STANDARD_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STANDARD_H_ + +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/bindings/array_traits.h" + +namespace mojo { + +template +struct ArrayTraits> { + using Element = T; + + static bool IsNull(const Array& input) { return input.is_null(); } + static void SetToNull(Array* output) { *output = nullptr; } + + static size_t GetSize(const Array& input) { return input.size(); } + + static T* GetData(Array& input) { return &input.front(); } + + static const T* GetData(const Array& input) { return &input.front(); } + + static typename Array::RefType GetAt(Array& input, size_t index) { + return input[index]; + } + + static typename Array::ConstRefType GetAt(const Array& input, + size_t index) { + return input[index]; + } + + static bool Resize(Array& input, size_t size) { + input.resize(size); + return true; + } +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STANDARD_H_ diff --git a/mojo/public/cpp/bindings/array_traits_stl.h b/mojo/public/cpp/bindings/array_traits_stl.h index dec47bf..9054a92 100644 --- a/mojo/public/cpp/bindings/array_traits_stl.h +++ b/mojo/public/cpp/bindings/array_traits_stl.h @@ -5,8 +5,6 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STL_H_ #define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STL_H_ -#include -#include #include #include "mojo/public/cpp/bindings/array_traits.h" @@ -44,17 +42,13 @@ struct ArrayTraits> { return input[index]; } - static inline bool Resize(std::vector& input, size_t size) { - // Instead of calling std::vector::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>. - // Because the deserialization code doesn't care about the original contents - // of |input|, we discard them directly. - // - // The "inline" keyword of this method matters. Without it, we have observed - // significant perf regression with some tests on Mac. crbug.com/631415 + static bool Resize(std::vector& input, size_t size) { if (input.size() != size) { + // This is a hack to make compilers for Mac and Android happy. They + // currently don't allow resizing types like + // std::vector>. + // Because the deserialization code doesn't care about the original + // contents of |input|, we discard them directly. std::vector temp(size); input.swap(temp); } @@ -63,65 +57,6 @@ struct ArrayTraits> { } }; -// This ArrayTraits specialization is used only for serialization. -template -struct ArrayTraits> { - using Element = T; - using ConstIterator = typename std::set::const_iterator; - - static bool IsNull(const std::set& input) { - // std::set<> is always converted to non-null mojom array. - return false; - } - - static size_t GetSize(const std::set& input) { return input.size(); } - - static ConstIterator GetBegin(const std::set& input) { - return input.begin(); - } - static void AdvanceIterator(ConstIterator& iterator) { - ++iterator; - } - static const T& GetValue(ConstIterator& iterator) { - return *iterator; - } -}; - -template -struct MapValuesArrayView { - explicit MapValuesArrayView(const std::map& map) : map(map) {} - const std::map& map; -}; - -// Convenience function to create a MapValuesArrayView<> that infers the -// template arguments from its argument type. -template -MapValuesArrayView MapValuesToArray(const std::map& map) { - return MapValuesArrayView(map); -} - -// This ArrayTraits specialization is used only for serialization and converts -// a map into an array, discarding the keys. -template -struct ArrayTraits> { - using Element = V; - using ConstIterator = typename std::map::const_iterator; - - static bool IsNull(const MapValuesArrayView& input) { - // std::map<> is always converted to non-null mojom array. - return false; - } - - static size_t GetSize(const MapValuesArrayView& input) { - return input.map.size(); - } - static ConstIterator GetBegin(const MapValuesArrayView& input) { - return input.map.begin(); - } - static void AdvanceIterator(ConstIterator& iterator) { ++iterator; } - static const V& GetValue(ConstIterator& iterator) { return iterator->second; } -}; - } // namespace mojo #endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STL_H_ diff --git a/mojo/public/cpp/bindings/array_traits_wtf.h b/mojo/public/cpp/bindings/array_traits_wtf.h new file mode 100644 index 0000000..7e773fc --- /dev/null +++ b/mojo/public/cpp/bindings/array_traits_wtf.h @@ -0,0 +1,40 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_WTF_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_WTF_H_ + +#include "mojo/public/cpp/bindings/array_traits.h" +#include "mojo/public/cpp/bindings/wtf_array.h" + +namespace mojo { + +template +struct ArrayTraits> { + using Element = U; + + static bool IsNull(const WTFArray& input) { return input.is_null(); } + static void SetToNull(WTFArray* output) { *output = nullptr; } + + static size_t GetSize(const WTFArray& input) { return input.size(); } + + static U* GetData(WTFArray& input) { return &input.front(); } + + static const U* GetData(const WTFArray& input) { return &input.front(); } + + static U& GetAt(WTFArray& input, size_t index) { return input[index]; } + + static const U& GetAt(const WTFArray& input, size_t index) { + return input[index]; + } + + static bool Resize(WTFArray& input, size_t size) { + input.resize(size); + return true; + } +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_WTF_H_ diff --git a/mojo/public/cpp/bindings/associated_binding.h b/mojo/public/cpp/bindings/associated_binding.h index 5941166..1da5009 100644 --- a/mojo/public/cpp/bindings/associated_binding.h +++ b/mojo/public/cpp/bindings/associated_binding.h @@ -6,78 +6,23 @@ #define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_BINDING_H_ #include -#include #include #include "base/bind.h" #include "base/callback.h" -#include "base/logging.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" -#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h" +#include "mojo/public/cpp/bindings/associated_group.h" +#include "mojo/public/cpp/bindings/associated_group_controller.h" #include "mojo/public/cpp/bindings/associated_interface_request.h" -#include "mojo/public/cpp/bindings/bindings_export.h" -#include "mojo/public/cpp/bindings/connection_error_callback.h" #include "mojo/public/cpp/bindings/interface_endpoint_client.h" -#include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h" #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" namespace mojo { -class MessageReceiver; - -// Base class used to factor out code in AssociatedBinding 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 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 payload_validator, - bool expect_sync_requests, - scoped_refptr runner, - uint32_t interface_version); - - std::unique_ptr endpoint_client_; -}; - // Represents the implementation side of an associated interface. It is similar // to Binding, except that it doesn't own a message pipe handle. // @@ -88,48 +33,53 @@ class MOJO_CPP_BINDINGS_EXPORT AssociatedBindingBase { // 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 > -class AssociatedBinding : public AssociatedBindingBase { +template +class AssociatedBinding { public: - using ImplPointerType = typename ImplRefTraits::PointerType; - // Constructs an incomplete associated binding that will use the // implementation |impl|. It may be completed with a subsequent call to the // |Bind| method. Does not take ownership of |impl|, which must outlive this // object. - explicit AssociatedBinding(ImplPointerType impl) { stub_.set_sink(impl); } + explicit AssociatedBinding(Interface* impl) : impl_(impl) { + stub_.set_sink(impl_); + } // Constructs a completed associated binding of |impl|. The output |ptr_info| - // should be sent by another interface. |impl| must outlive this object. - AssociatedBinding(ImplPointerType impl, + // should be passed through the message pipe endpoint referred to by + // |associated_group| to setup the corresponding asssociated interface + // pointer. |impl| must outlive this object. + AssociatedBinding(Interface* impl, AssociatedInterfacePtrInfo* ptr_info, + AssociatedGroup* associated_group, scoped_refptr runner = base::ThreadTaskRunnerHandle::Get()) - : AssociatedBinding(std::move(impl)) { - Bind(ptr_info, std::move(runner)); + : AssociatedBinding(impl) { + Bind(ptr_info, associated_group, std::move(runner)); } // Constructs a completed associated binding of |impl|. |impl| must outlive // the binding. - AssociatedBinding(ImplPointerType impl, + AssociatedBinding(Interface* impl, AssociatedInterfaceRequest request, scoped_refptr runner = base::ThreadTaskRunnerHandle::Get()) - : AssociatedBinding(std::move(impl)) { + : AssociatedBinding(impl) { Bind(std::move(request), std::move(runner)); } ~AssociatedBinding() {} // Creates an associated inteface and sets up this object as the - // implementation side. The output |ptr_info| should be sent by another - // interface. + // implementation side. The output |ptr_info| should be passed through the + // message pipe endpoint referred to by |associated_group| to setup the + // corresponding asssociated interface pointer. void Bind(AssociatedInterfacePtrInfo* ptr_info, + AssociatedGroup* associated_group, scoped_refptr runner = base::ThreadTaskRunnerHandle::Get()) { - auto request = MakeRequest(ptr_info); - ptr_info->set_version(Interface::Version_); + AssociatedInterfaceRequest request; + associated_group->CreateAssociatedInterface(AssociatedGroup::WILL_PASS_PTR, + ptr_info, &request); Bind(std::move(request), std::move(runner)); } @@ -137,10 +87,35 @@ class AssociatedBinding : public AssociatedBindingBase { void Bind(AssociatedInterfaceRequest request, scoped_refptr runner = base::ThreadTaskRunnerHandle::Get()) { - BindImpl(request.PassHandle(), &stub_, - base::WrapUnique(new typename Interface::RequestValidator_()), - Interface::HasSyncMethods_, std::move(runner), - Interface::Version_); + ScopedInterfaceEndpointHandle handle = request.PassHandle(); + + DCHECK(handle.is_local()) + << "The AssociatedInterfaceRequest is supposed to be used at the " + << "other side of the message pipe."; + + if (!handle.is_valid() || !handle.is_local()) { + endpoint_client_.reset(); + return; + } + + endpoint_client_.reset(new InterfaceEndpointClient( + std::move(handle), &stub_, + base::WrapUnique(new typename Interface::RequestValidator_()), + Interface::HasSyncMethods_, std::move(runner))); + endpoint_client_->set_connection_error_handler( + base::Bind(&AssociatedBinding::RunConnectionErrorHandler, + base::Unretained(this))); + + stub_.serialization_context()->group_controller = + endpoint_client_->group_controller(); + } + + // Closes the associated interface. Puts this object into a state where it can + // be rebound. + void Close() { + DCHECK(endpoint_client_); + endpoint_client_.reset(); + connection_error_handler_.Reset(); } // Unbinds and returns the associated interface request so it can be @@ -153,15 +128,44 @@ class AssociatedBinding : public AssociatedBindingBase { request.Bind(endpoint_client_->PassHandle()); endpoint_client_.reset(); + connection_error_handler_.Reset(); return request; } + // Sets an error handler that will be called if a connection error occurs. + // + // This method may only be called after this AssociatedBinding has been bound + // to a message pipe. The error handler will be reset when this + // AssociatedBinding is unbound or closed. + void set_connection_error_handler(const base::Closure& error_handler) { + DCHECK(is_bound()); + connection_error_handler_ = error_handler; + } + // Returns the interface implementation that was previously specified. - Interface* impl() { return ImplRefTraits::GetRawPointer(&stub_.sink()); } + Interface* impl() { return impl_; } + + // Indicates whether the associated binding has been completed. + bool is_bound() const { return !!endpoint_client_; } + + // Returns the associated group that this object belongs to. Returns null if + // the object is not bound. + AssociatedGroup* associated_group() { + return endpoint_client_ ? endpoint_client_->associated_group() : nullptr; + } private: - typename Interface::template Stub_ stub_; + void RunConnectionErrorHandler() { + if (!connection_error_handler_.is_null()) + connection_error_handler_.Run(); + } + + std::unique_ptr endpoint_client_; + + typename Interface::Stub_ stub_; + Interface* impl_; + base::Closure connection_error_handler_; DISALLOW_COPY_AND_ASSIGN(AssociatedBinding); }; diff --git a/mojo/public/cpp/bindings/associated_group.h b/mojo/public/cpp/bindings/associated_group.h index 14e78ec..836c0d6 100644 --- a/mojo/public/cpp/bindings/associated_group.h +++ b/mojo/public/cpp/bindings/associated_group.h @@ -5,9 +5,11 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_GROUP_H_ #define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_GROUP_H_ -#include "base/callback.h" +#include + #include "base/memory/ref_counted.h" -#include "mojo/public/cpp/bindings/bindings_export.h" +#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h" +#include "mojo/public/cpp/bindings/associated_interface_request.h" #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" namespace mojo { @@ -15,34 +17,71 @@ namespace mojo { class AssociatedGroupController; // AssociatedGroup refers to all the interface endpoints running at one end of a -// message pipe. +// message pipe. It is used to create associated interfaces for that message +// pipe. // It is thread safe and cheap to make copies. -class MOJO_CPP_BINDINGS_EXPORT AssociatedGroup { +class AssociatedGroup { public: - AssociatedGroup(); - - explicit AssociatedGroup(scoped_refptr controller); - - explicit AssociatedGroup(const ScopedInterfaceEndpointHandle& handle); + // 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); - // The return value of this getter if this object is initialized with a - // ScopedInterfaceEndpointHandle: - // - If the handle is invalid, the return value will always be null. - // - If the handle is valid and non-pending, the return value will be - // non-null and remain unchanged even if the handle is later reset. - // - If the handle is pending asssociation, the return value will initially - // be null, change to non-null when/if the handle is associated, and - // remain unchanged ever since. - AssociatedGroupController* GetController(); + // |config| indicates whether |ptr_info| or |request| will be sent to the + // remote side of the message pipe. + // + // NOTE: If |config| is |WILL_PASS_REQUEST|, you will want to bind |ptr_info| + // to a local AssociatedInterfacePtr to make calls. However, there is one + // restriction: the pointer should NOT be used to make calls before |request| + // is sent. Violating that will cause the message pipe to be closed. On the + // other hand, as soon as |request| is sent, the pointer is usable. There is + // no need to wait until |request| is bound to an implementation at the remote + // side. + template + void CreateAssociatedInterface( + AssociatedInterfaceConfig config, + AssociatedInterfacePtrInfo* ptr_info, + AssociatedInterfaceRequest* request) { + ScopedInterfaceEndpointHandle local; + ScopedInterfaceEndpointHandle remote; + CreateEndpointHandlePair(&local, &remote); + + if (!local.is_valid() || !remote.is_valid()) { + *ptr_info = AssociatedInterfacePtrInfo(); + *request = AssociatedInterfaceRequest(); + return; + } + + if (config == WILL_PASS_PTR) { + ptr_info->set_handle(std::move(remote)); + + // The implementation is local, therefore set the version according to + // the interface definition that this code is built against. + ptr_info->set_version(T::Version_); + request->Bind(std::move(local)); + } else { + ptr_info->set_handle(std::move(local)); + + // The implementation is remote, we don't know about its actual version + // yet. + ptr_info->set_version(0u); + request->Bind(std::move(remote)); + } + } private: - base::Callback controller_getter_; + friend class AssociatedGroupController; + + void CreateEndpointHandlePair( + ScopedInterfaceEndpointHandle* local_endpoint, + ScopedInterfaceEndpointHandle* remote_endpoint); + scoped_refptr controller_; }; diff --git a/mojo/public/cpp/bindings/associated_group_controller.h b/mojo/public/cpp/bindings/associated_group_controller.h index d33c277..0ab8253 100644 --- a/mojo/public/cpp/bindings/associated_group_controller.h +++ b/mojo/public/cpp/bindings/associated_group_controller.h @@ -9,47 +9,41 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "base/optional.h" +#include "base/memory/ref_counted_delete_on_message_loop.h" #include "base/single_thread_task_runner.h" -#include "mojo/public/cpp/bindings/bindings_export.h" -#include "mojo/public/cpp/bindings/disconnect_reason.h" #include "mojo/public/cpp/bindings/interface_id.h" #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" namespace mojo { +class AssociatedGroup; class InterfaceEndpointClient; class InterfaceEndpointController; -// An internal interface used to manage endpoints within an associated group, -// which corresponds to one end of a message pipe. -class MOJO_CPP_BINDINGS_EXPORT AssociatedGroupController - : public base::RefCountedThreadSafe { +// An internal interface used to manage endpoints within an associated group. +class AssociatedGroupController : + public base::RefCountedDeleteOnMessageLoop { public: - // Associates an interface with this AssociatedGroupController's message pipe. - // It takes ownership of |handle_to_send| and returns an interface ID that - // could be sent by any endpoints within the same associated group. - // If |handle_to_send| is not in pending association state, it returns - // kInvalidInterfaceId. Otherwise, the peer handle of |handle_to_send| joins - // the associated group and is no longer pending. - virtual InterfaceId AssociateInterface( - ScopedInterfaceEndpointHandle handle_to_send) = 0; + explicit AssociatedGroupController( + scoped_refptr task_runner); + + // Creates a pair of interface endpoint handles. The method generates a new + // interface ID and assigns it to the two handles. |local_endpoint| is used + // locally; while |remote_endpoint| is sent over the message pipe. + virtual void CreateEndpointHandlePair( + ScopedInterfaceEndpointHandle* local_endpoint, + ScopedInterfaceEndpointHandle* remote_endpoint) = 0; // Creates an interface endpoint handle from a given interface ID. The handle - // joins this associated group. + // is used locally. // Typically, this method is used to (1) create an endpoint handle for the // master interface; or (2) create an endpoint handle on receiving an // interface ID from the message pipe. - // - // On failure, the method returns an invalid handle. Usually that is because - // the ID has already been used to create a handle. virtual ScopedInterfaceEndpointHandle CreateLocalEndpointHandle( InterfaceId id) = 0; // Closes an interface endpoint handle. - virtual void CloseEndpointHandle( - InterfaceId id, - const base::Optional& reason) = 0; + virtual void CloseEndpointHandle(InterfaceId id, bool is_local) = 0; // Attaches a client to the specified endpoint to send and receive messages. // The returned object is still owned by the controller. It must only be used @@ -69,23 +63,21 @@ class MOJO_CPP_BINDINGS_EXPORT AssociatedGroupController // and notifies all interfaces running on this pipe. virtual void RaiseError() = 0; + std::unique_ptr CreateAssociatedGroup(); + protected: - friend class base::RefCountedThreadSafe; + friend class base::RefCountedDeleteOnMessageLoop; + friend class base::DeleteHelper; - // Creates a new ScopedInterfaceEndpointHandle within this associated group. + // Creates a new ScopedInterfaceEndpointHandle associated with this + // controller. ScopedInterfaceEndpointHandle CreateScopedInterfaceEndpointHandle( - InterfaceId id); - - // Notifies that the interface represented by |handle_to_send| and its peer - // has been associated with this AssociatedGroupController's message pipe, and - // |handle_to_send|'s peer has joined this associated group. (Note: it is the - // peer who has joined the associated group; |handle_to_send| will be sent to - // the remote side.) - // Returns false if |handle_to_send|'s peer has closed. - bool NotifyAssociation(ScopedInterfaceEndpointHandle* handle_to_send, - InterfaceId id); + InterfaceId id, + bool is_local); virtual ~AssociatedGroupController(); + + DISALLOW_COPY_AND_ASSIGN(AssociatedGroupController); }; } // namespace mojo diff --git a/mojo/public/cpp/bindings/associated_interface_ptr.h b/mojo/public/cpp/bindings/associated_interface_ptr.h index 8e66f4e..10494ce 100644 --- a/mojo/public/cpp/bindings/associated_interface_ptr.h +++ b/mojo/public/cpp/bindings/associated_interface_ptr.h @@ -6,8 +6,6 @@ #define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_ #include - -#include #include #include "base/callback.h" @@ -16,12 +14,10 @@ #include "base/memory/ref_counted.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" +#include "mojo/public/cpp/bindings/associated_group.h" #include "mojo/public/cpp/bindings/associated_interface_ptr_info.h" #include "mojo/public/cpp/bindings/associated_interface_request.h" -#include "mojo/public/cpp/bindings/connection_error_callback.h" #include "mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h" -#include "mojo/public/cpp/bindings/lib/multiplex_router.h" -#include "mojo/public/cpp/system/message_pipe.h" namespace mojo { @@ -30,9 +26,6 @@ namespace mojo { template class AssociatedInterfacePtr { public: - using InterfaceType = Interface; - using PtrInfoType = AssociatedInterfacePtrInfo; - // Constructs an unbound AssociatedInterfacePtr. AssociatedInterfacePtr() {} AssociatedInterfacePtr(decltype(nullptr)) {} @@ -65,16 +58,20 @@ class AssociatedInterfacePtr { // multiple task runners to a single thread for the purposes of task // scheduling. // - // NOTE: The corresponding AssociatedInterfaceRequest must be sent over - // another interface before using this object to make calls. Please see the - // comments of MakeRequest(AssociatedInterfacePtr*) for more - // details. + // NOTE: Please see the comments of + // AssociatedGroup.CreateAssociatedInterface() about when you can use this + // object to make calls. void Bind(AssociatedInterfacePtrInfo info, scoped_refptr runner = base::ThreadTaskRunnerHandle::Get()) { reset(); - if (info.is_valid()) + bool is_local = info.handle().is_local(); + + DCHECK(is_local) << "The AssociatedInterfacePtrInfo is supposed to be used " + "at the other side of the message pipe."; + + if (info.is_valid() && is_local) internal_state_.Bind(std::move(info), std::move(runner)); } @@ -89,6 +86,9 @@ 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,12 +107,6 @@ 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() { @@ -120,13 +114,6 @@ 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(); } @@ -139,11 +126,6 @@ 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. @@ -159,6 +141,12 @@ 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* internal_state() { return &internal_state_; @@ -191,95 +179,30 @@ class AssociatedInterfacePtr { DISALLOW_COPY_AND_ASSIGN(AssociatedInterfacePtr); }; -// Creates an associated interface. The returned request is supposed to be sent -// over another interface (either associated or non-associated). +// Creates an associated interface. The output |ptr| should be used locally +// while the returned request should be passed through the message pipe endpoint +// referred to by |associated_group| to setup the corresponding asssociated +// interface implementation at the remote side. // -// NOTE: |ptr| must NOT be used to make calls before the request is sent. -// Violating that will lead to crash. On the other hand, as soon as the request -// is sent, |ptr| is usable. There is no need to wait until the request is bound -// to an implementation at the remote side. +// NOTE: |ptr| should NOT be used to make calls before the request is sent. +// Violating that will cause the message pipe to be closed. On the other hand, +// as soon as the request is sent, |ptr| is usable. There is no need to wait +// until the request is bound to an implementation at the remote side. template -AssociatedInterfaceRequest MakeRequest( +AssociatedInterfaceRequest GetProxy( AssociatedInterfacePtr* ptr, + AssociatedGroup* group, scoped_refptr runner = base::ThreadTaskRunnerHandle::Get()) { - AssociatedInterfacePtrInfo ptr_info; - 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 -AssociatedInterfaceRequest MakeRequest( - AssociatedInterfacePtrInfo* ptr_info) { - ScopedInterfaceEndpointHandle handle0; - ScopedInterfaceEndpointHandle handle1; - ScopedInterfaceEndpointHandle::CreatePairPendingAssociation(&handle0, - &handle1); - - ptr_info->set_handle(std::move(handle0)); - ptr_info->set_version(0); - AssociatedInterfaceRequest 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 -AssociatedInterfaceRequest GetIsolatedProxy( - AssociatedInterfacePtr* ptr) { - MessagePipe pipe; - scoped_refptr router0 = - new internal::MultiplexRouter(std::move(pipe.handle0), - internal::MultiplexRouter::MULTI_INTERFACE, - false, base::ThreadTaskRunnerHandle::Get()); - scoped_refptr 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(std::move(endpoint0), - Interface::Version_)); + AssociatedInterfacePtrInfo ptr_info; + group->CreateAssociatedInterface(AssociatedGroup::WILL_PASS_REQUEST, + &ptr_info, &request); - AssociatedInterfaceRequest request; - request.Bind(std::move(endpoint1)); + ptr->Bind(std::move(ptr_info), std::move(runner)); 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 -AssociatedInterfaceRequest MakeRequestForTesting( - AssociatedInterfacePtr* ptr) { - return GetIsolatedProxy(ptr); -} - } // namespace mojo #endif // MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_PTR_H_ diff --git a/mojo/public/cpp/bindings/associated_interface_ptr_info.h b/mojo/public/cpp/bindings/associated_interface_ptr_info.h index 3c6ca54..bfb3297 100644 --- a/mojo/public/cpp/bindings/associated_interface_ptr_info.h +++ b/mojo/public/cpp/bindings/associated_interface_ptr_info.h @@ -20,7 +20,6 @@ template class AssociatedInterfacePtrInfo { public: AssociatedInterfacePtrInfo() : version_(0u) {} - AssociatedInterfacePtrInfo(std::nullptr_t) : version_(0u) {} AssociatedInterfacePtrInfo(AssociatedInterfacePtrInfo&& other) : handle_(std::move(other.handle_)), version_(other.version_) { diff --git a/mojo/public/cpp/bindings/associated_interface_request.h b/mojo/public/cpp/bindings/associated_interface_request.h index c37636c..30fcd16 100644 --- a/mojo/public/cpp/bindings/associated_interface_request.h +++ b/mojo/public/cpp/bindings/associated_interface_request.h @@ -5,7 +5,6 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_REQUEST_H_ #define MOJO_PUBLIC_CPP_BINDINGS_ASSOCIATED_INTERFACE_REQUEST_H_ -#include #include #include "base/macros.h" @@ -65,10 +64,6 @@ 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 1da331b..8c9ee2f 100644 --- a/mojo/public/cpp/bindings/binding.h +++ b/mojo/public/cpp/bindings/binding.h @@ -5,7 +5,6 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_BINDING_H_ #define MOJO_PUBLIC_CPP_BINDINGS_BINDING_H_ -#include #include #include "base/callback_forward.h" @@ -13,17 +12,15 @@ #include "base/memory/ref_counted.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" -#include "mojo/public/cpp/bindings/connection_error_callback.h" #include "mojo/public/cpp/bindings/interface_ptr.h" #include "mojo/public/cpp/bindings/interface_ptr_info.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/bindings/lib/binding_state.h" -#include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h" #include "mojo/public/cpp/system/core.h" namespace mojo { -class MessageReceiver; +class AssociatedGroup; // Represents the binding of an interface implementation to a message pipe. // When the |Binding| object is destroyed, the binding between the message pipe @@ -66,24 +63,21 @@ class MessageReceiver; // 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 > +template class Binding { public: - using ImplPointerType = typename ImplRefTraits::PointerType; - // Constructs an incomplete binding that will use the implementation |impl|. // The binding may be completed with a subsequent call to the |Bind| method. // Does not take ownership of |impl|, which must outlive the binding. - explicit Binding(ImplPointerType impl) : internal_state_(std::move(impl)) {} + explicit Binding(Interface* impl) : internal_state_(impl) {} // Constructs a completed binding of message pipe |handle| to implementation // |impl|. Does not take ownership of |impl|, which must outlive the binding. - Binding(ImplPointerType impl, + Binding(Interface* impl, ScopedMessagePipeHandle handle, scoped_refptr runner = base::ThreadTaskRunnerHandle::Get()) - : Binding(std::move(impl)) { + : Binding(impl) { Bind(std::move(handle), std::move(runner)); } @@ -92,22 +86,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(ImplPointerType impl, + Binding(Interface* impl, InterfacePtr* ptr, scoped_refptr runner = base::ThreadTaskRunnerHandle::Get()) - : Binding(std::move(impl)) { + : Binding(impl) { Bind(ptr, std::move(runner)); } // Constructs a completed binding of |impl| to the message pipe endpoint in // |request|, taking ownership of the endpoint. Does not take ownership of // |impl|, which must outlive the binding. - Binding(ImplPointerType impl, + Binding(Interface* impl, InterfaceRequest request, scoped_refptr runner = base::ThreadTaskRunnerHandle::Get()) - : Binding(std::move(impl)) { + : Binding(impl) { Bind(request.PassMessagePipe(), std::move(runner)); } @@ -158,14 +152,6 @@ 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 filter) { - DCHECK(is_bound()); - internal_state_.AddFilter(std::move(filter)); - } - // Whether there are any associated interfaces running on the pipe currently. bool HasAssociatedInterfaces() const { return internal_state_.HasAssociatedInterfaces(); @@ -203,11 +189,6 @@ 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 @@ -236,12 +217,6 @@ 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(); } @@ -256,17 +231,20 @@ class Binding { // transferred to the caller. MessagePipeHandle handle() const { return internal_state_.handle(); } - // Sends a no-op message on the underlying message pipe and runs the current - // message loop until its response is received. This can be used in tests to - // verify that no message was sent on a message pipe in response to some - // stimulus. - void FlushForTesting() { internal_state_.FlushForTesting(); } + // Returns the associated group that this object belongs to. Returns null if: + // - this object is not bound; or + // - the interface doesn't have methods to pass associated interface + // pointers or requests. + AssociatedGroup* associated_group() { + return internal_state_.associated_group(); + } // Exposed for testing, should not generally be used. void EnableTestingMode() { internal_state_.EnableTestingMode(); } private: - internal::BindingState internal_state_; + internal::BindingState + internal_state_; DISALLOW_COPY_AND_ASSIGN(Binding); }; diff --git a/mojo/public/cpp/bindings/binding_set.h b/mojo/public/cpp/bindings/binding_set.h index 919f9c0..b1baca6 100644 --- a/mojo/public/cpp/bindings/binding_set.h +++ b/mojo/public/cpp/bindings/binding_set.h @@ -5,263 +5,111 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_ #define MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_ -#include +#include #include +#include #include "base/bind.h" #include "base/callback.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" +#include "base/memory/weak_ptr.h" #include "mojo/public/cpp/bindings/binding.h" -#include "mojo/public/cpp/bindings/connection_error_callback.h" -#include "mojo/public/cpp/bindings/interface_ptr.h" -#include "mojo/public/cpp/bindings/interface_request.h" -#include "mojo/public/cpp/bindings/message.h" namespace mojo { -template -struct BindingSetTraits; - -template -struct BindingSetTraits> { - using ProxyType = InterfacePtr; - using RequestType = InterfaceRequest; - using BindingType = Binding; - using ImplPointerType = typename BindingType::ImplPointerType; - - static RequestType MakeRequest(ProxyType* proxy) { - return mojo::MakeRequest(proxy); - } -}; - -using BindingId = size_t; - -template -struct BindingSetContextTraits { - using Type = ContextType; - - static constexpr bool SupportsContext() { return true; } -}; - -template <> -struct BindingSetContextTraits { - // 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 -class BindingSetBase { +// Use this class to manage a set of bindings, which are automatically destroyed +// and removed from the set when the pipe they are bound to is disconnected. +template +class BindingSet { public: - using ContextTraits = BindingSetContextTraits; - using Context = typename ContextTraits::Type; - using PreDispatchCallback = base::Callback; - using Traits = BindingSetTraits; - using ProxyType = typename Traits::ProxyType; - using RequestType = typename Traits::RequestType; - using ImplPointerType = typename Traits::ImplPointerType; - - BindingSetBase() {} + BindingSet() {} + ~BindingSet() { CloseAllBindings(); } void set_connection_error_handler(const base::Closure& error_handler) { error_handler_ = error_handler; - error_with_reason_handler_.Reset(); - } - - void set_connection_error_with_reason_handler( - const ConnectionErrorWithReasonCallback& error_handler) { - error_with_reason_handler_ = error_handler; - error_handler_.Reset(); - } - - // 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; - } - - // 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)); + void AddBinding(Interface* impl, InterfaceRequest 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()); } - // 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 an InterfacePtr bound to one end of a pipe whose other end is + // bound to |this|. + InterfacePtr CreateInterfacePtrAndBind(Interface* impl) { + InterfacePtr interface_ptr; + AddBinding(impl, GetProxy(&interface_ptr)); + return interface_ptr; } - // 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() { + for (const auto& it : bindings_) { + if (it) { + it->Close(); + delete it.get(); + } + } + bindings_.clear(); } - void CloseAllBindings() { bindings_.clear(); } - bool empty() const { return bindings_.empty(); } - // Implementations may call this when processing a dispatched message or - // error. During the extent of message or error dispatch, this will return the - // context associated with the specific binding which received the message or - // error. Use AddBinding() to associated a context with a specific binding. - const Context& dispatch_context() const { - static_assert(ContextTraits::SupportsContext(), - "dispatch_context() requires non-void context type."); - DCHECK(dispatch_context_); - return *dispatch_context_; - } - - void FlushForTesting() { - for (auto& binding : bindings_) - binding.second->FlushForTesting(); - } - private: - friend class Entry; - - class Entry { + class Element { public: - Entry(ImplPointerType impl, - RequestType request, - BindingSetBase* binding_set, - BindingId binding_id, - Context context) - : binding_(std::move(impl), std::move(request)), - binding_set_(binding_set), - binding_id_(binding_id), - context_(std::move(context)) { - if (ContextTraits::SupportsContext()) - binding_.AddFilter(base::MakeUnique(this)); - binding_.set_connection_error_with_reason_handler( - base::Bind(&Entry::OnConnectionError, base::Unretained(this))); + Element(Interface* impl, InterfaceRequest request) + : binding_(impl, std::move(request)), weak_ptr_factory_(this) { + binding_.set_connection_error_handler( + base::Bind(&Element::OnConnectionError, base::Unretained(this))); } - void FlushForTesting() { binding_.FlushForTesting(); } - - private: - class DispatchFilter : public MessageReceiver { - public: - explicit DispatchFilter(Entry* entry) : entry_(entry) {} - ~DispatchFilter() override {} - - private: - // MessageReceiver: - bool Accept(Message* message) override { - entry_->WillDispatch(); - return true; - } - - Entry* entry_; - - DISALLOW_COPY_AND_ASSIGN(DispatchFilter); - }; + ~Element() {} - void WillDispatch() { - DCHECK(ContextTraits::SupportsContext()); - binding_set_->SetDispatchContext(&context_); + void set_connection_error_handler(const base::Closure& error_handler) { + error_handler_ = error_handler; } - void OnConnectionError(uint32_t custom_reason, - const std::string& description) { - if (ContextTraits::SupportsContext()) - WillDispatch(); - binding_set_->OnConnectionError(binding_id_, custom_reason, description); + base::WeakPtr GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); } - BindingType binding_; - BindingSetBase* const binding_set_; - const BindingId binding_id_; - Context const context_; + void Close() { binding_.Close(); } - DISALLOW_COPY_AND_ASSIGN(Entry); - }; - - void SetDispatchContext(const Context* context) { - DCHECK(ContextTraits::SupportsContext()); - dispatch_context_ = context; - if (!pre_dispatch_handler_.is_null()) - pre_dispatch_handler_.Run(*context); - } + void OnConnectionError() { + base::Closure error_handler = error_handler_; + delete this; + if (!error_handler.is_null()) + error_handler.Run(); + } - BindingId AddBindingImpl(ImplPointerType impl, - RequestType request, - Context context) { - BindingId id = next_binding_id_++; - DCHECK_GE(next_binding_id_, 0u); - auto entry = base::MakeUnique(std::move(impl), std::move(request), - this, id, std::move(context)); - bindings_.insert(std::make_pair(id, std::move(entry))); - return id; - } + private: + Binding binding_; + base::Closure error_handler_; + base::WeakPtrFactory weak_ptr_factory_; - void OnConnectionError(BindingId id, - uint32_t custom_reason, - const std::string& description) { - auto it = bindings_.find(id); - DCHECK(it != bindings_.end()); + DISALLOW_COPY_AND_ASSIGN(Element); + }; - // We keep the Entry alive throughout error dispatch. - std::unique_ptr entry = std::move(it->second); - bindings_.erase(it); + void OnConnectionError() { + // Clear any deleted bindings. + bindings_.erase(std::remove_if(bindings_.begin(), bindings_.end(), + [](const base::WeakPtr& p) { + return p.get() == nullptr; + }), + bindings_.end()); if (!error_handler_.is_null()) error_handler_.Run(); - else if (!error_with_reason_handler_.is_null()) - error_with_reason_handler_.Run(custom_reason, description); } base::Closure error_handler_; - ConnectionErrorWithReasonCallback error_with_reason_handler_; - PreDispatchCallback pre_dispatch_handler_; - BindingId next_binding_id_ = 0; - std::map> bindings_; - const Context* dispatch_context_ = nullptr; + std::vector> bindings_; - DISALLOW_COPY_AND_ASSIGN(BindingSetBase); + DISALLOW_COPY_AND_ASSIGN(BindingSet); }; -template -using BindingSet = BindingSetBase, ContextType>; - } // namespace mojo #endif // MOJO_PUBLIC_CPP_BINDINGS_BINDING_SET_H_ diff --git a/mojo/public/cpp/bindings/bindings_export.h b/mojo/public/cpp/bindings/bindings_export.h deleted file mode 100644 index 9fd7a27..0000000 --- a/mojo/public/cpp/bindings/bindings_export.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_BINDINGS_EXPORT_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_BINDINGS_EXPORT_H_ - -#if defined(COMPONENT_BUILD) - -#if defined(WIN32) - -#if defined(MOJO_CPP_BINDINGS_IMPLEMENTATION) -#define MOJO_CPP_BINDINGS_EXPORT __declspec(dllexport) -#else -#define MOJO_CPP_BINDINGS_EXPORT __declspec(dllimport) -#endif - -#else // !defined(WIN32) - -#if defined(MOJO_CPP_BINDINGS_IMPLEMENTATION) -#define MOJO_CPP_BINDINGS_EXPORT __attribute((visibility("default"))) -#else -#define MOJO_CPP_BINDINGS_EXPORT -#endif - -#endif // defined(WIN32) - -#else // !defined(COMPONENT_BUILD) - -#define MOJO_CPP_BINDINGS_EXPORT - -#endif // defined(COMPONENT_BUILD) - -#endif // MOJO_PUBLIC_CPP_BINDINGS_BINDINGS_EXPORT_H_ diff --git a/mojo/public/cpp/bindings/clone_traits.h b/mojo/public/cpp/bindings/clone_traits.h deleted file mode 100644 index 203ab34..0000000 --- a/mojo/public/cpp/bindings/clone_traits.h +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_CLONE_TRAITS_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_CLONE_TRAITS_H_ - -#include -#include -#include - -#include "base/optional.h" -#include "mojo/public/cpp/bindings/lib/template_util.h" - -namespace mojo { - -template -struct HasCloneMethod { - template - static char Test(decltype(&U::Clone)); - template - static int Test(...); - static const bool value = sizeof(Test(0)) == sizeof(char); - - private: - internal::EnsureTypeIsComplete check_t_; -}; - -template ::value> -struct CloneTraits; - -template -T Clone(const T& input); - -template -struct CloneTraits { - static T Clone(const T& input) { return input.Clone(); } -}; - -template -struct CloneTraits { - static T Clone(const T& input) { return input; } -}; - -template -struct CloneTraits, false> { - static base::Optional Clone(const base::Optional& input) { - if (!input) - return base::nullopt; - - return base::Optional(mojo::Clone(*input)); - } -}; - -template -struct CloneTraits, false> { - static std::vector Clone(const std::vector& input) { - std::vector result; - result.reserve(input.size()); - for (const auto& element : input) - result.push_back(mojo::Clone(element)); - - return result; - } -}; - -template -struct CloneTraits, false> { - static std::unordered_map Clone(const std::unordered_map& input) { - std::unordered_map result; - for (const auto& element : input) { - result.insert(std::make_pair(mojo::Clone(element.first), - mojo::Clone(element.second))); - } - return result; - } -}; - -template -T Clone(const T& input) { - return CloneTraits::Clone(input); -}; - -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_BINDINGS_CLONE_TRAITS_H_ diff --git a/mojo/public/cpp/bindings/connection_error_callback.h b/mojo/public/cpp/bindings/connection_error_callback.h deleted file mode 100644 index 306e99e..0000000 --- a/mojo/public/cpp/bindings/connection_error_callback.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_CONNECTION_ERROR_CALLBACK_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_CONNECTION_ERROR_CALLBACK_H_ - -#include "base/callback.h" - -namespace mojo { - -// This callback type accepts user-defined disconnect reason and description. If -// the other side specifies a reason on closing the connection, it will be -// passed to the error handler. -using ConnectionErrorWithReasonCallback = - base::Callback; - -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_BINDINGS_CONNECTION_ERROR_CALLBACK_H_ diff --git a/mojo/public/cpp/bindings/connector.h b/mojo/public/cpp/bindings/connector.h index 01e9236..d14ad17 100644 --- a/mojo/public/cpp/bindings/connector.h +++ b/mojo/public/cpp/bindings/connector.h @@ -8,13 +8,10 @@ #include #include "base/callback.h" -#include "base/compiler_specific.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" -#include "base/optional.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_checker.h" -#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/message.h" #include "mojo/public/cpp/bindings/sync_handle_watcher.h" #include "mojo/public/cpp/system/core.h" @@ -36,8 +33,7 @@ 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 MOJO_CPP_BINDINGS_EXPORT Connector - : NON_EXPORTED_BASE(public MessageReceiver) { +class Connector : public MessageReceiver { public: enum ConnectorConfig { // Connector::Accept() is only called from a single thread. @@ -142,7 +138,6 @@ class MOJO_CPP_BINDINGS_EXPORT Connector // Whether currently the control flow is inside the sync handle watcher // callback. - // It always returns false after CloseMessagePipe()/PassMessagePipe(). bool during_sync_handle_watcher_callback() const { return sync_handle_watcher_callback_count_ > 0; } @@ -151,10 +146,6 @@ class MOJO_CPP_BINDINGS_EXPORT Connector return task_runner_.get(); } - // Sets the tag used by the heap profiler. - // |tag| must be a const string literal. - void SetWatcherHeapProfilerTag(const char* tag); - private: // Callback of mojo::Watcher. void OnWatcherHandleReady(MojoResult result); @@ -164,8 +155,7 @@ class MOJO_CPP_BINDINGS_EXPORT Connector void WaitToReadMore(); - // Returns false if it is impossible to receive more messages in the future. - // |this| may have been destroyed in that case. + // Returns false if |this| was destroyed during message dispatch. WARN_UNUSED_RESULT bool ReadSingleMessage(MojoResult* read_result); // |this| can be destroyed during message dispatch. @@ -185,40 +175,31 @@ class MOJO_CPP_BINDINGS_EXPORT Connector base::Closure connection_error_handler_; ScopedMessagePipeHandle message_pipe_; - MessageReceiver* incoming_receiver_ = nullptr; + MessageReceiver* incoming_receiver_; scoped_refptr task_runner_; - std::unique_ptr handle_watcher_; + Watcher handle_watcher_; - bool error_ = false; - bool drop_writes_ = false; - bool enforce_errors_from_incoming_receiver_ = true; + bool error_; + bool drop_writes_; + bool enforce_errors_from_incoming_receiver_; - bool paused_ = false; + bool paused_; // If sending messages is allowed from multiple threads, |lock_| is used to // protect modifications to |message_pipe_| and |drop_writes_|. - base::Optional lock_; + std::unique_ptr lock_; std::unique_ptr sync_watcher_; - bool allow_woken_up_by_others_ = false; + bool allow_woken_up_by_others_; // If non-zero, currently the control flow is inside the sync handle watcher // callback. - size_t sync_handle_watcher_callback_count_ = 0; + size_t sync_handle_watcher_callback_count_; base::ThreadChecker thread_checker_; - base::Lock connected_lock_; - bool connected_ = true; - - // The tag used to track heap allocations that originated from a Watcher - // notification. - const char* heap_profiler_tag_ = nullptr; - // Create a single weak ptr and use it everywhere, to avoid the malloc/free // cost of creating a new weak ptr whenever it is needed. - // NOTE: This weak pointer is invalidated when the message pipe is closed or - // transferred (i.e., when |connected_| is set to false). base::WeakPtr weak_self_; base::WeakPtrFactory weak_factory_; diff --git a/mojo/public/cpp/bindings/disconnect_reason.h b/mojo/public/cpp/bindings/disconnect_reason.h deleted file mode 100644 index c04e8ad..0000000 --- a/mojo/public/cpp/bindings/disconnect_reason.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_DISCONNECT_REASON_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_DISCONNECT_REASON_H_ - -#include - -#include - -namespace mojo { - -struct DisconnectReason { - public: - DisconnectReason(uint32_t in_custom_reason, const std::string& in_description) - : custom_reason(in_custom_reason), description(in_description) {} - - uint32_t custom_reason; - std::string description; -}; - -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_BINDINGS_DISCONNECT_REASON_H_ diff --git a/mojo/public/cpp/bindings/filter_chain.h b/mojo/public/cpp/bindings/filter_chain.h deleted file mode 100644 index 1262f39..0000000 --- a/mojo/public/cpp/bindings/filter_chain.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_FILTER_CHAIN_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_FILTER_CHAIN_H_ - -#include -#include - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "mojo/public/cpp/bindings/bindings_export.h" -#include "mojo/public/cpp/bindings/message.h" - -namespace mojo { - -class MOJO_CPP_BINDINGS_EXPORT FilterChain - : NON_EXPORTED_BASE(public MessageReceiver) { - public: - // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while - // this object is alive. - explicit FilterChain(MessageReceiver* sink = nullptr); - - FilterChain(FilterChain&& other); - FilterChain& operator=(FilterChain&& other); - ~FilterChain() override; - - template - inline void Append(Args&&... args); - - void Append(std::unique_ptr filter); - - // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while - // this object is alive. - void SetSink(MessageReceiver* sink); - - // MessageReceiver: - bool Accept(Message* message) override; - - private: - std::vector> filters_; - - MessageReceiver* sink_; - - DISALLOW_COPY_AND_ASSIGN(FilterChain); -}; - -template -inline void FilterChain::Append(Args&&... args) { - Append(base::MakeUnique(std::forward(args)...)); -} - -template <> -inline void FilterChain::Append() { -} - -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_BINDINGS_FILTER_CHAIN_H_ diff --git a/mojo/public/cpp/bindings/interface_data_view.h b/mojo/public/cpp/bindings/interface_data_view.h deleted file mode 100644 index ef12254..0000000 --- a/mojo/public/cpp/bindings/interface_data_view.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_DATA_VIEW_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_DATA_VIEW_H_ - -namespace mojo { - -// They are used for type identification purpose only. -template -class AssociatedInterfacePtrInfoDataView {}; - -template -class AssociatedInterfaceRequestDataView {}; - -template -class InterfacePtrDataView {}; - -template -class InterfaceRequestDataView {}; - -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_DATA_VIEW_H_ diff --git a/mojo/public/cpp/bindings/interface_endpoint_client.h b/mojo/public/cpp/bindings/interface_endpoint_client.h index 0aea756..9dc40a2 100644 --- a/mojo/public/cpp/bindings/interface_endpoint_client.h +++ b/mojo/public/cpp/bindings/interface_endpoint_client.h @@ -11,42 +11,34 @@ #include #include "base/callback.h" -#include "base/compiler_specific.h" #include "base/logging.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" -#include "base/optional.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_checker.h" -#include "mojo/public/cpp/bindings/bindings_export.h" -#include "mojo/public/cpp/bindings/connection_error_callback.h" -#include "mojo/public/cpp/bindings/disconnect_reason.h" -#include "mojo/public/cpp/bindings/filter_chain.h" -#include "mojo/public/cpp/bindings/lib/control_message_handler.h" -#include "mojo/public/cpp/bindings/lib/control_message_proxy.h" #include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/bindings/message_filter.h" #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" namespace mojo { class AssociatedGroup; +class AssociatedGroupController; class InterfaceEndpointController; // InterfaceEndpointClient handles message sending and receiving of an interface // endpoint, either the implementation side or the client side. // It should only be accessed and destructed on the creating thread. -class MOJO_CPP_BINDINGS_EXPORT InterfaceEndpointClient - : NON_EXPORTED_BASE(public MessageReceiverWithResponder) { +class InterfaceEndpointClient : public MessageReceiverWithResponder { public: // |receiver| is okay to be null. If it is not null, it must outlive this // object. InterfaceEndpointClient(ScopedInterfaceEndpointHandle handle, MessageReceiverWithResponderStatus* receiver, - std::unique_ptr payload_validator, + std::unique_ptr payload_validator, bool expect_sync_requests, - scoped_refptr runner, - uint32_t interface_version); + scoped_refptr runner); ~InterfaceEndpointClient() override; // Sets the error handler to receive notifications when an error is @@ -54,14 +46,6 @@ class MOJO_CPP_BINDINGS_EXPORT InterfaceEndpointClient void set_connection_error_handler(const base::Closure& error_handler) { DCHECK(thread_checker_.CalledOnValidThread()); error_handler_ = error_handler; - error_with_reason_handler_.Reset(); - } - - void set_connection_error_with_reason_handler( - const ConnectionErrorWithReasonCallback& error_handler) { - DCHECK(thread_checker_.CalledOnValidThread()); - error_with_reason_handler_ = error_handler; - error_handler_.Reset(); } // Returns true if an error was encountered. @@ -76,11 +60,11 @@ class MOJO_CPP_BINDINGS_EXPORT InterfaceEndpointClient return !async_responders_.empty() || !sync_responses_.empty(); } + AssociatedGroupController* group_controller() const { + return handle_.group_controller(); + } AssociatedGroup* associated_group(); - - // Adds a MessageReceiver which can filter a message after validation but - // before dispatch. - void AddFilter(std::unique_ptr filter); + uint32_t interface_id() const; // After this call the object is in an invalid state and shouldn't be reused. ScopedInterfaceEndpointHandle PassHandle(); @@ -89,11 +73,7 @@ class MOJO_CPP_BINDINGS_EXPORT InterfaceEndpointClient // and notifies all interfaces running on this pipe. void RaiseError(); - void CloseWithReason(uint32_t custom_reason, const std::string& description); - // MessageReceiverWithResponder implementation: - // They must only be called when the handle is not in pending association - // state. bool Accept(Message* message) override; bool AcceptWithResponder(Message* message, MessageReceiver* responder) override; @@ -103,14 +83,7 @@ class MOJO_CPP_BINDINGS_EXPORT InterfaceEndpointClient // NOTE: |message| must have passed message header validation. bool HandleIncomingMessage(Message* message); - void NotifyError(const base::Optional& 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& callback); - void RequireVersion(uint32_t version); - void FlushForTesting(); + void NotifyError(); private: // Maps from the id of a response to the MessageReceiver that handles the @@ -123,7 +96,7 @@ class MOJO_CPP_BINDINGS_EXPORT InterfaceEndpointClient explicit SyncResponseInfo(bool* in_response_received); ~SyncResponseInfo(); - Message response; + std::unique_ptr response; // Points to a stack-allocated variable. bool* response_received; @@ -150,37 +123,26 @@ class MOJO_CPP_BINDINGS_EXPORT InterfaceEndpointClient 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 associated_group_; - InterfaceEndpointController* controller_ = nullptr; + InterfaceEndpointController* controller_; - MessageReceiverWithResponderStatus* const incoming_receiver_ = nullptr; + MessageReceiverWithResponderStatus* const incoming_receiver_; + std::unique_ptr payload_validator_; HandleIncomingMessageThunk thunk_; - FilterChain filters_; AsyncResponderMap async_responders_; SyncResponseMap sync_responses_; - uint64_t next_request_id_ = 1; + uint64_t next_request_id_; base::Closure error_handler_; - ConnectionErrorWithReasonCallback error_with_reason_handler_; - bool encountered_error_ = false; + bool encountered_error_; scoped_refptr task_runner_; - internal::ControlMessageProxy control_message_proxy_; - internal::ControlMessageHandler control_message_handler_; - base::ThreadChecker thread_checker_; base::WeakPtrFactory weak_ptr_factory_; diff --git a/mojo/public/cpp/bindings/interface_ptr.h b/mojo/public/cpp/bindings/interface_ptr.h index e88be74..edcb9bf 100644 --- a/mojo/public/cpp/bindings/interface_ptr.h +++ b/mojo/public/cpp/bindings/interface_ptr.h @@ -6,8 +6,6 @@ #define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_PTR_H_ #include - -#include #include #include "base/callback_forward.h" @@ -16,12 +14,13 @@ #include "base/memory/ref_counted.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" -#include "mojo/public/cpp/bindings/connection_error_callback.h" #include "mojo/public/cpp/bindings/interface_ptr_info.h" #include "mojo/public/cpp/bindings/lib/interface_ptr_state.h" namespace mojo { +class AssociatedGroup; + // A pointer to a local proxy of a remote Interface implementation. Uses a // message pipe to communicate with the remote implementation, and automatically // closes the pipe and deletes the proxy on destruction. The pointer must be @@ -38,9 +37,6 @@ namespace mojo { template class InterfacePtr { public: - using InterfaceType = Interface; - using PtrInfoType = InterfacePtrInfo; - // Constructs an unbound InterfacePtr. InterfacePtr() {} InterfacePtr(decltype(nullptr)) {} @@ -118,12 +114,6 @@ 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() { @@ -131,13 +121,6 @@ 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(); @@ -157,11 +140,6 @@ 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). @@ -184,6 +162,14 @@ 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; @@ -194,7 +180,8 @@ class InterfacePtr { } // DO NOT USE. Exposed only for internal use and for testing. - internal::InterfacePtrState* internal_state() { + internal::InterfacePtrState* + internal_state() { return &internal_state_; } @@ -202,7 +189,9 @@ class InterfacePtr { // implicitly convertible to a real bool (which is dangerous). private: // TODO(dcheng): Use an explicit conversion operator. - typedef internal::InterfacePtrState InterfacePtr::*Testable; + typedef internal::InterfacePtrState + InterfacePtr::*Testable; public: operator Testable() const { @@ -218,7 +207,8 @@ class InterfacePtr { template bool operator!=(const InterfacePtr& other) const = delete; - typedef internal::InterfacePtrState State; + typedef internal::InterfacePtrState State; mutable State internal_state_; DISALLOW_COPY_AND_ASSIGN(InterfacePtr); diff --git a/mojo/public/cpp/bindings/interface_ptr_set.h b/mojo/public/cpp/bindings/interface_ptr_set.h index 09a2682..d4b2046 100644 --- a/mojo/public/cpp/bindings/interface_ptr_set.h +++ b/mojo/public/cpp/bindings/interface_ptr_set.h @@ -16,10 +16,6 @@ namespace mojo { namespace internal { -// TODO(blundell): This class should be rewritten to be structured -// similarly to BindingSet if possible, with PtrSet owning its -// Elements and those Elements calling back into PtrSet on connection -// error. template class Ptr> class PtrSet { public: @@ -59,13 +55,7 @@ class PtrSet { ~Element() {} - void Close() { - ptr_.reset(); - - // Resetting the interface ptr means that it won't call this object back - // on connection error anymore, so this object must delete itself now. - DeleteElement(this); - } + void Close() { ptr_.reset(); } Interface* get() { return ptr_.get(); } diff --git a/mojo/public/cpp/bindings/interface_request.h b/mojo/public/cpp/bindings/interface_request.h index 29d8836..fc23aec 100644 --- a/mojo/public/cpp/bindings/interface_request.h +++ b/mojo/public/cpp/bindings/interface_request.h @@ -5,17 +5,12 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_ #define MOJO_PUBLIC_CPP_BINDINGS_INTERFACE_REQUEST_H_ -#include #include #include "base/macros.h" -#include "base/optional.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" -#include "mojo/public/cpp/bindings/disconnect_reason.h" #include "mojo/public/cpp/bindings/interface_ptr.h" -#include "mojo/public/cpp/bindings/pipe_control_message_proxy.h" -#include "mojo/public/cpp/system/message_pipe.h" namespace mojo { @@ -33,19 +28,6 @@ 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* ptr, - scoped_refptr runner = - base::ThreadTaskRunnerHandle::Get()) { - MessagePipe pipe; - ptr->Bind(InterfacePtrInfo(std::move(pipe.handle0), 0u), - std::move(runner)); - Bind(std::move(pipe.handle1)); - } - // Takes the message pipe from another InterfaceRequest. InterfaceRequest(InterfaceRequest&& other) { handle_ = std::move(other.handle_); @@ -82,20 +64,6 @@ 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_; @@ -135,9 +103,9 @@ InterfaceRequest MakeRequest(ScopedMessagePipeHandle handle) { // // DatabasePtr database = ...; // Connect to database. // TablePtr table; -// database->OpenTable(MakeRequest(&table)); +// database->OpenTable(GetProxy(&table)); // -// Upon return from MakeRequest, |table| is ready to have methods called on it. +// Upon return from GetProxy, |table| is ready to have methods called on it. // // Example #2: Registering a local implementation with a remote service. // ===================================================================== @@ -151,16 +119,19 @@ InterfaceRequest MakeRequest(ScopedMessagePipeHandle handle) { // // CollectorPtr collector = ...; // Connect to Collector. // SourcePtr source; -// InterfaceRequest source_request(&source); +// InterfaceRequest source_request = GetProxy(&source); // collector->RegisterSource(std::move(source)); // CreateSource(std::move(source_request)); // Create implementation locally. // template -InterfaceRequest MakeRequest( +InterfaceRequest GetProxy( InterfacePtr* ptr, scoped_refptr runner = base::ThreadTaskRunnerHandle::Get()) { - return InterfaceRequest(ptr, runner); + MessagePipe pipe; + ptr->Bind(InterfacePtrInfo(std::move(pipe.handle0), 0u), + std::move(runner)); + return MakeRequest(std::move(pipe.handle1)); } // Fuses an InterfaceRequest endpoint with an InterfacePtrInfo endpoint. diff --git a/mojo/public/cpp/bindings/lib/array_internal.h b/mojo/public/cpp/bindings/lib/array_internal.h index eecfcfb..ba6d16e 100644 --- a/mojo/public/cpp/bindings/lib/array_internal.h +++ b/mojo/public/cpp/bindings/lib/array_internal.h @@ -13,7 +13,6 @@ #include "base/logging.h" #include "mojo/public/c/system/macros.h" -#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/lib/bindings_internal.h" #include "mojo/public/cpp/bindings/lib/buffer.h" #include "mojo/public/cpp/bindings/lib/serialization_util.h" @@ -29,13 +28,13 @@ namespace internal { template class Map_Data; -MOJO_CPP_BINDINGS_EXPORT std::string -MakeMessageWithArrayIndex(const char* message, size_t size, size_t index); +std::string MakeMessageWithArrayIndex(const char* message, + size_t size, + size_t index); -MOJO_CPP_BINDINGS_EXPORT std::string MakeMessageWithExpectedArraySize( - const char* message, - size_t size, - size_t expected_size); +std::string MakeMessageWithExpectedArraySize(const char* message, + size_t size, + size_t expected_size); template struct ArrayDataTraits { @@ -68,7 +67,7 @@ template <> struct ArrayDataTraits { // Helper class to emulate a reference to a bool, used for direct element // access. - class MOJO_CPP_BINDINGS_EXPORT BitRef { + class BitRef { public: ~BitRef(); BitRef& operator=(bool value); @@ -110,7 +109,7 @@ struct ArrayDataTraits { // // TODO(yzshen): Validation code should be organzied in a way similar to // Serializer<>, or merged into it. It should be templatized with the mojo -// data view type instead of the data type, that way we can use MojomTypeTraits +// wrapper type instead of the data type, that way we can use MojomTypeTraits // to determine the categories. template @@ -263,7 +262,7 @@ class Array_Data { T, IsUnionDataType::value, std::is_same::value || - std::is_same::value || + std::is_same::value || std::is_same::value || std::is_same::value>; using Element = T; diff --git a/mojo/public/cpp/bindings/lib/array_serialization.h b/mojo/public/cpp/bindings/lib/array_serialization.h index d2f8ecf..5db27a5 100644 --- a/mojo/public/cpp/bindings/lib/array_serialization.h +++ b/mojo/public/cpp/bindings/lib/array_serialization.h @@ -14,11 +14,12 @@ #include #include "base/logging.h" -#include "mojo/public/cpp/bindings/array_data_view.h" +#include "mojo/public/cpp/bindings/array.h" #include "mojo/public/cpp/bindings/lib/array_internal.h" #include "mojo/public/cpp/bindings/lib/serialization_forward.h" #include "mojo/public/cpp/bindings/lib/template_util.h" #include "mojo/public/cpp/bindings/lib/validation_errors.h" +#include "mojo/public/cpp/bindings/map.h" namespace mojo { namespace internal { @@ -45,7 +46,7 @@ class ArrayIterator { using GetNextResult = decltype(Traits::GetValue(std::declval())); GetNextResult GetNext() { - GetNextResult value = Traits::GetValue(iter_); + auto& value = Traits::GetValue(iter_); Traits::AdvanceIterator(iter_); return value; } @@ -286,19 +287,13 @@ struct ArraySerializer< using Element = typename MojomType::Element; using Traits = ArrayTraits; + static_assert(std::is_same::value, + "Incorrect array serializer"); + static size_t GetSerializedSize(UserTypeIterator* input, SerializationContext* context) { - size_t element_count = input->GetSize(); - if (BelongsTo::value) { - for (size_t i = 0; i < element_count; ++i) { - typename UserTypeIterator::GetNextResult next = input->GetNext(); - size_t size = PrepareToSerialize(next, context); - DCHECK_EQ(size, 0u); - } - } - return sizeof(Data) + Align(element_count * sizeof(typename Data::Element)); + return sizeof(Data) + + Align(input->GetSize() * sizeof(typename Data::Element)); } static void SerializeElements(UserTypeIterator* input, @@ -311,8 +306,7 @@ struct ArraySerializer< size_t size = input->GetSize(); for (size_t i = 0; i < size; ++i) { - typename UserTypeIterator::GetNextResult next = input->GetNext(); - Serialize(next, &output->at(i), context); + Serialize(input->GetNext(), &output->at(i), context); static const ValidationError kError = BelongsToGetSize(); size_t size = sizeof(Data) + element_count * sizeof(typename Data::Element); - for (size_t i = 0; i < element_count; ++i) { - typename UserTypeIterator::GetNextResult next = input->GetNext(); - size += PrepareToSerialize(next, context); - } + for (size_t i = 0; i < element_count; ++i) + size += PrepareToSerialize(input->GetNext(), context); return size; } @@ -382,8 +374,7 @@ struct ArraySerializerGetSize(); for (size_t i = 0; i < size; ++i) { DataElementPtr data_ptr; - typename UserTypeIterator::GetNextResult next = input->GetNext(); - SerializeCaller::Run(next, buf, &data_ptr, + SerializeCaller::Run(input->GetNext(), buf, &data_ptr, validate_params->element_validate_params, context); output->at(i).Set(data_ptr); @@ -453,6 +444,10 @@ struct ArraySerializer< using Element = typename MojomType::Element; using Traits = ArrayTraits; + static_assert(std::is_same::value, + "Incorrect array serializer"); + static size_t GetSerializedSize(UserTypeIterator* input, SerializationContext* context) { size_t element_count = input->GetSize(); @@ -460,8 +455,7 @@ 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. - typename UserTypeIterator::GetNextResult next = input->GetNext(); - size += PrepareToSerialize(next, false, context); + size += PrepareToSerialize(input->GetNext(), false, context); } return size; } @@ -474,8 +468,7 @@ struct ArraySerializer< size_t size = input->GetSize(); for (size_t i = 0; i < size; ++i) { typename Data::Element* result = output->storage() + i; - typename UserTypeIterator::GetNextResult next = input->GetNext(); - Serialize(next, buf, &result, true, context); + Serialize(input->GetNext(), buf, &result, true, context); MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( !validate_params->element_is_nullable && output->at(i).is_null(), VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, @@ -499,13 +492,13 @@ struct ArraySerializer< }; template -struct Serializer, MaybeConstUserType> { +struct Serializer, MaybeConstUserType> { using UserType = typename std::remove_const::type; using Traits = ArrayTraits; - using Impl = ArraySerializer, + using Impl = ArraySerializer, MaybeConstUserType, ArrayIterator>; - using Data = typename MojomTypeTraits>::Data; + using Data = typename MojomTypeTraits>::Data; static size_t PrepareToSerialize(MaybeConstUserType& input, SerializationContext* context) { diff --git a/mojo/public/cpp/bindings/lib/associated_group.cc b/mojo/public/cpp/bindings/lib/associated_group.cc index 3e95eeb..a9c53b5 100644 --- a/mojo/public/cpp/bindings/lib/associated_group.cc +++ b/mojo/public/cpp/bindings/lib/associated_group.cc @@ -8,27 +8,28 @@ namespace mojo { -AssociatedGroup::AssociatedGroup() = default; +AssociatedGroup::AssociatedGroup() {} -AssociatedGroup::AssociatedGroup( - scoped_refptr controller) - : controller_(std::move(controller)) {} +AssociatedGroup::AssociatedGroup(const AssociatedGroup& other) + : controller_(other.controller_) {} -AssociatedGroup::AssociatedGroup(const ScopedInterfaceEndpointHandle& handle) - : controller_getter_(handle.CreateGroupControllerGetter()) {} +AssociatedGroup::~AssociatedGroup() {} -AssociatedGroup::AssociatedGroup(const AssociatedGroup& other) = default; +AssociatedGroup& AssociatedGroup::operator=(const AssociatedGroup& other) { + if (this == &other) + return *this; -AssociatedGroup::~AssociatedGroup() = default; - -AssociatedGroup& AssociatedGroup::operator=(const AssociatedGroup& other) = - default; + controller_ = other.controller_; + return *this; +} -AssociatedGroupController* AssociatedGroup::GetController() { - if (controller_) - return controller_.get(); +void AssociatedGroup::CreateEndpointHandlePair( + ScopedInterfaceEndpointHandle* local_endpoint, + ScopedInterfaceEndpointHandle* remote_endpoint) { + if (!controller_) + return; - return controller_getter_.Run(); + controller_->CreateEndpointHandlePair(local_endpoint, remote_endpoint); } } // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/associated_group_controller.cc b/mojo/public/cpp/bindings/lib/associated_group_controller.cc index f4a9aa2..42db9b3 100644 --- a/mojo/public/cpp/bindings/lib/associated_group_controller.cc +++ b/mojo/public/cpp/bindings/lib/associated_group_controller.cc @@ -8,17 +8,25 @@ namespace mojo { +AssociatedGroupController::AssociatedGroupController( + scoped_refptr task_runner) + : base::RefCountedDeleteOnMessageLoop( + task_runner) {} + AssociatedGroupController::~AssociatedGroupController() {} -ScopedInterfaceEndpointHandle -AssociatedGroupController::CreateScopedInterfaceEndpointHandle(InterfaceId id) { - return ScopedInterfaceEndpointHandle(id, this); +std::unique_ptr +AssociatedGroupController::CreateAssociatedGroup() { + std::unique_ptr group(new AssociatedGroup); + group->controller_ = this; + return group; } -bool AssociatedGroupController::NotifyAssociation( - ScopedInterfaceEndpointHandle* handle_to_send, - InterfaceId id) { - return handle_to_send->NotifyAssociation(id, this); +ScopedInterfaceEndpointHandle +AssociatedGroupController::CreateScopedInterfaceEndpointHandle( + InterfaceId id, + bool is_local) { + return ScopedInterfaceEndpointHandle(id, is_local, this); } } // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h b/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h index 72f7960..c7f74fb 100644 --- a/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h +++ b/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h @@ -9,7 +9,6 @@ #include // For |std::swap()|. #include -#include #include #include "base/bind.h" @@ -19,10 +18,11 @@ #include "base/memory/ref_counted.h" #include "base/single_thread_task_runner.h" #include "mojo/public/cpp/bindings/associated_group.h" +#include "mojo/public/cpp/bindings/associated_group_controller.h" #include "mojo/public/cpp/bindings/associated_interface_ptr_info.h" -#include "mojo/public/cpp/bindings/connection_error_callback.h" #include "mojo/public/cpp/bindings/interface_endpoint_client.h" #include "mojo/public/cpp/bindings/interface_id.h" +#include "mojo/public/cpp/bindings/lib/control_message_proxy.h" #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" #include "mojo/public/cpp/system/message_pipe.h" @@ -46,12 +46,18 @@ 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& callback) { - // It is safe to capture |this| because the callback won't be run after this - // object goes away. - endpoint_client_->QueryVersion( - base::Bind(&AssociatedInterfacePtrState::OnQueryVersion, - base::Unretained(this), callback)); + // Do a static cast in case the interface contains methods with the same + // name. It is safe to capture |this| because the callback won't be run + // after this object goes away. + static_cast(proxy_.get()) + ->QueryVersion(base::Bind(&AssociatedInterfacePtrState::OnQueryVersion, + base::Unretained(this), callback)); } void RequireVersion(uint32_t version) { @@ -59,13 +65,9 @@ class AssociatedInterfacePtrState { return; version_ = version; - endpoint_client_->RequireVersion(version); - } - - void FlushForTesting() { endpoint_client_->FlushForTesting(); } - - void CloseWithReason(uint32_t custom_reason, const std::string& description) { - endpoint_client_->CloseWithReason(custom_reason, description); + // Do a static cast in case the interface contains methods with the same + // name. + static_cast(proxy_.get())->RequireVersion(version); } void Swap(AssociatedInterfacePtrState* other) { @@ -83,13 +85,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), 0u)); + std::move(runner))); proxy_.reset(new Proxy(endpoint_client_.get())); + proxy_->serialization_context()->group_controller = + endpoint_client_->group_controller(); } // After this method is called, the object is in an invalid state and @@ -112,12 +114,6 @@ 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(); @@ -127,13 +123,6 @@ 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 responder) { - endpoint_client_->AcceptWithResponder(&message, responder.release()); - } - private: using Proxy = typename Interface::Proxy_; diff --git a/mojo/public/cpp/bindings/lib/binding_state.h b/mojo/public/cpp/bindings/lib/binding_state.h index 0b0dbee..c8d3e83 100644 --- a/mojo/public/cpp/bindings/lib/binding_state.h +++ b/mojo/public/cpp/bindings/lib/binding_state.h @@ -6,7 +6,6 @@ #define MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDING_STATE_H_ #include -#include #include #include "base/bind.h" @@ -16,15 +15,15 @@ #include "base/memory/ptr_util.h" #include "base/memory/ref_counted.h" #include "base/single_thread_task_runner.h" -#include "mojo/public/cpp/bindings/bindings_export.h" -#include "mojo/public/cpp/bindings/connection_error_callback.h" -#include "mojo/public/cpp/bindings/filter_chain.h" +#include "mojo/public/cpp/bindings/associated_group.h" #include "mojo/public/cpp/bindings/interface_endpoint_client.h" #include "mojo/public/cpp/bindings/interface_id.h" #include "mojo/public/cpp/bindings/interface_ptr.h" #include "mojo/public/cpp/bindings/interface_ptr_info.h" #include "mojo/public/cpp/bindings/interface_request.h" +#include "mojo/public/cpp/bindings/lib/filter_chain.h" #include "mojo/public/cpp/bindings/lib/multiplex_router.h" +#include "mojo/public/cpp/bindings/lib/router.h" #include "mojo/public/cpp/bindings/message_header_validator.h" #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" #include "mojo/public/cpp/system/core.h" @@ -32,35 +31,77 @@ namespace mojo { namespace internal { -class MOJO_CPP_BINDINGS_EXPORT BindingStateBase { +template +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 +class BindingState { public: - BindingStateBase(); - ~BindingStateBase(); + explicit BindingState(Interface* impl) : impl_(impl) { + stub_.set_sink(impl_); + } - void AddFilter(std::unique_ptr filter); + ~BindingState() { Close(); } - bool HasAssociatedInterfaces() const; + void Bind(ScopedMessagePipeHandle handle, + scoped_refptr runner) { + DCHECK(!router_); + internal::FilterChain filters; + filters.Append(Interface::Name_); + filters.Append(); + + router_ = + new internal::Router(std::move(handle), std::move(filters), + Interface::HasSyncMethods_, std::move(runner)); + router_->set_incoming_receiver(&stub_); + router_->set_connection_error_handler( + base::Bind(&BindingState::RunConnectionErrorHandler, + base::Unretained(this))); + } + + bool HasAssociatedInterfaces() const { return false; } - void PauseIncomingMethodCallProcessing(); - void ResumeIncomingMethodCallProcessing(); + void PauseIncomingMethodCallProcessing() { + DCHECK(router_); + router_->PauseIncomingMethodCallProcessing(); + } + void ResumeIncomingMethodCallProcessing() { + DCHECK(router_); + router_->ResumeIncomingMethodCallProcessing(); + } bool WaitForIncomingMethodCall( - MojoDeadline deadline = MOJO_DEADLINE_INDEFINITE); + MojoDeadline deadline = MOJO_DEADLINE_INDEFINITE) { + DCHECK(router_); + return router_->WaitForIncomingMessage(deadline); + } - void Close(); - void CloseWithReason(uint32_t custom_reason, const std::string& description); + void Close() { + if (!router_) + return; - void set_connection_error_handler(const base::Closure& error_handler) { - DCHECK(is_bound()); - endpoint_client_->set_connection_error_handler(error_handler); + router_->CloseMessagePipe(); + DestroyRouter(); + } + + InterfaceRequest Unbind() { + InterfaceRequest request = + MakeRequest(router_->PassMessagePipe()); + DestroyRouter(); + return std::move(request); } - void set_connection_error_with_reason_handler( - const ConnectionErrorWithReasonCallback& error_handler) { + void set_connection_error_handler(const base::Closure& error_handler) { DCHECK(is_bound()); - endpoint_client_->set_connection_error_with_reason_handler(error_handler); + connection_error_handler_ = error_handler; } + Interface* impl() { return impl_; } + bool is_bound() const { return !!router_; } MessagePipeHandle handle() const { @@ -68,42 +109,90 @@ class MOJO_CPP_BINDINGS_EXPORT BindingStateBase { return router_->handle(); } - void FlushForTesting(); + AssociatedGroup* associated_group() { return nullptr; } - void EnableTestingMode(); + void EnableTestingMode() { + DCHECK(is_bound()); + router_->EnableTestingMode(); + } - protected: - void BindInternal(ScopedMessagePipeHandle handle, - scoped_refptr runner, - const char* interface_name, - std::unique_ptr request_validator, - bool passes_associated_kinds, - bool has_sync_methods, - MessageReceiverWithResponderStatus* stub, - uint32_t interface_version); + private: + void DestroyRouter() { + router_->set_connection_error_handler(base::Closure()); + delete router_; + router_ = nullptr; + connection_error_handler_.Reset(); + } - scoped_refptr router_; - std::unique_ptr endpoint_client_; + void RunConnectionErrorHandler() { + if (!connection_error_handler_.is_null()) + connection_error_handler_.Run(); + } + + internal::Router* router_ = nullptr; + typename Interface::Stub_ stub_; + Interface* impl_; + base::Closure connection_error_handler_; + + DISALLOW_COPY_AND_ASSIGN(BindingState); }; -template -class BindingState : public BindingStateBase { +// Uses a multiplexing router. If |Interface| has methods to pass associated +// interface pointers or requests, this specialization should be used. +template +class BindingState { public: - using ImplPointerType = typename ImplRefTraits::PointerType; - - explicit BindingState(ImplPointerType impl) { - stub_.set_sink(std::move(impl)); + explicit BindingState(Interface* impl) : impl_(impl) { + stub_.set_sink(impl_); } ~BindingState() { Close(); } void Bind(ScopedMessagePipeHandle handle, scoped_refptr runner) { - BindingStateBase::BindInternal( - std::move(handle), runner, Interface::Name_, - base::MakeUnique(), - Interface::PassesAssociatedKinds_, Interface::HasSyncMethods_, &stub_, - Interface::Version_); + DCHECK(!router_); + + router_ = new internal::MultiplexRouter(false, std::move(handle), runner); + router_->SetMasterInterfaceName(Interface::Name_); + stub_.serialization_context()->group_controller = router_; + + endpoint_client_.reset(new InterfaceEndpointClient( + router_->CreateLocalEndpointHandle(kMasterInterfaceId), + &stub_, base::WrapUnique(new typename Interface::RequestValidator_()), + Interface::HasSyncMethods_, std::move(runner))); + + endpoint_client_->set_connection_error_handler( + base::Bind(&BindingState::RunConnectionErrorHandler, + base::Unretained(this))); + } + + bool HasAssociatedInterfaces() const { + return router_ ? router_->HasAssociatedEndpoints() : false; + } + + void PauseIncomingMethodCallProcessing() { + DCHECK(router_); + router_->PauseIncomingMethodCallProcessing(); + } + void ResumeIncomingMethodCallProcessing() { + DCHECK(router_); + router_->ResumeIncomingMethodCallProcessing(); + } + + bool WaitForIncomingMethodCall( + MojoDeadline deadline = MOJO_DEADLINE_INDEFINITE) { + DCHECK(router_); + return router_->WaitForIncomingMessage(deadline); + } + + void Close() { + if (!router_) + return; + + endpoint_client_.reset(); + router_->CloseMessagePipe(); + router_ = nullptr; + connection_error_handler_.Reset(); } InterfaceRequest Unbind() { @@ -111,13 +200,45 @@ class BindingState : public BindingStateBase { InterfaceRequest request = MakeRequest(router_->PassMessagePipe()); router_ = nullptr; + connection_error_handler_.Reset(); return request; } - Interface* impl() { return ImplRefTraits::GetRawPointer(&stub_.sink()); } + void set_connection_error_handler(const base::Closure& error_handler) { + DCHECK(is_bound()); + connection_error_handler_ = error_handler; + } + + Interface* impl() { return impl_; } + + bool is_bound() const { return !!router_; } + + MessagePipeHandle handle() const { + DCHECK(is_bound()); + return router_->handle(); + } + + AssociatedGroup* associated_group() { + return endpoint_client_ ? endpoint_client_->associated_group() : nullptr; + } + + void EnableTestingMode() { + DCHECK(is_bound()); + router_->EnableTestingMode(); + } private: - typename Interface::template Stub_ stub_; + void RunConnectionErrorHandler() { + if (!connection_error_handler_.is_null()) + connection_error_handler_.Run(); + } + + scoped_refptr router_; + std::unique_ptr endpoint_client_; + + typename Interface::Stub_ stub_; + Interface* impl_; + base::Closure connection_error_handler_; DISALLOW_COPY_AND_ASSIGN(BindingState); }; diff --git a/mojo/public/cpp/bindings/lib/bindings_internal.cc b/mojo/public/cpp/bindings/lib/bindings_internal.cc new file mode 100644 index 0000000..a3bdb1f --- /dev/null +++ b/mojo/public/cpp/bindings/lib/bindings_internal.cc @@ -0,0 +1,47 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/public/cpp/bindings/lib/bindings_internal.h" + +namespace mojo { +namespace internal { + +namespace { + +const size_t kAlignment = 8; + +template +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(AlignImpl(reinterpret_cast(ptr))); +} + +bool IsAligned(const void* ptr) { + return !(reinterpret_cast(ptr) % kAlignment); +} + +void EncodePointer(const void* ptr, uint64_t* offset) { + if (!ptr) { + *offset = 0; + return; + } + + const char* p_obj = reinterpret_cast(ptr); + const char* p_slot = reinterpret_cast(offset); + DCHECK(p_obj > p_slot); + + *offset = static_cast(p_obj - p_slot); +} + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/bindings_internal.h b/mojo/public/cpp/bindings/lib/bindings_internal.h index 631daec..b37d872 100644 --- a/mojo/public/cpp/bindings/lib/bindings_internal.h +++ b/mojo/public/cpp/bindings/lib/bindings_internal.h @@ -17,26 +17,30 @@ namespace mojo { template -class ArrayDataView; +class Array; template -class AssociatedInterfacePtrInfoDataView; +class AssociatedInterfacePtrInfo; template -class AssociatedInterfaceRequestDataView; +class AssociatedInterfaceRequest; template -class InterfacePtrDataView; +class InterfacePtr; template -class InterfaceRequestDataView; +class InterfaceRequest; template -class MapDataView; +class Map; -class NativeStructDataView; +class String; -class StringDataView; +template +class StructPtr; + +template +class InlinedStructPtr; namespace internal { @@ -54,17 +58,12 @@ class Array_Data; template class Map_Data; -class NativeStruct_Data; - using String_Data = Array_Data; -inline size_t Align(size_t size) { - return (size + 7) & ~0x7; -} +size_t Align(size_t size); +char* AlignPointer(char* ptr); -inline bool IsAligned(const void* ptr) { - return !(reinterpret_cast(ptr) & 0x7); -} +bool IsAligned(const void* ptr); // Pointers are encoded as relative offsets. The offsets are relative to the // address of where the offset value is stored, such that the pointer may be @@ -74,19 +73,7 @@ inline bool IsAligned(const void* ptr) { // // A null pointer is encoded as an offset value of 0. // -inline void EncodePointer(const void* ptr, uint64_t* offset) { - if (!ptr) { - *offset = 0; - return; - } - - const char* p_obj = reinterpret_cast(ptr); - const char* p_slot = reinterpret_cast(offset); - DCHECK(p_obj > p_slot); - - *offset = static_cast(p_obj - p_slot); -} - +void EncodePointer(const void* ptr, uint64_t* offset); // Note: This function doesn't validate the encoded pointer value. inline const void* DecodePointer(const uint64_t* offset) { if (!*offset) @@ -124,8 +111,6 @@ struct Pointer { }; static_assert(sizeof(Pointer) == 8, "Bad_sizeof(Pointer)"); -using GenericPointer = Pointer; - struct Handle_Data { Handle_Data() = default; explicit Handle_Data(uint32_t value) : value(value) {} @@ -142,24 +127,19 @@ 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 { - AssociatedEndpointHandle_Data handle; + InterfaceId interface_id; uint32_t version; }; static_assert(sizeof(AssociatedInterface_Data) == 8, "Bad_sizeof(AssociatedInterface_Data)"); +struct AssociatedInterfaceRequest_Data { + InterfaceId interface_id; +}; +static_assert(sizeof(AssociatedInterfaceRequest_Data) == 4, + "Bad_sizeof(AssociatedInterfaceRequest_Data)"); + #pragma pack(pop) template @@ -223,7 +203,7 @@ struct MojomTypeTraits { }; template -struct MojomTypeTraits, false> { +struct MojomTypeTraits, false> { using Data = Array_Data::DataAsArrayElement>; using DataAsArrayElement = Pointer; @@ -231,7 +211,7 @@ struct MojomTypeTraits, false> { }; template -struct MojomTypeTraits, false> { +struct MojomTypeTraits, false> { using Data = AssociatedInterface_Data; using DataAsArrayElement = Data; @@ -240,8 +220,8 @@ struct MojomTypeTraits, false> { }; template -struct MojomTypeTraits, false> { - using Data = AssociatedEndpointHandle_Data; +struct MojomTypeTraits, false> { + using Data = AssociatedInterfaceRequest_Data; using DataAsArrayElement = Data; static const MojomTypeCategory category = @@ -273,7 +253,7 @@ struct MojomTypeTraits, false> { }; template -struct MojomTypeTraits, false> { +struct MojomTypeTraits, false> { using Data = Interface_Data; using DataAsArrayElement = Data; @@ -281,7 +261,7 @@ struct MojomTypeTraits, false> { }; template -struct MojomTypeTraits, false> { +struct MojomTypeTraits, false> { using Data = Handle_Data; using DataAsArrayElement = Data; @@ -290,7 +270,7 @@ struct MojomTypeTraits, false> { }; template -struct MojomTypeTraits, false> { +struct MojomTypeTraits, false> { using Data = Map_Data::DataAsArrayElement, typename MojomTypeTraits::DataAsArrayElement>; using DataAsArrayElement = Pointer; @@ -299,19 +279,37 @@ struct MojomTypeTraits, false> { }; template <> -struct MojomTypeTraits { - using Data = internal::NativeStruct_Data; +struct MojomTypeTraits { + using Data = String_Data; using DataAsArrayElement = Pointer; - static const MojomTypeCategory category = MojomTypeCategory::STRUCT; + static const MojomTypeCategory category = MojomTypeCategory::STRING; }; -template <> -struct MojomTypeTraits { - using Data = String_Data; - using DataAsArrayElement = Pointer; +template +struct MojomTypeTraits, false> { + using Data = typename T::Data_; + using DataAsArrayElement = + typename std::conditional::value, + Data, + Pointer>::type; + + static const MojomTypeCategory category = IsUnionDataType::value + ? MojomTypeCategory::UNION + : MojomTypeCategory::STRUCT; +}; - static const MojomTypeCategory category = MojomTypeCategory::STRING; +template +struct MojomTypeTraits, false> { + using Data = typename T::Data_; + using DataAsArrayElement = + typename std::conditional::value, + Data, + Pointer>::type; + + static const MojomTypeCategory category = IsUnionDataType::value + ? MojomTypeCategory::UNION + : MojomTypeCategory::STRUCT; }; template diff --git a/mojo/public/cpp/bindings/lib/buffer.h b/mojo/public/cpp/bindings/lib/buffer.h index 213a445..c3b570e 100644 --- a/mojo/public/cpp/bindings/lib/buffer.h +++ b/mojo/public/cpp/bindings/lib/buffer.h @@ -7,61 +7,15 @@ #include -#include "base/logging.h" -#include "base/macros.h" -#include "mojo/public/cpp/bindings/lib/bindings_internal.h" - namespace mojo { namespace internal { -// Buffer provides an interface to allocate memory blocks which are 8-byte -// aligned and zero-initialized. It doesn't own the underlying memory. Users -// must ensure that the memory stays valid while using the allocated blocks from -// Buffer. +// Buffer provides a way to allocate memory. Allocations are 8-byte aligned and +// zero-initialized. Allocations remain valid for the lifetime of the Buffer. class Buffer { public: - Buffer() {} - - // The memory must have been zero-initialized. |data| must be 8-byte - // aligned. - void Initialize(void* data, size_t size) { - DCHECK(IsAligned(data)); - - data_ = data; - size_ = size; - cursor_ = reinterpret_cast(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(result); - } - - private: - void* data_ = nullptr; - size_t size_ = 0; - - uintptr_t cursor_ = 0; - uintptr_t data_end_ = 0; - - DISALLOW_COPY_AND_ASSIGN(Buffer); + virtual ~Buffer() {} + virtual void* Allocate(size_t num_bytes) = 0; }; } // namespace internal diff --git a/mojo/public/cpp/bindings/lib/clone_equals_util.h b/mojo/public/cpp/bindings/lib/clone_equals_util.h new file mode 100644 index 0000000..f7bd898 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/clone_equals_util.h @@ -0,0 +1,161 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_CLONE_EQUALS_UTIL_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_CLONE_EQUALS_UTIL_H_ + +#include +#include +#include + +#include "base/optional.h" +#include "mojo/public/cpp/bindings/lib/template_util.h" + +namespace mojo { +namespace internal { + +template +struct HasCloneMethod { + template + static char Test(decltype(&U::Clone)); + template + static int Test(...); + static const bool value = sizeof(Test(0)) == sizeof(char); + + private: + EnsureTypeIsComplete check_t_; +}; + +template ::value> +struct CloneTraits; + +template +T Clone(const T& input); + +template +struct CloneTraits { + static T Clone(const T& input) { return input.Clone(); } +}; + +template +struct CloneTraits { + static T Clone(const T& input) { return input; } +}; + +template +struct CloneTraits, false> { + static base::Optional Clone(const base::Optional& input) { + if (!input) + return base::nullopt; + + return base::Optional(internal::Clone(*input)); + } +}; + +template +struct CloneTraits, false> { + static std::vector Clone(const std::vector& input) { + std::vector result; + result.reserve(input.size()); + for (const auto& element : input) + result.push_back(internal::Clone(element)); + + return result; + } +}; + +template +struct CloneTraits, false> { + static std::unordered_map Clone(const std::unordered_map& input) { + std::unordered_map result; + for (const auto& element : input) { + result.insert(std::make_pair(internal::Clone(element.first), + internal::Clone(element.second))); + } + return result; + } +}; + +template +T Clone(const T& input) { + return CloneTraits::Clone(input); +}; + +template +struct HasEqualsMethod { + template + static char Test(decltype(&U::Equals)); + template + static int Test(...); + static const bool value = sizeof(Test(0)) == sizeof(char); + + private: + EnsureTypeIsComplete check_t_; +}; + +template ::value> +struct EqualsTraits; + +template +bool Equals(const T& a, const T& b); + +template +struct EqualsTraits { + static bool Equals(const T& a, const T& b) { return a.Equals(b); } +}; + +template +struct EqualsTraits { + static bool Equals(const T& a, const T& b) { return a == b; } +}; + +template +struct EqualsTraits, false> { + static bool Equals(const base::Optional& a, const base::Optional& b) { + if (!a && !b) + return true; + if (!a || !b) + return false; + + return internal::Equals(*a, *b); + } +}; + +template +struct EqualsTraits, false> { + static bool Equals(const std::vector& a, const std::vector& b) { + if (a.size() != b.size()) + return false; + for (size_t i = 0; i < a.size(); ++i) { + if (!internal::Equals(a[i], b[i])) + return false; + } + return true; + } +}; + +template +struct EqualsTraits, false> { + static bool Equals(const std::unordered_map& a, + const std::unordered_map& b) { + if (a.size() != b.size()) + return false; + for (const auto& element : a) { + auto iter = b.find(element.first); + if (iter == b.end() || !internal::Equals(element.second, iter->second)) + return false; + } + return true; + } +}; + +template +bool Equals(const T& a, const T& b) { + return EqualsTraits::Equals(a, b); +} + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_CLONE_EQUALS_UTIL_H_ diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc index 4426def..1bb38f0 100644 --- a/mojo/public/cpp/bindings/lib/connector.cc +++ b/mojo/public/cpp/bindings/lib/connector.cc @@ -12,20 +12,52 @@ #include "base/logging.h" #include "base/macros.h" #include "base/synchronization/lock.h" -#include "mojo/public/cpp/bindings/lib/may_auto_lock.h" #include "mojo/public/cpp/bindings/sync_handle_watcher.h" namespace mojo { +namespace { + +// Similar to base::AutoLock, except that it does nothing if |lock| passed into +// the constructor is null. +class MayAutoLock { + public: + explicit MayAutoLock(base::Lock* lock) : lock_(lock) { + if (lock_) + lock_->Acquire(); + } + + ~MayAutoLock() { + if (lock_) { + lock_->AssertAcquired(); + lock_->Release(); + } + } + + private: + base::Lock* lock_; + DISALLOW_COPY_AND_ASSIGN(MayAutoLock); +}; + +} // namespace + +// ---------------------------------------------------------------------------- + Connector::Connector(ScopedMessagePipeHandle message_pipe, ConnectorConfig config, scoped_refptr runner) : message_pipe_(std::move(message_pipe)), + incoming_receiver_(nullptr), task_runner_(std::move(runner)), + handle_watcher_(task_runner_), + error_(false), + drop_writes_(false), + enforce_errors_from_incoming_receiver_(true), + paused_(false), + lock_(config == MULTI_THREADED_SEND ? new base::Lock : nullptr), + allow_woken_up_by_others_(false), + sync_handle_watcher_callback_count_(0), weak_factory_(this) { - if (config == MULTI_THREADED_SEND) - lock_.emplace(); - weak_self_ = weak_factory_.GetWeakPtr(); // Even though we don't have an incoming receiver, we still want to monitor // the message pipe to know if is closed or encounters an error. @@ -33,34 +65,25 @@ Connector::Connector(ScopedMessagePipeHandle message_pipe, } Connector::~Connector() { - { - // Allow for quick destruction on any thread if the pipe is already closed. - base::AutoLock lock(connected_lock_); - if (!connected_) - return; - } - DCHECK(thread_checker_.CalledOnValidThread()); + CancelWait(); } void Connector::CloseMessagePipe() { - // Throw away the returned message pipe. - PassMessagePipe(); + DCHECK(thread_checker_.CalledOnValidThread()); + + CancelWait(); + MayAutoLock locker(lock_.get()); + message_pipe_.reset(); } ScopedMessagePipeHandle Connector::PassMessagePipe() { DCHECK(thread_checker_.CalledOnValidThread()); CancelWait(); - internal::MayAutoLock locker(&lock_); - ScopedMessagePipeHandle message_pipe = std::move(message_pipe_); - weak_factory_.InvalidateWeakPtrs(); - sync_handle_watcher_callback_count_ = 0; - - base::AutoLock lock(connected_lock_); - connected_ = false; - return message_pipe; + MayAutoLock locker(lock_.get()); + return std::move(message_pipe_); } void Connector::RaiseError() { @@ -120,7 +143,7 @@ bool Connector::Accept(Message* message) { if (error_) return false; - internal::MayAutoLock locker(&lock_); + MayAutoLock locker(lock_.get()); if (!message_pipe_.is_valid() || drop_writes_) return true; @@ -181,13 +204,6 @@ 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); } @@ -198,10 +214,8 @@ void Connector::OnSyncHandleWatcherHandleReady(MojoResult result) { sync_handle_watcher_callback_count_++; OnHandleReadyInternal(result); // At this point, this object might have been deleted. - if (weak_self) { - DCHECK_LT(0u, sync_handle_watcher_callback_count_); + if (weak_self) sync_handle_watcher_callback_count_--; - } } void Connector::OnHandleReadyInternal(MojoResult result) { @@ -217,14 +231,12 @@ void Connector::OnHandleReadyInternal(MojoResult result) { void Connector::WaitToReadMore() { CHECK(!paused_); - DCHECK(!handle_watcher_); + DCHECK(!handle_watcher_.IsWatching()); - handle_watcher_.reset(new Watcher(FROM_HERE, task_runner_)); - if (heap_profiler_tag_) - handle_watcher_->set_heap_profiler_tag(heap_profiler_tag_); - MojoResult rv = handle_watcher_->Start( + MojoResult rv = handle_watcher_.Start( message_pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE, - base::Bind(&Connector::OnWatcherHandleReady, base::Unretained(this))); + base::Bind(&Connector::OnWatcherHandleReady, + base::Unretained(this))); if (rv != MOJO_RESULT_OK) { // If the watch failed because the handle is invalid or its conditions can @@ -245,8 +257,8 @@ bool Connector::ReadSingleMessage(MojoResult* read_result) { bool receiver_result = false; - // Detect if |this| was destroyed or the message pipe was closed/transferred - // during message dispatch. + // Detect if |this| was destroyed during message dispatch. Allow for the + // possibility of re-entering ReadMore() through message dispatch. base::WeakPtr weak_self = weak_self_; Message message; @@ -280,11 +292,9 @@ void Connector::ReadAllAvailableMessages() { while (!error_) { MojoResult rv; - if (!ReadSingleMessage(&rv)) { - // Return immediately without touching any members. |this| may have been - // destroyed. + // Return immediately if |this| was destroyed. Do not touch any members! + if (!ReadSingleMessage(&rv)) return; - } if (paused_) return; @@ -295,7 +305,7 @@ void Connector::ReadAllAvailableMessages() { } void Connector::CancelWait() { - handle_watcher_.reset(); + handle_watcher_.Cancel(); sync_watcher_.reset(); } @@ -315,7 +325,7 @@ void Connector::HandleError(bool force_pipe_reset, bool force_async_handler) { if (force_pipe_reset) { CancelWait(); - internal::MayAutoLock locker(&lock_); + MayAutoLock locker(lock_.get()); message_pipe_.reset(); MessagePipe dummy_pipe; message_pipe_ = std::move(dummy_pipe.handle0); diff --git a/mojo/public/cpp/bindings/lib/control_message_handler.cc b/mojo/public/cpp/bindings/lib/control_message_handler.cc index c90aada..9f44e88 100644 --- a/mojo/public/cpp/bindings/lib/control_message_handler.cc +++ b/mojo/public/cpp/bindings/lib/control_message_handler.cc @@ -11,53 +11,15 @@ #include "base/logging.h" #include "mojo/public/cpp/bindings/lib/message_builder.h" #include "mojo/public/cpp/bindings/lib/serialization.h" -#include "mojo/public/cpp/bindings/lib/validation_util.h" #include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h" namespace mojo { namespace internal { -namespace { - -bool ValidateControlRequestWithResponse(Message* message) { - ValidationContext validation_context(message->payload(), - message->payload_num_bytes(), 0, 0, - message, "ControlRequestValidator"); - if (!ValidateMessageIsRequestExpectingResponse(message, &validation_context)) - return false; - - switch (message->header()->name) { - case interface_control::kRunMessageId: - return ValidateMessagePayload< - interface_control::internal::RunMessageParams_Data>( - message, &validation_context); - } - return false; -} - -bool ValidateControlRequestWithoutResponse(Message* message) { - ValidationContext validation_context(message->payload(), - message->payload_num_bytes(), 0, 0, - message, "ControlRequestValidator"); - if (!ValidateMessageIsRequestWithoutResponse(message, &validation_context)) - return false; - - switch (message->header()->name) { - case interface_control::kRunOrClosePipeMessageId: - return ValidateMessageIsRequestWithoutResponse(message, - &validation_context) && - ValidateMessagePayload< - interface_control::internal::RunOrClosePipeMessageParams_Data>( - message, &validation_context); - } - return false; -} - -} // namespace // static bool ControlMessageHandler::IsControlMessage(const Message* message) { - return message->header()->name == interface_control::kRunMessageId || - message->header()->name == interface_control::kRunOrClosePipeMessageId; + return message->header()->name == kRunMessageId || + message->header()->name == kRunOrClosePipeMessageId; } ControlMessageHandler::ControlMessageHandler(uint32_t interface_version) @@ -68,10 +30,7 @@ ControlMessageHandler::~ControlMessageHandler() { } bool ControlMessageHandler::Accept(Message* message) { - if (!ValidateControlRequestWithoutResponse(message)) - return false; - - if (message->header()->name == interface_control::kRunOrClosePipeMessageId) + if (message->header()->name == kRunOrClosePipeMessageId) return RunOrClosePipe(message); NOTREACHED(); @@ -81,10 +40,7 @@ bool ControlMessageHandler::Accept(Message* message) { bool ControlMessageHandler::AcceptWithResponder( Message* message, MessageReceiverWithStatus* responder) { - if (!ValidateControlRequestWithResponse(message)) - return false; - - if (message->header()->name == interface_control::kRunMessageId) + if (message->header()->name == kRunMessageId) return Run(message, responder); NOTREACHED(); @@ -93,37 +49,20 @@ bool ControlMessageHandler::AcceptWithResponder( bool ControlMessageHandler::Run(Message* message, MessageReceiverWithStatus* responder) { - interface_control::internal::RunMessageParams_Data* params = - reinterpret_cast( - message->mutable_payload()); - interface_control::RunMessageParamsPtr params_ptr; - Deserialize(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( - 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( - response_params_ptr, builder.buffer(), &response_params, &context_); + 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( + response_params_ptr, &context_); + ResponseMessageBuilder builder(kRunMessageId, size, message->request_id()); + + RunResponseMessageParams_Data* response_params = nullptr; + Serialize(response_params_ptr, builder.buffer(), + &response_params, &context_); bool ok = responder->Accept(builder.message()); ALLOW_UNUSED_LOCAL(ok); delete responder; @@ -132,18 +71,13 @@ bool ControlMessageHandler::Run(Message* message, } bool ControlMessageHandler::RunOrClosePipe(Message* message) { - interface_control::internal::RunOrClosePipeMessageParams_Data* params = - reinterpret_cast< - interface_control::internal::RunOrClosePipeMessageParams_Data*>( + RunOrClosePipeMessageParams_Data* params = + reinterpret_cast( message->mutable_payload()); - interface_control::RunOrClosePipeMessageParamsPtr params_ptr; - Deserialize( - params, ¶ms_ptr, &context_); - auto& input = *params_ptr->input; - if (input.is_require_version()) - return interface_version_ >= input.get_require_version()->version; + RunOrClosePipeMessageParamsPtr params_ptr; + Deserialize(params, ¶ms_ptr, &context_); - return false; + return interface_version_ >= params_ptr->require_version->version; } } // namespace internal diff --git a/mojo/public/cpp/bindings/lib/control_message_handler.h b/mojo/public/cpp/bindings/lib/control_message_handler.h index 3c385e4..13b5aa6 100644 --- a/mojo/public/cpp/bindings/lib/control_message_handler.h +++ b/mojo/public/cpp/bindings/lib/control_message_handler.h @@ -7,9 +7,7 @@ #include -#include "base/compiler_specific.h" #include "base/macros.h" -#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/lib/serialization_context.h" #include "mojo/public/cpp/bindings/message.h" @@ -17,8 +15,7 @@ namespace mojo { namespace internal { // Handlers for request messages defined in interface_control_messages.mojom. -class MOJO_CPP_BINDINGS_EXPORT ControlMessageHandler - : NON_EXPORTED_BASE(public MessageReceiverWithResponderStatus) { +class ControlMessageHandler : public MessageReceiverWithResponderStatus { public: static bool IsControlMessage(const Message* message); diff --git a/mojo/public/cpp/bindings/lib/control_message_proxy.cc b/mojo/public/cpp/bindings/lib/control_message_proxy.cc index 23de991..7af409d 100644 --- a/mojo/public/cpp/bindings/lib/control_message_proxy.cc +++ b/mojo/public/cpp/bindings/lib/control_message_proxy.cc @@ -9,12 +9,9 @@ #include #include "base/bind.h" -#include "base/callback_helpers.h" #include "base/macros.h" -#include "base/run_loop.h" #include "mojo/public/cpp/bindings/lib/message_builder.h" #include "mojo/public/cpp/bindings/lib/serialization.h" -#include "mojo/public/cpp/bindings/lib/validation_util.h" #include "mojo/public/cpp/bindings/message.h" #include "mojo/public/interfaces/bindings/interface_control_messages.mojom.h" @@ -23,28 +20,11 @@ namespace internal { namespace { -bool ValidateControlResponse(Message* message) { - ValidationContext validation_context(message->payload(), - message->payload_num_bytes(), 0, 0, - message, "ControlResponseValidator"); - if (!ValidateMessageIsResponse(message, &validation_context)) - return false; - - switch (message->header()->name) { - case interface_control::kRunMessageId: - return ValidateMessagePayload< - interface_control::internal::RunResponseMessageParams_Data>( - message, &validation_context); - } - return false; -} - -using RunCallback = - base::Callback; +using RunCallback = base::Callback; class RunResponseForwardToCallback : public MessageReceiver { public: - explicit RunResponseForwardToCallback(const RunCallback& callback) + RunResponseForwardToCallback(const RunCallback& callback) : callback_(callback) {} bool Accept(Message* message) override; @@ -54,83 +34,59 @@ class RunResponseForwardToCallback : public MessageReceiver { }; bool RunResponseForwardToCallback::Accept(Message* message) { - if (!ValidateControlResponse(message)) - return false; - - interface_control::internal::RunResponseMessageParams_Data* params = - reinterpret_cast< - interface_control::internal::RunResponseMessageParams_Data*>( + RunResponseMessageParams_Data* params = + reinterpret_cast( message->mutable_payload()); - interface_control::RunResponseMessageParamsPtr params_ptr; + RunResponseMessageParamsPtr params_ptr; SerializationContext context; - Deserialize( - params, ¶ms_ptr, &context); + Deserialize(params, ¶ms_ptr, &context); - callback_.Run(std::move(params_ptr)); + callback_.Run(std::move(params_ptr->query_version_result)); return true; } void SendRunMessage(MessageReceiverWithResponder* receiver, - interface_control::RunInputPtr input_ptr, - const RunCallback& callback) { - SerializationContext context; - - auto params_ptr = interface_control::RunMessageParams::New(); - params_ptr->input = std::move(input_ptr); - size_t size = PrepareToSerialize( - params_ptr, &context); - MessageBuilder builder(interface_control::kRunMessageId, - Message::kFlagExpectsResponse, size, 0); - - interface_control::internal::RunMessageParams_Data* params = nullptr; - Serialize( - params_ptr, builder.buffer(), ¶ms, &context); + QueryVersionPtr query_version, + const RunCallback& callback, + SerializationContext* context) { + RunMessageParamsPtr params_ptr(RunMessageParams::New()); + params_ptr->reserved0 = 16u; + params_ptr->reserved1 = 0u; + params_ptr->query_version = std::move(query_version); + + size_t size = PrepareToSerialize(params_ptr, context); + RequestMessageBuilder builder(kRunMessageId, size); + + RunMessageParams_Data* params = nullptr; + Serialize(params_ptr, builder.buffer(), ¶ms, + context); MessageReceiver* responder = new RunResponseForwardToCallback(callback); if (!receiver->AcceptWithResponder(builder.message(), responder)) delete responder; } -Message ConstructRunOrClosePipeMessage( - interface_control::RunOrClosePipeInputPtr input_ptr) { - SerializationContext context; - - 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( - 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); +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(params_ptr, context); + MessageBuilder builder(kRunOrClosePipeMessageId, size); + + RunOrClosePipeMessageParams_Data* params = nullptr; + Serialize(params_ptr, builder.buffer(), + ¶ms, context); + bool ok = receiver->Accept(builder.message()); ALLOW_UNUSED_LOCAL(ok); } -void RunVersionCallback( - const base::Callback& callback, - interface_control::RunResponseMessageParamsPtr run_response) { - uint32_t version = 0u; - if (run_response->output && run_response->output->is_query_version_result()) - version = run_response->output->get_query_version_result()->version; - callback.Run(version); -} - -void RunClosure(const base::Closure& callback, - interface_control::RunResponseMessageParamsPtr run_response) { - callback.Run(); +void RunVersionCallback(const base::Callback& callback, + QueryVersionResultPtr query_version_result) { + callback.Run(query_version_result->version); } } // namespace @@ -139,49 +95,16 @@ ControlMessageProxy::ControlMessageProxy(MessageReceiverWithResponder* receiver) : receiver_(receiver) { } -ControlMessageProxy::~ControlMessageProxy() = default; - void ControlMessageProxy::QueryVersion( const base::Callback& callback) { - auto input_ptr = interface_control::RunInput::New(); - input_ptr->set_query_version(interface_control::QueryVersion::New()); - SendRunMessage(receiver_, std::move(input_ptr), - base::Bind(&RunVersionCallback, callback)); + SendRunMessage(receiver_, QueryVersion::New(), + base::Bind(&RunVersionCallback, callback), &context_); } void ControlMessageProxy::RequireVersion(uint32_t version) { - auto require_version = interface_control::RequireVersion::New(); + RequireVersionPtr require_version(RequireVersion::New()); require_version->version = version; - auto input_ptr = interface_control::RunOrClosePipeInput::New(); - input_ptr->set_require_version(std::move(require_version)); - SendRunOrClosePipeMessage(receiver_, std::move(input_ptr)); -} - -void ControlMessageProxy::FlushForTesting() { - if (encountered_error_) - return; - - auto input_ptr = interface_control::RunInput::New(); - input_ptr->set_flush_for_testing(interface_control::FlushForTesting::New()); - base::RunLoop run_loop; - run_loop_quit_closure_ = run_loop.QuitClosure(); - SendRunMessage( - receiver_, std::move(input_ptr), - base::Bind(&RunClosure, - base::Bind(&ControlMessageProxy::RunFlushForTestingClosure, - base::Unretained(this)))); - run_loop.Run(); -} - -void ControlMessageProxy::RunFlushForTestingClosure() { - DCHECK(!run_loop_quit_closure_.is_null()); - base::ResetAndReturn(&run_loop_quit_closure_).Run(); -} - -void ControlMessageProxy::OnConnectionError() { - encountered_error_ = true; - if (!run_loop_quit_closure_.is_null()) - RunFlushForTestingClosure(); + SendRunOrClosePipeMessage(receiver_, std::move(require_version), &context_); } } // namespace internal diff --git a/mojo/public/cpp/bindings/lib/control_message_proxy.h b/mojo/public/cpp/bindings/lib/control_message_proxy.h index 2f9314e..5ec6ddc 100644 --- a/mojo/public/cpp/bindings/lib/control_message_proxy.h +++ b/mojo/public/cpp/bindings/lib/control_message_proxy.h @@ -7,9 +7,8 @@ #include -#include "base/callback.h" +#include "base/callback_forward.h" #include "base/macros.h" -#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/lib/serialization_context.h" namespace mojo { @@ -19,26 +18,18 @@ class MessageReceiverWithResponder; namespace internal { // Proxy for request messages defined in interface_control_messages.mojom. -class MOJO_CPP_BINDINGS_EXPORT ControlMessageProxy { +class ControlMessageProxy { public: // Doesn't take ownership of |receiver|. It must outlive this object. explicit ControlMessageProxy(MessageReceiverWithResponder* receiver); - ~ControlMessageProxy(); void QueryVersion(const base::Callback& callback); void RequireVersion(uint32_t version); - void FlushForTesting(); - void OnConnectionError(); - - private: - void RunFlushForTestingClosure(); - + protected: // Not owned. MessageReceiverWithResponder* receiver_; - bool encountered_error_ = false; - - base::Closure run_loop_quit_closure_; + SerializationContext context_; DISALLOW_COPY_AND_ASSIGN(ControlMessageProxy); }; diff --git a/mojo/public/cpp/bindings/lib/equals_traits.h b/mojo/public/cpp/bindings/lib/equals_traits.h deleted file mode 100644 index 53c7dce..0000000 --- a/mojo/public/cpp/bindings/lib/equals_traits.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_EQUALS_TRAITS_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_LIB_EQUALS_TRAITS_H_ - -#include -#include -#include - -#include "base/optional.h" -#include "mojo/public/cpp/bindings/lib/template_util.h" - -namespace mojo { -namespace internal { - -template -struct HasEqualsMethod { - template - static char Test(decltype(&U::Equals)); - template - static int Test(...); - static const bool value = sizeof(Test(0)) == sizeof(char); - - private: - EnsureTypeIsComplete check_t_; -}; - -template ::value> -struct EqualsTraits; - -template -bool Equals(const T& a, const T& b); - -template -struct EqualsTraits { - static bool Equals(const T& a, const T& b) { return a.Equals(b); } -}; - -template -struct EqualsTraits { - static bool Equals(const T& a, const T& b) { return a == b; } -}; - -template -struct EqualsTraits, false> { - static bool Equals(const base::Optional& a, const base::Optional& b) { - if (!a && !b) - return true; - if (!a || !b) - return false; - - return internal::Equals(*a, *b); - } -}; - -template -struct EqualsTraits, false> { - static bool Equals(const std::vector& a, const std::vector& b) { - if (a.size() != b.size()) - return false; - for (size_t i = 0; i < a.size(); ++i) { - if (!internal::Equals(a[i], b[i])) - return false; - } - return true; - } -}; - -template -struct EqualsTraits, false> { - static bool Equals(const std::unordered_map& a, - const std::unordered_map& b) { - if (a.size() != b.size()) - return false; - for (const auto& element : a) { - auto iter = b.find(element.first); - if (iter == b.end() || !internal::Equals(element.second, iter->second)) - return false; - } - return true; - } -}; - -template -bool Equals(const T& a, const T& b) { - return EqualsTraits::Equals(a, b); -} - -} // namespace internal -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_EQUALS_TRAITS_H_ diff --git a/mojo/public/cpp/bindings/lib/filter_chain.cc b/mojo/public/cpp/bindings/lib/filter_chain.cc index 5d919fe..899bac1 100644 --- a/mojo/public/cpp/bindings/lib/filter_chain.cc +++ b/mojo/public/cpp/bindings/lib/filter_chain.cc @@ -2,13 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "mojo/public/cpp/bindings/filter_chain.h" +#include "mojo/public/cpp/bindings/lib/filter_chain.h" #include #include "base/logging.h" namespace mojo { +namespace internal { FilterChain::FilterChain(MessageReceiver* sink) : sink_(sink) { } @@ -25,23 +26,24 @@ FilterChain& FilterChain::operator=(FilterChain&& other) { } FilterChain::~FilterChain() { + for (std::vector::iterator iter = filters_.begin(); + iter != filters_.end(); + ++iter) { + delete *iter; + } } void FilterChain::SetSink(MessageReceiver* sink) { DCHECK(!sink_); sink_ = sink; + if (!filters_.empty()) + filters_.back()->set_sink(sink); } -bool FilterChain::Accept(Message* message) { +MessageReceiver* FilterChain::GetHead() { DCHECK(sink_); - for (auto& filter : filters_) - if (!filter->Accept(message)) - return false; - return sink_->Accept(message); -} - -void FilterChain::Append(std::unique_ptr filter) { - filters_.emplace_back(std::move(filter)); + return filters_.empty() ? sink_ : filters_.front(); } +} // namespace internal } // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/filter_chain.h b/mojo/public/cpp/bindings/lib/filter_chain.h new file mode 100644 index 0000000..447be3d --- /dev/null +++ b/mojo/public/cpp/bindings/lib/filter_chain.h @@ -0,0 +1,66 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_ + +#include +#include + +#include "base/macros.h" +#include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/bindings/message_filter.h" + +namespace mojo { +namespace internal { + +class FilterChain { + public: + // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while + // this object is alive. + explicit FilterChain(MessageReceiver* sink = nullptr); + + FilterChain(FilterChain&& other); + FilterChain& operator=(FilterChain&& other); + ~FilterChain(); + + template + inline void Append(Args&&... args); + + // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while + // this object is alive. + void SetSink(MessageReceiver* sink); + + // Returns a receiver to accept messages. Messages flow through all filters in + // the same order as they were appended to the chain. If all filters allow a + // message to pass, it will be forwarded to |sink_|. + // The returned value is invalidated when this object goes away. + MessageReceiver* GetHead(); + + private: + // Owned by this object. + // TODO(dcheng): Use unique_ptr. + std::vector filters_; + + MessageReceiver* sink_; + + DISALLOW_COPY_AND_ASSIGN(FilterChain); +}; + +template +inline void FilterChain::Append(Args&&... args) { + FilterType* filter = new FilterType(std::forward(args)..., sink_); + if (!filters_.empty()) + filters_.back()->set_sink(filter); + filters_.push_back(filter); +} + +template <> +inline void FilterChain::Append() { +} + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_FILTER_CHAIN_H_ diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.cc b/mojo/public/cpp/bindings/lib/fixed_buffer.cc index 725a193..50b8a21 100644 --- a/mojo/public/cpp/bindings/lib/fixed_buffer.cc +++ b/mojo/public/cpp/bindings/lib/fixed_buffer.cc @@ -4,25 +4,56 @@ #include "mojo/public/cpp/bindings/lib/fixed_buffer.h" +#include #include +#include + +#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(memory); + cursor_ = 0; + size_ = size; +} + +void* FixedBuffer::Allocate(size_t delta) { + delta = internal::Align(delta); + + if (delta == 0 || delta > size_ - cursor_) { + NOTREACHED(); + return nullptr; + } + + char* result = ptr_ + cursor_; + cursor_ += delta; + + return result; +} + FixedBufferForTesting::FixedBufferForTesting(size_t size) { - size = internal::Align(size); + size_ = internal::Align(size); // Use calloc here to ensure all message memory is zero'd out. - void* ptr = calloc(size, 1); - Initialize(ptr, size); + ptr_ = static_cast(calloc(size_, 1)); } FixedBufferForTesting::~FixedBufferForTesting() { - free(data()); + free(ptr_); } void* FixedBufferForTesting::Leak() { - void* ptr = data(); - Initialize(nullptr, 0); + char* ptr = ptr_; + ptr_ = nullptr; + cursor_ = 0; + size_ = 0; return ptr; } diff --git a/mojo/public/cpp/bindings/lib/fixed_buffer.h b/mojo/public/cpp/bindings/lib/fixed_buffer.h index 070b0c8..9a5704b 100644 --- a/mojo/public/cpp/bindings/lib/fixed_buffer.h +++ b/mojo/public/cpp/bindings/lib/fixed_buffer.h @@ -7,21 +7,62 @@ #include -#include "base/compiler_specific.h" #include "base/macros.h" -#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/lib/buffer.h" namespace mojo { namespace internal { -// FixedBufferForTesting owns its buffer. The Leak method may be used to steal -// the underlying memory. -class MOJO_CPP_BINDINGS_EXPORT FixedBufferForTesting - : NON_EXPORTED_BASE(public Buffer) { +// FixedBuffer provides a simple way to allocate objects within a fixed chunk +// of memory. Objects are allocated by calling the |Allocate| method, which +// extends the buffer accordingly. Objects allocated in this way are not freed +// explicitly. Instead, they remain valid so long as the FixedBuffer remains +// valid. The Leak method may be used to steal the underlying memory from the +// FixedBuffer. +// +// Typical usage: +// +// { +// FixedBuffer buf(8 + 8); +// +// int* a = static_cast(buf->Allocate(sizeof(int))); +// *a = 2; +// +// double* b = static_cast(buf->Allocate(sizeof(double))); +// *b = 3.14f; +// +// void* data = buf.Leak(); +// Process(data); +// +// free(data); +// } + +class FixedBuffer : public Buffer { + public: + FixedBuffer(); + + // |size| should be aligned using internal::Align. + void Initialize(void* memory, size_t size); + + size_t size() const { return size_; } + + // Grows the buffer by |num_bytes| and returns a pointer to the start of the + // addition. The resulting address is 8-byte aligned, and the content of the + // memory is zero-filled. + void* Allocate(size_t num_bytes) override; + + protected: + char* ptr_; + size_t cursor_; + size_t size_; + + DISALLOW_COPY_AND_ASSIGN(FixedBuffer); +}; + +class FixedBufferForTesting : public FixedBuffer { public: explicit FixedBufferForTesting(size_t size); - ~FixedBufferForTesting(); + ~FixedBufferForTesting() override; // Returns the internal memory owned by the Buffer to the caller. The Buffer // relinquishes its pointer, effectively resetting the state of the Buffer diff --git a/mojo/public/cpp/bindings/lib/handle_interface_serialization.h b/mojo/public/cpp/bindings/lib/handle_interface_serialization.h index 14ed21f..344c2ca 100644 --- a/mojo/public/cpp/bindings/lib/handle_interface_serialization.h +++ b/mojo/public/cpp/bindings/lib/handle_interface_serialization.h @@ -5,12 +5,9 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_HANDLE_INTERFACE_SERIALIZATION_H_ #define MOJO_PUBLIC_CPP_BINDINGS_LIB_HANDLE_INTERFACE_SERIALIZATION_H_ -#include - #include "mojo/public/cpp/bindings/associated_group_controller.h" #include "mojo/public/cpp/bindings/associated_interface_ptr_info.h" #include "mojo/public/cpp/bindings/associated_interface_request.h" -#include "mojo/public/cpp/bindings/interface_data_view.h" #include "mojo/public/cpp/bindings/interface_ptr.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/bindings/lib/bindings_internal.h" @@ -21,98 +18,52 @@ namespace mojo { namespace internal { -template -struct Serializer, +template +struct Serializer, AssociatedInterfacePtrInfo> { - static_assert(std::is_base_of::value, "Interface type mismatch."); - - static size_t PrepareToSerialize(const AssociatedInterfacePtrInfo& input, - SerializationContext* context) { - if (input.handle().is_valid()) - context->associated_endpoint_count++; - return 0; - } - static void Serialize(AssociatedInterfacePtrInfo& input, AssociatedInterface_Data* output, SerializationContext* context) { - DCHECK(!input.handle().is_valid() || input.handle().pending_association()); - if (input.handle().is_valid()) { - // Set to the index of the element pushed to the back of the vector. - output->handle.value = - static_cast(context->associated_endpoint_handles.size()); - context->associated_endpoint_handles.push_back(input.PassHandle()); - } else { - output->handle.value = kEncodedInvalidHandleValue; - } + DCHECK(!input.handle().is_valid() || !input.handle().is_local()); + DCHECK_EQ(input.handle().group_controller(), + context->group_controller.get()); output->version = input.version(); + output->interface_id = input.PassHandle().release(); } static bool Deserialize(AssociatedInterface_Data* input, AssociatedInterfacePtrInfo* output, SerializationContext* context) { - if (input->handle.is_valid()) { - DCHECK_LT(input->handle.value, - context->associated_endpoint_handles.size()); - output->set_handle( - std::move(context->associated_endpoint_handles[input->handle.value])); - } else { - output->set_handle(ScopedInterfaceEndpointHandle()); - } + output->set_handle(context->group_controller->CreateLocalEndpointHandle( + FetchAndReset(&input->interface_id))); output->set_version(input->version); return true; } }; -template -struct Serializer, +template +struct Serializer, AssociatedInterfaceRequest> { - static_assert(std::is_base_of::value, "Interface type mismatch."); - - static size_t PrepareToSerialize(const AssociatedInterfaceRequest& input, - SerializationContext* context) { - if (input.handle().is_valid()) - context->associated_endpoint_count++; - return 0; - } - static void Serialize(AssociatedInterfaceRequest& input, - AssociatedEndpointHandle_Data* output, + AssociatedInterfaceRequest_Data* output, SerializationContext* context) { - DCHECK(!input.handle().is_valid() || input.handle().pending_association()); - if (input.handle().is_valid()) { - // Set to the index of the element pushed to the back of the vector. - output->value = - static_cast(context->associated_endpoint_handles.size()); - context->associated_endpoint_handles.push_back(input.PassHandle()); - } else { - output->value = kEncodedInvalidHandleValue; - } + DCHECK(!input.handle().is_valid() || !input.handle().is_local()); + DCHECK_EQ(input.handle().group_controller(), + context->group_controller.get()); + output->interface_id = input.PassHandle().release(); } - static bool Deserialize(AssociatedEndpointHandle_Data* input, + static bool Deserialize(AssociatedInterfaceRequest_Data* input, AssociatedInterfaceRequest* output, SerializationContext* context) { - if (input->is_valid()) { - DCHECK_LT(input->value, context->associated_endpoint_handles.size()); - output->Bind( - std::move(context->associated_endpoint_handles[input->value])); - } else { - output->Bind(ScopedInterfaceEndpointHandle()); - } + output->Bind(context->group_controller->CreateLocalEndpointHandle( + FetchAndReset(&input->interface_id))); return true; } }; -template -struct Serializer, InterfacePtr> { - static_assert(std::is_base_of::value, "Interface type mismatch."); - - static size_t PrepareToSerialize(const InterfacePtr& input, - SerializationContext* context) { - return 0; - } - +template +struct Serializer, InterfacePtr> { static void Serialize(InterfacePtr& input, Interface_Data* output, SerializationContext* context) { @@ -131,15 +82,8 @@ struct Serializer, InterfacePtr> { } }; -template -struct Serializer, InterfaceRequest> { - static_assert(std::is_base_of::value, "Interface type mismatch."); - - static size_t PrepareToSerialize(const InterfaceRequest& input, - SerializationContext* context) { - return 0; - } - +template +struct Serializer, InterfaceRequest> { static void Serialize(InterfaceRequest& input, Handle_Data* output, SerializationContext* context) { @@ -156,11 +100,6 @@ struct Serializer, InterfaceRequest> { template struct Serializer, ScopedHandleBase> { - static size_t PrepareToSerialize(const ScopedHandleBase& input, - SerializationContext* context) { - return 0; - } - static void Serialize(ScopedHandleBase& input, Handle_Data* output, SerializationContext* context) { diff --git a/mojo/public/cpp/bindings/lib/hash_util.h b/mojo/public/cpp/bindings/lib/hash_util.h deleted file mode 100644 index 93280d6..0000000 --- a/mojo/public/cpp/bindings/lib/hash_util.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_HASH_UTIL_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_LIB_HASH_UTIL_H_ - -#include -#include -#include -#include - -#include "base/optional.h" -#include "mojo/public/cpp/bindings/lib/template_util.h" - -namespace mojo { -namespace internal { - -template -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()(value) + (seed << 6) + (seed >> 2)); -} - -template -struct HasHashMethod { - template - static char Test(decltype(&U::Hash)); - template - static int Test(...); - static const bool value = sizeof(Test(0)) == sizeof(char); - - private: - EnsureTypeIsComplete check_t_; -}; - -template ::value> -struct HashTraits; - -template -size_t Hash(size_t seed, const T& value); - -template -struct HashTraits { - static size_t Hash(size_t seed, const T& value) { return value.Hash(seed); } -}; - -template -struct HashTraits { - static size_t Hash(size_t seed, const T& value) { - return HashCombine(seed, value); - } -}; - -template -struct HashTraits, false> { - static size_t Hash(size_t seed, const std::vector& value) { - for (const auto& element : value) { - seed = HashCombine(seed, element); - } - return seed; - } -}; - -template -struct HashTraits>, false> { - static size_t Hash(size_t seed, const base::Optional>& value) { - if (!value) - return HashCombine(seed, 0); - - return Hash(seed, *value); - } -}; - -template -size_t Hash(size_t seed, const T& value) { - return HashTraits::Hash(seed, value); -} - -} // namespace internal -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_HASH_UTIL_H_ diff --git a/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc b/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc index 3eca5a1..e1f388a 100644 --- a/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc +++ b/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc @@ -10,7 +10,6 @@ #include "base/bind.h" #include "base/location.h" -#include "base/logging.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" @@ -18,7 +17,6 @@ #include "mojo/public/cpp/bindings/associated_group.h" #include "mojo/public/cpp/bindings/associated_group_controller.h" #include "mojo/public/cpp/bindings/interface_endpoint_controller.h" -#include "mojo/public/cpp/bindings/lib/validation_util.h" #include "mojo/public/cpp/bindings/sync_call_restrictions.h" namespace mojo { @@ -47,7 +45,7 @@ class ResponderThunk : public MessageReceiverWithStatus { task_runner_(std::move(runner)) {} ~ResponderThunk() override { if (!accept_was_invoked_) { - // The Service handled a message that was expecting a response + // The Mojo application handled a message that was expecting a response // but did not send a response. // We raise an error to signal the calling application that an error // condition occurred. Without this the calling application would have no @@ -134,47 +132,47 @@ bool InterfaceEndpointClient::HandleIncomingMessageThunk::Accept( InterfaceEndpointClient::InterfaceEndpointClient( ScopedInterfaceEndpointHandle handle, MessageReceiverWithResponderStatus* receiver, - std::unique_ptr payload_validator, + std::unique_ptr payload_validator, bool expect_sync_requests, - scoped_refptr runner, - uint32_t interface_version) - : expect_sync_requests_(expect_sync_requests), - handle_(std::move(handle)), + scoped_refptr runner) + : handle_(std::move(handle)), incoming_receiver_(receiver), + payload_validator_(std::move(payload_validator)), thunk_(this), - filters_(&thunk_), + next_request_id_(1), + encountered_error_(false), task_runner_(std::move(runner)), - control_message_proxy_(this), - control_message_handler_(interface_version), weak_ptr_factory_(this) { DCHECK(handle_.is_valid()); + DCHECK(handle_.is_local()); // TODO(yzshen): the way to use validator (or message filter in general) // directly is a little awkward. - if (payload_validator) - filters_.Append(std::move(payload_validator)); + payload_validator_->set_sink(&thunk_); - if (handle_.pending_association()) { - handle_.SetAssociationEventHandler(base::Bind( - &InterfaceEndpointClient::OnAssociationEvent, base::Unretained(this))); - } else { - InitControllerIfNecessary(); - } + controller_ = handle_.group_controller()->AttachEndpointClient( + handle_, this, task_runner_); + if (expect_sync_requests) + controller_->AllowWokenUpBySyncWatchOnSameThread(); } InterfaceEndpointClient::~InterfaceEndpointClient() { DCHECK(thread_checker_.CalledOnValidThread()); - if (controller_) - handle_.group_controller()->DetachEndpointClient(handle_); + handle_.group_controller()->DetachEndpointClient(handle_); } AssociatedGroup* InterfaceEndpointClient::associated_group() { if (!associated_group_) - associated_group_ = base::MakeUnique(handle_); + associated_group_ = handle_.group_controller()->CreateAssociatedGroup(); return associated_group_.get(); } +uint32_t InterfaceEndpointClient::interface_id() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return handle_.id(); +} + ScopedInterfaceEndpointHandle InterfaceEndpointClient::PassHandle() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!has_pending_responders()); @@ -182,73 +180,38 @@ ScopedInterfaceEndpointHandle InterfaceEndpointClient::PassHandle() { if (!handle_.is_valid()) return ScopedInterfaceEndpointHandle(); - handle_.SetAssociationEventHandler( - ScopedInterfaceEndpointHandle::AssociationEventCallback()); - - if (controller_) { - controller_ = nullptr; - handle_.group_controller()->DetachEndpointClient(handle_); - } + controller_ = nullptr; + handle_.group_controller()->DetachEndpointClient(handle_); return std::move(handle_); } -void InterfaceEndpointClient::AddFilter( - std::unique_ptr filter) { - filters_.Append(std::move(filter)); -} - void InterfaceEndpointClient::RaiseError() { DCHECK(thread_checker_.CalledOnValidThread()); - if (!handle_.pending_association()) - handle_.group_controller()->RaiseError(); -} - -void InterfaceEndpointClient::CloseWithReason(uint32_t custom_reason, - const std::string& description) { - DCHECK(thread_checker_.CalledOnValidThread()); - - auto handle = PassHandle(); - handle.ResetWithReason(custom_reason, description); + handle_.group_controller()->RaiseError(); } bool InterfaceEndpointClient::Accept(Message* message) { DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(controller_); DCHECK(!message->has_flag(Message::kFlagExpectsResponse)); - DCHECK(!handle_.pending_association()); - - // This has to been done even if connection error has occurred. For example, - // the message contains a pending associated request. The user may try to use - // the corresponding associated interface pointer after sending this message. - // That associated interface pointer has to join an associated group in order - // to work properly. - if (!message->associated_endpoint_handles()->empty()) - message->SerializeAssociatedEndpointHandles(handle_.group_controller()); if (encountered_error_) return false; - InitControllerIfNecessary(); - return controller_->SendMessage(message); } bool InterfaceEndpointClient::AcceptWithResponder(Message* message, MessageReceiver* responder) { DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(controller_); DCHECK(message->has_flag(Message::kFlagExpectsResponse)); - DCHECK(!handle_.pending_association()); - - // Please see comments in Accept(). - if (!message->associated_endpoint_handles()->empty()) - message->SerializeAssociatedEndpointHandles(handle_.group_controller()); if (encountered_error_) return false; - InitControllerIfNecessary(); - // Reserve 0 in case we want it to convey special meaning in the future. uint64_t request_id = next_request_id_++; if (request_id == 0) @@ -271,18 +234,20 @@ bool InterfaceEndpointClient::AcceptWithResponder(Message* message, bool response_received = false; std::unique_ptr sync_responder(responder); sync_responses_.insert(std::make_pair( - request_id, base::MakeUnique(&response_received))); + request_id, base::WrapUnique(new SyncResponseInfo(&response_received)))); base::WeakPtr weak_self = weak_ptr_factory_.GetWeakPtr(); controller_->SyncWatch(&response_received); // Make sure that this instance hasn't been destroyed. if (weak_self) { - DCHECK(base::ContainsKey(sync_responses_, request_id)); + DCHECK(ContainsKey(sync_responses_, request_id)); auto iter = sync_responses_.find(request_id); DCHECK_EQ(&response_received, iter->second->response_received); - if (response_received) - ignore_result(sync_responder->Accept(&iter->second->response)); + if (response_received) { + std::unique_ptr response = std::move(iter->second->response); + ignore_result(sync_responder->Accept(response.get())); + } sync_responses_.erase(iter); } @@ -292,97 +257,30 @@ bool InterfaceEndpointClient::AcceptWithResponder(Message* message, bool InterfaceEndpointClient::HandleIncomingMessage(Message* message) { DCHECK(thread_checker_.CalledOnValidThread()); - return filters_.Accept(message); + + return payload_validator_->Accept(message); } -void InterfaceEndpointClient::NotifyError( - const base::Optional& reason) { +void InterfaceEndpointClient::NotifyError() { DCHECK(thread_checker_.CalledOnValidThread()); if (encountered_error_) return; encountered_error_ = true; - - // Response callbacks may hold on to resource, and there's no need to keep - // them alive any longer. Note that it's allowed that a pending response - // callback may own this endpoint, so we simply move the responders onto the - // stack here and let them be destroyed when the stack unwinds. - AsyncResponderMap responders = std::move(async_responders_); - - control_message_proxy_.OnConnectionError(); - - if (!error_handler_.is_null()) { - base::Closure error_handler = std::move(error_handler_); - error_handler.Run(); - } else if (!error_with_reason_handler_.is_null()) { - ConnectionErrorWithReasonCallback error_with_reason_handler = - std::move(error_with_reason_handler_); - if (reason) { - error_with_reason_handler.Run(reason->custom_reason, reason->description); - } else { - error_with_reason_handler.Run(0, std::string()); - } - } -} - -void InterfaceEndpointClient::QueryVersion( - const base::Callback& callback) { - control_message_proxy_.QueryVersion(callback); -} - -void InterfaceEndpointClient::RequireVersion(uint32_t version) { - control_message_proxy_.RequireVersion(version); -} - -void InterfaceEndpointClient::FlushForTesting() { - control_message_proxy_.FlushForTesting(); -} - -void InterfaceEndpointClient::InitControllerIfNecessary() { - if (controller_ || handle_.pending_association()) - return; - - controller_ = handle_.group_controller()->AttachEndpointClient(handle_, this, - task_runner_); - if (expect_sync_requests_) - controller_->AllowWokenUpBySyncWatchOnSameThread(); -} - -void InterfaceEndpointClient::OnAssociationEvent( - ScopedInterfaceEndpointHandle::AssociationEvent event) { - if (event == ScopedInterfaceEndpointHandle::ASSOCIATED) { - InitControllerIfNecessary(); - } else if (event == - ScopedInterfaceEndpointHandle::PEER_CLOSED_BEFORE_ASSOCIATION) { - task_runner_->PostTask(FROM_HERE, - base::Bind(&InterfaceEndpointClient::NotifyError, - weak_ptr_factory_.GetWeakPtr(), - handle_.disconnect_reason())); - } + if (!error_handler_.is_null()) + error_handler_.Run(); } bool InterfaceEndpointClient::HandleValidatedMessage(Message* message) { DCHECK_EQ(handle_.id(), message->interface_id()); - if (encountered_error_) { - // This message is received after error has been encountered. For associated - // interfaces, this means the remote side sends a - // PeerAssociatedEndpointClosed event but continues to send more messages - // for the same interface. Close the pipe because this shouldn't happen. - DVLOG(1) << "A message is received for an interface after it has been " - << "disconnected. Closing the pipe."; - return false; - } - if (message->has_flag(Message::kFlagExpectsResponse)) { + if (!incoming_receiver_) + return false; + MessageReceiverWithStatus* responder = new ResponderThunk(weak_ptr_factory_.GetWeakPtr(), task_runner_); - bool ok = false; - if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) { - ok = control_message_handler_.AcceptWithResponder(message, responder); - } else { - ok = incoming_receiver_->AcceptWithResponder(message, responder); - } + bool ok = incoming_receiver_->AcceptWithResponder(message, responder); if (!ok) delete responder; return ok; @@ -393,7 +291,8 @@ bool InterfaceEndpointClient::HandleValidatedMessage(Message* message) { auto it = sync_responses_.find(request_id); if (it == sync_responses_.end()) return false; - it->second->response = std::move(*message); + it->second->response.reset(new Message()); + message->MoveTo(it->second->response.get()); *it->second->response_received = true; return true; } @@ -405,8 +304,8 @@ bool InterfaceEndpointClient::HandleValidatedMessage(Message* message) { async_responders_.erase(it); return responder->Accept(message); } else { - if (mojo::internal::ControlMessageHandler::IsControlMessage(message)) - return control_message_handler_.Accept(message); + if (!incoming_receiver_) + return false; return incoming_receiver_->Accept(message); } diff --git a/mojo/public/cpp/bindings/lib/interface_ptr_state.h b/mojo/public/cpp/bindings/lib/interface_ptr_state.h index 8f5b4ff..584933e 100644 --- a/mojo/public/cpp/bindings/lib/interface_ptr_state.h +++ b/mojo/public/cpp/bindings/lib/interface_ptr_state.h @@ -9,7 +9,6 @@ #include // For |std::swap()|. #include -#include #include #include "base/bind.h" @@ -20,35 +19,44 @@ #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 +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 -class InterfacePtrState { +class InterfacePtrState { public: - InterfacePtrState() : version_(0u) {} + InterfacePtrState() : proxy_(nullptr), router_(nullptr), version_(0u) {} ~InterfacePtrState() { - endpoint_client_.reset(); - proxy_.reset(); - if (router_) - router_->CloseMessagePipe(); + // 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_.get(); + return proxy_; } uint32_t version() const { return version_; } @@ -56,10 +64,12 @@ class InterfacePtrState { void QueryVersion(const base::Callback& callback) { ConfigureProxyIfNecessary(); - // It is safe to capture |this| because the callback won't be run after this - // object goes away. - endpoint_client_->QueryVersion(base::Bind( - &InterfacePtrState::OnQueryVersion, base::Unretained(this), callback)); + // Do a static cast in case the interface contains methods with the same + // name. It is safe to capture |this| because the callback won't be run + // after this object goes away. + static_cast(proxy_)->QueryVersion( + base::Bind(&InterfacePtrState::OnQueryVersion, base::Unretained(this), + callback)); } void RequireVersion(uint32_t version) { @@ -69,17 +79,155 @@ class InterfacePtrState { return; version_ = version; - endpoint_client_->RequireVersion(version); + // Do a static cast in case the interface contains methods with the same + // name. + static_cast(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 info, + scoped_refptr 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 PassInterface() { + return InterfacePtrInfo( + 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(Interface::Name_); + filters.Append(); + + router_ = new Router(std::move(handle_), std::move(filters), false, + std::move(runner_)); + + proxy_ = new Proxy(router_); + } + + void OnQueryVersion(const base::Callback& 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 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 +class InterfacePtrState { + public: + InterfacePtrState() : version_(0u) {} + + ~InterfacePtrState() { + endpoint_client_.reset(); + proxy_.reset(); + if (router_) + router_->CloseMessagePipe(); + } + + Interface* instance() { + ConfigureProxyIfNecessary(); + + // This will be null if the object is not bound. + return proxy_.get(); } - void FlushForTesting() { + uint32_t version() const { return version_; } + + void QueryVersion(const base::Callback& callback) { ConfigureProxyIfNecessary(); - endpoint_client_->FlushForTesting(); + + + // 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(proxy_.get())->QueryVersion( + base::Bind(&InterfacePtrState::OnQueryVersion, base::Unretained(this), + callback)); } - void CloseWithReason(uint32_t custom_reason, const std::string& description) { + void RequireVersion(uint32_t version) { ConfigureProxyIfNecessary(); - endpoint_client_->CloseWithReason(custom_reason, description); + + if (version <= version_) + return; + + version_ = version; + // Do a static cast in case the interface contains methods with the same + // name. + static_cast(proxy_.get())->RequireVersion(version); } void Swap(InterfacePtrState* other) { @@ -132,14 +280,6 @@ class InterfacePtrState { endpoint_client_->set_connection_error_handler(error_handler); } - void set_connection_error_with_reason_handler( - const ConnectionErrorWithReasonCallback& error_handler) { - ConfigureProxyIfNecessary(); - - DCHECK(endpoint_client_); - endpoint_client_->set_connection_error_with_reason_handler(error_handler); - } - // Returns true if bound and awaiting a response to a message. bool has_pending_callbacks() const { return endpoint_client_ && endpoint_client_->has_pending_responders(); @@ -155,17 +295,6 @@ class InterfacePtrState { router_->EnableTestingMode(); } - void ForwardMessage(Message message) { - ConfigureProxyIfNecessary(); - endpoint_client_->Accept(&message); - } - - void ForwardMessageWithResponder(Message message, - std::unique_ptr responder) { - ConfigureProxyIfNecessary(); - endpoint_client_->AcceptWithResponder(&message, responder.release()); - } - private: using Proxy = typename Interface::Proxy_; @@ -180,22 +309,15 @@ class InterfacePtrState { if (!handle_.is_valid()) return; - MultiplexRouter::Config config = - Interface::PassesAssociatedKinds_ - ? MultiplexRouter::MULTI_INTERFACE - : (Interface::HasSyncMethods_ - ? MultiplexRouter::SINGLE_INTERFACE_WITH_SYNC_METHODS - : MultiplexRouter::SINGLE_INTERFACE); - router_ = new MultiplexRouter(std::move(handle_), config, true, runner_); + router_ = new MultiplexRouter(true, std::move(handle_), runner_); router_->SetMasterInterfaceName(Interface::Name_); endpoint_client_.reset(new InterfaceEndpointClient( router_->CreateLocalEndpointHandle(kMasterInterfaceId), nullptr, base::WrapUnique(new typename Interface::ResponseValidator_()), false, - std::move(runner_), - // The version is only queried from the client so the value passed here - // will not be used. - 0u)); + std::move(runner_))); proxy_.reset(new Proxy(endpoint_client_.get())); + proxy_->serialization_context()->group_controller = + endpoint_client_->group_controller(); } void OnQueryVersion(const base::Callback& callback, diff --git a/mojo/public/cpp/bindings/lib/map_serialization.h b/mojo/public/cpp/bindings/lib/map_serialization.h index 718a763..c28b835 100644 --- a/mojo/public/cpp/bindings/lib/map_serialization.h +++ b/mojo/public/cpp/bindings/lib/map_serialization.h @@ -8,11 +8,11 @@ #include #include -#include "mojo/public/cpp/bindings/array_data_view.h" +#include "mojo/public/cpp/bindings/array.h" #include "mojo/public/cpp/bindings/lib/array_serialization.h" #include "mojo/public/cpp/bindings/lib/map_data_internal.h" #include "mojo/public/cpp/bindings/lib/serialization_forward.h" -#include "mojo/public/cpp/bindings/map_data_view.h" +#include "mojo/public/cpp/bindings/map.h" namespace mojo { namespace internal { @@ -46,15 +46,12 @@ class MapKeyReader : public MapReaderBase { public: using Base = MapReaderBase; using Traits = typename Base::Traits; - using MaybeConstIterator = typename Base::MaybeConstIterator; explicit MapKeyReader(MaybeConstUserType& input) : Base(input) {} ~MapKeyReader() {} - using GetNextResult = - decltype(Traits::GetKey(std::declval())); - GetNextResult GetNext() { - GetNextResult key = Traits::GetKey(this->iter_); + const typename Traits::Key& GetNext() { + const typename Traits::Key& key = Traits::GetKey(this->iter_); Traits::AdvanceIterator(this->iter_); return key; } @@ -81,17 +78,17 @@ class MapValueReader : public MapReaderBase { }; template -struct Serializer, MaybeConstUserType> { +struct Serializer, MaybeConstUserType> { using UserType = typename std::remove_const::type; using Traits = MapTraits; using UserKey = typename Traits::Key; using UserValue = typename Traits::Value; - using Data = typename MojomTypeTraits>::Data; - using KeyArraySerializer = ArraySerializer, + using Data = typename MojomTypeTraits>::Data; + using KeyArraySerializer = ArraySerializer, std::vector, MapKeyReader>; using ValueArraySerializer = - ArraySerializer, + ArraySerializer, std::vector, MapValueReader>; @@ -125,8 +122,8 @@ struct Serializer, MaybeConstUserType> { auto result = Data::New(buf); if (result) { - auto keys_ptr = MojomTypeTraits>::Data::New( - Traits::GetSize(input), buf); + auto keys_ptr = + MojomTypeTraits>::Data::New(Traits::GetSize(input), buf); if (keys_ptr) { MapKeyReader key_reader(input); KeyArraySerializer::SerializeElements( @@ -135,8 +132,8 @@ struct Serializer, MaybeConstUserType> { result->keys.Set(keys_ptr); } - auto values_ptr = MojomTypeTraits>::Data::New( - Traits::GetSize(input), buf); + auto values_ptr = + MojomTypeTraits>::Data::New(Traits::GetSize(input), buf); if (values_ptr) { MapValueReader value_reader(input); ValueArraySerializer::SerializeElements( diff --git a/mojo/public/cpp/bindings/lib/may_auto_lock.h b/mojo/public/cpp/bindings/lib/may_auto_lock.h deleted file mode 100644 index 06091fe..0000000 --- a/mojo/public/cpp/bindings/lib/may_auto_lock.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MAY_AUTO_LOCK_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MAY_AUTO_LOCK_H_ - -#include "base/macros.h" -#include "base/optional.h" -#include "base/synchronization/lock.h" - -namespace mojo { -namespace internal { - -// Similar to base::AutoLock, except that it does nothing if |lock| passed into -// the constructor is null. -class MayAutoLock { - public: - explicit MayAutoLock(base::Optional* 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* lock) - : lock_(lock->has_value() ? &lock->value() : nullptr) { - if (lock_) { - lock_->AssertAcquired(); - lock_->Release(); - } - } - - ~MayAutoUnlock() { - if (lock_) - lock_->Acquire(); - } - - private: - base::Lock* lock_; - DISALLOW_COPY_AND_ASSIGN(MayAutoUnlock); -}; - -} // namespace internal -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_MAY_AUTO_LOCK_H_ diff --git a/mojo/public/cpp/bindings/lib/message.cc b/mojo/public/cpp/bindings/lib/message.cc index e5f3808..939e064 100644 --- a/mojo/public/cpp/bindings/lib/message.cc +++ b/mojo/public/cpp/bindings/lib/message.cc @@ -11,58 +11,18 @@ #include #include -#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>:: - DestructorAtExit g_tls_message_dispatch_context = LAZY_INSTANCE_INITIALIZER; - -base::LazyInstance>:: - DestructorAtExit g_tls_sync_response_context = LAZY_INSTANCE_INITIALIZER; - -void DoNotifyBadMessage(Message message, const std::string& error) { - message.NotifyBadMessage(error); -} - -} // namespace - Message::Message() { } -Message::Message(Message&& other) - : buffer_(std::move(other.buffer_)), - handles_(std::move(other.handles_)), - associated_endpoint_handles_( - std::move(other.associated_endpoint_handles_)) {} - Message::~Message() { CloseHandles(); } -Message& Message::operator=(Message&& other) { - Reset(); - std::swap(other.buffer_, buffer_); - std::swap(other.handles_, handles_); - std::swap(other.associated_endpoint_handles_, associated_endpoint_handles_); - return *this; -} - -void Message::Reset() { - CloseHandles(); - handles_.clear(); - associated_endpoint_handles_.clear(); - buffer_.reset(); -} - void Message::Initialize(size_t capacity, bool zero_initialized) { DCHECK(!buffer_); buffer_.reset(new internal::MessageBuffer(capacity, zero_initialized)); @@ -76,52 +36,19 @@ void Message::InitializeFromMojoMessage(ScopedMessageHandle message, handles_.swap(*handles); } -const uint8_t* Message::payload() const { - if (version() < 2) - return data() + header()->num_bytes; +void Message::MoveTo(Message* destination) { + DCHECK(this != destination); - return static_cast(header_v2()->payload.Get()); -} - -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(header_v2()->payload.Get()); - if (!payload) { - num_bytes = 0; - } else { - auto payload_end = - reinterpret_cast(header_v2()->payload_interface_ids.Get()); - if (!payload_end) - payload_end = reinterpret_cast(data() + data_num_bytes()); - DCHECK_GE(payload_end, payload); - num_bytes = payload_end - payload; - } - } - DCHECK_LE(num_bytes, std::numeric_limits::max()); - return static_cast(num_bytes); -} + // No copy needed. + std::swap(destination->buffer_, buffer_); + std::swap(destination->handles_, handles_); -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(array_pointer->size()) : 0; -} - -const uint32_t* Message::payload_interface_ids() const { - auto* array_pointer = - version() < 2 ? nullptr : header_v2()->payload_interface_ids.Get(); - return array_pointer ? array_pointer->storage() : nullptr; + CloseHandles(); + handles_.clear(); + buffer_.reset(); } ScopedMessageHandle Message::TakeMojoMessage() { - // If there are associated endpoints transferred, - // SerializeAssociatedEndpointHandles() must be called before this method. - DCHECK(associated_endpoint_handles_.empty()); - if (handles_.empty()) // Fast path for the common case: No handles. return buffer_->TakeMessage(); @@ -153,7 +80,6 @@ ScopedMessageHandle Message::TakeMojoMessage() { } void Message::NotifyBadMessage(const std::string& error) { - DCHECK(buffer_); buffer_->NotifyBadMessage(error); } @@ -165,88 +91,6 @@ 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::New(size, buffer()); - header_v2()->payload_interface_ids.Set(data); - - for (size_t i = 0; i < size; ++i) { - ScopedInterfaceEndpointHandle& handle = associated_endpoint_handles_[i]; - - DCHECK(handle.pending_association()); - data->storage()[i] = - group_controller->AssociateInterface(std::move(handle)); - } - associated_endpoint_handles_.clear(); -} - -bool Message::DeserializeAssociatedEndpointHandles( - AssociatedGroupController* group_controller) { - associated_endpoint_handles_.clear(); - - uint32_t num_ids = payload_num_interface_ids(); - if (num_ids == 0) - return true; - - associated_endpoint_handles_.reserve(num_ids); - uint32_t* ids = header_v2()->payload_interface_ids.Get()->storage(); - bool result = true; - for (uint32_t i = 0; i < num_ids; ++i) { - auto handle = group_controller->CreateLocalEndpointHandle(ids[i]); - if (IsValidInterfaceId(ids[i]) && !handle.is_valid()) { - // |ids[i]| itself is valid but handle creation failed. In that case, mark - // deserialization as failed but continue to deserialize the rest of - // handles. - result = false; - } - - associated_endpoint_handles_.push_back(std::move(handle)); - ids[i] = kInvalidInterfaceId; - } - return result; -} - -PassThroughFilter::PassThroughFilter() {} - -PassThroughFilter::~PassThroughFilter() {} - -bool PassThroughFilter::Accept(Message* message) { return true; } - -SyncMessageResponseContext::SyncMessageResponseContext() - : outer_context_(current()) { - g_tls_sync_response_context.Get().Set(this); -} - -SyncMessageResponseContext::~SyncMessageResponseContext() { - DCHECK_EQ(current(), this); - g_tls_sync_response_context.Get().Set(outer_context_); -} - -// static -SyncMessageResponseContext* SyncMessageResponseContext::current() { - return g_tls_sync_response_context.Get().Get(); -} - -void SyncMessageResponseContext::ReportBadMessage(const std::string& error) { - GetBadMessageCallback().Run(error); -} - -const ReportBadMessageCallback& -SyncMessageResponseContext::GetBadMessageCallback() { - if (bad_message_callback_.is_null()) { - bad_message_callback_ = - base::Bind(&DoNotifyBadMessage, base::Passed(&response_)); - } - return bad_message_callback_; -} - MojoResult ReadMessage(MessagePipeHandle handle, Message* message) { MojoResult rv; @@ -278,55 +122,4 @@ 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 cc12ef6..af79cfd 100644 --- a/mojo/public/cpp/bindings/lib/message_buffer.cc +++ b/mojo/public/cpp/bindings/lib/message_buffer.cc @@ -13,35 +13,54 @@ namespace internal { MessageBuffer::MessageBuffer(size_t capacity, bool zero_initialized) { DCHECK_LE(capacity, std::numeric_limits::max()); + data_num_bytes_ = static_cast(capacity); MojoResult rv = AllocMessage(capacity, nullptr, 0, MOJO_ALLOC_MESSAGE_FLAG_NONE, &message_); CHECK_EQ(rv, MOJO_RESULT_OK); - void* buffer = nullptr; - if (capacity != 0) { - rv = GetMessageBuffer(message_.get(), &buffer); + if (capacity == 0) { + buffer_ = nullptr; + } else { + rv = GetMessageBuffer(message_.get(), &buffer_); CHECK_EQ(rv, MOJO_RESULT_OK); if (zero_initialized) - memset(buffer, 0, capacity); + memset(buffer_, 0, capacity); } - Initialize(buffer, capacity); } MessageBuffer::MessageBuffer(ScopedMessageHandle message, uint32_t num_bytes) { message_ = std::move(message); + data_num_bytes_ = num_bytes; - void* buffer = nullptr; - if (num_bytes != 0) { - MojoResult rv = GetMessageBuffer(message_.get(), &buffer); + if (num_bytes == 0) { + buffer_ = nullptr; + } else { + MojoResult rv = GetMessageBuffer(message_.get(), &buffer_); CHECK_EQ(rv, MOJO_RESULT_OK); } - Initialize(buffer, num_bytes); } MessageBuffer::~MessageBuffer() {} +void* MessageBuffer::Allocate(size_t delta) { + delta = internal::Align(delta); + + DCHECK_LE(delta, static_cast(data_num_bytes_)); + DCHECK_GT(bytes_claimed_ + static_cast(delta), bytes_claimed_); + + uint32_t new_bytes_claimed = bytes_claimed_ + static_cast(delta); + if (new_bytes_claimed > data_num_bytes_) { + NOTREACHED(); + return nullptr; + } + + char* start = static_cast(buffer_) + bytes_claimed_; + bytes_claimed_ = new_bytes_claimed; + return static_cast(start); +} + void MessageBuffer::NotifyBadMessage(const std::string& error) { DCHECK(message_.is_valid()); MojoResult result = mojo::NotifyBadMessage(message_.get(), error); diff --git a/mojo/public/cpp/bindings/lib/message_buffer.h b/mojo/public/cpp/bindings/lib/message_buffer.h index 96d5140..0382131 100644 --- a/mojo/public/cpp/bindings/lib/message_buffer.h +++ b/mojo/public/cpp/bindings/lib/message_buffer.h @@ -5,6 +5,7 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_LIB_MESSAGE_BUFFER_H_ #define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_LIB_MESSAGE_BUFFER_H_ +#include #include #include @@ -16,7 +17,7 @@ namespace mojo { namespace internal { -// A fixed-size Buffer using a Mojo message object for storage. +// A fixed-size Buffer implementation using a Mojo message object for storage. class MessageBuffer : public Buffer { public: // Initializes this buffer to carry a fixed byte capacity and no handles. @@ -25,14 +26,24 @@ class MessageBuffer : public Buffer { // Initializes this buffer from an existing Mojo MessageHandle. MessageBuffer(ScopedMessageHandle message, uint32_t num_bytes); - ~MessageBuffer(); + ~MessageBuffer() override; + + void* data() const { return buffer_; } + uint32_t data_num_bytes() const { return data_num_bytes_; } + + // Buffer: + void* Allocate(size_t delta) override; ScopedMessageHandle TakeMessage() { return std::move(message_); } void NotifyBadMessage(const std::string& error); private: + uint32_t data_num_bytes_ = 0; ScopedMessageHandle message_; + void* buffer_; + + uint32_t bytes_claimed_ = 0; DISALLOW_COPY_AND_ASSIGN(MessageBuffer); }; diff --git a/mojo/public/cpp/bindings/lib/message_builder.cc b/mojo/public/cpp/bindings/lib/message_builder.cc index 6806a73..4ffa180 100644 --- a/mojo/public/cpp/bindings/lib/message_builder.cc +++ b/mojo/public/cpp/bindings/lib/message_builder.cc @@ -4,10 +4,11 @@ #include "mojo/public/cpp/bindings/lib/message_builder.h" -#include "mojo/public/cpp/bindings/lib/array_internal.h" -#include "mojo/public/cpp/bindings/lib/bindings_internal.h" -#include "mojo/public/cpp/bindings/lib/buffer.h" -#include "mojo/public/cpp/bindings/lib/message_internal.h" +#include +#include + +#include "mojo/public/cpp/bindings/lib/serialization_util.h" +#include "mojo/public/cpp/bindings/message.h" namespace mojo { namespace internal { @@ -18,52 +19,38 @@ void Allocate(Buffer* buf, Header** header) { (*header)->num_bytes = sizeof(Header); } -MessageBuilder::MessageBuilder(uint32_t name, - uint32_t flags, - size_t payload_size, - size_t payload_interface_id_count) { - if (payload_interface_id_count > 0) { - // Version 2 - InitializeMessage( - sizeof(MessageHeaderV2) + Align(payload_size) + - ArrayDataTraits::GetStorageSize( - static_cast(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); +MessageBuilder::MessageBuilder(uint32_t name, size_t payload_size) { + InitializeMessage(sizeof(MessageHeader) + payload_size); - MessageHeader* header; - Allocate(message_.buffer(), &header); - header->version = 0; - header->name = name; - header->flags = flags; - } + MessageHeader* header; + Allocate(message_.buffer(), &header); + header->version = 0; + header->name = name; } MessageBuilder::~MessageBuilder() { } +MessageBuilder::MessageBuilder() {} + void MessageBuilder::InitializeMessage(size_t size) { message_.Initialize(static_cast(Align(size)), true /* zero_initialized */); } +MessageWithRequestIDBuilder::MessageWithRequestIDBuilder(uint32_t name, + size_t payload_size, + uint32_t flags, + uint64_t request_id) { + InitializeMessage(sizeof(MessageHeaderWithRequestID) + payload_size); + + MessageHeaderWithRequestID* header; + Allocate(message_.buffer(), &header); + header->version = 1; + header->name = name; + header->flags = flags; + header->request_id = request_id; +} + } // namespace internal } // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/message_builder.h b/mojo/public/cpp/bindings/lib/message_builder.h index 8a4d5c4..a5a050f 100644 --- a/mojo/public/cpp/bindings/lib/message_builder.h +++ b/mojo/public/cpp/bindings/lib/message_builder.h @@ -8,30 +8,24 @@ #include #include -#include "base/macros.h" -#include "mojo/public/cpp/bindings/bindings_export.h" +#include "mojo/public/cpp/bindings/lib/message_internal.h" #include "mojo/public/cpp/bindings/message.h" namespace mojo { - class Message; namespace internal { -class Buffer; - -class MOJO_CPP_BINDINGS_EXPORT MessageBuilder { +class MessageBuilder { public: - MessageBuilder(uint32_t name, - uint32_t flags, - size_t payload_size, - size_t payload_interface_id_count); + MessageBuilder(uint32_t name, size_t payload_size); ~MessageBuilder(); Buffer* buffer() { return message_.buffer(); } Message* message() { return &message_; } - private: + protected: + MessageBuilder(); void InitializeMessage(size_t size); Message message_; @@ -39,6 +33,51 @@ class MOJO_CPP_BINDINGS_EXPORT 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 new file mode 100644 index 0000000..b09f40d --- /dev/null +++ b/mojo/public/cpp/bindings/lib/message_filter.cc @@ -0,0 +1,23 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/public/cpp/bindings/message_filter.h" + +namespace mojo { + +MessageFilter::MessageFilter(MessageReceiver* sink) : sink_(sink) { +} + +MessageFilter::~MessageFilter() { +} + +PassThroughFilter::PassThroughFilter(MessageReceiver* sink) + : MessageFilter(sink) { +} + +bool PassThroughFilter::Accept(Message* message) { + return sink_->Accept(message); +} + +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/message_header_validator.cc b/mojo/public/cpp/bindings/lib/message_header_validator.cc index 9f8c627..10f7774 100644 --- a/mojo/public/cpp/bindings/lib/message_header_validator.cc +++ b/mojo/public/cpp/bindings/lib/message_header_validator.cc @@ -4,8 +4,6 @@ #include "mojo/public/cpp/bindings/message_header_validator.h" -#include "mojo/public/cpp/bindings/lib/array_internal.h" -#include "mojo/public/cpp/bindings/lib/validate_params.h" #include "mojo/public/cpp/bindings/lib/validation_context.h" #include "mojo/public/cpp/bindings/lib/validation_errors.h" #include "mojo/public/cpp/bindings/lib/validation_util.h" @@ -13,40 +11,40 @@ namespace mojo { namespace { -// TODO(yzshen): Define a mojom struct for message header and use the generated -// validation and data view code. bool IsValidMessageHeader(const internal::MessageHeader* header, internal::ValidationContext* validation_context) { // NOTE: Our goal is to preserve support for future extension of the message // header. If we encounter fields we do not understand, we must ignore them. // Extra validation of the struct header: - do { - if (header->version == 0) { - if (header->num_bytes == sizeof(internal::MessageHeader)) - break; - } else if (header->version == 1) { - if (header->num_bytes == sizeof(internal::MessageHeaderV1)) - break; - } else if (header->version == 2) { - if (header->num_bytes == sizeof(internal::MessageHeaderV2)) - break; - } else if (header->version > 2) { - if (header->num_bytes >= sizeof(internal::MessageHeaderV2)) - break; + if (header->version == 0) { + if (header->num_bytes != sizeof(internal::MessageHeader)) { + internal::ReportValidationError( + validation_context, + internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; } - internal::ReportValidationError( - validation_context, - internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); - return false; - } while (false); + } else if (header->version == 1) { + if (header->num_bytes != sizeof(internal::MessageHeaderWithRequestID)) { + internal::ReportValidationError( + validation_context, + internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; + } + } else if (header->version > 1) { + if (header->num_bytes < sizeof(internal::MessageHeaderWithRequestID)) { + internal::ReportValidationError( + validation_context, + internal::VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER); + return false; + } + } // Validate flags (allow unknown bits): // These flags require a RequestID. - constexpr uint32_t kRequestIdFlags = - Message::kFlagExpectsResponse | Message::kFlagIsResponse; - if (header->version == 0 && (header->flags & kRequestIdFlags)) { + if (header->version < 1 && ((header->flags & Message::kFlagExpectsResponse) || + (header->flags & Message::kFlagIsResponse))) { internal::ReportValidationError( validation_context, internal::VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID); @@ -54,60 +52,25 @@ bool IsValidMessageHeader(const internal::MessageHeader* header, } // These flags are mutually exclusive. - if ((header->flags & kRequestIdFlags) == kRequestIdFlags) { + if ((header->flags & Message::kFlagExpectsResponse) && + (header->flags & Message::kFlagIsResponse)) { internal::ReportValidationError( validation_context, internal::VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS); return false; } - if (header->version < 2) - return true; - - auto* header_v2 = static_cast(header); - // For the payload pointer: - // - Check that the pointer can be safely decoded. - // - Claim one byte that the pointer points to. It makes sure not only the - // address is within the message, but also the address precedes the array - // storing interface IDs (which is important for safely calculating the - // payload size). - // - Validation of the payload contents will be done separately based on the - // payload type. - if (!header_v2->payload.is_null() && - (!internal::ValidatePointer(header_v2->payload, validation_context) || - !validation_context->ClaimMemory(header_v2->payload.Get(), 1))) { - return false; - } - - const internal::ContainerValidateParams validate_params(0, false, nullptr); - if (!internal::ValidateContainer(header_v2->payload_interface_ids, - validation_context, &validate_params)) { - return false; - } - - if (!header_v2->payload_interface_ids.is_null()) { - size_t num_ids = header_v2->payload_interface_ids.Get()->size(); - const uint32_t* ids = header_v2->payload_interface_ids.Get()->storage(); - for (size_t i = 0; i < num_ids; ++i) { - if (!IsValidInterfaceId(ids[i]) || IsMasterInterfaceId(ids[i])) { - internal::ReportValidationError( - validation_context, - internal::VALIDATION_ERROR_ILLEGAL_INTERFACE_ID); - return false; - } - } - } - return true; } } // namespace -MessageHeaderValidator::MessageHeaderValidator() - : MessageHeaderValidator("MessageHeaderValidator") {} +MessageHeaderValidator::MessageHeaderValidator(MessageReceiver* sink) + : MessageHeaderValidator("MessageHeaderValidator", sink) {} -MessageHeaderValidator::MessageHeaderValidator(const std::string& description) - : description_(description) { +MessageHeaderValidator::MessageHeaderValidator(const std::string& description, + MessageReceiver* sink) + : MessageFilter(sink), description_(description) { } void MessageHeaderValidator::SetDescription(const std::string& description) { @@ -115,10 +78,10 @@ void MessageHeaderValidator::SetDescription(const std::string& description) { } bool MessageHeaderValidator::Accept(Message* message) { - // Pass 0 as number of handles and associated endpoint handles because we - // don't expect any in the header, even if |message| contains handles. + // Pass 0 as number of handles because we don't expect any in the header, even + // if |message| contains handles. internal::ValidationContext validation_context( - message->data(), message->data_num_bytes(), 0, 0, message, description_); + message->data(), message->data_num_bytes(), 0, message, description_); if (!internal::ValidateStructHeaderAndClaimMemory(message->data(), &validation_context)) @@ -127,7 +90,7 @@ bool MessageHeaderValidator::Accept(Message* message) { if (!IsValidMessageHeader(message->header(), &validation_context)) return false; - return true; + return sink_->Accept(message); } } // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/message_internal.h b/mojo/public/cpp/bindings/lib/message_internal.h index 6693198..63edffd 100644 --- a/mojo/public/cpp/bindings/lib/message_internal.h +++ b/mojo/public/cpp/bindings/lib/message_internal.h @@ -7,22 +7,11 @@ #include -#include - -#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 -class Array_Data; - #pragma pack(push, 1) struct MessageHeader : internal::StructHeader { @@ -38,44 +27,16 @@ struct MessageHeader : internal::StructHeader { }; static_assert(sizeof(MessageHeader) == 24, "Bad sizeof(MessageHeader)"); -struct MessageHeaderV1 : MessageHeader { +struct MessageHeaderWithRequestID : MessageHeader { // Only used if either kFlagExpectsResponse or kFlagIsResponse is set in // order to match responses with corresponding requests. uint64_t request_id; }; -static_assert(sizeof(MessageHeaderV1) == 32, "Bad sizeof(MessageHeaderV1)"); - -struct MessageHeaderV2 : MessageHeaderV1 { - MessageHeaderV2(); - GenericPointer payload; - Pointer> payload_interface_ids; -}; -static_assert(sizeof(MessageHeaderV2) == 48, "Bad sizeof(MessageHeaderV2)"); +static_assert(sizeof(MessageHeaderWithRequestID) == 32, + "Bad sizeof(MessageHeaderWithRequestID)"); #pragma pack(pop) -class MOJO_CPP_BINDINGS_EXPORT MessageDispatchContext { - public: - explicit MessageDispatchContext(Message* message); - ~MessageDispatchContext(); - - static MessageDispatchContext* current(); - - const base::Callback& GetBadMessageCallback(); - - private: - MessageDispatchContext* outer_context_; - Message* message_; - base::Callback bad_message_callback_; - - DISALLOW_COPY_AND_ASSIGN(MessageDispatchContext); -}; - -class MOJO_CPP_BINDINGS_EXPORT SyncMessageResponseSetup { - public: - static void SetCurrentSyncResponseMessage(Message* message); -}; - } // namespace internal } // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.cc b/mojo/public/cpp/bindings/lib/multiplex_router.cc index 2da459a..dcfbab1 100644 --- a/mojo/public/cpp/bindings/lib/multiplex_router.cc +++ b/mojo/public/cpp/bindings/lib/multiplex_router.cc @@ -15,9 +15,9 @@ #include "base/single_thread_task_runner.h" #include "base/stl_util.h" #include "base/threading/thread_task_runner_handle.h" +#include "mojo/public/cpp/bindings/associated_group.h" #include "mojo/public/cpp/bindings/interface_endpoint_client.h" #include "mojo/public/cpp/bindings/interface_endpoint_controller.h" -#include "mojo/public/cpp/bindings/lib/may_auto_lock.h" #include "mojo/public/cpp/bindings/sync_handle_watcher.h" namespace mojo { @@ -36,7 +36,6 @@ class MultiplexRouter::InterfaceEndpoint id_(id), closed_(false), peer_closed_(false), - handle_created_(false), client_(nullptr), event_signalled_(false) {} @@ -51,31 +50,16 @@ class MultiplexRouter::InterfaceEndpoint bool closed() const { return closed_; } void set_closed() { - router_->AssertLockAcquired(); + router_->lock_.AssertAcquired(); closed_ = true; } bool peer_closed() const { return peer_closed_; } void set_peer_closed() { - router_->AssertLockAcquired(); + router_->lock_.AssertAcquired(); peer_closed_ = true; } - bool handle_created() const { return handle_created_; } - void set_handle_created() { - router_->AssertLockAcquired(); - handle_created_ = true; - } - - const base::Optional& disconnect_reason() const { - return disconnect_reason_; - } - void set_disconnect_reason( - const base::Optional& disconnect_reason) { - router_->AssertLockAcquired(); - disconnect_reason_ = disconnect_reason; - } - base::SingleThreadTaskRunner* task_runner() const { return task_runner_.get(); } @@ -84,7 +68,7 @@ class MultiplexRouter::InterfaceEndpoint void AttachClient(InterfaceEndpointClient* client, scoped_refptr runner) { - router_->AssertLockAcquired(); + router_->lock_.AssertAcquired(); DCHECK(!client_); DCHECK(!closed_); DCHECK(runner->BelongsToCurrentThread()); @@ -96,7 +80,7 @@ class MultiplexRouter::InterfaceEndpoint // This method must be called on the same thread as the corresponding // AttachClient() call. void DetachClient() { - router_->AssertLockAcquired(); + router_->lock_.AssertAcquired(); DCHECK(client_); DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(!closed_); @@ -107,36 +91,18 @@ class MultiplexRouter::InterfaceEndpoint } void SignalSyncMessageEvent() { - router_->AssertLockAcquired(); + router_->lock_.AssertAcquired(); if (event_signalled_) return; + EnsureEventMessagePipeExists(); event_signalled_ = true; - if (!sync_message_event_sender_.is_valid()) - return; - MojoResult result = WriteMessageRaw(sync_message_event_sender_.get(), nullptr, 0, nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE); DCHECK_EQ(MOJO_RESULT_OK, result); } - void ResetSyncMessageSignal() { - router_->AssertLockAcquired(); - - if (!event_signalled_) - return; - - event_signalled_ = false; - if (!sync_message_event_receiver_.is_valid()) - return; - - MojoResult result = - ReadMessageRaw(sync_message_event_receiver_.get(), nullptr, nullptr, - nullptr, nullptr, MOJO_READ_MESSAGE_FLAG_MAY_DISCARD); - DCHECK_EQ(MOJO_RESULT_OK, result); - } - // --------------------------------------------------------------------------- // The following public methods (i.e., InterfaceEndpointController // implementation) are called by the client on the same thread as the @@ -166,7 +132,7 @@ class MultiplexRouter::InterfaceEndpoint friend class base::RefCounted; ~InterfaceEndpoint() override { - router_->AssertLockAcquired(); + router_->lock_.AssertAcquired(); DCHECK(!client_); DCHECK(closed_); @@ -176,23 +142,26 @@ class MultiplexRouter::InterfaceEndpoint void OnHandleReady(MojoResult result) { DCHECK(task_runner_->BelongsToCurrentThread()); + scoped_refptr self_protector(this); scoped_refptr router_protector(router_); // Because we never close |sync_message_event_{sender,receiver}_| before // destruction or set a deadline, |result| should always be MOJO_RESULT_OK. DCHECK_EQ(MOJO_RESULT_OK, result); + bool reset_sync_watcher = false; + { + base::AutoLock locker(router_->lock_); - MayAutoLock locker(&router_->lock_); - scoped_refptr self_protector(this); - - bool more_to_process = router_->ProcessFirstSyncMessageForEndpoint(id_); + bool more_to_process = router_->ProcessFirstSyncMessageForEndpoint(id_); - if (!more_to_process) - ResetSyncMessageSignal(); + if (!more_to_process) + ResetSyncMessageSignal(); - // Currently there are no queued sync messages and the peer has closed so - // there won't be incoming sync messages in the future. - if (!more_to_process && peer_closed_) { + // Currently there are no queued sync messages and the peer has closed so + // there won't be incoming sync messages in the future. + reset_sync_watcher = !more_to_process && peer_closed_; + } + if (reset_sync_watcher) { // If a SyncWatch() call (or multiple ones) of this interface endpoint is // on the call stack, resetting the sync watcher will allow it to exit // when the call stack unwinds to that frame. @@ -206,21 +175,12 @@ class MultiplexRouter::InterfaceEndpoint return; { - 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(); - } - } + 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(); } sync_watcher_.reset(new SyncHandleWatcher( @@ -228,6 +188,31 @@ 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. @@ -242,12 +227,6 @@ 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 disconnect_reason_; - // The task runner on which |client_|'s methods can be called. scoped_refptr task_runner_; // Not owned. It is null if no client is attached to this endpoint. @@ -271,53 +250,13 @@ 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 CreateMessageTask( - MessageWrapper message_wrapper) { + static std::unique_ptr CreateMessageTask(Message* message) { Task* task = new Task(MESSAGE); - task->message_wrapper = std::move(message_wrapper); + task->message.reset(new Message); + message->MoveTo(task->message.get()); return base::WrapUnique(task); } static std::unique_ptr CreateNotifyErrorTask( @@ -332,7 +271,7 @@ struct MultiplexRouter::Task { bool IsMessageTask() const { return type == MESSAGE; } bool IsNotifyErrorTask() const { return type == NOTIFY_ERROR; } - MessageWrapper message_wrapper; + std::unique_ptr message; scoped_refptr endpoint_to_notify; enum Type { MESSAGE, NOTIFY_ERROR }; @@ -340,56 +279,36 @@ struct MultiplexRouter::Task { private: explicit Task(Type in_type) : type(in_type) {} - - DISALLOW_COPY_AND_ASSIGN(Task); }; MultiplexRouter::MultiplexRouter( - ScopedMessagePipeHandle message_pipe, - Config config, bool set_interface_id_namesapce_bit, + ScopedMessagePipeHandle message_pipe, scoped_refptr runner) - : set_interface_id_namespace_bit_(set_interface_id_namesapce_bit), - task_runner_(runner), - header_validator_(nullptr), - filters_(this), + : AssociatedGroupController(base::ThreadTaskRunnerHandle::Get()), + set_interface_id_namespace_bit_(set_interface_id_namesapce_bit), + header_validator_(this), connector_(std::move(message_pipe), - config == MULTI_INTERFACE ? Connector::MULTI_THREADED_SEND - : Connector::SINGLE_THREADED_SEND, + Connector::MULTI_THREADED_SEND, std::move(runner)), control_message_handler_(this), control_message_proxy_(&connector_), next_interface_id_value_(1), posted_to_process_tasks_(false), encountered_error_(false), - paused_(false), testing_mode_(false) { - DCHECK(task_runner_->BelongsToCurrentThread()); - - if (config == MULTI_INTERFACE) - lock_.emplace(); - - if (config == SINGLE_INTERFACE_WITH_SYNC_METHODS || - config == MULTI_INTERFACE) { - // Always participate in sync handle watching in multi-interface mode, - // because even if it doesn't expect sync requests during sync handle - // watching, it may still need to dispatch messages to associated endpoints - // on a different thread. - connector_.AllowWokenUpBySyncWatchOnSameThread(); - } - connector_.set_incoming_receiver(&filters_); + // Always participate in sync handle watching, because even if it doesn't + // expect sync requests during sync handle watching, it may still need to + // dispatch messages to associated endpoints on a different thread. + connector_.AllowWokenUpBySyncWatchOnSameThread(); + connector_.set_incoming_receiver(&header_validator_); connector_.set_connection_error_handler( base::Bind(&MultiplexRouter::OnPipeConnectionError, base::Unretained(this))); - - std::unique_ptr header_validator = - base::MakeUnique(); - header_validator_ = header_validator.get(); - filters_.Append(std::move(header_validator)); } MultiplexRouter::~MultiplexRouter() { - MayAutoLock locker(&lock_); + base::AutoLock locker(lock_); sync_message_tasks_.clear(); tasks_.clear(); @@ -400,66 +319,40 @@ MultiplexRouter::~MultiplexRouter() { // because it may remove the corresponding value from the map. ++iter; - if (!endpoint->closed()) { - // This happens when a NotifyPeerEndpointClosed message been received, but - // the interface ID hasn't been used to create local endpoint handle. - DCHECK(!endpoint->client()); - DCHECK(endpoint->peer_closed()); - UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED); - } else { - UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED); - } + DCHECK(endpoint->closed()); + UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED); } DCHECK(endpoints_.empty()); } -void MultiplexRouter::SetMasterInterfaceName(const char* name) { +void MultiplexRouter::SetMasterInterfaceName(const std::string& name) { DCHECK(thread_checker_.CalledOnValidThread()); - header_validator_->SetDescription( - std::string(name) + " [master] MessageHeaderValidator"); + header_validator_.SetDescription(name + " [master] MessageHeaderValidator"); control_message_handler_.SetDescription( - std::string(name) + " [master] PipeControlMessageHandler"); - connector_.SetWatcherHeapProfilerTag(name); + name + " [master] PipeControlMessageHandler"); } -InterfaceId MultiplexRouter::AssociateInterface( - ScopedInterfaceEndpointHandle handle_to_send) { - if (!handle_to_send.pending_association()) - return kInvalidInterfaceId; - +void MultiplexRouter::CreateEndpointHandlePair( + ScopedInterfaceEndpointHandle* local_endpoint, + ScopedInterfaceEndpointHandle* remote_endpoint) { + base::AutoLock locker(lock_); uint32_t id = 0; - { - MayAutoLock locker(&lock_); - do { - if (next_interface_id_value_ >= kInterfaceIdNamespaceMask) - next_interface_id_value_ = 1; - id = next_interface_id_value_++; - if (set_interface_id_namespace_bit_) - id |= kInterfaceIdNamespaceMask; - } while (base::ContainsKey(endpoints_, id)); - - 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); - } + 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); - control_message_proxy_.NotifyPeerEndpointClosed( - id, handle_to_send.disconnect_reason()); - } - return id; + *local_endpoint = CreateScopedInterfaceEndpointHandle(id, true); + *remote_endpoint = CreateScopedInterfaceEndpointHandle(id, false); } ScopedInterfaceEndpointHandle MultiplexRouter::CreateLocalEndpointHandle( @@ -467,12 +360,10 @@ ScopedInterfaceEndpointHandle MultiplexRouter::CreateLocalEndpointHandle( if (!IsValidInterfaceId(id)) return ScopedInterfaceEndpointHandle(); - MayAutoLock locker(&lock_); + base::AutoLock locker(lock_); bool inserted = false; InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, &inserted); if (inserted) { - DCHECK(!endpoint->handle_created()); - if (encountered_error_) UpdateEndpointStateMayRemove(endpoint, PEER_ENDPOINT_CLOSED); } else { @@ -480,32 +371,34 @@ ScopedInterfaceEndpointHandle MultiplexRouter::CreateLocalEndpointHandle( // notification that the peer endpoint has closed. CHECK(!endpoint->closed()); CHECK(endpoint->peer_closed()); - - if (endpoint->handle_created()) - return ScopedInterfaceEndpointHandle(); } - - endpoint->set_handle_created(); - return CreateScopedInterfaceEndpointHandle(id); + return CreateScopedInterfaceEndpointHandle(id, true); } -void MultiplexRouter::CloseEndpointHandle( - InterfaceId id, - const base::Optional& reason) { +void MultiplexRouter::CloseEndpointHandle(InterfaceId id, bool is_local) { if (!IsValidInterfaceId(id)) return; - MayAutoLock locker(&lock_); - DCHECK(base::ContainsKey(endpoints_, id)); + base::AutoLock locker(lock_); + + if (!is_local) { + DCHECK(ContainsKey(endpoints_, id)); + DCHECK(!IsMasterInterfaceId(id)); + + // We will receive a NotifyPeerEndpointClosed message from the other side. + control_message_proxy_.NotifyEndpointClosedBeforeSent(id); + + return; + } + + DCHECK(ContainsKey(endpoints_, id)); InterfaceEndpoint* endpoint = endpoints_[id].get(); DCHECK(!endpoint->client()); DCHECK(!endpoint->closed()); UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED); - if (!IsMasterInterfaceId(id) || reason) { - MayAutoUnlock unlocker(&lock_); - control_message_proxy_.NotifyPeerEndpointClosed(id, reason); - } + if (!IsMasterInterfaceId(id)) + control_message_proxy_.NotifyPeerEndpointClosed(id); ProcessTasks(NO_DIRECT_CLIENT_CALLS, nullptr); } @@ -519,8 +412,8 @@ InterfaceEndpointController* MultiplexRouter::AttachEndpointClient( DCHECK(IsValidInterfaceId(id)); DCHECK(client); - MayAutoLock locker(&lock_); - DCHECK(base::ContainsKey(endpoints_, id)); + base::AutoLock locker(lock_); + DCHECK(ContainsKey(endpoints_, id)); InterfaceEndpoint* endpoint = endpoints_[id].get(); endpoint->AttachClient(client, std::move(runner)); @@ -538,8 +431,8 @@ void MultiplexRouter::DetachEndpointClient( DCHECK(IsValidInterfaceId(id)); - MayAutoLock locker(&lock_); - DCHECK(base::ContainsKey(endpoints_, id)); + base::AutoLock locker(lock_); + DCHECK(ContainsKey(endpoints_, id)); InterfaceEndpoint* endpoint = endpoints_[id].get(); endpoint->DetachClient(); @@ -563,51 +456,21 @@ 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()); - MayAutoLock locker(&lock_); + base::AutoLock locker(lock_); if (endpoints_.size() > 1) return true; if (endpoints_.size() == 0) return false; - return !base::ContainsKey(endpoints_, kMasterInterfaceId); + return !ContainsKey(endpoints_, kMasterInterfaceId); } void MultiplexRouter::EnableTestingMode() { DCHECK(thread_checker_.CalledOnValidThread()); - MayAutoLock locker(&lock_); + base::AutoLock locker(lock_); testing_mode_ = true; connector_.set_enforce_errors_from_incoming_receiver(false); @@ -616,13 +479,8 @@ void MultiplexRouter::EnableTestingMode() { bool MultiplexRouter::Accept(Message* message) { DCHECK(thread_checker_.CalledOnValidThread()); - if (!message->DeserializeAssociatedEndpointHandles(this)) - return false; - scoped_refptr protector(this); - MayAutoLock locker(&lock_); - - DCHECK(!paused_); + base::AutoLock locker(lock_); ClientCallBehavior client_call_behavior = connector_.during_sync_handle_watcher_callback() @@ -636,16 +494,15 @@ 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(MessageWrapper(this, std::move(*message)))); + tasks_.push_back(Task::CreateMessageTask(message)); Task* task = tasks_.back().get(); - if (task->message_wrapper.value().has_flag(Message::kFlagIsSync)) { - InterfaceId id = task->message_wrapper.value().interface_id(); + if (task->message->has_flag(Message::kFlagIsSync)) { + InterfaceId id = task->message->interface_id(); sync_message_tasks_[id].push_back(task); - InterfaceEndpoint* endpoint = FindEndpoint(id); - if (endpoint) - endpoint->SignalSyncMessageEvent(); + auto iter = endpoints_.find(id); + if (iter != endpoints_.end()) + iter->second->SignalSyncMessageEvent(); } } else if (!tasks_.empty()) { // Processing the message may result in new tasks (for error notification) @@ -659,16 +516,13 @@ bool MultiplexRouter::Accept(Message* message) { return true; } -bool MultiplexRouter::OnPeerAssociatedEndpointClosed( - InterfaceId id, - const base::Optional& reason) { - DCHECK(!IsMasterInterfaceId(id) || reason); +bool MultiplexRouter::OnPeerAssociatedEndpointClosed(InterfaceId id) { + lock_.AssertAcquired(); - MayAutoLock locker(&lock_); - InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, nullptr); + if (IsMasterInterfaceId(id)) + return false; - if (reason) - endpoint->set_disconnect_reason(reason); + InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, nullptr); // 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 @@ -687,11 +541,26 @@ bool MultiplexRouter::OnPeerAssociatedEndpointClosed( 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 protector(this); - MayAutoLock locker(&lock_); + base::AutoLock locker(lock_); encountered_error_ = true; @@ -716,21 +585,20 @@ void MultiplexRouter::OnPipeConnectionError() { void MultiplexRouter::ProcessTasks( ClientCallBehavior client_call_behavior, base::SingleThreadTaskRunner* current_task_runner) { - AssertLockAcquired(); + lock_.AssertAcquired(); if (posted_to_process_tasks_) return; - while (!tasks_.empty() && !paused_) { + while (!tasks_.empty()) { std::unique_ptr task(std::move(tasks_.front())); tasks_.pop_front(); InterfaceId id = kInvalidInterfaceId; - bool sync_message = - task->IsMessageTask() && !task->message_wrapper.value().IsNull() && - task->message_wrapper.value().has_flag(Message::kFlagIsSync); + bool sync_message = task->IsMessageTask() && task->message && + task->message->has_flag(Message::kFlagIsSync); if (sync_message) { - id = task->message_wrapper.value().interface_id(); + id = task->message->interface_id(); auto& sync_message_queue = sync_message_tasks_[id]; DCHECK_EQ(task.get(), sync_message_queue.front()); sync_message_queue.pop_front(); @@ -740,8 +608,8 @@ void MultiplexRouter::ProcessTasks( task->IsNotifyErrorTask() ? ProcessNotifyErrorTask(task.get(), client_call_behavior, current_task_runner) - : ProcessIncomingMessage(&task->message_wrapper.value(), - client_call_behavior, current_task_runner); + : ProcessIncomingMessage(task->message.get(), client_call_behavior, + current_task_runner); if (!processed) { if (sync_message) { @@ -761,25 +629,21 @@ void MultiplexRouter::ProcessTasks( } bool MultiplexRouter::ProcessFirstSyncMessageForEndpoint(InterfaceId id) { - AssertLockAcquired(); + lock_.AssertAcquired(); auto iter = sync_message_tasks_.find(id); if (iter == sync_message_tasks_.end()) return false; - if (paused_) - return true; - MultiplexRouter::Task* task = iter->second.front(); iter->second.pop_front(); DCHECK(task->IsMessageTask()); - MessageWrapper message_wrapper = std::move(task->message_wrapper); + std::unique_ptr message(std::move(task->message)); - // Note: after this call, |task| and |iter| may be invalidated. + // Note: after this call, |task| and |iter| may be invalidated. bool processed = ProcessIncomingMessage( - &message_wrapper.value(), ALLOW_DIRECT_CLIENT_CALLS_FOR_SYNC_MESSAGES, - nullptr); + message.get(), ALLOW_DIRECT_CLIENT_CALLS_FOR_SYNC_MESSAGES, nullptr); DCHECK(processed); iter = sync_message_tasks_.find(id); @@ -799,9 +663,7 @@ bool MultiplexRouter::ProcessNotifyErrorTask( ClientCallBehavior client_call_behavior, base::SingleThreadTaskRunner* current_task_runner) { DCHECK(!current_task_runner || current_task_runner->BelongsToCurrentThread()); - DCHECK(!paused_); - - AssertLockAcquired(); + lock_.AssertAcquired(); InterfaceEndpoint* endpoint = task->endpoint_to_notify.get(); if (!endpoint->client()) return true; @@ -815,17 +677,14 @@ bool MultiplexRouter::ProcessNotifyErrorTask( DCHECK(endpoint->task_runner()->BelongsToCurrentThread()); InterfaceEndpointClient* client = endpoint->client(); - base::Optional disconnect_reason( - endpoint->disconnect_reason()); - { // We must unlock before calling into |client| because it may call this // object within NotifyError(). Holding the lock will lead to deadlock. // // It is safe to call into |client| without the lock. Because |client| is // always accessed on the same thread, including DetachEndpointClient(). - MayAutoUnlock unlocker(&lock_); - client->NotifyError(disconnect_reason); + base::AutoUnlock unlocker(lock_); + client->NotifyError(); } return true; } @@ -835,35 +694,47 @@ bool MultiplexRouter::ProcessIncomingMessage( ClientCallBehavior client_call_behavior, base::SingleThreadTaskRunner* current_task_runner) { DCHECK(!current_task_runner || current_task_runner->BelongsToCurrentThread()); - DCHECK(!paused_); - DCHECK(message); - AssertLockAcquired(); + lock_.AssertAcquired(); - if (message->IsNull()) { + if (!message) { // This is a sync message and has been processed during sync handle // watching. return true; } if (PipeControlMessageHandler::IsPipeControlMessage(message)) { - bool result = false; - - { - MayAutoUnlock unlocker(&lock_); - result = control_message_handler_.Accept(message); - } - - if (!result) + if (!control_message_handler_.Accept(message)) RaiseErrorInNonTestingMode(); - return true; } InterfaceId id = message->interface_id(); DCHECK(IsValidInterfaceId(id)); - InterfaceEndpoint* endpoint = FindEndpoint(id); - if (!endpoint || endpoint->closed()) + bool inserted = false; + InterfaceEndpoint* endpoint = FindOrInsertEndpoint(id, &inserted); + if (inserted) { + // Currently, it is legitimate to receive messages for an endpoint + // that is not registered. For example, the endpoint is transferred in + // a message that is discarded. Once we add support to specify all + // enclosing endpoints in message header, we should be able to remove + // this. + UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED); + + // It is also possible that this newly-inserted endpoint is the master + // endpoint. When the master InterfacePtr/Binding goes away, the message + // pipe is closed and we explicitly trigger a pipe connection error. The + // error updates all the endpoints, including the master endpoint, with + // PEER_ENDPOINT_CLOSED and removes the master endpoint from the + // registration. We continue to process remaining tasks in the queue, as + // long as there are refs keeping the router alive. If there are remaining + // messages for the master endpoint, we will get here. + if (!IsMasterInterfaceId(id)) + control_message_proxy_.NotifyPeerEndpointClosed(id); + return true; + } + + if (endpoint->closed()) return true; if (!endpoint->client()) { @@ -897,7 +768,7 @@ bool MultiplexRouter::ProcessIncomingMessage( // // It is safe to call into |client| without the lock. Because |client| is // always accessed on the same thread, including DetachEndpointClient(). - MayAutoUnlock unlocker(&lock_); + base::AutoUnlock unlocker(lock_); result = client->HandleIncomingMessage(message); } if (!result) @@ -908,7 +779,7 @@ bool MultiplexRouter::ProcessIncomingMessage( void MultiplexRouter::MaybePostToProcessTasks( base::SingleThreadTaskRunner* task_runner) { - AssertLockAcquired(); + lock_.AssertAcquired(); if (posted_to_process_tasks_) return; @@ -921,7 +792,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. - MayAutoLock locker(&lock_); + base::AutoLock locker(lock_); posted_to_process_tasks_ = false; scoped_refptr runner( std::move(posted_to_task_runner_)); @@ -931,20 +802,23 @@ void MultiplexRouter::LockAndCallProcessTasks() { void MultiplexRouter::UpdateEndpointStateMayRemove( InterfaceEndpoint* endpoint, EndpointStateUpdateType type) { - if (type == ENDPOINT_CLOSED) { - endpoint->set_closed(); - } else { - endpoint->set_peer_closed(); - // If the interface endpoint is performing a sync watch, this makes sure - // it is notified and eventually exits the sync watch. - endpoint->SignalSyncMessageEvent(); + switch (type) { + case ENDPOINT_CLOSED: + endpoint->set_closed(); + break; + case PEER_ENDPOINT_CLOSED: + endpoint->set_peer_closed(); + // If the interface endpoint is performing a sync watch, this makes sure + // it is notified and eventually exits the sync watch. + endpoint->SignalSyncMessageEvent(); + break; } if (endpoint->closed() && endpoint->peer_closed()) endpoints_.erase(endpoint->id()); } void MultiplexRouter::RaiseErrorInNonTestingMode() { - AssertLockAcquired(); + lock_.AssertAcquired(); if (!testing_mode_) RaiseError(); } @@ -952,35 +826,24 @@ void MultiplexRouter::RaiseErrorInNonTestingMode() { MultiplexRouter::InterfaceEndpoint* MultiplexRouter::FindOrInsertEndpoint( InterfaceId id, bool* inserted) { - AssertLockAcquired(); + lock_.AssertAcquired(); // Either |inserted| is nullptr or it points to a boolean initialized as // false. DCHECK(!inserted || !*inserted); - InterfaceEndpoint* endpoint = FindEndpoint(id); - if (!endpoint) { + auto iter = endpoints_.find(id); + InterfaceEndpoint* endpoint; + if (iter == endpoints_.end()) { endpoint = new InterfaceEndpoint(this, id); endpoints_[id] = endpoint; if (inserted) *inserted = true; + } else { + endpoint = iter->second.get(); } return endpoint; } -MultiplexRouter::InterfaceEndpoint* MultiplexRouter::FindEndpoint( - InterfaceId id) { - AssertLockAcquired(); - auto iter = endpoints_.find(id); - return iter != endpoints_.end() ? iter->second.get() : nullptr; -} - -void MultiplexRouter::AssertLockAcquired() { -#if DCHECK_IS_ON() - if (lock_) - lock_->AssertAcquired(); -#endif -} - } // namespace internal } // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.h b/mojo/public/cpp/bindings/lib/multiplex_router.h index cac138b..dc66e8e 100644 --- a/mojo/public/cpp/bindings/lib/multiplex_router.h +++ b/mojo/public/cpp/bindings/lib/multiplex_router.h @@ -12,19 +12,15 @@ #include #include -#include "base/compiler_specific.h" #include "base/logging.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" -#include "base/optional.h" #include "base/single_thread_task_runner.h" #include "base/synchronization/lock.h" #include "base/threading/thread_checker.h" #include "mojo/public/cpp/bindings/associated_group_controller.h" -#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/connector.h" -#include "mojo/public/cpp/bindings/filter_chain.h" #include "mojo/public/cpp/bindings/interface_id.h" #include "mojo/public/cpp/bindings/message_header_validator.h" #include "mojo/public/cpp/bindings/pipe_control_message_handler.h" @@ -38,6 +34,8 @@ class SingleThreadTaskRunner; namespace mojo { +class AssociatedGroup; + namespace internal { // MultiplexRouter supports routing messages for multiple interfaces over a @@ -49,51 +47,31 @@ 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. -// -// NOTE: CloseMessagePipe() or PassMessagePipe() MUST be called on |runner|'s -// thread before this object is destroyed. -class MOJO_CPP_BINDINGS_EXPORT MultiplexRouter - : NON_EXPORTED_BASE(public MessageReceiver), +class MultiplexRouter + : public MessageReceiver, public AssociatedGroupController, - NON_EXPORTED_BASE(public PipeControlMessageHandlerDelegate) { + public PipeControlMessageHandlerDelegate { public: - enum Config { - // There is only the master interface running on this router. Please note - // that because of interface versioning, the other side of the message pipe - // may use a newer master interface definition which passes associated - // interfaces. In that case, this router may still receive pipe control - // messages or messages targetting associated interfaces. - SINGLE_INTERFACE, - // Similar to the mode above, there is only the master interface running on - // this router. Besides, the master interface has sync methods. - SINGLE_INTERFACE_WITH_SYNC_METHODS, - // There may be associated interfaces running on this router. - MULTI_INTERFACE - }; - // If |set_interface_id_namespace_bit| is true, the interface IDs generated by // this router will have the highest bit set. - MultiplexRouter(ScopedMessagePipeHandle message_pipe, - Config config, - bool set_interface_id_namespace_bit, + MultiplexRouter(bool set_interface_id_namespace_bit, + ScopedMessagePipeHandle message_pipe, scoped_refptr runner); // Sets the master interface name for this router. Only used when reporting // message header or control message validation errors. - // |name| must be a string literal. - void SetMasterInterfaceName(const char* name); + void SetMasterInterfaceName(const std::string& name); // --------------------------------------------------------------------------- // The following public methods are safe to call from any threads. // AssociatedGroupController implementation: - InterfaceId AssociateInterface( - ScopedInterfaceEndpointHandle handle_to_send) override; + void CreateEndpointHandlePair( + ScopedInterfaceEndpointHandle* local_endpoint, + ScopedInterfaceEndpointHandle* remote_endpoint) override; ScopedInterfaceEndpointHandle CreateLocalEndpointHandle( InterfaceId id) override; - void CloseEndpointHandle( - InterfaceId id, - const base::Optional& reason) override; + void CloseEndpointHandle(InterfaceId id, bool is_local) override; InterfaceEndpointController* AttachEndpointClient( const ScopedInterfaceEndpointHandle& handle, InterfaceEndpointClient* endpoint_client, @@ -124,8 +102,14 @@ class MOJO_CPP_BINDINGS_EXPORT MultiplexRouter } // See Binding for details of pause/resume. - void PauseIncomingMethodCallProcessing(); - void ResumeIncomingMethodCallProcessing(); + void PauseIncomingMethodCallProcessing() { + DCHECK(thread_checker_.CalledOnValidThread()); + connector_.PauseIncomingMethodCallProcessing(); + } + void ResumeIncomingMethodCallProcessing() { + DCHECK(thread_checker_.CalledOnValidThread()); + connector_.ResumeIncomingMethodCallProcessing(); + } // Whether there are any associated interfaces running currently. bool HasAssociatedEndpoints() const; @@ -147,13 +131,8 @@ class MOJO_CPP_BINDINGS_EXPORT MultiplexRouter return connector_.handle(); } - bool SimulateReceivingMessageForTesting(Message* message) { - return filters_.Accept(message); - } - private: class InterfaceEndpoint; - class MessageWrapper; struct Task; ~MultiplexRouter() override; @@ -162,9 +141,8 @@ class MOJO_CPP_BINDINGS_EXPORT MultiplexRouter bool Accept(Message* message) override; // PipeControlMessageHandlerDelegate implementation: - bool OnPeerAssociatedEndpointClosed( - InterfaceId id, - const base::Optional& reason) override; + bool OnPeerAssociatedEndpointClosed(InterfaceId id) override; + bool OnAssociatedEndpointClosedBeforeSent(InterfaceId id) override; void OnPipeConnectionError(); @@ -224,30 +202,19 @@ class MOJO_CPP_BINDINGS_EXPORT 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_; - scoped_refptr task_runner_; - - // Owned by |filters_| below. - MessageHeaderValidator* header_validator_; - - FilterChain filters_; + MessageHeaderValidator header_validator_; Connector connector_; base::ThreadChecker thread_checker_; // Protects the following members. - // Not set in Config::SINGLE_INTERFACE* mode. - mutable base::Optional lock_; + mutable base::Lock lock_; PipeControlMessageHandler control_message_handler_; - - // NOTE: It is unsafe to call into this object while holding |lock_|. PipeControlMessageProxy control_message_proxy_; std::map> endpoints_; @@ -262,8 +229,6 @@ class MOJO_CPP_BINDINGS_EXPORT 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 7b1a1a6..837b75a 100644 --- a/mojo/public/cpp/bindings/lib/native_struct.cc +++ b/mojo/public/cpp/bindings/lib/native_struct.cc @@ -4,31 +4,27 @@ #include "mojo/public/cpp/bindings/native_struct.h" -#include "mojo/public/cpp/bindings/lib/hash_util.h" - namespace mojo { // static NativeStructPtr NativeStruct::New() { - return NativeStructPtr(base::in_place); + NativeStructPtr rv; + internal::StructHelper::Initialize(&rv); + return rv; } -NativeStruct::NativeStruct() {} +NativeStruct::NativeStruct() : data(nullptr) {} NativeStruct::~NativeStruct() {} NativeStructPtr NativeStruct::Clone() const { NativeStructPtr rv(New()); - rv->data = data; + rv->data = data.Clone(); return rv; } bool NativeStruct::Equals(const NativeStruct& other) const { - return data == other.data; -} - -size_t NativeStruct::Hash(size_t seed) const { - return internal::Hash(seed, data); + return data.Equals(other.data); } } // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/native_struct_data.h b/mojo/public/cpp/bindings/lib/native_struct_data.h index 1c7cd81..5c58774 100644 --- a/mojo/public/cpp/bindings/lib/native_struct_data.h +++ b/mojo/public/cpp/bindings/lib/native_struct_data.h @@ -7,16 +7,16 @@ #include -#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/lib/array_internal.h" #include "mojo/public/cpp/system/handle.h" namespace mojo { namespace internal { +class Buffer; class ValidationContext; -class MOJO_CPP_BINDINGS_EXPORT NativeStruct_Data { +class NativeStruct_Data { public: static bool Validate(const void* data, ValidationContext* validation_context); diff --git a/mojo/public/cpp/bindings/lib/native_struct_serialization.cc b/mojo/public/cpp/bindings/lib/native_struct_serialization.cc index fa0dbf3..ac06059 100644 --- a/mojo/public/cpp/bindings/lib/native_struct_serialization.cc +++ b/mojo/public/cpp/bindings/lib/native_struct_serialization.cc @@ -15,8 +15,7 @@ size_t UnmappedNativeStructSerializerImpl::PrepareToSerialize( SerializationContext* context) { if (!input) return 0; - return internal::PrepareToSerialize>(input->data, - context); + return internal::PrepareToSerialize>(input->data, context); } // static @@ -32,8 +31,8 @@ void UnmappedNativeStructSerializerImpl::Serialize( Array_Data* data = nullptr; const ContainerValidateParams params(0, false, nullptr); - internal::Serialize>(input->data, buffer, &data, - ¶ms, context); + internal::Serialize>(input->data, buffer, &data, ¶ms, + context); *output = reinterpret_cast(data); } @@ -45,8 +44,7 @@ bool UnmappedNativeStructSerializerImpl::Deserialize( Array_Data* data = reinterpret_cast*>(input); NativeStructPtr result(NativeStruct::New()); - if (!internal::Deserialize>(data, &result->data, - context)) { + if (!internal::Deserialize>(data, &result->data, context)) { output = nullptr; return false; } diff --git a/mojo/public/cpp/bindings/lib/native_struct_serialization.h b/mojo/public/cpp/bindings/lib/native_struct_serialization.h index 457435b..e64b862 100644 --- a/mojo/public/cpp/bindings/lib/native_struct_serialization.h +++ b/mojo/public/cpp/bindings/lib/native_struct_serialization.h @@ -13,14 +13,12 @@ #include "base/logging.h" #include "base/pickle.h" #include "ipc/ipc_param_traits.h" -#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/lib/array_internal.h" #include "mojo/public/cpp/bindings/lib/bindings_internal.h" #include "mojo/public/cpp/bindings/lib/native_struct_data.h" #include "mojo/public/cpp/bindings/lib/serialization_forward.h" #include "mojo/public/cpp/bindings/lib/serialization_util.h" #include "mojo/public/cpp/bindings/native_struct.h" -#include "mojo/public/cpp/bindings/native_struct_data_view.h" namespace mojo { namespace internal { @@ -104,7 +102,7 @@ struct NativeStructSerializerImpl { } }; -struct MOJO_CPP_BINDINGS_EXPORT UnmappedNativeStructSerializerImpl { +struct UnmappedNativeStructSerializerImpl { static size_t PrepareToSerialize(const NativeStructPtr& input, SerializationContext* context); static void Serialize(const NativeStructPtr& input, @@ -125,7 +123,7 @@ struct NativeStructSerializerImpl : public UnmappedNativeStructSerializerImpl {}; template -struct Serializer +struct Serializer : public NativeStructSerializerImpl {}; } // namespace internal diff --git a/mojo/public/cpp/bindings/lib/no_interface.cc b/mojo/public/cpp/bindings/lib/no_interface.cc new file mode 100644 index 0000000..9e0945c --- /dev/null +++ b/mojo/public/cpp/bindings/lib/no_interface.cc @@ -0,0 +1,20 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/public/cpp/bindings/no_interface.h" + +namespace mojo { + +const char* NoInterface::Name_ = "mojo::NoInterface"; + +bool NoInterfaceStub::Accept(Message* message) { + return false; +} + +bool NoInterfaceStub::AcceptWithResponder(Message* message, + MessageReceiver* responder) { + return false; +} + +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc b/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc index d451c05..7ee9f8a 100644 --- a/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc +++ b/mojo/public/cpp/bindings/lib/pipe_control_message_handler.cc @@ -5,10 +5,8 @@ #include "mojo/public/cpp/bindings/pipe_control_message_handler.h" #include "base/logging.h" -#include "mojo/public/cpp/bindings/interface_id.h" #include "mojo/public/cpp/bindings/lib/message_builder.h" #include "mojo/public/cpp/bindings/lib/serialization.h" -#include "mojo/public/cpp/bindings/lib/serialization_context.h" #include "mojo/public/cpp/bindings/lib/validation_context.h" #include "mojo/public/cpp/bindings/lib/validation_util.h" #include "mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h" @@ -43,9 +41,8 @@ bool PipeControlMessageHandler::Accept(Message* message) { } bool PipeControlMessageHandler::Validate(Message* message) { - internal::ValidationContext validation_context(message->payload(), - message->payload_num_bytes(), - 0, 0, message, description_); + internal::ValidationContext validation_context( + message->data(), message->data_num_bytes(), 0, message, description_); if (message->name() == pipe_control::kRunOrClosePipeMessageId) { if (!internal::ValidateMessageIsRequestWithoutResponse( @@ -61,25 +58,22 @@ bool PipeControlMessageHandler::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( - params, ¶ms_ptr, &context); + internal::Deserialize( + params, ¶ms_ptr, &context_); if (params_ptr->input->is_peer_associated_endpoint_closed_event()) { - const auto& event = - params_ptr->input->get_peer_associated_endpoint_closed_event(); - - base::Optional reason; - if (event->disconnect_reason) { - reason.emplace(event->disconnect_reason->custom_reason, - event->disconnect_reason->description); - } - return delegate_->OnPeerAssociatedEndpointClosed(event->id, reason); + return delegate_->OnPeerAssociatedEndpointClosed( + params_ptr->input->get_peer_associated_endpoint_closed_event()->id); + } + if (params_ptr->input->is_associated_endpoint_closed_before_sent_event()) { + return delegate_->OnAssociatedEndpointClosedBeforeSent( + params_ptr->input->get_associated_endpoint_closed_before_sent_event() + ->id); } DVLOG(1) << "Unsupported command in a RunOrClosePipe message pipe control " diff --git a/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc b/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc index 701108e..55ee64b 100644 --- a/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc +++ b/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc @@ -11,28 +11,33 @@ #include "base/logging.h" #include "mojo/public/cpp/bindings/lib/message_builder.h" #include "mojo/public/cpp/bindings/lib/serialization.h" +#include "mojo/public/cpp/bindings/message.h" #include "mojo/public/interfaces/bindings/pipe_control_messages.mojom.h" namespace mojo { namespace { -Message ConstructRunOrClosePipeMessage( - pipe_control::RunOrClosePipeInputPtr input_ptr) { - internal::SerializationContext context; +void SendRunOrClosePipeMessage(MessageReceiver* receiver, + pipe_control::RunOrClosePipeInputPtr input, + internal::SerializationContext* context) { + pipe_control::RunOrClosePipeMessageParamsPtr params_ptr( + pipe_control::RunOrClosePipeMessageParams::New()); + params_ptr->input = std::move(input); - auto params_ptr = pipe_control::RunOrClosePipeMessageParams::New(); - params_ptr->input = std::move(input_ptr); - - size_t size = internal::PrepareToSerialize< - pipe_control::RunOrClosePipeMessageParamsDataView>(params_ptr, &context); - internal::MessageBuilder builder(pipe_control::kRunOrClosePipeMessageId, 0, - size, 0); + size_t size = + internal::PrepareToSerialize< + pipe_control::RunOrClosePipeMessageParamsPtr>(params_ptr, context); + internal::MessageBuilder builder(pipe_control::kRunOrClosePipeMessageId, + size); pipe_control::internal::RunOrClosePipeMessageParams_Data* params = nullptr; - internal::Serialize( - params_ptr, builder.buffer(), ¶ms, &context); + internal::Serialize( + params_ptr, builder.buffer(), ¶ms, context); builder.message()->set_interface_id(kInvalidInterfaceId); - return std::move(*builder.message()); + bool ok = receiver->Accept(builder.message()); + // This return value may be ignored as !ok implies the underlying message pipe + // has encountered an error, which will be visible through other means. + ALLOW_UNUSED_LOCAL(ok); } } // namespace @@ -40,30 +45,30 @@ Message ConstructRunOrClosePipeMessage( PipeControlMessageProxy::PipeControlMessageProxy(MessageReceiver* receiver) : receiver_(receiver) {} -void PipeControlMessageProxy::NotifyPeerEndpointClosed( - InterfaceId id, - const base::Optional& reason) { - Message message(ConstructPeerEndpointClosedMessage(id, reason)); - bool ok = receiver_->Accept(&message); - ALLOW_UNUSED_LOCAL(ok); +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_); } -// static -Message PipeControlMessageProxy::ConstructPeerEndpointClosedMessage( - InterfaceId id, - const base::Optional& reason) { - auto event = pipe_control::PeerAssociatedEndpointClosedEvent::New(); +void PipeControlMessageProxy::NotifyEndpointClosedBeforeSent(InterfaceId id) { + DCHECK(!IsMasterInterfaceId(id)); + pipe_control::AssociatedEndpointClosedBeforeSentEventPtr event( + pipe_control::AssociatedEndpointClosedBeforeSentEvent::New()); event->id = id; - if (reason) { - event->disconnect_reason = pipe_control::DisconnectReason::New(); - event->disconnect_reason->custom_reason = reason->custom_reason; - event->disconnect_reason->description = reason->description; - } - auto input = pipe_control::RunOrClosePipeInput::New(); - input->set_peer_associated_endpoint_closed_event(std::move(event)); + pipe_control::RunOrClosePipeInputPtr input( + pipe_control::RunOrClosePipeInput::New()); + input->set_associated_endpoint_closed_before_sent_event(std::move(event)); - return ConstructRunOrClosePipeMessage(std::move(input)); + SendRunOrClosePipeMessage(receiver_, std::move(input), &context_); } } // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/router.cc b/mojo/public/cpp/bindings/lib/router.cc new file mode 100644 index 0000000..8c1b77d --- /dev/null +++ b/mojo/public/cpp/bindings/lib/router.cc @@ -0,0 +1,323 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/public/cpp/bindings/lib/router.h" + +#include + +#include + +#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, + 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, + scoped_refptr 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_; + bool accept_was_invoked_; + scoped_refptr 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 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 sync_responder(responder); + sync_responses_.insert(std::make_pair( + request_id, base::WrapUnique(new SyncResponseInfo(&response_received)))); + + base::WeakPtr 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 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 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 weak_self = weak_factory_.GetWeakPtr(); + while (!pending_messages_.empty()) { + std::unique_ptr 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 responder = std::move(it->second); + async_responders_.erase(it); + return responder->Accept(message); + } else { + if (!incoming_receiver_) + return false; + + return incoming_receiver_->Accept(message); + } +} + +void Router::OnConnectionError() { + if (encountered_error_) + return; + + if (!pending_messages_.empty()) { + // After all the pending messages are processed, we will check whether an + // error has been encountered and run the user's connection error handler + // if necessary. + DCHECK(pending_task_for_messages_); + return; + } + + if (connector_.during_sync_handle_watcher_callback()) { + // We don't want the error handler to reenter an ongoing sync call. + connector_.task_runner()->PostTask( + FROM_HERE, + base::Bind(&Router::OnConnectionError, weak_factory_.GetWeakPtr())); + return; + } + + encountered_error_ = true; + if (!error_handler_.is_null()) + error_handler_.Run(); +} + +// ---------------------------------------------------------------------------- + +} // namespace internal +} // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/router.h b/mojo/public/cpp/bindings/lib/router.h new file mode 100644 index 0000000..6dbe08d --- /dev/null +++ b/mojo/public/cpp/bindings/lib/router.h @@ -0,0 +1,177 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_ + +#include + +#include +#include +#include + +#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 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>; + + struct SyncResponseInfo { + public: + explicit SyncResponseInfo(bool* in_response_received); + ~SyncResponseInfo(); + + std::unique_ptr response; + + // Points to a stack-allocated variable. + bool* response_received; + + private: + DISALLOW_COPY_AND_ASSIGN(SyncResponseInfo); + }; + + using SyncResponseMap = std::map>; + + 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> 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 weak_factory_; +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_ROUTER_H_ diff --git a/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc b/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc index c134507..f54c3f7 100644 --- a/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc +++ b/mojo/public/cpp/bindings/lib/scoped_interface_endpoint_handle.cc @@ -4,379 +4,69 @@ #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" -#include "base/bind.h" #include "base/logging.h" -#include "base/synchronization/lock.h" #include "mojo/public/cpp/bindings/associated_group_controller.h" -#include "mojo/public/cpp/bindings/lib/may_auto_lock.h" namespace mojo { -// ScopedInterfaceEndpointHandle::State ---------------------------------------- - -// State could be called from multiple threads. -class ScopedInterfaceEndpointHandle::State - : public base::RefCountedThreadSafe { - public: - State() = default; - - State(InterfaceId id, - scoped_refptr group_controller) - : id_(id), group_controller_(group_controller) {} - - void InitPendingState(scoped_refptr peer) { - DCHECK(!lock_); - DCHECK(!pending_association_); - - lock_.emplace(); - pending_association_ = true; - peer_state_ = std::move(peer); - } - - void Close(const base::Optional& reason) { - scoped_refptr cached_group_controller; - InterfaceId cached_id = kInvalidInterfaceId; - scoped_refptr 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 peer_group_controller) { - scoped_refptr 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& disconnect_reason() const { - internal::MayAutoLock locker(&lock_); - return disconnect_reason_; - } - - private: - friend class base::RefCountedThreadSafe; - - ~State() { - DCHECK(!pending_association_); - DCHECK(!IsValidInterfaceId(id_)); - } - - // Called by the peer, maybe from a different thread. - void OnAssociated(InterfaceId id, - scoped_refptr 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& 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 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 lock_; - - bool pending_association_ = false; - base::Optional disconnect_reason_; - - scoped_refptr peer_state_; - - AssociationEventCallback association_event_handler_; - scoped_refptr runner_; - - InterfaceId id_ = kInvalidInterfaceId; - scoped_refptr group_controller_; - - DISALLOW_COPY_AND_ASSIGN(State); -}; - -// ScopedInterfaceEndpointHandle ----------------------------------------------- - -// static -void ScopedInterfaceEndpointHandle::CreatePairPendingAssociation( - ScopedInterfaceEndpointHandle* handle0, - ScopedInterfaceEndpointHandle* handle1) { - ScopedInterfaceEndpointHandle result0; - ScopedInterfaceEndpointHandle result1; - result0.state_->InitPendingState(result1.state_); - result1.state_->InitPendingState(result0.state_); - - *handle0 = std::move(result0); - *handle1 = std::move(result1); -} - ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle() - : state_(new State) {} + : ScopedInterfaceEndpointHandle(kInvalidInterfaceId, true, nullptr) {} ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle( ScopedInterfaceEndpointHandle&& other) - : state_(new State) { - state_.swap(other.state_); + : id_(other.id_), is_local_(other.is_local_) { + group_controller_.swap(other.group_controller_); + other.id_ = kInvalidInterfaceId; } ScopedInterfaceEndpointHandle::~ScopedInterfaceEndpointHandle() { - state_->Close(base::nullopt); + reset(); } ScopedInterfaceEndpointHandle& ScopedInterfaceEndpointHandle::operator=( ScopedInterfaceEndpointHandle&& other) { reset(); - state_.swap(other.state_); - return *this; -} + swap(other); -bool ScopedInterfaceEndpointHandle::is_valid() const { - return state_->is_valid(); + return *this; } -bool ScopedInterfaceEndpointHandle::pending_association() const { - return state_->pending_association(); -} +void ScopedInterfaceEndpointHandle::reset() { + if (!IsValidInterfaceId(id_)) + return; -InterfaceId ScopedInterfaceEndpointHandle::id() const { - return state_->id(); -} + group_controller_->CloseEndpointHandle(id_, is_local_); -AssociatedGroupController* ScopedInterfaceEndpointHandle::group_controller() - const { - return state_->group_controller(); + id_ = kInvalidInterfaceId; + is_local_ = true; + group_controller_ = nullptr; } -const base::Optional& -ScopedInterfaceEndpointHandle::disconnect_reason() const { - return state_->disconnect_reason(); +void ScopedInterfaceEndpointHandle::swap(ScopedInterfaceEndpointHandle& other) { + using std::swap; + swap(other.id_, id_); + swap(other.is_local_, is_local_); + swap(other.group_controller_, group_controller_); } -void ScopedInterfaceEndpointHandle::SetAssociationEventHandler( - AssociationEventCallback handler) { - state_->SetAssociationEventHandler(std::move(handler)); -} +InterfaceId ScopedInterfaceEndpointHandle::release() { + InterfaceId result = id_; -void ScopedInterfaceEndpointHandle::reset() { - ResetInternal(base::nullopt); -} + id_ = kInvalidInterfaceId; + is_local_ = true; + group_controller_ = nullptr; -void ScopedInterfaceEndpointHandle::ResetWithReason( - uint32_t custom_reason, - const std::string& description) { - ResetInternal(DisconnectReason(custom_reason, description)); + return result; } ScopedInterfaceEndpointHandle::ScopedInterfaceEndpointHandle( InterfaceId id, + bool is_local, scoped_refptr group_controller) - : state_(new State(id, std::move(group_controller))) { - DCHECK(!IsValidInterfaceId(state_->id()) || state_->group_controller()); -} - -bool ScopedInterfaceEndpointHandle::NotifyAssociation( - InterfaceId id, - scoped_refptr peer_group_controller) { - return state_->NotifyAssociation(id, peer_group_controller); -} - -void ScopedInterfaceEndpointHandle::ResetInternal( - const base::Optional& reason) { - scoped_refptr new_state(new State); - state_->Close(reason); - state_.swap(new_state); -} - -base::Callback -ScopedInterfaceEndpointHandle::CreateGroupControllerGetter() const { - // We allow this callback to be run on any thread. If this handle is created - // in non-pending state, we don't have a lock but it should still be safe - // because the group controller never changes. - return base::Bind(&State::group_controller, state_); + : id_(id), + is_local_(is_local), + group_controller_(std::move(group_controller)) { + DCHECK(!IsValidInterfaceId(id) || group_controller_); } } // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/serialization.h b/mojo/public/cpp/bindings/lib/serialization.h index 359b02b..6d7dd8e 100644 --- a/mojo/public/cpp/bindings/lib/serialization.h +++ b/mojo/public/cpp/bindings/lib/serialization.h @@ -8,16 +8,19 @@ #include #include "mojo/public/cpp/bindings/array_traits_carray.h" +#include "mojo/public/cpp/bindings/array_traits_standard.h" #include "mojo/public/cpp/bindings/array_traits_stl.h" #include "mojo/public/cpp/bindings/lib/array_serialization.h" -#include "mojo/public/cpp/bindings/lib/buffer.h" +#include "mojo/public/cpp/bindings/lib/fixed_buffer.h" #include "mojo/public/cpp/bindings/lib/handle_interface_serialization.h" #include "mojo/public/cpp/bindings/lib/map_serialization.h" #include "mojo/public/cpp/bindings/lib/native_enum_serialization.h" #include "mojo/public/cpp/bindings/lib/native_struct_serialization.h" #include "mojo/public/cpp/bindings/lib/string_serialization.h" #include "mojo/public/cpp/bindings/lib/template_util.h" +#include "mojo/public/cpp/bindings/map_traits_standard.h" #include "mojo/public/cpp/bindings/map_traits_stl.h" +#include "mojo/public/cpp/bindings/string_traits_standard.h" #include "mojo/public/cpp/bindings/string_traits_stl.h" #include "mojo/public/cpp/bindings/string_traits_string16.h" #include "mojo/public/cpp/bindings/string_traits_string_piece.h" @@ -41,7 +44,7 @@ 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 std::vector. + // practice, this should never happen for mojo::Array (backed by std::vector). bool need_copy = !IsAligned(result_buffer); if (need_copy) { @@ -50,9 +53,9 @@ DataArrayType StructSerializeImpl(UserType* input) { DCHECK(IsAligned(result_buffer)); } - Buffer buffer; + FixedBuffer buffer; buffer.Initialize(result_buffer, size); - typename MojomTypeTraits::Data* data = nullptr; + typename MojomType::Struct::Data_* data = nullptr; Serialize(*input, &buffer, &data, &context); if (need_copy) { @@ -67,11 +70,13 @@ template bool StructDeserializeImpl(const DataArrayType& input, UserType* output) { static_assert(BelongsTo::value, "Unexpected type."); - using DataType = typename MojomTypeTraits::Data; + using DataType = typename MojomType::Struct::Data_; + + if (input.is_null()) + return false; - // TODO(sammc): Use DataArrayType::empty() once WTF::Vector::empty() exists. void* input_buffer = - input.size() == 0 + input.empty() ? nullptr : const_cast(reinterpret_cast(&input.front())); @@ -84,7 +89,7 @@ bool StructDeserializeImpl(const DataArrayType& input, UserType* output) { memcpy(input_buffer, &input.front(), input.size()); } - ValidationContext validation_context(input_buffer, input.size(), 0, 0); + ValidationContext validation_context(input_buffer, input.size(), 0); bool result = false; if (DataType::Validate(input_buffer, &validation_context)) { auto data = reinterpret_cast(input_buffer); diff --git a/mojo/public/cpp/bindings/lib/serialization_context.cc b/mojo/public/cpp/bindings/lib/serialization_context.cc index e2fd5c6..7fd80be 100644 --- a/mojo/public/cpp/bindings/lib/serialization_context.cc +++ b/mojo/public/cpp/bindings/lib/serialization_context.cc @@ -7,6 +7,7 @@ #include #include "base/logging.h" +#include "mojo/public/cpp/bindings/associated_group_controller.h" #include "mojo/public/cpp/system/core.h" namespace mojo { @@ -49,6 +50,10 @@ void SerializedHandleVector::Swap(std::vector* other) { SerializationContext::SerializationContext() {} +SerializationContext::SerializationContext( + scoped_refptr in_group_controller) + : group_controller(std::move(in_group_controller)) {} + SerializationContext::~SerializationContext() { DCHECK(!custom_contexts || custom_contexts->empty()); } diff --git a/mojo/public/cpp/bindings/lib/serialization_context.h b/mojo/public/cpp/bindings/lib/serialization_context.h index a34fe3d..64d2a1a 100644 --- a/mojo/public/cpp/bindings/lib/serialization_context.h +++ b/mojo/public/cpp/bindings/lib/serialization_context.h @@ -12,16 +12,18 @@ #include #include "base/macros.h" -#include "mojo/public/cpp/bindings/bindings_export.h" +#include "base/memory/ref_counted.h" #include "mojo/public/cpp/bindings/lib/bindings_internal.h" -#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" #include "mojo/public/cpp/system/handle.h" namespace mojo { + +class AssociatedGroupController; + namespace internal { // A container for handles during serialization/deserialization. -class MOJO_CPP_BINDINGS_EXPORT SerializedHandleVector { +class SerializedHandleVector { public: SerializedHandleVector(); ~SerializedHandleVector(); @@ -52,23 +54,21 @@ class MOJO_CPP_BINDINGS_EXPORT SerializedHandleVector { }; // Context information for serialization/deserialization routines. -struct MOJO_CPP_BINDINGS_EXPORT SerializationContext { +struct SerializationContext { SerializationContext(); + explicit SerializationContext( + scoped_refptr in_group_controller); ~SerializationContext(); + // Used to serialize/deserialize associated interface pointers and requests. + scoped_refptr group_controller; + // Opaque context pointers returned by StringTraits::SetUpContext(). std::unique_ptr> 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 associated_endpoint_handles; }; } // namespace internal diff --git a/mojo/public/cpp/bindings/lib/serialization_forward.h b/mojo/public/cpp/bindings/lib/serialization_forward.h index 55c9982..5bed126 100644 --- a/mojo/public/cpp/bindings/lib/serialization_forward.h +++ b/mojo/public/cpp/bindings/lib/serialization_forward.h @@ -12,7 +12,6 @@ #include "mojo/public/cpp/bindings/map_traits.h" #include "mojo/public/cpp/bindings/string_traits.h" #include "mojo/public/cpp/bindings/struct_traits.h" -#include "mojo/public/cpp/bindings/union_traits.h" // This file is included by serialization implementation files to avoid circular // includes. diff --git a/mojo/public/cpp/bindings/lib/string_serialization.h b/mojo/public/cpp/bindings/lib/string_serialization.h index 6e0c758..5e65891 100644 --- a/mojo/public/cpp/bindings/lib/string_serialization.h +++ b/mojo/public/cpp/bindings/lib/string_serialization.h @@ -11,14 +11,14 @@ #include "mojo/public/cpp/bindings/lib/array_internal.h" #include "mojo/public/cpp/bindings/lib/serialization_forward.h" #include "mojo/public/cpp/bindings/lib/serialization_util.h" -#include "mojo/public/cpp/bindings/string_data_view.h" +#include "mojo/public/cpp/bindings/string.h" #include "mojo/public/cpp/bindings/string_traits.h" namespace mojo { namespace internal { template -struct Serializer { +struct Serializer { using UserType = typename std::remove_const::type; using Traits = StringTraits; @@ -60,7 +60,7 @@ struct Serializer { SerializationContext* context) { if (!input) return CallSetToNullIfExists(output); - return Traits::Read(StringDataView(input, context), output); + return Traits::Read(StringDataView(input), output); } }; diff --git a/mojo/public/cpp/bindings/lib/string_traits_wtf.cc b/mojo/public/cpp/bindings/lib/string_traits_wtf.cc index 203f6f5..19fa907 100644 --- a/mojo/public/cpp/bindings/lib/string_traits_wtf.cc +++ b/mojo/public/cpp/bindings/lib/string_traits_wtf.cc @@ -16,7 +16,7 @@ namespace { struct UTF8AdaptorInfo { explicit UTF8AdaptorInfo(const WTF::String& input) : utf8_adaptor(input) { #if DCHECK_IS_ON() - original_size_in_bytes = input.charactersSizeInBytes(); + original_size_in_bytes = static_cast(input.sizeInBytes()); #endif } @@ -34,7 +34,8 @@ UTF8AdaptorInfo* ToAdaptor(const WTF::String& input, void* context) { UTF8AdaptorInfo* adaptor = static_cast(context); #if DCHECK_IS_ON() - DCHECK_EQ(adaptor->original_size_in_bytes, input.charactersSizeInBytes()); + DCHECK_EQ(adaptor->original_size_in_bytes, + static_cast(input.sizeInBytes())); #endif return adaptor; } diff --git a/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc b/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc index 585a8f0..3d864af 100644 --- a/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc +++ b/mojo/public/cpp/bindings/lib/sync_call_restrictions.cc @@ -6,7 +6,6 @@ #if ENABLE_SYNC_CALL_RESTRICTIONS -#include "base/debug/leak_annotations.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/threading/thread_local.h" @@ -38,7 +37,7 @@ class SyncCallSettings { size_t scoped_allow_count_ = 0; }; -base::LazyInstance>::DestructorAtExit +base::LazyInstance> g_sync_call_settings = LAZY_INSTANCE_INITIALIZER; // static @@ -46,7 +45,6 @@ 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 5ae763b..f6372d9 100644 --- a/mojo/public/cpp/bindings/lib/sync_handle_registry.cc +++ b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc @@ -13,7 +13,7 @@ namespace mojo { namespace { -base::LazyInstance>::Leaky +base::LazyInstance> 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 (base::ContainsKey(handles_, handle)) + if (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 (!base::ContainsKey(handles_, handle)) + if (!ContainsKey(handles_, handle)) return; MojoResult result = @@ -107,19 +107,6 @@ 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 new file mode 100644 index 0000000..d6b8c38 --- /dev/null +++ b/mojo/public/cpp/bindings/lib/sync_handle_registry.h @@ -0,0 +1,67 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_SYNC_HANDLE_REGISTRY_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_LIB_SYNC_HANDLE_REGISTRY_H_ + +#include + +#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 { + public: + // Returns a thread-local object. + static scoped_refptr current(); + + using HandleCallback = base::Callback; + 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; + + struct HandleHasher { + size_t operator()(const Handle& handle) const { + return std::hash()(static_cast(handle.value())); + } + }; + using HandleMap = std::unordered_map; + + SyncHandleRegistry(); + ~SyncHandleRegistry(); + + HandleMap handles_; + + ScopedHandle wait_set_handle_; + + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(SyncHandleRegistry); +}; + +} // namespace internal +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_SYNC_HANDLE_REGISTRY_H_ diff --git a/mojo/public/cpp/bindings/lib/validation_context.cc b/mojo/public/cpp/bindings/lib/validation_context.cc index ad0a364..a95e07e 100644 --- a/mojo/public/cpp/bindings/lib/validation_context.cc +++ b/mojo/public/cpp/bindings/lib/validation_context.cc @@ -4,7 +4,13 @@ #include "mojo/public/cpp/bindings/lib/validation_context.h" +#include +#include + #include "base/logging.h" +#include "mojo/public/cpp/bindings/lib/serialization_util.h" +#include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/system/handle.h" namespace mojo { namespace internal { @@ -12,39 +18,69 @@ 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, - int stack_depth) + const base::StringPiece& description) : message_(message), description_(description), data_begin_(reinterpret_cast(data)), data_end_(data_begin_ + data_num_bytes), handle_begin_(0), - handle_end_(static_cast(num_handles)), - associated_endpoint_handle_begin_(0), - associated_endpoint_handle_end_( - static_cast(num_associated_endpoint_handles)), - stack_depth_(stack_depth) { - // Check whether the calculation of |data_end_| or static_cast from size_t to - // uint32_t causes overflow. - // They shouldn't happen but they do, set the corresponding range to empty. + handle_end_(static_cast(num_handles)) { if (data_end_ < data_begin_) { + // The calculation of |data_end_| overflowed. + // It shouldn't happen but if it does, set the range to empty so + // IsValidRange() and ClaimMemory() always fail. NOTREACHED(); data_end_ = data_begin_; } if (handle_end_ < num_handles) { + // Assigning |num_handles| to |handle_end_| overflowed. + // It shouldn't happen but if it does, set the handle index range to empty. NOTREACHED(); handle_end_ = 0; } - if (associated_endpoint_handle_end_ < num_associated_endpoint_handles) { - NOTREACHED(); - associated_endpoint_handle_end_ = 0; - } } ValidationContext::~ValidationContext() { } +bool ValidationContext::ClaimMemory(const void* position, uint32_t num_bytes) { + uintptr_t begin = reinterpret_cast(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(position); + uintptr_t end = begin + num_bytes; + + return InternalIsValidRange(begin, end); +} + +bool ValidationContext::InternalIsValidRange(uintptr_t begin, + uintptr_t end) const { + return end > begin && begin >= data_begin_ && end <= data_end_; +} + } // namespace internal } // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/validation_context.h b/mojo/public/cpp/bindings/lib/validation_context.h index ed6c654..6045ca8 100644 --- a/mojo/public/cpp/bindings/lib/validation_context.h +++ b/mojo/public/cpp/bindings/lib/validation_context.h @@ -8,28 +8,23 @@ #include #include -#include "base/compiler_specific.h" #include "base/macros.h" #include "base/strings/string_piece.h" -#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/lib/bindings_internal.h" -static const int kMaxRecursionDepth = 100; - namespace mojo { +class Handle; class Message; namespace internal { // ValidationContext is used when validating object sizes, pointers and handle // indices in the payload of incoming messages. -class MOJO_CPP_BINDINGS_EXPORT ValidationContext { +class ValidationContext { public: // [data, data + data_num_bytes) specifies the initial valid memory range. // [0, num_handles) specifies the initial valid range of handle indices. - // [0, num_associated_endpoint_handles) specifies the initial valid range of - // associated endpoint handle indices. // // If provided, |message| and |description| provide additional information // to use when reporting validation errors. In addition if |message| is @@ -38,10 +33,8 @@ class MOJO_CPP_BINDINGS_EXPORT 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 = "", - int stack_depth = 0); + const base::StringPiece& description = ""); ~ValidationContext(); @@ -50,97 +43,24 @@ class MOJO_CPP_BINDINGS_EXPORT 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) { - uintptr_t begin = reinterpret_cast(position); - uintptr_t end = begin + num_bytes; - - if (!InternalIsValidRange(begin, end)) - return false; - - data_begin_ = end; - return true; - } + bool ClaimMemory(const void* position, uint32_t num_bytes); // Claims the specified encoded handle (which is basically a handle index). // The method succeeds if: // - |encoded_handle|'s value is |kEncodedInvalidHandleValue|. // - the handle is contained inside the valid range of handle indices. In this // case, the valid range is shinked to begin right after the claimed handle. - bool ClaimHandle(const Handle_Data& encoded_handle) { - uint32_t index = encoded_handle.value; - if (index == kEncodedInvalidHandleValue) - return true; - - if (index < handle_begin_ || index >= handle_end_) - return false; - - // |index| + 1 shouldn't overflow, because |index| is not the max value of - // uint32_t (it is less than |handle_end_|). - handle_begin_ = index + 1; - return true; - } - - // Claims the specified encoded associated endpoint handle. - // The method succeeds if: - // - |encoded_handle|'s value is |kEncodedInvalidHandleValue|. - // - the handle is contained inside the valid range of associated endpoint - // handle indices. In this case, the valid range is shinked to begin right - // after the claimed handle. - bool ClaimAssociatedEndpointHandle( - const AssociatedEndpointHandle_Data& encoded_handle) { - uint32_t index = encoded_handle.value; - if (index == kEncodedInvalidHandleValue) - return true; - - if (index < associated_endpoint_handle_begin_ || - index >= associated_endpoint_handle_end_) - return false; - - // |index| + 1 shouldn't overflow, because |index| is not the max value of - // uint32_t (it is less than |associated_endpoint_handle_end_|). - associated_endpoint_handle_begin_ = index + 1; - return true; - } + bool ClaimHandle(const Handle_Data& encoded_handle); // Returns true if the specified range is not empty, and the range is // contained inside the valid memory range. - bool IsValidRange(const void* position, uint32_t num_bytes) const { - uintptr_t begin = reinterpret_cast(position); - uintptr_t end = begin + num_bytes; - - return InternalIsValidRange(begin, end); - } - - // This object should be created on the stack once every time we recurse down - // into a subfield during validation to make sure we don't recurse too deep - // and blow the stack. - class ScopedDepthTracker { - public: - // |ctx| must outlive this object. - explicit ScopedDepthTracker(ValidationContext* ctx) : ctx_(ctx) { - ++ctx_->stack_depth_; - } - - ~ScopedDepthTracker() { --ctx_->stack_depth_; } - - private: - ValidationContext* ctx_; - - DISALLOW_COPY_AND_ASSIGN(ScopedDepthTracker); - }; - - // Returns true if the recursion depth limit has been reached. - bool ExceedsMaxDepth() WARN_UNUSED_RESULT { - return stack_depth_ > kMaxRecursionDepth; - } + bool IsValidRange(const void* position, uint32_t num_bytes) const; Message* message() const { return message_; } const base::StringPiece& description() const { return description_; } private: - bool InternalIsValidRange(uintptr_t begin, uintptr_t end) const { - return end > begin && begin >= data_begin_ && end <= data_end_; - } + bool InternalIsValidRange(uintptr_t begin, uintptr_t end) const; Message* const message_; const base::StringPiece description_; @@ -153,13 +73,6 @@ class MOJO_CPP_BINDINGS_EXPORT 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 904f5e4..90652de 100644 --- a/mojo/public/cpp/bindings/lib/validation_errors.cc +++ b/mojo/public/cpp/bindings/lib/validation_errors.cc @@ -14,7 +14,6 @@ namespace { ValidationErrorObserverForTesting* g_validation_error_observer = nullptr; SerializationWarningObserverForTesting* g_serialization_warning_observer = nullptr; -bool g_suppress_logging = false; } // namespace @@ -56,8 +55,6 @@ 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"; @@ -72,10 +69,8 @@ void ReportValidationError(ValidationContext* context, } if (description) { - if (!g_suppress_logging) { - LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error) - << " (" << description << ")"; - } + LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error) << " (" + << description << ")"; if (context->message()) { context->message()->NotifyBadMessage( base::StringPrintf("Validation failed for %s [%s (%s)]", @@ -83,8 +78,7 @@ void ReportValidationError(ValidationContext* context, ValidationErrorToString(error), description)); } } else { - if (!g_suppress_logging) - LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error); + LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error); if (context->message()) { context->message()->NotifyBadMessage( base::StringPrintf("Validation failed for %s [%s]", @@ -94,25 +88,6 @@ void 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 122418d..ec0aa27 100644 --- a/mojo/public/cpp/bindings/lib/validation_errors.h +++ b/mojo/public/cpp/bindings/lib/validation_errors.h @@ -8,13 +8,9 @@ #include "base/callback.h" #include "base/logging.h" #include "base/macros.h" -#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/lib/validation_context.h" namespace mojo { - -class Message; - namespace internal { enum ValidationError { @@ -71,41 +67,17 @@ 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, }; -MOJO_CPP_BINDINGS_EXPORT const char* ValidationErrorToString( - ValidationError error); - -MOJO_CPP_BINDINGS_EXPORT void ReportValidationError( - ValidationContext* context, - ValidationError error, - const char* description = nullptr); +const char* ValidationErrorToString(ValidationError error); -MOJO_CPP_BINDINGS_EXPORT void ReportValidationErrorForMessage( - mojo::Message* message, - ValidationError error, - const char* description = nullptr); - -// This class may be used by tests to suppress validation error logging. This is -// not thread-safe and must only be instantiated on the main thread with no -// other threads using Mojo bindings at the time of construction or destruction. -class MOJO_CPP_BINDINGS_EXPORT ScopedSuppressValidationErrorLoggingForTests { - public: - ScopedSuppressValidationErrorLoggingForTests(); - ~ScopedSuppressValidationErrorLoggingForTests(); - - private: - const bool was_suppressed_; - - DISALLOW_COPY_AND_ASSIGN(ScopedSuppressValidationErrorLoggingForTests); -}; +void ReportValidationError(ValidationContext* context, + ValidationError error, + const char* description = nullptr); // Only used by validation tests and when there is only one thread doing message // validation. -class MOJO_CPP_BINDINGS_EXPORT ValidationErrorObserverForTesting { +class ValidationErrorObserverForTesting { public: explicit ValidationErrorObserverForTesting(const base::Closure& callback); ~ValidationErrorObserverForTesting(); @@ -127,11 +99,11 @@ class MOJO_CPP_BINDINGS_EXPORT ValidationErrorObserverForTesting { // // The function returns true if the error is recorded (by a // SerializationWarningObserverForTesting object), false otherwise. -MOJO_CPP_BINDINGS_EXPORT bool ReportSerializationWarning(ValidationError error); +bool ReportSerializationWarning(ValidationError error); // Only used by serialization tests and when there is only one thread doing // message serialization. -class MOJO_CPP_BINDINGS_EXPORT SerializationWarningObserverForTesting { +class SerializationWarningObserverForTesting { public: SerializationWarningObserverForTesting(); ~SerializationWarningObserverForTesting(); diff --git a/mojo/public/cpp/bindings/lib/validation_util.cc b/mojo/public/cpp/bindings/lib/validation_util.cc index 7614df5..9e63521 100644 --- a/mojo/public/cpp/bindings/lib/validation_util.cc +++ b/mojo/public/cpp/bindings/lib/validation_util.cc @@ -16,6 +16,16 @@ namespace mojo { namespace internal { +bool ValidateEncodedPointer(const uint64_t* offset) { + // - Make sure |*offset| is no more than 32-bits. + // - Cast |offset| to uintptr_t so overflow behavior is well defined across + // 32-bit and 64-bit systems. + return *offset <= std::numeric_limits::max() && + (reinterpret_cast(offset) + + static_cast(*offset) >= + reinterpret_cast(offset)); +} + bool ValidateStructHeaderAndClaimMemory(const void* data, ValidationContext* validation_context) { if (!IsAligned(data)) { @@ -46,17 +56,20 @@ bool ValidateStructHeaderAndClaimMemory(const void* data, return true; } -bool ValidateNonInlinedUnionHeaderAndClaimMemory( - const void* data, - ValidationContext* validation_context) { +bool ValidateUnionHeaderAndClaimMemory(const void* data, + bool inlined, + ValidationContext* validation_context) { if (!IsAligned(data)) { ReportValidationError(validation_context, VALIDATION_ERROR_MISALIGNED_OBJECT); return false; } - if (!validation_context->ClaimMemory(data, kUnionDataSize) || - *static_cast(data) != kUnionDataSize) { + // If the union is inlined in another structure its memory was already + // claimed. + // This ONLY applies to the union itself, NOT anything which the union points + // to. + if (!inlined && !validation_context->ClaimMemory(data, kUnionDataSize)) { ReportValidationError(validation_context, VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE); return false; @@ -100,12 +113,41 @@ 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(message, + validation_context); + case kRunOrClosePipeMessageId: + return ValidateMessageIsRequestWithoutResponse(message, + validation_context) && + ValidateMessagePayload( + 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( + message, validation_context); + } + return false; +} + bool IsHandleOrInterfaceValid(const AssociatedInterface_Data& input) { - return input.handle.is_valid(); + return IsValidInterfaceId(input.interface_id); } -bool IsHandleOrInterfaceValid(const AssociatedEndpointHandle_Data& input) { - return input.is_valid(); +bool IsHandleOrInterfaceValid(const AssociatedInterfaceRequest_Data& input) { + return IsValidInterfaceId(input.interface_id); } bool IsHandleOrInterfaceValid(const Interface_Data& input) { @@ -130,7 +172,7 @@ bool ValidateHandleOrInterfaceNonNullable( } bool ValidateHandleOrInterfaceNonNullable( - const AssociatedEndpointHandle_Data& input, + const AssociatedInterfaceRequest_Data& input, const char* error_message, ValidationContext* validation_context) { if (IsHandleOrInterfaceValid(input)) @@ -170,7 +212,7 @@ bool ValidateHandleOrInterfaceNonNullable( bool ValidateHandleOrInterface(const AssociatedInterface_Data& input, ValidationContext* validation_context) { - if (validation_context->ClaimAssociatedEndpointHandle(input.handle)) + if (!IsMasterInterfaceId(input.interface_id)) return true; ReportValidationError(validation_context, @@ -178,9 +220,9 @@ bool ValidateHandleOrInterface(const AssociatedInterface_Data& input, return false; } -bool ValidateHandleOrInterface(const AssociatedEndpointHandle_Data& input, +bool ValidateHandleOrInterface(const AssociatedInterfaceRequest_Data& input, ValidationContext* validation_context) { - if (validation_context->ClaimAssociatedEndpointHandle(input)) + if (!IsMasterInterfaceId(input.interface_id)) return true; ReportValidationError(validation_context, diff --git a/mojo/public/cpp/bindings/lib/validation_util.h b/mojo/public/cpp/bindings/lib/validation_util.h index ea5a991..c883392 100644 --- a/mojo/public/cpp/bindings/lib/validation_util.h +++ b/mojo/public/cpp/bindings/lib/validation_util.h @@ -7,7 +7,6 @@ #include -#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/lib/bindings_internal.h" #include "mojo/public/cpp/bindings/lib/serialization_util.h" #include "mojo/public/cpp/bindings/lib/validate_params.h" @@ -20,15 +19,7 @@ namespace internal { // Checks whether decoding the pointer will overflow and produce a pointer // smaller than |offset|. -inline bool ValidateEncodedPointer(const uint64_t* offset) { - // - Make sure |*offset| is no more than 32-bits. - // - Cast |offset| to uintptr_t so overflow behavior is well defined across - // 32-bit and 64-bit systems. - return *offset <= std::numeric_limits::max() && - (reinterpret_cast(offset) + - static_cast(*offset) >= - reinterpret_cast(offset)); -} +bool ValidateEncodedPointer(const uint64_t* offset); template bool ValidatePointer(const Pointer& input, @@ -47,32 +38,30 @@ bool ValidatePointer(const Pointer& 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. -MOJO_CPP_BINDINGS_EXPORT bool ValidateStructHeaderAndClaimMemory( - const void* data, - ValidationContext* validation_context); +bool ValidateStructHeaderAndClaimMemory(const void* data, + ValidationContext* validation_context); // Validates that |data| contains a valid union header, in terms of alignment -// and size. It checks that the memory range [data, data + kUnionDataSize) is -// not marked as occupied by other objects in |validation_context|. On success, -// the memory range is marked as occupied. -MOJO_CPP_BINDINGS_EXPORT bool ValidateNonInlinedUnionHeaderAndClaimMemory( - const void* data, - ValidationContext* validation_context); +// and size. If not inlined, it checks that the memory range +// [data, data + num_bytes) is not marked as occupied by other objects in +// |validation_context|. On success, the memory range is marked as occupied. +bool ValidateUnionHeaderAndClaimMemory(const void* data, + bool inlined, + ValidationContext* validation_context); // Validates that the message is a request which doesn't expect a response. -MOJO_CPP_BINDINGS_EXPORT bool ValidateMessageIsRequestWithoutResponse( +bool ValidateMessageIsRequestWithoutResponse( const Message* message, ValidationContext* validation_context); // Validates that the message is a request expecting a response. -MOJO_CPP_BINDINGS_EXPORT bool ValidateMessageIsRequestExpectingResponse( +bool ValidateMessageIsRequestExpectingResponse( const Message* message, ValidationContext* validation_context); // Validates that the message is a response. -MOJO_CPP_BINDINGS_EXPORT bool ValidateMessageIsResponse( - const Message* message, - ValidationContext* validation_context); +bool ValidateMessageIsResponse(const Message* message, + ValidationContext* validation_context); // Validates that the message payload is a valid struct of type ParamsType. template @@ -81,6 +70,13 @@ 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 @@ -109,28 +105,24 @@ bool ValidateInlinedUnionNonNullable(const T& input, return false; } -MOJO_CPP_BINDINGS_EXPORT bool IsHandleOrInterfaceValid( - const AssociatedInterface_Data& input); -MOJO_CPP_BINDINGS_EXPORT bool IsHandleOrInterfaceValid( - const AssociatedEndpointHandle_Data& input); -MOJO_CPP_BINDINGS_EXPORT bool IsHandleOrInterfaceValid( - const Interface_Data& input); -MOJO_CPP_BINDINGS_EXPORT bool IsHandleOrInterfaceValid( - const Handle_Data& input); +bool IsHandleOrInterfaceValid(const AssociatedInterface_Data& input); +bool IsHandleOrInterfaceValid(const AssociatedInterfaceRequest_Data& input); +bool IsHandleOrInterfaceValid(const Interface_Data& input); +bool IsHandleOrInterfaceValid(const Handle_Data& input); -MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable( +bool ValidateHandleOrInterfaceNonNullable( const AssociatedInterface_Data& input, const char* error_message, ValidationContext* validation_context); -MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable( - const AssociatedEndpointHandle_Data& input, +bool ValidateHandleOrInterfaceNonNullable( + const AssociatedInterfaceRequest_Data& input, const char* error_message, ValidationContext* validation_context); -MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable( +bool ValidateHandleOrInterfaceNonNullable( const Interface_Data& input, const char* error_message, ValidationContext* validation_context); -MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterfaceNonNullable( +bool ValidateHandleOrInterfaceNonNullable( const Handle_Data& input, const char* error_message, ValidationContext* validation_context); @@ -139,12 +131,6 @@ template bool ValidateContainer(const Pointer& input, ValidationContext* validation_context, const ContainerValidateParams* validate_params) { - ValidationContext::ScopedDepthTracker depth_tracker(validation_context); - if (validation_context->ExceedsMaxDepth()) { - ReportValidationError(validation_context, - VALIDATION_ERROR_MAX_RECURSION_DEPTH); - return false; - } return ValidatePointer(input, validation_context) && T::Validate(input.Get(), validation_context, validate_params); } @@ -152,12 +138,6 @@ bool ValidateContainer(const Pointer& input, template bool ValidateStruct(const Pointer& input, ValidationContext* validation_context) { - ValidationContext::ScopedDepthTracker depth_tracker(validation_context); - if (validation_context->ExceedsMaxDepth()) { - ReportValidationError(validation_context, - VALIDATION_ERROR_MAX_RECURSION_DEPTH); - return false; - } return ValidatePointer(input, validation_context) && T::Validate(input.Get(), validation_context); } @@ -165,40 +145,24 @@ bool ValidateStruct(const Pointer& input, template 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 bool ValidateNonInlinedUnion(const Pointer& input, ValidationContext* validation_context) { - ValidationContext::ScopedDepthTracker depth_tracker(validation_context); - if (validation_context->ExceedsMaxDepth()) { - ReportValidationError(validation_context, - VALIDATION_ERROR_MAX_RECURSION_DEPTH); - return false; - } return ValidatePointer(input, validation_context) && T::Validate(input.Get(), validation_context, false); } -MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterface( - const AssociatedInterface_Data& input, - ValidationContext* validation_context); -MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterface( - const AssociatedEndpointHandle_Data& input, - ValidationContext* validation_context); -MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterface( - const Interface_Data& input, - ValidationContext* validation_context); -MOJO_CPP_BINDINGS_EXPORT bool ValidateHandleOrInterface( - const Handle_Data& input, - ValidationContext* validation_context); +bool ValidateHandleOrInterface(const AssociatedInterface_Data& input, + ValidationContext* validation_context); +bool ValidateHandleOrInterface(const AssociatedInterfaceRequest_Data& input, + ValidationContext* validation_context); +bool ValidateHandleOrInterface(const Interface_Data& input, + ValidationContext* validation_context); +bool ValidateHandleOrInterface(const Handle_Data& input, + ValidationContext* validation_context); } // namespace internal } // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h b/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h index cb24bc4..edbf27b 100644 --- a/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h +++ b/mojo/public/cpp/bindings/lib/wtf_clone_equals_util.h @@ -7,14 +7,14 @@ #include -#include "mojo/public/cpp/bindings/clone_traits.h" -#include "mojo/public/cpp/bindings/lib/equals_traits.h" +#include "mojo/public/cpp/bindings/lib/clone_equals_util.h" #include "third_party/WebKit/Source/wtf/HashMap.h" #include "third_party/WebKit/Source/wtf/Optional.h" #include "third_party/WebKit/Source/wtf/Vector.h" #include "third_party/WebKit/Source/wtf/text/WTFString.h" namespace mojo { +namespace internal { template struct CloneTraits, false> { @@ -22,7 +22,7 @@ struct CloneTraits, false> { WTF::Vector result; result.reserveCapacity(input.size()); for (const auto& element : input) - result.push_back(mojo::Clone(element)); + result.append(internal::Clone(element)); return result; } @@ -34,13 +34,11 @@ struct CloneTraits, false> { WTF::HashMap result; auto input_end = input.end(); for (auto it = input.begin(); it != input_end; ++it) - result.add(mojo::Clone(it->key), mojo::Clone(it->value)); + result.add(internal::Clone(it->key), internal::Clone(it->value)); return result; } }; -namespace internal { - template struct EqualsTraits, false> { static bool Equals(const WTF::Vector& a, const WTF::Vector& b) { diff --git a/mojo/public/cpp/bindings/lib/wtf_serialization.h b/mojo/public/cpp/bindings/lib/wtf_serialization.h index 0f112b9..132e19c 100644 --- a/mojo/public/cpp/bindings/lib/wtf_serialization.h +++ b/mojo/public/cpp/bindings/lib/wtf_serialization.h @@ -5,7 +5,9 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_WTF_SERIALIZATION_H_ #define MOJO_PUBLIC_CPP_BINDINGS_LIB_WTF_SERIALIZATION_H_ +#include "mojo/public/cpp/bindings/array_traits_wtf.h" #include "mojo/public/cpp/bindings/array_traits_wtf_vector.h" +#include "mojo/public/cpp/bindings/map_traits_wtf.h" #include "mojo/public/cpp/bindings/map_traits_wtf_hash_map.h" #include "mojo/public/cpp/bindings/string_traits_wtf.h" diff --git a/mojo/public/cpp/bindings/map.h b/mojo/public/cpp/bindings/map.h index c1ba075..d4c7952 100644 --- a/mojo/public/cpp/bindings/map.h +++ b/mojo/public/cpp/bindings/map.h @@ -5,37 +5,299 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_MAP_H_ #define MOJO_PUBLIC_CPP_BINDINGS_MAP_H_ +#include #include #include #include +#include "base/logging.h" +#include "base/macros.h" +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/cpp/bindings/lib/map_data_internal.h" +#include "mojo/public/cpp/bindings/lib/template_util.h" +#include "mojo/public/cpp/bindings/type_converter.h" + namespace mojo { -// TODO(yzshen): These conversion functions should be removed and callsites -// should be revisited and changed to use the same map type. -template -std::unordered_map MapToUnorderedMap( - const std::map& input) { - return std::unordered_map(input.begin(), input.end()); -} - -template -std::unordered_map MapToUnorderedMap(std::map&& input) { - return std::unordered_map(std::make_move_iterator(input.begin()), - std::make_move_iterator(input.end())); -} - -template -std::map UnorderedMapToMap( - const std::unordered_map& input) { - return std::map(input.begin(), input.end()); -} - -template -std::map UnorderedMapToMap(std::unordered_map&& input) { - return std::map(std::make_move_iterator(input.begin()), - std::make_move_iterator(input.end())); -} +// A move-only map that can handle move-only values. Map has the following +// characteristics: +// - The map itself can be null, and this is distinct from empty. +// - Keys must not be move-only. +// - The Key-type's "<" operator is used to sort the entries, and also is +// used to determine equality of the key values. +// - There can only be one entry per unique key. +// - Values of move-only types will be moved into the Map when they are added +// using the insert() method. +template +class Map { + public: + using Key = K; + using Value = V; + + // Map keys cannot be move only classes. + static_assert(!internal::IsMoveOnlyType::value, + "Map keys cannot be move only types."); + + using Iterator = typename std::map::iterator; + using ConstIterator = typename std::map::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 keys, mojo::Array 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&& other) : map_(std::move(other)), is_null_(false) {} + Map(Map&& other) : is_null_(true) { Take(&other); } + + Map& operator=(std::map&& 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 + static Map From(const U& other) { + return TypeConverter::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 + U To() const { + return TypeConverter::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& storage() const { return map_; } + + // Passes the underlying storage and resets this map to null. + std::map PassStorage() { + is_null_ = true; + return std::move(map_); + } + + operator const std::map&() const { return map_; } + + // Swaps the contents of this Map with another Map of the same type (including + // nullness). + void Swap(Map* 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* 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* keys, mojo::Array* values) { + std::vector key_vector; + key_vector.reserve(map_.size()); + std::vector 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 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 + bool operator==(const Map& other) const = delete; + template + bool operator!=(const Map& other) const = delete; + + void Take(Map* other) { + operator=(nullptr); + Swap(other); + } + + std::map 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 +struct TypeConverter, std::map> { + static Map Convert( + const std::map& input) { + Map result; + for (auto& pair : input) { + result.insert(TypeConverter::Convert(pair.first), + TypeConverter::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 +struct TypeConverter, Map> { + static std::map Convert( + const Map& input) { + std::map result; + if (!input.is_null()) { + for (auto it = input.begin(); it != input.end(); ++it) { + result.insert(std::make_pair( + TypeConverter::Convert(it->first), + TypeConverter::Convert(it->second))); + } + } + return result; + } +}; } // namespace mojo diff --git a/mojo/public/cpp/bindings/map_data_view.h b/mojo/public/cpp/bindings/map_data_view.h deleted file mode 100644 index a65bb9e..0000000 --- a/mojo/public/cpp/bindings/map_data_view.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_MAP_DATA_VIEW_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_MAP_DATA_VIEW_H_ - -#include "base/logging.h" -#include "mojo/public/cpp/bindings/array_data_view.h" -#include "mojo/public/cpp/bindings/lib/bindings_internal.h" -#include "mojo/public/cpp/bindings/lib/map_data_internal.h" -#include "mojo/public/cpp/bindings/lib/serialization_context.h" -#include "mojo/public/cpp/bindings/lib/serialization_forward.h" - -namespace mojo { - -template -class MapDataView { - public: - using Data_ = typename internal::MojomTypeTraits>::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& keys() { return keys_; } - const ArrayDataView& keys() const { return keys_; } - - template - bool ReadKeys(U* output) { - return internal::Deserialize>(keys_.data_, output, - keys_.context_); - } - - ArrayDataView& values() { return values_; } - const ArrayDataView& values() const { return values_; } - - template - bool ReadValues(U* output) { - return internal::Deserialize>(values_.data_, output, - values_.context_); - } - - private: - ArrayDataView keys_; - ArrayDataView values_; -}; - -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_BINDINGS_MAP_DATA_VIEW_H_ diff --git a/mojo/public/cpp/bindings/map_traits.h b/mojo/public/cpp/bindings/map_traits.h index 5c0d8b2..01dd66d 100644 --- a/mojo/public/cpp/bindings/map_traits.h +++ b/mojo/public/cpp/bindings/map_traits.h @@ -37,13 +37,13 @@ namespace mojo { // static const V& GetValue(CustomConstIterator& iterator); // // // Returning false results in deserialization failure and causes the -// // message pipe receiving it to be disconnected. |IK| and |IV| are -// // separate input key/value template parameters that allows for the -// // the key/value types to be forwarded. -// template +// // message pipe receiving it to be disconnected. // static bool Insert(CustomMap& input, -// IK&& key, -// IV&& value); +// const K& key, +// V&& value); +// static bool Insert(CustomMap& input, +// const K& key, +// const V& value); // // static void SetToEmpty(CustomMap* output); // }; diff --git a/mojo/public/cpp/bindings/map_traits_standard.h b/mojo/public/cpp/bindings/map_traits_standard.h new file mode 100644 index 0000000..0c76890 --- /dev/null +++ b/mojo/public/cpp/bindings/map_traits_standard.h @@ -0,0 +1,53 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_STANDARD_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_STANDARD_H_ + +#include "mojo/public/cpp/bindings/map.h" +#include "mojo/public/cpp/bindings/map_traits.h" + +namespace mojo { + +template +struct MapTraits> { + using Key = K; + using Value = V; + using Iterator = typename Map::Iterator; + using ConstIterator = typename Map::ConstIterator; + + static bool IsNull(const Map& input) { return input.is_null(); } + static void SetToNull(Map* output) { *output = nullptr; } + + static size_t GetSize(const Map& input) { return input.size(); } + + static ConstIterator GetBegin(const Map& input) { + return input.begin(); + } + static Iterator GetBegin(Map& 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& input, const K& key, V&& value) { + input.insert(key, std::forward(value)); + return true; + } + static bool Insert(Map& input, const K& key, const V& value) { + input.insert(key, value); + return true; + } + + static void SetToEmpty(Map* output) { output->SetToEmpty(); } +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_STANDARD_H_ diff --git a/mojo/public/cpp/bindings/map_traits_stl.h b/mojo/public/cpp/bindings/map_traits_stl.h index 83a4399..ff79a20 100644 --- a/mojo/public/cpp/bindings/map_traits_stl.h +++ b/mojo/public/cpp/bindings/map_traits_stl.h @@ -94,10 +94,14 @@ struct MapTraits> { static V& GetValue(Iterator& iterator) { return iterator->second; } static const V& GetValue(ConstIterator& iterator) { return iterator->second; } - template - static bool Insert(std::unordered_map& input, IK&& key, IV&& value) { - input.insert( - std::make_pair(std::forward(key), std::forward(value))); + static bool Insert(std::unordered_map& input, const K& key, V&& value) { + input.insert(std::make_pair(key, std::forward(value))); + return true; + } + static bool Insert(std::unordered_map& input, + const K& key, + const V& value) { + input.insert(std::make_pair(key, value)); return true; } diff --git a/mojo/public/cpp/bindings/map_traits_wtf.h b/mojo/public/cpp/bindings/map_traits_wtf.h new file mode 100644 index 0000000..805e4c9 --- /dev/null +++ b/mojo/public/cpp/bindings/map_traits_wtf.h @@ -0,0 +1,62 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_WTF_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_WTF_H_ + +#include "base/logging.h" +#include "mojo/public/cpp/bindings/map_traits.h" +#include "mojo/public/cpp/bindings/wtf_map.h" + +namespace mojo { + +template +struct MapTraits> { + using Key = K; + using Value = V; + using Iterator = typename WTFMap::Iterator; + using ConstIterator = typename WTFMap::ConstIterator; + + static bool IsNull(const WTFMap& input) { return input.is_null(); } + static void SetToNull(WTFMap* output) { *output = nullptr; } + + static size_t GetSize(const WTFMap& input) { return input.size(); } + + static ConstIterator GetBegin(const WTFMap& input) { + return input.begin(); + } + static Iterator GetBegin(WTFMap& 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& input, const K& key, V&& value) { + if (!WTFMap::IsValidKey(key)) { + LOG(ERROR) << "The key value is disallowed by WTF::HashMap: " << key; + return false; + } + input.insert(key, std::forward(value)); + return true; + } + static bool Insert(WTFMap& input, const K& key, const V& value) { + if (!WTFMap::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* output) { output->SetToEmpty(); } +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_MAP_TRAITS_WTF_H_ diff --git a/mojo/public/cpp/bindings/map_traits_wtf_hash_map.h b/mojo/public/cpp/bindings/map_traits_wtf_hash_map.h index dd68b36..4392201 100644 --- a/mojo/public/cpp/bindings/map_traits_wtf_hash_map.h +++ b/mojo/public/cpp/bindings/map_traits_wtf_hash_map.h @@ -46,13 +46,20 @@ struct MapTraits> { static V& GetValue(Iterator& iterator) { return iterator->value; } static const V& GetValue(ConstIterator& iterator) { return iterator->value; } - template - static bool Insert(WTF::HashMap& input, IK&& key, IV&& value) { + static bool Insert(WTF::HashMap& input, const K& key, V&& value) { if (!WTF::HashMap::isValidKey(key)) { - LOG(ERROR) << "The key value is disallowed by WTF::HashMap"; + LOG(ERROR) << "The key value is disallowed by WTF::HashMap: " << key; return false; } - input.insert(std::forward(key), std::forward(value)); + input.add(key, std::forward(value)); + return true; + } + static bool Insert(WTF::HashMap& input, const K& key, const V& value) { + if (!WTF::HashMap::isValidKey(key)) { + LOG(ERROR) << "The key value is disallowed by WTF::HashMap: " << key; + return false; + } + input.add(key, value); return true; } diff --git a/mojo/public/cpp/bindings/message.h b/mojo/public/cpp/bindings/message.h index 65d6cec..e758432 100644 --- a/mojo/public/cpp/bindings/message.h +++ b/mojo/public/cpp/bindings/message.h @@ -13,46 +13,26 @@ #include #include -#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; - // Message is a holder for the data and handles to be sent over a MessagePipe. // Message owns its data and handles, but a consumer of Message is free to // mutate the data and handles. The message's data is comprised of a header // followed by payload. -class MOJO_CPP_BINDINGS_EXPORT Message { +class Message { public: static const uint32_t kFlagExpectsResponse = 1 << 0; static const uint32_t kFlagIsResponse = 1 << 1; static const uint32_t kFlagIsSync = 1 << 2; Message(); - Message(Message&& other); - ~Message(); - Message& operator=(Message&& other); - - // Resets the Message to an uninitialized state. Upon reset, the Message - // exists as if it were default-constructed: it has no data buffer and owns no - // handles. - void Reset(); - - // Indicates whether this Message is uninitialized. - bool IsNull() const { return !buffer_; } - // Initializes a Message with enough space for |capacity| bytes. void Initialize(size_t capacity, bool zero_initialized); @@ -61,9 +41,10 @@ class MOJO_CPP_BINDINGS_EXPORT Message { uint32_t num_bytes, std::vector* handles); - uint32_t data_num_bytes() const { - return static_cast(buffer_->size()); - } + // Transfers data and handles to |destination|. + void MoveTo(Message* destination); + + uint32_t data_num_bytes() const { return buffer_->data_num_bytes(); } // Access the raw bytes of the message. const uint8_t* data() const { @@ -76,30 +57,12 @@ class MOJO_CPP_BINDINGS_EXPORT Message { const internal::MessageHeader* header() const { return static_cast(buffer_->data()); } - internal::MessageHeader* header() { - return static_cast(buffer_->data()); - } - const internal::MessageHeaderV1* header_v1() const { - DCHECK_GE(version(), 1u); - return static_cast(buffer_->data()); - } - internal::MessageHeaderV1* header_v1() { - DCHECK_GE(version(), 1u); - return static_cast(buffer_->data()); - } - - const internal::MessageHeaderV2* header_v2() const { - DCHECK_GE(version(), 2u); - return static_cast(buffer_->data()); - } - internal::MessageHeaderV2* header_v2() { - DCHECK_GE(version(), 2u); - return static_cast(buffer_->data()); + internal::MessageHeader* header() { + return const_cast( + static_cast(this)->header()); } - uint32_t version() const { return header()->version; } - uint32_t interface_id() const { return header()->interface_id; } void set_interface_id(uint32_t id) { header()->interface_id = id; } @@ -107,32 +70,32 @@ class MOJO_CPP_BINDINGS_EXPORT Message { bool has_flag(uint32_t flag) const { return !!(header()->flags & flag); } // Access the request_id field (if present). - uint64_t request_id() const { return header_v1()->request_id; } + bool has_request_id() const { return header()->version >= 1; } + uint64_t request_id() const { + DCHECK(has_request_id()); + return static_cast( + header())->request_id; + } void set_request_id(uint64_t request_id) { - header_v1()->request_id = request_id; + DCHECK(has_request_id()); + static_cast(header()) + ->request_id = request_id; } // Access the payload. - const uint8_t* payload() const; + const uint8_t* payload() const { return data() + header()->num_bytes; } uint8_t* mutable_payload() { return const_cast(payload()); } - uint32_t payload_num_bytes() const; - - uint32_t payload_num_interface_ids() const; - const uint32_t* payload_interface_ids() const; + uint32_t payload_num_bytes() const { + DCHECK(buffer_->data_num_bytes() >= header()->num_bytes); + size_t num_bytes = buffer_->data_num_bytes() - header()->num_bytes; + DCHECK(num_bytes <= std::numeric_limits::max()); + return static_cast(num_bytes); + } // Access the handles. const std::vector* handles() const { return &handles_; } std::vector* mutable_handles() { return &handles_; } - const std::vector* - associated_endpoint_handles() const { - return &associated_endpoint_handles_; - } - std::vector* - mutable_associated_endpoint_handles() { - return &associated_endpoint_handles_; - } - // Access the underlying Buffer interface. internal::Buffer* buffer() { return buffer_.get(); } @@ -145,22 +108,11 @@ class MOJO_CPP_BINDINGS_EXPORT 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 buffer_; std::vector handles_; - std::vector associated_endpoint_handles_; DISALLOW_COPY_AND_ASSIGN(Message); }; @@ -234,57 +186,6 @@ 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 @@ -294,22 +195,6 @@ class MOJO_CPP_BINDINGS_EXPORT SyncMessageResponseContext { // NOTE: The message hasn't been validated and may be malformed! MojoResult ReadMessage(MessagePipeHandle handle, Message* message); -// Reports the currently dispatching Message as bad. Note that this is only -// legal to call from directly within the stack frame of a message dispatch. If -// you need to do asynchronous work before you can determine the legitimacy of -// a message, use TakeBadMessageCallback() and retain its result until you're -// ready to invoke or discard it. -MOJO_CPP_BINDINGS_EXPORT -void ReportBadMessage(const std::string& error); - -// Acquires a callback which may be run to report the currently dispatching -// Message as bad. Note that this is only legal to call from directly within the -// stack frame of a message dispatch, but the returned callback may be called -// exactly once any time thereafter to report the message as bad. This may only -// be called once per message. -MOJO_CPP_BINDINGS_EXPORT -ReportBadMessageCallback GetBadMessageCallback(); - } // namespace mojo #endif // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_H_ diff --git a/mojo/public/cpp/bindings/message_filter.h b/mojo/public/cpp/bindings/message_filter.h new file mode 100644 index 0000000..638c53b --- /dev/null +++ b/mojo/public/cpp/bindings/message_filter.h @@ -0,0 +1,38 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_ + +#include "mojo/public/cpp/bindings/message.h" + +namespace mojo { + +// This class is the base class for message filters. Subclasses should +// implement the pure virtual method Accept() inherited from MessageReceiver to +// process messages and/or forward them to |sink_|. +class MessageFilter : public MessageReceiver { + public: + // Doesn't take ownership of |sink|. Therefore |sink| has to stay alive while + // this object is alive. + explicit MessageFilter(MessageReceiver* sink = nullptr); + ~MessageFilter() override; + + void set_sink(MessageReceiver* sink) { sink_ = sink; } + + protected: + MessageReceiver* sink_; +}; + +// A trivial filter that simply forwards every message it receives to |sink_|. +class PassThroughFilter : public MessageFilter { + public: + explicit PassThroughFilter(MessageReceiver* sink = nullptr); + + bool Accept(Message* message) override; +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_FILTER_H_ diff --git a/mojo/public/cpp/bindings/message_header_validator.h b/mojo/public/cpp/bindings/message_header_validator.h index 50c19db..3bcbd0a 100644 --- a/mojo/public/cpp/bindings/message_header_validator.h +++ b/mojo/public/cpp/bindings/message_header_validator.h @@ -5,17 +5,16 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_HEADER_VALIDATOR_H_ #define MOJO_PUBLIC_CPP_BINDINGS_MESSAGE_HEADER_VALIDATOR_H_ -#include "base/compiler_specific.h" -#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/bindings/message_filter.h" namespace mojo { -class MOJO_CPP_BINDINGS_EXPORT MessageHeaderValidator - : NON_EXPORTED_BASE(public MessageReceiver) { +class MessageHeaderValidator : public MessageFilter { public: - MessageHeaderValidator(); - explicit MessageHeaderValidator(const std::string& description); + explicit MessageHeaderValidator(MessageReceiver* sink = nullptr); + MessageHeaderValidator(const std::string& description, + MessageReceiver* sink = nullptr); // Sets the description associated with this validator. Used for reporting // more detailed validation errors. diff --git a/mojo/public/cpp/bindings/native_struct.h b/mojo/public/cpp/bindings/native_struct.h index ac27250..882c970 100644 --- a/mojo/public/cpp/bindings/native_struct.h +++ b/mojo/public/cpp/bindings/native_struct.h @@ -5,10 +5,7 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_H_ #define MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_H_ -#include - -#include "base/optional.h" -#include "mojo/public/cpp/bindings/bindings_export.h" +#include "mojo/public/cpp/bindings/array.h" #include "mojo/public/cpp/bindings/lib/native_struct_data.h" #include "mojo/public/cpp/bindings/struct_ptr.h" #include "mojo/public/cpp/bindings/type_converter.h" @@ -20,7 +17,7 @@ using NativeStructPtr = StructPtr; // Native-only structs correspond to "[Native] struct Foo;" definitions in // mojom. -class MOJO_CPP_BINDINGS_EXPORT NativeStruct { +class NativeStruct { public: using Data_ = internal::NativeStruct_Data; @@ -41,9 +38,8 @@ class MOJO_CPP_BINDINGS_EXPORT NativeStruct { NativeStructPtr Clone() const; bool Equals(const NativeStruct& other) const; - size_t Hash(size_t seed) const; - base::Optional> data; + Array data; }; } // namespace mojo diff --git a/mojo/public/cpp/bindings/native_struct_data_view.h b/mojo/public/cpp/bindings/native_struct_data_view.h deleted file mode 100644 index 613bd7a..0000000 --- a/mojo/public/cpp/bindings/native_struct_data_view.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_DATA_VIEW_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_DATA_VIEW_H_ - -#include "mojo/public/cpp/bindings/lib/native_struct_data.h" -#include "mojo/public/cpp/bindings/lib/serialization_context.h" - -namespace mojo { - -class NativeStructDataView { - public: - using Data_ = internal::NativeStruct_Data; - - NativeStructDataView() {} - - NativeStructDataView(Data_* data, internal::SerializationContext* context) - : data_(data) {} - - bool is_null() const { return !data_; } - - size_t size() const { return data_->data.size(); } - - uint8_t operator[](size_t index) const { return data_->data.at(index); } - - const uint8_t* data() const { return data_->data.storage(); } - - private: - Data_* data_ = nullptr; -}; - -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_BINDINGS_NATIVE_STRUCT_DATA_VIEW_H_ diff --git a/mojo/public/cpp/bindings/no_interface.h b/mojo/public/cpp/bindings/no_interface.h new file mode 100644 index 0000000..d8915cd --- /dev/null +++ b/mojo/public/cpp/bindings/no_interface.h @@ -0,0 +1,52 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_NO_INTERFACE_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_NO_INTERFACE_H_ + +#include "mojo/public/cpp/bindings/message.h" +#include "mojo/public/cpp/bindings/message_filter.h" +#include "mojo/public/cpp/system/core.h" + +namespace mojo { + +// NoInterface is for use in cases when a non-existent or empty interface is +// needed. + +class NoInterfaceProxy; +class NoInterfaceStub; + +class NoInterface { + public: + static const char* Name_; + typedef NoInterfaceProxy Proxy_; + typedef NoInterfaceStub Stub_; + typedef PassThroughFilter RequestValidator_; + typedef PassThroughFilter ResponseValidator_; + virtual ~NoInterface() {} +}; + +class NoInterfaceProxy : public NoInterface { + public: + explicit NoInterfaceProxy(MessageReceiver* receiver) {} +}; + +class NoInterfaceStub : public MessageReceiverWithResponder { + public: + NoInterfaceStub() {} + void set_sink(NoInterface* sink) {} + NoInterface* sink() { return nullptr; } + bool Accept(Message* message) override; + bool AcceptWithResponder(Message* message, + MessageReceiver* responder) override; +}; + +// AnyInterface is for use in cases where any interface would do (e.g., see the +// Shell::Connect method). + +typedef NoInterface AnyInterface; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_NO_INTERFACE_H_ diff --git a/mojo/public/cpp/bindings/pipe_control_message_handler.h b/mojo/public/cpp/bindings/pipe_control_message_handler.h index a5c04da..b387b06 100644 --- a/mojo/public/cpp/bindings/pipe_control_message_handler.h +++ b/mojo/public/cpp/bindings/pipe_control_message_handler.h @@ -5,11 +5,9 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_H_ #define MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_H_ -#include - -#include "base/compiler_specific.h" #include "base/macros.h" -#include "mojo/public/cpp/bindings/bindings_export.h" +#include "mojo/public/cpp/bindings/interface_id.h" +#include "mojo/public/cpp/bindings/lib/serialization_context.h" #include "mojo/public/cpp/bindings/message.h" namespace mojo { @@ -17,8 +15,7 @@ namespace mojo { class PipeControlMessageHandlerDelegate; // Handler for messages defined in pipe_control_messages.mojom. -class MOJO_CPP_BINDINGS_EXPORT PipeControlMessageHandler - : NON_EXPORTED_BASE(public MessageReceiver) { +class PipeControlMessageHandler : public MessageReceiver { public: explicit PipeControlMessageHandler( PipeControlMessageHandlerDelegate* delegate); @@ -46,6 +43,7 @@ class MOJO_CPP_BINDINGS_EXPORT PipeControlMessageHandler std::string description_; PipeControlMessageHandlerDelegate* const delegate_; + internal::SerializationContext context_; DISALLOW_COPY_AND_ASSIGN(PipeControlMessageHandler); }; diff --git a/mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h b/mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h index 16fd918..06f0695 100644 --- a/mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h +++ b/mojo/public/cpp/bindings/pipe_control_message_handler_delegate.h @@ -5,8 +5,6 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_DELEGATE_H_ #define MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_HANDLER_DELEGATE_H_ -#include "base/optional.h" -#include "mojo/public/cpp/bindings/disconnect_reason.h" #include "mojo/public/cpp/bindings/interface_id.h" namespace mojo { @@ -16,9 +14,8 @@ 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, - const base::Optional& reason) = 0; + virtual bool OnPeerAssociatedEndpointClosed(InterfaceId id) = 0; + virtual bool OnAssociatedEndpointClosedBeforeSent(InterfaceId id) = 0; protected: virtual ~PipeControlMessageHandlerDelegate() {} diff --git a/mojo/public/cpp/bindings/pipe_control_message_proxy.h b/mojo/public/cpp/bindings/pipe_control_message_proxy.h index 52c408f..7f3e006 100644 --- a/mojo/public/cpp/bindings/pipe_control_message_proxy.h +++ b/mojo/public/cpp/bindings/pipe_control_message_proxy.h @@ -6,36 +6,26 @@ #define MOJO_PUBLIC_CPP_BINDINGS_PIPE_CONTROL_MESSAGE_PROXY_H_ #include "base/macros.h" -#include "base/optional.h" -#include "mojo/public/cpp/bindings/bindings_export.h" -#include "mojo/public/cpp/bindings/disconnect_reason.h" #include "mojo/public/cpp/bindings/interface_id.h" #include "mojo/public/cpp/bindings/lib/serialization_context.h" -#include "mojo/public/cpp/bindings/message.h" namespace mojo { class MessageReceiver; // Proxy for request messages defined in pipe_control_messages.mojom. -// -// NOTE: This object may be used from multiple threads. -class MOJO_CPP_BINDINGS_EXPORT PipeControlMessageProxy { +class PipeControlMessageProxy { public: - // Doesn't take ownership of |receiver|. If This PipeControlMessageProxy will - // be used from multiple threads, |receiver| must be thread-safe. + // Doesn't take ownership of |receiver|. It must outlive this object. explicit PipeControlMessageProxy(MessageReceiver* receiver); - void NotifyPeerEndpointClosed(InterfaceId id, - const base::Optional& reason); - - static Message ConstructPeerEndpointClosedMessage( - InterfaceId id, - const base::Optional& reason); + void NotifyPeerEndpointClosed(InterfaceId id); + void NotifyEndpointClosedBeforeSent(InterfaceId id); private: // Not owned. MessageReceiver* receiver_; + internal::SerializationContext context_; DISALLOW_COPY_AND_ASSIGN(PipeControlMessageProxy); }; diff --git a/mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h b/mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h deleted file mode 100644 index 4d40cdf..0000000 --- a/mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_RAW_PTR_IMPL_REF_TRAITS_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_RAW_PTR_IMPL_REF_TRAITS_H_ - -namespace mojo { - -// Default traits for a binding's implementation reference type. This -// corresponds to a raw pointer. -template -struct RawPtrImplRefTraits { - using PointerType = Interface*; - - static bool IsNull(PointerType ptr) { return !ptr; } - static Interface* GetRawPointer(PointerType* ptr) { return *ptr; } -}; - -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_RAW_PTR_IMPL_REF_TRAITS_H_ diff --git a/mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h b/mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h index 16527cf..1f45b0c 100644 --- a/mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h +++ b/mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h @@ -5,16 +5,8 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_SCOPED_INTERFACE_ENDPOINT_HANDLE_H_ #define MOJO_PUBLIC_CPP_BINDINGS_SCOPED_INTERFACE_ENDPOINT_HANDLE_H_ -#include - -#include "base/callback.h" #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "base/optional.h" -#include "base/single_thread_task_runner.h" -#include "base/threading/thread_task_runner_handle.h" -#include "mojo/public/cpp/bindings/bindings_export.h" -#include "mojo/public/cpp/bindings/disconnect_reason.h" #include "mojo/public/cpp/bindings/interface_id.h" namespace mojo { @@ -23,16 +15,8 @@ class AssociatedGroupController; // ScopedInterfaceEndpointHandle refers to one end of an interface, either the // implementation side or the client side. -// Threading: At any given time, a ScopedInterfaceEndpointHandle should only -// be accessed from a single thread. -class MOJO_CPP_BINDINGS_EXPORT ScopedInterfaceEndpointHandle { +class ScopedInterfaceEndpointHandle { public: - // Creates a pair of handles representing the two endpoints of an interface, - // which are not yet associated with a message pipe. - static void CreatePairPendingAssociation( - ScopedInterfaceEndpointHandle* handle0, - ScopedInterfaceEndpointHandle* handle1); - // Creates an invalid endpoint handle. ScopedInterfaceEndpointHandle(); @@ -43,77 +27,39 @@ class MOJO_CPP_BINDINGS_EXPORT ScopedInterfaceEndpointHandle { ScopedInterfaceEndpointHandle& operator=( ScopedInterfaceEndpointHandle&& other); - bool is_valid() const; + bool is_valid() const { return IsValidInterfaceId(id_); } - // Returns true if the interface hasn't associated with a message pipe. - bool pending_association() const; + bool is_local() const { return is_local_; } - // Returns kInvalidInterfaceId when in pending association state or the handle - // is invalid. - InterfaceId id() const; + void reset(); + void swap(ScopedInterfaceEndpointHandle& other); - // Returns null when in pending association state or the handle is invalid. - AssociatedGroupController* group_controller() const; + // DO NOT USE METHODS BELOW THIS LINE. These are for internal use and testing. - // Returns the disconnect reason if the peer handle is closed before - // association and specifies a custom disconnect reason. - const base::Optional& disconnect_reason() const; + InterfaceId id() const { return id_; } - 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 - }; + AssociatedGroupController* group_controller() const { + return group_controller_.get(); + } - using AssociationEventCallback = base::OnceCallback; - // 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); + // Releases the handle without closing it. + InterfaceId release(); private: friend class AssociatedGroupController; - friend class AssociatedGroup; - - class State; - // Used by AssociatedGroupController. + // This is supposed to be used by AssociatedGroupController only. + // |id| is the corresponding interface ID. + // If |is_local| is false, this handle is meant to be passed over a message + // pipe the remote side of the associated group. ScopedInterfaceEndpointHandle( InterfaceId id, + bool is_local, scoped_refptr group_controller); - // Used by AssociatedGroupController. - // The peer of this handle will join |peer_group_controller|. - bool NotifyAssociation( - InterfaceId id, - scoped_refptr peer_group_controller); - - void ResetInternal(const base::Optional& 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 CreateGroupControllerGetter() - const; - - scoped_refptr state_; + InterfaceId id_; + bool is_local_; + scoped_refptr group_controller_; DISALLOW_COPY_AND_ASSIGN(ScopedInterfaceEndpointHandle); }; diff --git a/mojo/public/cpp/bindings/stl_converters.h b/mojo/public/cpp/bindings/stl_converters.h new file mode 100644 index 0000000..92b2924 --- /dev/null +++ b/mojo/public/cpp/bindings/stl_converters.h @@ -0,0 +1,245 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_STL_CONVERTERS_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_STL_CONVERTERS_H_ + +#include +#include +#include +#include + +#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_obj; +// +// std::vector>> 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 will +// result in std::vector. It won't convert +// mojo::Map inside the struct. + +namespace mojo { +namespace internal { + +template +struct UnwrapTraits; + +template +struct UnwrapShouldGoDeeper { + public: + static const bool value = + !std::is_same::Type>::value; +}; + +template +struct UnwrapTraits { + public: + using Type = T; + static Type Unwrap(T input) { return input; } +}; + +template +struct UnwrapTraits> { + public: + using Type = std::vector::Type>; + + static Type Unwrap(Array input) { + return Helper::Run(std::move(input)); + } + + private: + template ::value> + struct Helper {}; + + template + struct Helper { + public: + static Type Run(Array input) { + Type output; + output.reserve(input.size()); + for (size_t i = 0; i < input.size(); ++i) + output.push_back(UnwrapTraits::Unwrap(std::move(input[i]))); + return output; + } + }; + + template + struct Helper { + public: + static Type Run(Array input) { return input.PassStorage(); } + }; +}; + +template +struct UnwrapTraits> { + public: + using Type = + std::map::Type, typename UnwrapTraits::Type>; + + static Type Unwrap(Map input) { + return Helper::Run(std::move(input)); + } + + private: + template ::value || + UnwrapShouldGoDeeper::value> + struct Helper {}; + + template + struct Helper { + public: + static Type Run(Map input) { + std::map input_storage = input.PassStorage(); + Type output; + for (auto& pair : input_storage) { + output.insert( + std::make_pair(UnwrapTraits::Unwrap(pair.first), + UnwrapTraits::Unwrap(std::move(pair.second)))); + } + return output; + } + }; + + template + struct Helper { + public: + static Type Run(Map input) { return input.PassStorage(); } + }; +}; + +template <> +struct UnwrapTraits { + public: + using Type = std::string; + + static std::string Unwrap(const String& input) { return input; } +}; + +template +struct WrapTraits; + +template +struct WrapShouldGoDeeper { + public: + static const bool value = + !std::is_same::Type>::value; +}; + +template +struct WrapTraits { + public: + using Type = T; + + static T Wrap(T input) { return input; } +}; + +template +struct WrapTraits> { + public: + using Type = Array::Type>; + + static Type Wrap(std::vector input) { + return Helper::Run(std::move(input)); + } + + private: + template ::value> + struct Helper {}; + + template + struct Helper { + public: + static Type Run(std::vector input) { + std::vector::Type> output_storage; + output_storage.reserve(input.size()); + for (auto& element : input) + output_storage.push_back(WrapTraits::Wrap(std::move(element))); + return Type(std::move(output_storage)); + } + }; + + template + struct Helper { + public: + static Type Run(std::vector input) { return Type(std::move(input)); } + }; +}; + +template +struct WrapTraits> { + public: + using Type = Map::Type, typename WrapTraits::Type>; + + static Type Wrap(std::map input) { + return Helper::Run(std::move(input)); + } + + private: + template ::value || WrapShouldGoDeeper::value> + struct Helper {}; + + template + struct Helper { + public: + static Type Run(std::map input) { + Type output; + for (auto& pair : input) { + output.insert(WrapTraits::Wrap(pair.first), + WrapTraits::Wrap(std::move(pair.second))); + } + return output; + } + }; + + template + struct Helper { + public: + static Type Run(std::map input) { return Type(std::move(input)); } + }; +}; + +template <> +struct WrapTraits { + public: + using Type = String; + + static String Wrap(const std::string& input) { return input; } +}; + +} // namespace internal + +template +typename internal::UnwrapTraits::Type UnwrapToSTLType(T&& input) { + return internal::UnwrapTraits::Unwrap(std::move(input)); +} + +template +typename internal::WrapTraits::Type WrapSTLType(T&& input) { + return internal::WrapTraits::Wrap(std::move(input)); +} + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_STL_CONVERTERS_H_ diff --git a/mojo/public/cpp/bindings/string.h b/mojo/public/cpp/bindings/string.h new file mode 100644 index 0000000..7cfd713 --- /dev/null +++ b/mojo/public/cpp/bindings/string.h @@ -0,0 +1,196 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRING_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_STRING_H_ + +#include + +#include + +#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 + 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 + static String From(const U& other) { + return TypeConverter::Convert(other); + } + + template + U To() const { + return TypeConverter::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 { + static String Convert(const std::string& input) { return String(input); } +}; + +template <> +struct TypeConverter { + static std::string Convert(const String& input) { return input; } +}; + +template +struct TypeConverter { + static String Convert(const char input[N]) { + DCHECK(input); + return String(input, N - 1); + } +}; + +// Appease MSVC. +template +struct TypeConverter { + static String Convert(const char input[N]) { + DCHECK(input); + return String(input, N - 1); + } +}; + +template <> +struct TypeConverter { + // |input| may be null, in which case a null String will be returned. + static String Convert(const char* input) { return String(input); } +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_STRING_H_ diff --git a/mojo/public/cpp/bindings/string_data_view.h b/mojo/public/cpp/bindings/string_data_view.h deleted file mode 100644 index 2b091b4..0000000 --- a/mojo/public/cpp/bindings/string_data_view.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRING_DATA_VIEW_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_STRING_DATA_VIEW_H_ - -#include "mojo/public/cpp/bindings/lib/array_internal.h" -#include "mojo/public/cpp/bindings/lib/serialization_context.h" - -namespace mojo { - -// Access to the contents of a serialized string. -class StringDataView { - public: - StringDataView() {} - - StringDataView(internal::String_Data* data, - internal::SerializationContext* context) - : data_(data) {} - - bool is_null() const { return !data_; } - - const char* storage() const { return data_->storage(); } - - size_t size() const { return data_->size(); } - - private: - internal::String_Data* data_ = nullptr; -}; - -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_BINDINGS_STRING_DATA_VIEW_H_ diff --git a/mojo/public/cpp/bindings/string_traits.h b/mojo/public/cpp/bindings/string_traits.h index 7d3075a..a6ade6f 100644 --- a/mojo/public/cpp/bindings/string_traits.h +++ b/mojo/public/cpp/bindings/string_traits.h @@ -5,10 +5,26 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_H_ #define MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_H_ -#include "mojo/public/cpp/bindings/string_data_view.h" +#include "base/logging.h" +#include "mojo/public/cpp/bindings/lib/array_internal.h" namespace mojo { +// Access to the contents of a serialized string. +class StringDataView { + public: + explicit StringDataView(internal::String_Data* data) : data_(data) { + DCHECK(data_); + } + + const char* storage() const { return data_->storage(); } + + size_t size() const { return data_->size(); } + + private: + internal::String_Data* data_; +}; + // This must be specialized for any type |T| to be serialized/deserialized as // a mojom string. // @@ -24,7 +40,6 @@ namespace mojo { // static size_t GetSize(const CustomString& input); // static const char* GetData(const CustomString& input); // -// // The caller guarantees that |!input.is_null()|. // static bool Read(StringDataView input, CustomString* output); // }; // diff --git a/mojo/public/cpp/bindings/string_traits_standard.h b/mojo/public/cpp/bindings/string_traits_standard.h new file mode 100644 index 0000000..9b78d24 --- /dev/null +++ b/mojo/public/cpp/bindings/string_traits_standard.h @@ -0,0 +1,31 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_STANDARD_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_STANDARD_H_ + +#include "mojo/public/cpp/bindings/string.h" +#include "mojo/public/cpp/bindings/string_traits.h" + +namespace mojo { + +template <> +struct StringTraits { + static bool IsNull(const String& input) { return input.is_null(); } + static void SetToNull(String* output) { *output = nullptr; } + + static size_t GetSize(const String& input) { return input.size(); } + + static const char* GetData(const String& input) { return input.data(); } + + static bool Read(StringDataView input, String* output) { + String result(input.storage(), input.size()); + result.Swap(output); + return true; + } +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_STANDARD_H_ diff --git a/mojo/public/cpp/bindings/string_traits_string16.h b/mojo/public/cpp/bindings/string_traits_string16.h index f96973a..5a08908 100644 --- a/mojo/public/cpp/bindings/string_traits_string16.h +++ b/mojo/public/cpp/bindings/string_traits_string16.h @@ -6,13 +6,12 @@ #define MOJO_PUBLIC_CPP_BINDINGS_STRING_TRAITS_STRING16_H_ #include "base/strings/string16.h" -#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/string_traits.h" namespace mojo { template <> -struct MOJO_CPP_BINDINGS_EXPORT StringTraits { +struct StringTraits { static bool IsNull(const base::string16& input) { // base::string16 is always converted to non-null mojom string. return false; diff --git a/mojo/public/cpp/bindings/strong_binding.h b/mojo/public/cpp/bindings/strong_binding.h index f4b4a06..7fb7eea 100644 --- a/mojo/public/cpp/bindings/strong_binding.h +++ b/mojo/public/cpp/bindings/strong_binding.h @@ -5,121 +5,122 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_H_ #define MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_H_ -#include -#include #include #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 -class StrongBinding; - -template -using StrongBindingPtr = base::WeakPtr>; - // This connects an interface implementation strongly to a pipe. When a -// connection error is detected the implementation is deleted. If the task -// runner that a StrongBinding is bound on is stopped, the connection error -// handler will not be invoked and the implementation will not be deleted. +// connection error is detected the implementation is deleted. Deleting the +// connector also closes the pipe. +// +// Example of an implementation that is always bound strongly to a pipe // -// To use, call StrongBinding::Create() (see below) or the helper -// MakeStrongBinding function: +// class StronglyBound : public Foo { +// public: +// explicit StronglyBound(InterfaceRequest request) +// : binding_(this, std::move(request)) {} // -// mojo::MakeStrongBinding(base::MakeUnique(), -// std::move(foo_request)); +// // Foo implementation here // +// private: +// StrongBinding binding_; +// }; +// +// class MyFooFactory : public InterfaceFactory { +// public: +// void Create(..., InterfaceRequest 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 class StrongBinding { public: - // Create a new StrongBinding instance. The instance owns itself, cleaning up - // only in the event of a pipe connection error. Returns a WeakPtr to the new - // StrongBinding instance. - static StrongBindingPtr Create( - std::unique_ptr impl, - InterfaceRequest request) { - StrongBinding* binding = - new StrongBinding(std::move(impl), std::move(request)); - return binding->weak_factory_.GetWeakPtr(); + explicit StrongBinding(Interface* impl) : binding_(impl) {} + + StrongBinding(Interface* impl, ScopedMessagePipeHandle handle) + : StrongBinding(impl) { + Bind(std::move(handle)); } - // 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, InterfacePtr* ptr) + : StrongBinding(impl) { + Bind(ptr); } - 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(Interface* impl, InterfaceRequest request) + : StrongBinding(impl) { + Bind(std::move(request)); } - // Forces the binding to close. This destroys the StrongBinding instance. - void Close() { delete this; } + ~StrongBinding() {} - Interface* impl() { return impl_.get(); } + 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))); + } - // 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(InterfacePtr* ptr) { + DCHECK(!binding_.is_bound()); + binding_.Bind(ptr); + binding_.set_connection_error_handler( + base::Bind(&StrongBinding::OnConnectionError, base::Unretained(this))); + } - private: - StrongBinding(std::unique_ptr impl, - InterfaceRequest request) - : impl_(std::move(impl)), - binding_(impl_.get(), std::move(request)), - weak_factory_(this) { - binding_.set_connection_error_with_reason_handler( + void Bind(InterfaceRequest request) { + DCHECK(!binding_.is_bound()); + binding_.Bind(std::move(request)); + binding_.set_connection_error_handler( base::Bind(&StrongBinding::OnConnectionError, base::Unretained(this))); } - ~StrongBinding() {} + 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(); } - void OnConnectionError(uint32_t custom_reason, - const std::string& description) { + void OnConnectionError() { if (!connection_error_handler_.is_null()) connection_error_handler_.Run(); - else if (!connection_error_with_reason_handler_.is_null()) - connection_error_with_reason_handler_.Run(custom_reason, description); - Close(); + delete binding_.impl(); } - std::unique_ptr impl_; + private: base::Closure connection_error_handler_; - ConnectionErrorWithReasonCallback connection_error_with_reason_handler_; Binding binding_; - base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(StrongBinding); }; -template -StrongBindingPtr MakeStrongBinding( - std::unique_ptr impl, - InterfaceRequest request) { - return StrongBinding::Create(std::move(impl), std::move(request)); -} - } // namespace mojo #endif // MOJO_PUBLIC_CPP_BINDINGS_STRONG_BINDING_H_ diff --git a/mojo/public/cpp/bindings/struct_ptr.h b/mojo/public/cpp/bindings/struct_ptr.h index b135312..92f2728 100644 --- a/mojo/public/cpp/bindings/struct_ptr.h +++ b/mojo/public/cpp/bindings/struct_ptr.h @@ -5,26 +5,23 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_ #define MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_ -#include -#include #include #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 -class StructPtrWTFHelper; - template -class InlinedStructPtrWTFHelper; +class StructHelper { + public: + template + static void Initialize(Ptr* ptr) { + ptr->Initialize(); + } +}; } // namespace internal @@ -34,34 +31,35 @@ class StructPtr { public: using Struct = S; - StructPtr() = default; - StructPtr(decltype(nullptr)) {} + StructPtr() : ptr_(nullptr) {} + StructPtr(decltype(nullptr)) : ptr_(nullptr) {} - ~StructPtr() = default; + ~StructPtr() { delete ptr_; } StructPtr& operator=(decltype(nullptr)) { reset(); return *this; } - StructPtr(StructPtr&& other) { Take(&other); } + StructPtr(StructPtr&& other) : ptr_(nullptr) { Take(&other); } StructPtr& operator=(StructPtr&& other) { Take(&other); return *this; } - template - StructPtr(base::in_place_t, Args&&... args) - : ptr_(new Struct(std::forward(args)...)) {} - template U To() const { return TypeConverter::Convert(*this); } - void reset() { ptr_.reset(); } + void reset() { + if (ptr_) { + delete ptr_; + ptr_ = nullptr; + } + } - bool is_null() const { return !ptr_; } + bool is_null() const { return ptr_ == nullptr; } Struct& operator*() const { DCHECK(ptr_); @@ -69,9 +67,9 @@ class StructPtr { } Struct* operator->() const { DCHECK(ptr_); - return ptr_.get(); + return ptr_; } - Struct* get() const { return ptr_.get(); } + Struct* get() const { return ptr_; } void Swap(StructPtr* other) { std::swap(ptr_, other->ptr_); } @@ -80,52 +78,52 @@ 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_); } - // Hashes based on the pointee (which might be null). - size_t Hash(size_t seed) const { - if (is_null()) - return internal::HashCombine(seed, 0); - return ptr_->Hash(seed); - } + private: + // TODO(dcheng): Use an explicit conversion operator. + typedef Struct* StructPtr::*Testable; - explicit operator bool() const { return !is_null(); } + public: + operator Testable() const { return ptr_ ? &StructPtr::ptr_ : 0; } private: - friend class internal::StructPtrWTFHelper; + friend class internal::StructHelper; + + // Forbid the == and != operators explicitly, otherwise StructPtr will be + // converted to Testable to do == or != comparison. + template + bool operator==(const StructPtr& other) const = delete; + template + bool operator!=(const StructPtr& other) const = delete; + + void Initialize() { + DCHECK(!ptr_); + ptr_ = new Struct(); + } + void Take(StructPtr* other) { reset(); Swap(other); } - std::unique_ptr ptr_; + Struct* ptr_; DISALLOW_COPY_AND_ASSIGN(StructPtr); }; -template -bool operator==(const StructPtr& lhs, const StructPtr& rhs) { - return lhs.Equals(rhs); -} -template -bool operator!=(const StructPtr& lhs, const StructPtr& rhs) { - return !(lhs == rhs); -} - // Designed to be used when Struct is small and copyable. template class InlinedStructPtr { public: using Struct = S; - InlinedStructPtr() : state_(NIL) {} - InlinedStructPtr(decltype(nullptr)) : state_(NIL) {} + InlinedStructPtr() : is_null_(true) {} + InlinedStructPtr(decltype(nullptr)) : is_null_(true) {} ~InlinedStructPtr() {} @@ -134,150 +132,79 @@ class InlinedStructPtr { return *this; } - InlinedStructPtr(InlinedStructPtr&& other) : state_(NIL) { Take(&other); } + InlinedStructPtr(InlinedStructPtr&& other) : is_null_(true) { Take(&other); } InlinedStructPtr& operator=(InlinedStructPtr&& other) { Take(&other); return *this; } - template - InlinedStructPtr(base::in_place_t, Args&&... args) - : value_(std::forward(args)...), state_(VALID) {} - template U To() const { return TypeConverter::Convert(*this); } void reset() { - state_ = NIL; + is_null_ = true; value_. ~Struct(); new (&value_) Struct(); } - bool is_null() const { return state_ == NIL; } + bool is_null() const { return is_null_; } Struct& operator*() const { - DCHECK(state_ == VALID); + DCHECK(!is_null_); return value_; } Struct* operator->() const { - DCHECK(state_ == VALID); + DCHECK(!is_null_); return &value_; } Struct* get() const { return &value_; } void Swap(InlinedStructPtr* other) { std::swap(value_, other->value_); - std::swap(state_, other->state_); + std::swap(is_null_, other->is_null_); } InlinedStructPtr Clone() const { return is_null() ? InlinedStructPtr() : value_.Clone(); } - - // Compares the pointees (which might both be null). bool Equals(const InlinedStructPtr& other) const { if (is_null() || other.is_null()) return is_null() && other.is_null(); return value_.Equals(other.value_); } - // Hashes based on the pointee (which might be null). - size_t Hash(size_t seed) const { - if (is_null()) - return internal::HashCombine(seed, 0); - return value_.Hash(seed); - } + private: + // TODO(dcheng): Use an explicit conversion operator. + typedef Struct InlinedStructPtr::*Testable; - explicit operator bool() const { return !is_null(); } + public: + operator Testable() const { return is_null_ ? 0 : &InlinedStructPtr::value_; } private: - friend class internal::InlinedStructPtrWTFHelper; + friend class internal::StructHelper; + + // Forbid the == and != operators explicitly, otherwise InlinedStructPtr will + // be converted to Testable to do == or != comparison. + template + bool operator==(const InlinedStructPtr& other) const = delete; + template + bool operator!=(const InlinedStructPtr& other) const = delete; + + void Initialize() { is_null_ = false; } + void Take(InlinedStructPtr* other) { reset(); Swap(other); } - enum State { - VALID, - NIL, - DELETED, // For use in WTF::HashMap only - }; - mutable Struct value_; - State state_; + bool is_null_; DISALLOW_COPY_AND_ASSIGN(InlinedStructPtr); }; -template -bool operator==(const InlinedStructPtr& lhs, - const InlinedStructPtr& rhs) { - return lhs.Equals(rhs); -} -template -bool operator!=(const InlinedStructPtr& lhs, - const InlinedStructPtr& rhs) { - return !(lhs == rhs); -} - -namespace internal { - -template -class StructPtrWTFHelper { - public: - static bool IsHashTableDeletedValue(const StructPtr& value) { - return value.ptr_.get() == reinterpret_cast(1u); - } - - static void ConstructDeletedValue(mojo::StructPtr& 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(); - slot.ptr_.reset(reinterpret_cast(1u)); - } -}; - -template -class InlinedStructPtrWTFHelper { - public: - static bool IsHashTableDeletedValue(const InlinedStructPtr& value) { - return value.state_ == InlinedStructPtr::DELETED; - } - - static void ConstructDeletedValue(mojo::InlinedStructPtr& 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(); - slot.state_ = InlinedStructPtr::DELETED; - } -}; - -} // namespace internal } // namespace mojo -namespace std { - -template -struct hash> { - size_t operator()(const mojo::StructPtr& value) const { - return value.Hash(mojo::internal::kHashSeed); - } -}; - -template -struct hash> { - size_t operator()(const mojo::InlinedStructPtr& value) const { - return value.Hash(mojo::internal::kHashSeed); - } -}; - -} // namespace std - #endif // MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_ diff --git a/mojo/public/cpp/bindings/struct_traits.h b/mojo/public/cpp/bindings/struct_traits.h index 6cc070f..0f0bea7 100644 --- a/mojo/public/cpp/bindings/struct_traits.h +++ b/mojo/public/cpp/bindings/struct_traits.h @@ -8,11 +8,7 @@ namespace mojo { // This must be specialized for any type |T| to be serialized/deserialized as -// a mojom struct. |DataViewType| is the corresponding data view type of the -// mojom struct. For example, if the mojom struct is example.Foo, -// |DataViewType| will be example::FooDataView, which can also be referred to by -// example::Foo::DataView (in chromium) and example::blink::Foo::DataView (in -// blink). +// a mojom struct of type |MojomType|. // // Each specialization needs to implement a few things: // 1. Static getters for each field in the Mojom type. These should be @@ -24,21 +20,19 @@ namespace mojo { // from |input|. // // Serializable form of a field: -// Value or reference of the same type used in the generated stuct -// wrapper type, or the following alternatives: +// Value or reference of the same type used in |MojomType|, or the +// following alternatives: // - string: // Value or reference of any type that has a StringTraits defined. -// Supported by default: base::StringPiece, std::string, -// WTF::String (in blink). +// Supported by default: base::StringPiece, std::string. // // - array: // Value or reference of any type that has an ArrayTraits defined. -// Supported by default: std::vector, CArray, WTF::Vector (in blink) +// Supported by default: std::vector, WTF::Vector (in blink), CArray. // // - map: // Value or reference of any type that has a MapTraits defined. -// Supported by default: std::map, std::unordered_map, -// WTF::HashMap (in blink). +// Supported by default: std::map. // // - struct: // Value or reference of any type that has a StructTraits defined. @@ -46,10 +40,6 @@ 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/WTF::Optional, if T -// has the right *Traits defined. -// // During serialization, getters for string/struct/array/map/union fields // are called twice (one for size calculation and one for actual // serialization). If you want to return a value (as opposed to a @@ -59,13 +49,13 @@ 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 -// DataViewType. +// |MojomType|DataView (e.g., if |MojomType| is test::Example, the data +// view will be test::ExampleDataView). // -// static bool Read(DataViewType data, T* output); +// static bool Read(|MojomType|DataView data, T* output); // -// The generated DataViewType provides a convenient, inexpensive view of a -// serialized struct's field data. The caller guarantees that -// |!data.is_null()|. +// The generated |MojomType|DataView type provides a convenient, +// inexpensive view of a serialized struct's field data. // // Returning false indicates invalid incoming data and causes the message // pipe receiving it to be disconnected. Therefore, you can do custom @@ -121,12 +111,9 @@ 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[move_only]" ] -// or -// type_mappings = [ "MojomType=T[copyable_pass_by_value]" ] -// -// - if another type U's StructTraits/UnionTraits has a getter for T, it -// needs to return non-const reference/value. +// type_mappings = [ "MojomType=T(pass_by_value)" ] +// - if another type U's StructTraits has a getter for T, it needs to return +// non-const reference/value. // // EXAMPLE: // @@ -141,7 +128,7 @@ namespace mojo { // // StructTraits for Foo: // template <> -// struct StructTraits { +// struct StructTraits { // // Optional methods dealing with null: // static bool IsNull(const CustomFoo& input); // static void SetToNull(CustomFoo* output); @@ -157,7 +144,7 @@ namespace mojo { // static bool Read(FooDataView data, CustomFoo* output); // }; // -template +template struct StructTraits; } // namespace mojo diff --git a/mojo/public/cpp/bindings/sync_call_restrictions.h b/mojo/public/cpp/bindings/sync_call_restrictions.h index 39a77a8..e8fc1c4 100644 --- a/mojo/public/cpp/bindings/sync_call_restrictions.h +++ b/mojo/public/cpp/bindings/sync_call_restrictions.h @@ -7,7 +7,6 @@ #include "base/macros.h" #include "base/threading/thread_restrictions.h" -#include "mojo/public/cpp/bindings/bindings_export.h" #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) #define ENABLE_SYNC_CALL_RESTRICTIONS 1 @@ -15,12 +14,8 @@ #define ENABLE_SYNC_CALL_RESTRICTIONS 0 #endif -namespace leveldb { -class LevelDBMojoProxy; -} - namespace ui { -class Gpu; +class GpuService; } namespace views { @@ -41,7 +36,7 @@ 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 MOJO_CPP_BINDINGS_EXPORT SyncCallRestrictions { +class SyncCallRestrictions { public: #if ENABLE_SYNC_CALL_RESTRICTIONS // Checks whether the current thread is allowed to make sync calls, and causes @@ -55,9 +50,7 @@ class MOJO_CPP_BINDINGS_EXPORT SyncCallRestrictions { private: // DO NOT ADD ANY OTHER FRIEND STATEMENTS, talk to mojo/OWNERS first. // BEGIN ALLOWED USAGE. - friend class ui::Gpu; // http://crbug.com/620058 - // LevelDBMojoProxy makes same-process sync calls from the DB thread. - friend class leveldb::LevelDBMojoProxy; + friend class ui::GpuService; // http://crbug.com/620058 // END ALLOWED USAGE. // BEGIN USAGE THAT NEEDS TO BE FIXED. diff --git a/mojo/public/cpp/bindings/sync_handle_registry.h b/mojo/public/cpp/bindings/sync_handle_registry.h index b5415af..6c0701e 100644 --- a/mojo/public/cpp/bindings/sync_handle_registry.h +++ b/mojo/public/cpp/bindings/sync_handle_registry.h @@ -11,7 +11,6 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/threading/thread_checker.h" -#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/system/core.h" namespace mojo { @@ -20,8 +19,7 @@ namespace mojo { // be watched together. // // This class is not thread safe. -class MOJO_CPP_BINDINGS_EXPORT SyncHandleRegistry - : public base::RefCounted { +class SyncHandleRegistry : public base::RefCounted { public: // Returns a thread-local object. static scoped_refptr current(); diff --git a/mojo/public/cpp/bindings/sync_handle_watcher.h b/mojo/public/cpp/bindings/sync_handle_watcher.h index eff73dd..36b796b 100644 --- a/mojo/public/cpp/bindings/sync_handle_watcher.h +++ b/mojo/public/cpp/bindings/sync_handle_watcher.h @@ -8,7 +8,6 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/threading/thread_checker.h" -#include "mojo/public/cpp/bindings/bindings_export.h" #include "mojo/public/cpp/bindings/sync_handle_registry.h" #include "mojo/public/cpp/system/core.h" @@ -26,7 +25,7 @@ namespace mojo { // associated endpoints on different threads. // // This class is not thread safe. -class MOJO_CPP_BINDINGS_EXPORT SyncHandleWatcher { +class SyncHandleWatcher { public: // Note: |handle| must outlive this object. SyncHandleWatcher(const Handle& handle, diff --git a/mojo/public/cpp/bindings/thread_safe_interface_ptr.h b/mojo/public/cpp/bindings/thread_safe_interface_ptr.h deleted file mode 100644 index bab6d22..0000000 --- a/mojo/public/cpp/bindings/thread_safe_interface_ptr.h +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_THREAD_SAFE_INTERFACE_PTR_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_THREAD_SAFE_INTERFACE_PTR_H_ - -#include - -#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 -class ThreadSafeForwarder : public MessageReceiverWithResponder { - public: - using ProxyType = typename Interface::Proxy_; - using ForwardMessageCallback = base::Callback; - using ForwardMessageWithResponderCallback = - base::Callback)>; - - // 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& 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 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( - 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 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 responder, - Message message) { - ignore_result(responder->Accept(&message)); - } - - std::unique_ptr responder_; - scoped_refptr caller_task_runner_; - }; - - ProxyType proxy_; - const scoped_refptr task_runner_; - const ForwardMessageCallback forward_; - const ForwardMessageWithResponderCallback forward_with_responder_; - AssociatedGroup associated_group_; - - DISALLOW_COPY_AND_ASSIGN(ThreadSafeForwarder); -}; - -template -class ThreadSafeInterfacePtrBase - : public base::RefCountedThreadSafe< - ThreadSafeInterfacePtrBase> { - public: - using InterfaceType = typename InterfacePtrType::InterfaceType; - using PtrInfoType = typename InterfacePtrType::PtrInfoType; - - explicit ThreadSafeInterfacePtrBase( - std::unique_ptr> 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 Create( - InterfacePtrType interface_ptr) { - scoped_refptr 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 Create( - PtrInfoType ptr_info, - const scoped_refptr& bind_task_runner) { - scoped_refptr 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>; - - 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 { - 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& task_runner) - : task_runner_(task_runner) {} - - void BindOnTaskRunner(AssociatedInterfacePtrInfo 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 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> CreateForwarder() { - return base::MakeUnique>( - 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 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 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> forwarder_; - - DISALLOW_COPY_AND_ASSIGN(ThreadSafeInterfacePtrBase); -}; - -template -using ThreadSafeAssociatedInterfacePtr = - ThreadSafeInterfacePtrBase>; - -template -using ThreadSafeInterfacePtr = - ThreadSafeInterfacePtrBase>; - -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_BINDINGS_THREAD_SAFE_INTERFACE_PTR_H_ diff --git a/mojo/public/cpp/bindings/type_converter.h b/mojo/public/cpp/bindings/type_converter.h index 395eeb4..1446ab3 100644 --- a/mojo/public/cpp/bindings/type_converter.h +++ b/mojo/public/cpp/bindings/type_converter.h @@ -7,15 +7,8 @@ #include -#include - 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 struct TypeConverter; // to perform type conversion for Mojom-defined structs and arrays. Here, T is @@ -81,9 +74,6 @@ namespace mojo { template struct TypeConverter; -template -inline T ConvertTo(const U& obj); - // The following specialization is useful when you are converting between // Array and std::vector. template @@ -91,18 +81,6 @@ struct TypeConverter { static T Convert(const T& obj) { return obj; } }; -template -struct TypeConverter, Container> { - static std::vector Convert(const Container& container) { - std::vector output; - output.reserve(container.size()); - for (const auto& obj : container) { - output.push_back(ConvertTo(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(input); diff --git a/mojo/public/cpp/bindings/union_traits.h b/mojo/public/cpp/bindings/union_traits.h deleted file mode 100644 index 292ee58..0000000 --- a/mojo/public/cpp/bindings/union_traits.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_BINDINGS_UNION_TRAITS_H_ -#define MOJO_PUBLIC_CPP_BINDINGS_UNION_TRAITS_H_ - -namespace mojo { - -// This must be specialized for any type |T| to be serialized/deserialized as -// a mojom union. |DataViewType| is the corresponding data view type of the -// mojom union. For example, if the mojom union is example.Foo, |DataViewType| -// will be example::FooDataView, which can also be referred to by -// example::Foo::DataView (in chromium) and example::blink::Foo::DataView (in -// blink). -// -// Similar to StructTraits, each specialization of UnionTraits implements the -// following methods: -// 1. Getters for each field in the Mojom type. -// 2. Read() method. -// 3. [Optional] IsNull() and SetToNull(). -// 4. [Optional] SetUpContext() and TearDownContext(). -// Please see the documentation of StructTraits for details of these methods. -// -// Unlike StructTraits, there is one more method to implement: -// 5. A static GetTag() method indicating which field is the current active -// field for serialization: -// -// static DataViewType::Tag GetTag(const T& input); -// -// During serialization, only the field getter corresponding to this tag -// will be called. -// -template -struct UnionTraits; - -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_BINDINGS_UNION_TRAITS_H_ diff --git a/mojo/public/cpp/bindings/wtf_array.h b/mojo/public/cpp/bindings/wtf_array.h new file mode 100644 index 0000000..46d9a69 --- /dev/null +++ b/mojo/public/cpp/bindings/wtf_array.h @@ -0,0 +1,197 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_WTF_ARRAY_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_WTF_ARRAY_H_ + +#include +#include + +#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 and mojo::WTFArray: +// - constructor WTFArray(WTF::Vector&&) takes the contents of a +// WTF::Vector; +// - method PassStorage() passes the underlying WTF::Vector. +template +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&& other) : vec_(std::move(other)), is_null_(false) {} + WTFArray(WTFArray&& other) : is_null_(true) { Take(&other); } + + WTFArray& operator=(WTF::Vector&& 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 + static WTFArray From(const U& other) { + return TypeConverter::Convert(other); + } + + // Copies the contents of this array to a new object of type |U|. + template + U To() const { + return TypeConverter::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& 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 after we move to MSVC 2015. + WTF::Vector 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* 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 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 + bool operator==(const WTFArray& other) const = delete; + template + bool operator!=(const WTFArray& other) const = delete; + + void Take(WTFArray* other) { + operator=(nullptr); + Swap(other); + } + + WTF::Vector vec_; + bool is_null_; + + DISALLOW_COPY_AND_ASSIGN(WTFArray); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_WTF_ARRAY_H_ diff --git a/mojo/public/cpp/bindings/wtf_map.h b/mojo/public/cpp/bindings/wtf_map.h new file mode 100644 index 0000000..0aba959 --- /dev/null +++ b/mojo/public/cpp/bindings/wtf_map.h @@ -0,0 +1,200 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_CPP_BINDINGS_WTF_MAP_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_WTF_MAP_H_ + +#include +#include + +#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 and mojo::WTFMap: +// - constructor WTFMap(WTF::HashMap&&) takes the contents of a +// WTF::HashMap; +// - 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 +class WTFMap { + public: + using Iterator = typename WTF::HashMap::iterator; + using ConstIterator = typename WTF::HashMap::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&& other) + : map_(std::move(other)), is_null_(false) {} + WTFMap(WTFMap&& other) : is_null_(true) { Take(&other); } + + WTFMap& operator=(WTF::HashMap&& 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::isValidKey(key); + } + + // Copies the contents of some other type of map into a new WTFMap using a + // TypeConverter. + template + static WTFMap From(const U& other) { + return TypeConverter::Convert(other); + } + + // Copies the contents of the WTFMap into some other type of map. + template + U To() const { + return TypeConverter::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& storage() const { return map_; } + + // Passes the underlying storage and resets this map to null. + WTF::HashMap 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* 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* 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 map_; + bool is_null_; + + DISALLOW_COPY_AND_ASSIGN(WTFMap); +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_WTF_MAP_H_ -- cgit v1.2.3