diff options
99 files changed, 2057 insertions, 217 deletions
diff --git a/content/browser/geolocation/geolocation_service_impl.cc b/content/browser/geolocation/geolocation_service_impl.cc index ffea846..7747cc4 100644 --- a/content/browser/geolocation/geolocation_service_impl.cc +++ b/content/browser/geolocation/geolocation_service_impl.cc @@ -69,6 +69,14 @@ GeolocationServiceImpl::GeolocationServiceImpl( } GeolocationServiceImpl::~GeolocationServiceImpl() { + // Make sure to respond to any pending callback even without a valid position. + if (!position_callback_.is_null()) { + if (!current_position_.valid) { + current_position_.error_code = MojoGeoposition::ErrorCode( + GEOPOSITION_ERROR_CODE_POSITION_UNAVAILABLE); + } + ReportCurrentPosition(); + } } void GeolocationServiceImpl::PauseUpdates() { diff --git a/content/browser/presentation/presentation_service_impl.cc b/content/browser/presentation/presentation_service_impl.cc index 9e2fd22..2d6769e 100644 --- a/content/browser/presentation/presentation_service_impl.cc +++ b/content/browser/presentation/presentation_service_impl.cc @@ -25,6 +25,7 @@ PresentationServiceImpl::PresentationServiceImpl( : WebContentsObserver(web_contents), render_frame_host_(render_frame_host), delegate_(delegate), + is_start_session_pending_(false), next_request_session_id_(0), weak_factory_(this) { DCHECK(render_frame_host_); @@ -39,6 +40,7 @@ PresentationServiceImpl::PresentationServiceImpl( PresentationServiceImpl::~PresentationServiceImpl() { if (delegate_) delegate_->RemoveObserver(this); + FlushNewSessionCallbacks(); } // static @@ -146,10 +148,13 @@ void PresentationServiceImpl::StartSession( return; } - queued_start_session_requests_.push_back(make_linked_ptr( - new StartSessionRequest(presentation_url, presentation_id, callback))); - if (queued_start_session_requests_.size() == 1) - DoStartSession(presentation_url, presentation_id, callback); + if (is_start_session_pending_) { + queued_start_session_requests_.push_back(make_linked_ptr( + new StartSessionRequest(presentation_url, presentation_id, callback))); + return; + } + + DoStartSession(presentation_url, presentation_id, callback); } void PresentationServiceImpl::JoinSession( @@ -175,15 +180,16 @@ void PresentationServiceImpl::JoinSession( } void PresentationServiceImpl::HandleQueuedStartSessionRequests() { - DCHECK(!queued_start_session_requests_.empty()); - queued_start_session_requests_.pop_front(); - if (!queued_start_session_requests_.empty()) { - const linked_ptr<StartSessionRequest>& request = - queued_start_session_requests_.front(); - DoStartSession(request->presentation_url, - request->presentation_id, - request->callback); + if (queued_start_session_requests_.empty()) { + is_start_session_pending_ = false; + return; } + linked_ptr<StartSessionRequest> request = + queued_start_session_requests_.front(); + queued_start_session_requests_.pop_front(); + DoStartSession(request->presentation_url(), + request->presentation_id(), + request->PassCallback()); } int PresentationServiceImpl::RegisterNewSessionCallback( @@ -194,11 +200,19 @@ int PresentationServiceImpl::RegisterNewSessionCallback( return next_request_session_id_; } +void PresentationServiceImpl::FlushNewSessionCallbacks() { + for (auto& pending_entry : pending_session_cbs_) { + InvokeNewSessionMojoCallbackWithError(*pending_entry.second); + } + pending_session_cbs_.clear(); +} + void PresentationServiceImpl::DoStartSession( const std::string& presentation_url, const std::string& presentation_id, const NewSessionMojoCallback& callback) { int request_session_id = RegisterNewSessionCallback(callback); + is_start_session_pending_ = true; delegate_->StartSession( render_frame_host_->GetProcess()->GetID(), render_frame_host_->GetRoutingID(), @@ -361,20 +375,12 @@ void PresentationServiceImpl::Reset() { default_presentation_url_.clear(); default_presentation_id_.clear(); - for (const auto& context_entry : availability_contexts_) { - context_entry.second->OnScreenAvailabilityChanged(false); - } availability_contexts_.clear(); - for (auto& request_ptr : queued_start_session_requests_) { - InvokeNewSessionMojoCallbackWithError(request_ptr->callback); - } queued_start_session_requests_.clear(); - for (auto& pending_entry : pending_session_cbs_) { - InvokeNewSessionMojoCallbackWithError(*pending_entry.second); - } - pending_session_cbs_.clear(); + FlushNewSessionCallbacks(); } +// static void PresentationServiceImpl::InvokeNewSessionMojoCallbackWithError( const NewSessionMojoCallback& callback) { callback.Run( @@ -396,6 +402,8 @@ PresentationServiceImpl::ScreenAvailabilityContext::ScreenAvailabilityContext( PresentationServiceImpl::ScreenAvailabilityContext:: ~ScreenAvailabilityContext() { + // Ensure that pending callbacks are flushed. + OnScreenAvailabilityChanged(false); } void PresentationServiceImpl::ScreenAvailabilityContext::CallbackReceived( @@ -452,12 +460,22 @@ PresentationServiceImpl::StartSessionRequest::StartSessionRequest( const std::string& presentation_url, const std::string& presentation_id, const NewSessionMojoCallback& callback) - : presentation_url(presentation_url), - presentation_id(presentation_id), - callback(callback) { + : presentation_url_(presentation_url), + presentation_id_(presentation_id), + callback_(callback) { } PresentationServiceImpl::StartSessionRequest::~StartSessionRequest() { + // Ensure that a pending callback is not dropped. + if (!callback_.is_null()) + InvokeNewSessionMojoCallbackWithError(callback_); +} + +PresentationServiceImpl::NewSessionMojoCallback +PresentationServiceImpl::StartSessionRequest::PassCallback() { + NewSessionMojoCallback callback = callback_; + callback_.reset(); + return callback; } } // namespace content diff --git a/content/browser/presentation/presentation_service_impl.h b/content/browser/presentation/presentation_service_impl.h index 5b4972d..84962e7 100644 --- a/content/browser/presentation/presentation_service_impl.h +++ b/content/browser/presentation/presentation_service_impl.h @@ -110,15 +110,24 @@ class CONTENT_EXPORT PresentationServiceImpl }; // Context for a StartSession request. - struct CONTENT_EXPORT StartSessionRequest { + class CONTENT_EXPORT StartSessionRequest { + public: StartSessionRequest(const std::string& presentation_url, const std::string& presentation_id, const NewSessionMojoCallback& callback); ~StartSessionRequest(); - const std::string presentation_url; - const std::string presentation_id; - const NewSessionMojoCallback callback; + // Retrieves the pending callback from this request, transferring ownership + // to the caller. + NewSessionMojoCallback PassCallback(); + + const std::string& presentation_url() const { return presentation_url_; } + const std::string& presentation_id() const { return presentation_id_; } + + private: + const std::string presentation_url_; + const std::string presentation_id_; + NewSessionMojoCallback callback_; }; friend class PresentationServiceImplTest; @@ -235,8 +244,11 @@ class CONTENT_EXPORT PresentationServiceImpl int RegisterNewSessionCallback( const NewSessionMojoCallback& callback); + // Flushes all pending new session callbacks with error responses. + void FlushNewSessionCallbacks(); + // Invokes |callback| with an error. - void InvokeNewSessionMojoCallbackWithError( + static void InvokeNewSessionMojoCallbackWithError( const NewSessionMojoCallback& callback); // Gets the ScreenAvailabilityContext for |presentation_url|, or creates one @@ -259,6 +271,9 @@ class CONTENT_EXPORT PresentationServiceImpl // it is removed from head of the queue. std::deque<linked_ptr<StartSessionRequest>> queued_start_session_requests_; + // Indicates that a StartSession request is currently being processed. + bool is_start_session_pending_; + int next_request_session_id_; base::hash_map<int, linked_ptr<NewSessionMojoCallback>> pending_session_cbs_; diff --git a/third_party/mojo/mojom_bindings_generator.gypi b/third_party/mojo/mojom_bindings_generator.gypi index e18ab05..948ce059 100644 --- a/third_party/mojo/mojom_bindings_generator.gypi +++ b/third_party/mojo/mojom_bindings_generator.gypi @@ -57,6 +57,7 @@ '<@(mojom_import_args)', '-o', '<(SHARED_INTERMEDIATE_DIR)', '--java_output_directory=<(java_out_dir)', + '--dart_mojo_root=//third_party/mojo/src', ], 'message': 'Generating Mojo bindings from <(RULE_INPUT_DIRNAME)/<(RULE_INPUT_ROOT).mojom', 'process_outputs_as_sources': 1, diff --git a/third_party/mojo/mojom_bindings_generator_explicit.gypi b/third_party/mojo/mojom_bindings_generator_explicit.gypi index f65ec3e..3d2d80b 100644 --- a/third_party/mojo/mojom_bindings_generator_explicit.gypi +++ b/third_party/mojo/mojom_bindings_generator_explicit.gypi @@ -60,6 +60,7 @@ '<@(mojom_import_args)', '-o', '<(SHARED_INTERMEDIATE_DIR)', '--java_output_directory=<(java_out_dir)', + '--dart_mojo_root=//third_party/mojo/src', ], 'message': 'Generating Mojo bindings from <@(mojom_files)', } diff --git a/third_party/mojo/src/mojo/public/BUILD.gn b/third_party/mojo/src/mojo/public/BUILD.gn index 9ee041e..576625f 100644 --- a/third_party/mojo/src/mojo/public/BUILD.gn +++ b/third_party/mojo/src/mojo/public/BUILD.gn @@ -38,6 +38,7 @@ group("sdk") { "cpp/environment:standalone", "cpp/utility", "interfaces/application", + "interfaces/bindings", "js", ] } diff --git a/third_party/mojo/src/mojo/public/VERSION b/third_party/mojo/src/mojo/public/VERSION index 6979599..ba0a3bc 100644 --- a/third_party/mojo/src/mojo/public/VERSION +++ b/third_party/mojo/src/mojo/public/VERSION @@ -1 +1 @@ -912f52f69dadbe1e3cf9576f26731863770bcfc3
\ No newline at end of file +d459e688a608f6eda850a23bb5e308a76ea51a47
\ No newline at end of file diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/BUILD.gn b/third_party/mojo/src/mojo/public/cpp/bindings/BUILD.gn index 2dac828..f6a9b1b 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/BUILD.gn +++ b/third_party/mojo/src/mojo/public/cpp/bindings/BUILD.gn @@ -9,16 +9,9 @@ mojo_sdk_source_set("bindings") { "array.h", "binding.h", "error_handler.h", - "interface_ptr_info.h", "interface_ptr.h", - "map.h", - "message.h", - "message_filter.h", - "no_interface.h", - "strong_binding.h", - "string.h", - "struct_ptr.h", - "type_converter.h", + "interface_ptr_info.h", + "interface_request.h", "lib/array_internal.cc", "lib/array_internal.h", "lib/array_serialization.h", @@ -55,6 +48,14 @@ mojo_sdk_source_set("bindings") { "lib/validate_params.h", "lib/validation_errors.cc", "lib/validation_errors.h", + "map.h", + "message.h", + "message_filter.h", + "no_interface.h", + "string.h", + "strong_binding.h", + "struct_ptr.h", + "type_converter.h", ] deps = [ @@ -71,9 +72,9 @@ mojo_sdk_source_set("callback") { sources = [ "callback.h", "lib/callback_internal.h", - "lib/template_util.h", "lib/shared_data.h", "lib/shared_ptr.h", + "lib/template_util.h", ] mojo_sdk_deps = [ "mojo/public/cpp/system" ] diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/array_internal.h b/third_party/mojo/src/mojo/public/cpp/bindings/lib/array_internal.h index eb25e35..f56319b 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/array_internal.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/array_internal.h @@ -285,8 +285,13 @@ struct ArraySerializationHelper<P*, false> { } private: + template <typename T, + typename Params, + bool is_union = IsUnionDataType<T>::value> + struct ValidateCaller {}; + template <typename T, typename Params> - struct ValidateCaller { + struct ValidateCaller<T, Params, false> { static bool Run(const void* data, BoundsChecker* bounds_checker) { static_assert((IsSame<Params, NoValidateParams>::value), "Struct type should not have array validate params"); @@ -295,8 +300,18 @@ struct ArraySerializationHelper<P*, false> { } }; + template <typename T, typename Params> + struct ValidateCaller<T, Params, true> { + static bool Run(const void* data, BoundsChecker* bounds_checker) { + static_assert((IsSame<Params, NoValidateParams>::value), + "Union type should not have array validate params"); + + return T::Validate(data, bounds_checker, true); + } + }; + template <typename Key, typename Value, typename Params> - struct ValidateCaller<Map_Data<Key, Value>, Params> { + struct ValidateCaller<Map_Data<Key, Value>, Params, false> { static bool Run(const void* data, BoundsChecker* bounds_checker) { return Map_Data<Key, Value>::template Validate<Params>(data, bounds_checker); @@ -304,7 +319,7 @@ struct ArraySerializationHelper<P*, false> { }; template <typename T, typename Params> - struct ValidateCaller<Array_Data<T>, Params> { + struct ValidateCaller<Array_Data<T>, Params, false> { static bool Run(const void* data, BoundsChecker* bounds_checker) { return Array_Data<T>::template Validate<Params>(data, bounds_checker); } diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/array_serialization.h b/third_party/mojo/src/mojo/public/cpp/bindings/lib/array_serialization.h index eacf298..827cb3f 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/array_serialization.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/array_serialization.h @@ -45,11 +45,16 @@ inline void Deserialize_(internal::Array_Data<F>* data, Array<E>* output); namespace internal { -template <typename E, typename F, bool move_only = IsMoveOnlyType<E>::value> +template <typename E, + typename F, + bool move_only = IsMoveOnlyType<E>::value, + bool is_union = + IsUnionDataType<typename RemovePointer<F>::type>::value> struct ArraySerializer; +// Handles serialization and deserialization of arrays of pod types. template <typename E, typename F> -struct ArraySerializer<E, F, false> { +struct ArraySerializer<E, F, false, false> { static_assert(sizeof(E) == sizeof(F), "Incorrect array serializer"); static size_t GetSerializedSize(const Array<E>& input) { return sizeof(Array_Data<F>) + Align(input.size() * sizeof(E)); @@ -74,8 +79,9 @@ struct ArraySerializer<E, F, false> { } }; +// Serializes and deserializes arrays of bools. template <> -struct ArraySerializer<bool, bool, false> { +struct ArraySerializer<bool, bool, false, false> { static size_t GetSerializedSize(const Array<bool>& input) { return sizeof(Array_Data<bool>) + Align((input.size() + 7) / 8); } @@ -102,8 +108,9 @@ struct ArraySerializer<bool, bool, false> { } }; +// Serializes and deserializes arrays of handles. template <typename H> -struct ArraySerializer<ScopedHandleBase<H>, H, true> { +struct ArraySerializer<ScopedHandleBase<H>, H, true, false> { static size_t GetSerializedSize(const Array<ScopedHandleBase<H>>& input) { return sizeof(Array_Data<H>) + Align(input.size() * sizeof(H)); } @@ -137,17 +144,17 @@ struct ArraySerializer<ScopedHandleBase<H>, H, true> { // This template must only apply to pointer mojo entity (structs and arrays). // This is done by ensuring that WrapperTraits<S>::DataType is a pointer. template <typename S> -struct ArraySerializer<S, - typename internal::EnableIf< - internal::IsPointer<typename internal::WrapperTraits< - S>::DataType>::value, - typename internal::WrapperTraits<S>::DataType>::type, - true> { - typedef typename internal::RemovePointer< - typename internal::WrapperTraits<S>::DataType>::type S_Data; +struct ArraySerializer< + S, + typename EnableIf<IsPointer<typename WrapperTraits<S>::DataType>::value, + typename WrapperTraits<S>::DataType>::type, + true, + false> { + typedef + typename RemovePointer<typename WrapperTraits<S>::DataType>::type S_Data; static size_t GetSerializedSize(const Array<S>& input) { size_t size = sizeof(Array_Data<S_Data*>) + - input.size() * sizeof(internal::StructPointer<S_Data>); + input.size() * sizeof(StructPointer<S_Data>); for (size_t i = 0; i < input.size(); ++i) size += GetSerializedSize_(input[i]); return size; @@ -172,9 +179,7 @@ struct ArraySerializer<S, Array<S>* output) { Array<S> result(input->size()); for (size_t i = 0; i < input->size(); ++i) { - S element; - Deserialize_(input->at(i), &element); - result[i] = element.Pass(); + Deserialize_(input->at(i), &result[i]); } output->Swap(&result); } @@ -184,7 +189,7 @@ struct ArraySerializer<S, struct SerializeCaller { static void Run(T input, Buffer* buf, - typename internal::WrapperTraits<T>::DataType* output) { + typename WrapperTraits<T>::DataType* output) { static_assert((IsSame<Params, NoValidateParams>::value), "Struct type should not have array validate params"); @@ -211,11 +216,49 @@ struct ArraySerializer<S, }; }; +// Handles serialization and deserialization of arrays of unions. +template <typename U, typename U_Data> +struct ArraySerializer<U, U_Data, true, true> { + static size_t GetSerializedSize(const Array<U>& input) { + size_t size = sizeof(Array_Data<U_Data>); + for (size_t i = 0; i < input.size(); ++i) { + // GetSerializedSize_ will account for both the data in the union and the + // space in the array used to hold the union. + size += GetSerializedSize_(input[i], false); + } + return size; + } + + template <bool element_is_nullable, typename ElementValidateParams> + static void SerializeElements(Array<U> input, + Buffer* buf, + Array_Data<U_Data>* output) { + for (size_t i = 0; i < input.size(); ++i) { + U_Data* result = output->storage() + i; + SerializeUnion_(input[i].Pass(), buf, &result, true); + MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( + !element_is_nullable && output->at(i).is_null(), + VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, + MakeMessageWithArrayIndex("null in array expecting valid unions", + input.size(), i)); + } + } + + static void DeserializeElements(Array_Data<U_Data>* input, Array<U>* output) { + Array<U> result(input->size()); + for (size_t i = 0; i < input->size(); ++i) { + Deserialize_(&input->at(i), &result[i]); + } + output->Swap(&result); + } +}; + +// Handles serialization and deserialization of arrays of strings. template <> struct ArraySerializer<String, String_Data*, false> { static size_t GetSerializedSize(const Array<String>& input) { - size_t size = sizeof(Array_Data<String_Data*>) + - input.size() * sizeof(internal::StringPointer); + size_t size = + sizeof(Array_Data<String_Data*>) + input.size() * sizeof(StringPointer); for (size_t i = 0; i < input.size(); ++i) size += GetSerializedSize_(input[i]); return size; diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/bindings_internal.h b/third_party/mojo/src/mojo/public/cpp/bindings/lib/bindings_internal.h index a8f95b2..4b5bb49 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/bindings_internal.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/bindings_internal.h @@ -76,6 +76,18 @@ struct IsHandle { enum { value = IsBaseOf<Handle, H>::value }; }; +template <typename T> +struct IsUnionDataType { + template <typename U> + static YesType Test(const typename U::MojomUnionDataType*); + + template <typename U> + static NoType Test(...); + + static const bool value = + sizeof(Test<T>(0)) == sizeof(YesType) && !IsConst<T>::value; +}; + template <typename T, bool move_only = IsMoveOnlyType<T>::value> struct WrapperTraits; diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/connector.h b/third_party/mojo/src/mojo/public/cpp/bindings/lib/connector.h index 7c1d445..dfbd514 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/connector.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/connector.h @@ -63,6 +63,9 @@ class Connector : public MessageReceiver { // a quiescent state. ScopedMessagePipeHandle PassMessagePipe(); + // Is the connector bound to a MessagePipe handle? + bool is_valid() const { return message_pipe_.is_valid(); } + // Waits for the next message on the pipe, blocking until one arrives or an // error happens. Returns |true| if a message has been delivered, |false| // otherwise. diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/router.cc b/third_party/mojo/src/mojo/public/cpp/bindings/lib/router.cc index da1e7e1..e64f1fa 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/router.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/router.cc @@ -11,7 +11,7 @@ namespace internal { // ---------------------------------------------------------------------------- -class ResponderThunk : public MessageReceiver { +class ResponderThunk : public MessageReceiverWithStatus { public: explicit ResponderThunk(const SharedData<Router*>& router) : router_(router), accept_was_invoked_(false) {} @@ -44,6 +44,12 @@ class ResponderThunk : public MessageReceiver { return result; } + // MessageReceiverWithStatus implementation: + bool IsValid() override { + Router* router = router_.value(); + return router && !router->encountered_error() && router->is_valid(); + } + private: SharedData<Router*> router_; bool accept_was_invoked_; @@ -118,7 +124,7 @@ void Router::EnableTestingMode() { bool Router::HandleIncomingMessage(Message* message) { if (message->has_flag(kMessageExpectsResponse)) { if (incoming_receiver_) { - MessageReceiver* responder = new ResponderThunk(weak_self_); + MessageReceiverWithStatus* responder = new ResponderThunk(weak_self_); bool ok = incoming_receiver_->AcceptWithResponder(message, responder); if (!ok) delete responder; diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/router.h b/third_party/mojo/src/mojo/public/cpp/bindings/lib/router.h index 8254bab..86b8897 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/router.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/router.h @@ -24,7 +24,7 @@ class Router : public MessageReceiverWithResponder { // Sets the receiver to handle messages read from the message pipe that do // not have the kMessageIsResponse flag set. - void set_incoming_receiver(MessageReceiverWithResponder* receiver) { + void set_incoming_receiver(MessageReceiverWithResponderStatus* receiver) { incoming_receiver_ = receiver; } @@ -38,6 +38,9 @@ class Router : public MessageReceiverWithResponder { // waiting to read from the pipe. bool encountered_error() const { return connector_.encountered_error(); } + // Is the router bound to a MessagePipe handle? + bool is_valid() const { return connector_.is_valid(); } + void CloseMessagePipe() { connector_.CloseMessagePipe(); } ScopedMessagePipeHandle PassMessagePipe() { @@ -81,7 +84,7 @@ class Router : public MessageReceiverWithResponder { FilterChain filters_; Connector connector_; SharedData<Router*> weak_self_; - MessageReceiverWithResponder* incoming_receiver_; + MessageReceiverWithResponderStatus* incoming_receiver_; ResponderMap responders_; uint64_t next_request_id_; bool testing_mode_; diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/template_util.h b/third_party/mojo/src/mojo/public/cpp/bindings/lib/template_util.h index a90ee44..9a5788c 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/template_util.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/template_util.h @@ -100,7 +100,9 @@ struct IsBaseOf { }; template <class T> -struct RemovePointer {}; +struct RemovePointer { + typedef T type; +}; template <class T> struct RemovePointer<T*> { typedef T type; diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/validation_errors.cc b/third_party/mojo/src/mojo/public/cpp/bindings/lib/validation_errors.cc index 0ae4eca..15c2d3d 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/validation_errors.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/validation_errors.cc @@ -40,6 +40,8 @@ const char* ValidationErrorToString(ValidationError error) { return "VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION"; case VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID: return "VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID"; + case VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD: + return "VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD"; case VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP: return "VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP"; case VALIDATION_ERROR_UNKNOWN_UNION_TAG: diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/lib/validation_errors.h b/third_party/mojo/src/mojo/public/cpp/bindings/lib/validation_errors.h index d09f63b..da93598 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/lib/validation_errors.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/lib/validation_errors.h @@ -44,6 +44,8 @@ enum ValidationError { // |flags| in the message header indicates that a request ID is required but // there isn't one. VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID, + // The |name| field in a message header contains an unexpected value. + VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD, // Two parallel arrays which are supposed to represent a map have different // lengths. VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP, diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/message.h b/third_party/mojo/src/mojo/public/cpp/bindings/message.h index 80cd6d5..b763d9a 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/message.h +++ b/third_party/mojo/src/mojo/public/cpp/bindings/message.h @@ -106,6 +106,39 @@ class MessageReceiverWithResponder : public MessageReceiver { MOJO_WARN_UNUSED_RESULT = 0; }; +// A MessageReceiver that is also able to provide status about the state +// of the underlying MessagePipe to which it will be forwarding messages +// received via the |Accept()| call. +class MessageReceiverWithStatus : public MessageReceiver { + public: + ~MessageReceiverWithStatus() override {} + + // Returns |true| if this MessageReceiver is currently bound to a MessagePipe, + // the pipe has not been closed, and the pipe has not encountered an error. + virtual bool IsValid() = 0; +}; + +// An alternative to MessageReceiverWithResponder for cases in which it +// is necessary for the implementor of this interface to know about the status +// of the MessagePipe which will carry the responses. +class MessageReceiverWithResponderStatus : public MessageReceiver { + public: + ~MessageReceiverWithResponderStatus() override {} + + // A variant on Accept that registers a MessageReceiverWithStatus (known as + // the responder) to handle the response message generated from the given + // message. Any of the responder's methods (Accept or IsValid) may be called + // during AcceptWithResponder or some time after its return. + // + // NOTE: Upon returning true, AcceptWithResponder assumes ownership of + // |responder| and will delete it after calling |responder->Accept| or upon + // its own destruction. + // + virtual bool AcceptWithResponder(Message* message, + MessageReceiverWithStatus* responder) + MOJO_WARN_UNUSED_RESULT = 0; +}; + // Read a single message from the pipe and dispatch to the given receiver. The // receiver may be null, in which case the message is simply discarded. // Returns MOJO_RESULT_SHOULD_WAIT if the caller should wait on the handle to diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/BUILD.gn b/third_party/mojo/src/mojo/public/cpp/bindings/tests/BUILD.gn index 6d09151..46c2e50 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/tests/BUILD.gn +++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/BUILD.gn @@ -9,6 +9,7 @@ mojo_sdk_source_set("tests") { sources = [ "array_unittest.cc", + "binding_callback_unittest.cc", "binding_unittest.cc", "bounds_checker_unittest.cc", "buffer_unittest.cc", @@ -26,7 +27,6 @@ mojo_sdk_source_set("tests") { "string_unittest.cc", "struct_unittest.cc", "type_conversion_unittest.cc", - "union_unittest.cc", "validation_unittest.cc", ] diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc b/third_party/mojo/src/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc new file mode 100644 index 0000000..19e36b7 --- /dev/null +++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/binding_callback_unittest.cc @@ -0,0 +1,301 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/bindings/string.h" +#include "mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/system/message_pipe.h" +#include "mojo/public/cpp/test_support/test_support.h" +#include "mojo/public/cpp/utility/run_loop.h" +#include "mojo/public/interfaces/bindings/tests/sample_interfaces.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// The tests in this file are designed to test the interaction between a +// Callback and its associated Binding. If a Callback is deleted before +// being used we DCHECK fail--unless the associated Binding has already +// been closed or deleted. This contract must be explained to the Mojo +// application developer. For example it is the developer's responsibility to +// ensure that the Binding is destroyed before an unused Callback is destroyed. +// +/////////////////////////////////////////////////////////////////////////////// + +namespace mojo { +namespace test { +namespace { + +// A Runnable object that saves the last value it sees via the +// provided int32_t*. Used on the client side. +class ValueSaver { + public: + explicit ValueSaver(int32_t* last_value_seen) + : last_value_seen_(last_value_seen) {} + void Run(int32_t x) const { *last_value_seen_ = x; } + + private: + int32_t* const last_value_seen_; +}; + +// An implementation of sample::Provider used on the server side. +// It only implements one of the methods: EchoInt(). +// All it does is save the values and Callbacks it sees. +class InterfaceImpl : public sample::Provider { + public: + InterfaceImpl() + : last_server_value_seen_(0), + callback_saved_(new Callback<void(int32_t)>()) {} + + ~InterfaceImpl() override { + if (callback_saved_) { + delete callback_saved_; + } + } + + // Run's the callback previously saved from the last invocation + // of |EchoInt()|. + bool RunCallback() { + if (callback_saved_) { + callback_saved_->Run(last_server_value_seen_); + return true; + } + return false; + } + + // Delete's the previously saved callback. + void DeleteCallback() { + delete callback_saved_; + callback_saved_ = nullptr; + } + + // sample::Provider implementation + + // Saves its two input values in member variables and does nothing else. + void EchoInt(int32_t x, const Callback<void(int32_t)>& callback) override { + last_server_value_seen_ = x; + *callback_saved_ = callback; + } + + void EchoString(const String& a, + const Callback<void(String)>& callback) override { + MOJO_CHECK(false) << "Not implemented."; + } + + void EchoStrings(const String& a, + const String& b, + const Callback<void(String, String)>& callback) override { + MOJO_CHECK(false) << "Not implemented."; + } + + void EchoMessagePipeHandle( + ScopedMessagePipeHandle a, + const Callback<void(ScopedMessagePipeHandle)>& callback) override { + MOJO_CHECK(false) << "Not implemented."; + } + + void EchoEnum(sample::Enum a, + const Callback<void(sample::Enum)>& callback) override { + MOJO_CHECK(false) << "Not implemented."; + } + + void resetLastServerValueSeen() { last_server_value_seen_ = 0; } + + int32_t last_server_value_seen() const { return last_server_value_seen_; } + + private: + int32_t last_server_value_seen_; + Callback<void(int32_t)>* callback_saved_; +}; + +class BindingCallbackTest : public testing::Test { + public: + ~BindingCallbackTest() override {} + + protected: + int32_t last_client_callback_value_seen_; + sample::ProviderPtr interface_ptr_; + + void PumpMessages() { loop_.RunUntilIdle(); } + + private: + Environment env_; + RunLoop loop_; +}; + +// Tests that the InterfacePtr and the Binding can communicate with each +// other normally. +TEST_F(BindingCallbackTest, Basic) { + // Create the ServerImpl and the Binding. + InterfaceImpl server_impl; + Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_)); + + // Initialize the test values. + server_impl.resetLastServerValueSeen(); + last_client_callback_value_seen_ = 0; + + // Invoke the Echo method. + interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_)); + PumpMessages(); + + // Check that server saw the correct value, but the client has not yet. + EXPECT_EQ(7, server_impl.last_server_value_seen()); + EXPECT_EQ(0, last_client_callback_value_seen_); + + // Now run the Callback. + server_impl.RunCallback(); + PumpMessages(); + + // Check that the client has now seen the correct value. + EXPECT_EQ(7, last_client_callback_value_seen_); + + // Initialize the test values again. + server_impl.resetLastServerValueSeen(); + last_client_callback_value_seen_ = 0; + + // Invoke the Echo method again. + interface_ptr_->EchoInt(13, ValueSaver(&last_client_callback_value_seen_)); + PumpMessages(); + + // Check that server saw the correct value, but the client has not yet. + EXPECT_EQ(13, server_impl.last_server_value_seen()); + EXPECT_EQ(0, last_client_callback_value_seen_); + + // Now run the Callback again. + server_impl.RunCallback(); + PumpMessages(); + + // Check that the client has now seen the correct value again. + EXPECT_EQ(13, last_client_callback_value_seen_); +} + +// Tests that running the Callback after the Binding has been deleted +// results in a clean failure. +TEST_F(BindingCallbackTest, DeleteBindingThenRunCallback) { + // Create the ServerImpl. + InterfaceImpl server_impl; + { + // Create the binding in an inner scope so it can be deleted first. + Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_)); + + // Initialize the test values. + server_impl.resetLastServerValueSeen(); + last_client_callback_value_seen_ = 0; + + // Invoke the Echo method. + interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_)); + PumpMessages(); + } + // The binding has now been destroyed and the pipe is closed. + + // Check that server saw the correct value, but the client has not yet. + EXPECT_EQ(7, server_impl.last_server_value_seen()); + EXPECT_EQ(0, last_client_callback_value_seen_); + + // Now try to run the Callback. This should do nothing since the pipe + // is closed. + EXPECT_TRUE(server_impl.RunCallback()); + PumpMessages(); + + // Check that the client has still not seen the correct value. + EXPECT_EQ(0, last_client_callback_value_seen_); + + // Attempt to invoke the method again and confirm that an error was + // encountered. + interface_ptr_->EchoInt(13, ValueSaver(&last_client_callback_value_seen_)); + PumpMessages(); + EXPECT_TRUE(interface_ptr_.encountered_error()); +} + +// Tests that deleting a Callback without running it after the corresponding +// binding has already been deleted does not result in a crash. +TEST_F(BindingCallbackTest, DeleteBindingThenDeleteCallback) { + // Create the ServerImpl. + InterfaceImpl server_impl; + { + // Create the binding in an inner scope so it can be deleted first. + Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_)); + + // Initialize the test values. + server_impl.resetLastServerValueSeen(); + last_client_callback_value_seen_ = 0; + + // Invoke the Echo method. + interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_)); + PumpMessages(); + } + // The binding has now been destroyed and the pipe is closed. + + // Check that server saw the correct value, but the client has not yet. + EXPECT_EQ(7, server_impl.last_server_value_seen()); + EXPECT_EQ(0, last_client_callback_value_seen_); + + // Delete the callback without running it. This should not + // cause a problem because the insfrastructure can detect that the + // binding has already been destroyed and the pipe is closed. + server_impl.DeleteCallback(); +} + +// Tests that closing a Binding allows us to delete a callback +// without running it without encountering a crash. +TEST_F(BindingCallbackTest, CloseBindingBeforeDeletingCallback) { + // Create the ServerImpl and the Binding. + InterfaceImpl server_impl; + Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_)); + + // Initialize the test values. + server_impl.resetLastServerValueSeen(); + last_client_callback_value_seen_ = 0; + + // Invoke the Echo method. + interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_)); + PumpMessages(); + + // Check that server saw the correct value, but the client has not yet. + EXPECT_EQ(7, server_impl.last_server_value_seen()); + EXPECT_EQ(0, last_client_callback_value_seen_); + + // Now close the Binding. + binding.Close(); + + // Delete the callback without running it. This should not + // cause a crash because the insfrastructure can detect that the + // binding has already been closed. + server_impl.DeleteCallback(); + + // Check that the client has still not seen the correct value. + EXPECT_EQ(0, last_client_callback_value_seen_); +} + +// Tests that deleting a Callback without using it before the +// Binding has been destroyed or closed results in a DCHECK. +// Temporarily disabled due to +// https://code.google.com/p/chromium/issues/detail?id=472218 +TEST_F(BindingCallbackTest, DISABLED_DeleteCallbackBeforeBindingDeathTest) { + // Create the ServerImpl and the Binding. + InterfaceImpl server_impl; + Binding<sample::Provider> binding(&server_impl, GetProxy(&interface_ptr_)); + + // Initialize the test values. + server_impl.resetLastServerValueSeen(); + last_client_callback_value_seen_ = 0; + + // Invoke the Echo method. + interface_ptr_->EchoInt(7, ValueSaver(&last_client_callback_value_seen_)); + PumpMessages(); + + // Check that server saw the correct value, but the client has not yet. + EXPECT_EQ(7, server_impl.last_server_value_seen()); + EXPECT_EQ(0, last_client_callback_value_seen_); + +#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) + // Delete the callback without running it. This should cause a crash in debug + // builds due to a DCHECK. + EXPECT_DEATH(server_impl.DeleteCallback(), + "Check failed: !callback_was_dropped."); +#endif +} + +} // namespace +} // namespace test +} // namespace mojo diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/request_response_unittest.cc b/third_party/mojo/src/mojo/public/cpp/bindings/tests/request_response_unittest.cc index a1fe8e6..f7c3cf6 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/tests/request_response_unittest.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/request_response_unittest.cc @@ -43,6 +43,10 @@ class ProviderImpl : public sample::Provider { callback.Run(a); } + void EchoInt(int32_t a, const EchoIntCallback& callback) override { + callback.Run(a); + } + Binding<sample::Provider> binding_; }; diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/router_unittest.cc b/third_party/mojo/src/mojo/public/cpp/bindings/tests/router_unittest.cc index ed3792f..d3d8ccb 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/tests/router_unittest.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/router_unittest.cc @@ -47,19 +47,20 @@ class MessageAccumulator : public MessageReceiver { internal::MessageQueue* queue_; }; -class ResponseGenerator : public MessageReceiverWithResponder { +class ResponseGenerator : public MessageReceiverWithResponderStatus { public: ResponseGenerator() {} bool Accept(Message* message) override { return false; } bool AcceptWithResponder(Message* message, - MessageReceiver* responder) override { + MessageReceiverWithStatus* responder) override { EXPECT_TRUE(message->has_flag(internal::kMessageExpectsResponse)); bool result = SendResponse( message->name(), message->request_id(), reinterpret_cast<const char*>(message->payload()), responder); + EXPECT_TRUE(responder->IsValid()); delete responder; return result; } @@ -84,7 +85,7 @@ class LazyResponseGenerator : public ResponseGenerator { ~LazyResponseGenerator() override { delete responder_; } bool AcceptWithResponder(Message* message, - MessageReceiver* responder) override { + MessageReceiverWithStatus* responder) override { name_ = message->name(); request_id_ = message->request_id(); request_string_ = @@ -95,6 +96,8 @@ class LazyResponseGenerator : public ResponseGenerator { bool has_responder() const { return !!responder_; } + bool responder_is_valid() const { return responder_->IsValid(); } + // Send the response and delete the responder. void CompleteWithResponse() { Complete(true); } @@ -112,7 +115,7 @@ class LazyResponseGenerator : public ResponseGenerator { responder_ = nullptr; } - MessageReceiver* responder_; + MessageReceiverWithStatus* responder_; uint32_t name_; uint64_t request_id_; std::string request_string_; @@ -261,6 +264,7 @@ TEST_F(RouterTest, LazyResponses) { EXPECT_TRUE(message_queue.IsEmpty()); // Send the response. + EXPECT_TRUE(generator.responder_is_valid()); generator.CompleteWithResponse(); PumpMessages(); @@ -283,6 +287,7 @@ TEST_F(RouterTest, LazyResponses) { EXPECT_TRUE(message_queue.IsEmpty()); // Send the second response. + EXPECT_TRUE(generator.responder_is_valid()); generator.CompleteWithResponse(); PumpMessages(); @@ -358,6 +363,7 @@ TEST_F(RouterTest, LateResponse) { EXPECT_TRUE(generator.has_responder()); } + EXPECT_FALSE(generator.responder_is_valid()); generator.CompleteWithResponse(); // This should end up doing nothing. } diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/struct_unittest.cc b/third_party/mojo/src/mojo/public/cpp/bindings/tests/struct_unittest.cc index 7f5a376..3fb4a28 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/tests/struct_unittest.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/struct_unittest.cc @@ -418,6 +418,5 @@ TEST_F(StructTest, Versioning_NewToOld) { EXPECT_TRUE(output->Equals(*expected_output)); } } - } // namespace test } // namespace mojo diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/union_unittest.cc b/third_party/mojo/src/mojo/public/cpp/bindings/tests/union_unittest.cc index aba7faa..ef9bac1 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/tests/union_unittest.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/union_unittest.cc @@ -2,10 +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/array.h" +#include "mojo/public/cpp/bindings/lib/array_internal.h" +#include "mojo/public/cpp/bindings/lib/array_serialization.h" #include "mojo/public/cpp/bindings/lib/bounds_checker.h" #include "mojo/public/cpp/bindings/lib/fixed_buffer.h" #include "mojo/public/cpp/bindings/string.h" #include "mojo/public/cpp/environment/environment.h" +#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h" #include "mojo/public/interfaces/bindings/tests/test_unions.mojom.h" #include "testing/gtest/include/gtest/gtest.h" @@ -104,12 +108,12 @@ TEST(UnionTest, SerializationPod) { PodUnionPtr pod1(PodUnion::New()); pod1->set_f_int8(10); - size_t size = GetSerializedSize_(pod1); + size_t size = GetSerializedSize_(pod1, false); EXPECT_EQ(16U, size); mojo::internal::FixedBuffer buf(size); - internal::PodUnion_Data* data; - Serialize_(pod1.Pass(), &buf, &data); + internal::PodUnion_Data* data = nullptr; + SerializeUnion_(pod1.Pass(), &buf, &data, false); PodUnionPtr pod2; Deserialize_(data, &pod2); @@ -119,27 +123,68 @@ TEST(UnionTest, SerializationPod) { EXPECT_EQ(pod2->which(), PodUnion::Tag::F_INT8); } -TEST(UnionTest, ValidationJustWorksPod) { +TEST(UnionTest, PodValidation) { PodUnionPtr pod(PodUnion::New()); pod->set_f_int8(10); - size_t size = GetSerializedSize_(pod); + size_t size = GetSerializedSize_(pod, false); EXPECT_EQ(16U, size); mojo::internal::FixedBuffer buf(size); - internal::PodUnion_Data* data; - Serialize_(pod.Pass(), &buf, &data); + internal::PodUnion_Data* data = nullptr; + SerializeUnion_(pod.Pass(), &buf, &data, false); void* raw_buf = buf.Leak(); mojo::internal::BoundsChecker bounds_checker(data, static_cast<uint32_t>(size), 0); - EXPECT_TRUE(internal::PodUnion_Data::Validate(raw_buf, &bounds_checker)); + EXPECT_TRUE( + internal::PodUnion_Data::Validate(raw_buf, &bounds_checker, false)); free(raw_buf); } +TEST(UnionTest, SerializeNotNull) { + PodUnionPtr pod(PodUnion::New()); + pod->set_f_int8(0); + size_t size = GetSerializedSize_(pod, false); + mojo::internal::FixedBuffer buf(size); + internal::PodUnion_Data* data = nullptr; + SerializeUnion_(pod.Pass(), &buf, &data, false); + EXPECT_FALSE(data->is_null()); +} + +TEST(UnionTest, SerializeIsNullInlined) { + PodUnionPtr pod; + size_t size = GetSerializedSize_(pod, false); + EXPECT_EQ(16U, size); + mojo::internal::FixedBuffer buf(size); + internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf); + + // Check that dirty output buffers are handled correctly by serialization. + data->size = 16U; + data->tag = PodUnion::Tag::F_UINT16; + data->data.f_f_int16 = 20; + + SerializeUnion_(pod.Pass(), &buf, &data, true); + EXPECT_TRUE(data->is_null()); + + PodUnionPtr pod2; + Deserialize_(data, &pod2); + EXPECT_TRUE(pod2.is_null()); +} + +TEST(UnionTest, SerializeIsNullNotInlined) { + PodUnionPtr pod; + size_t size = GetSerializedSize_(pod, false); + EXPECT_EQ(16U, size); + mojo::internal::FixedBuffer buf(size); + internal::PodUnion_Data* data = nullptr; + SerializeUnion_(pod.Pass(), &buf, &data, false); + EXPECT_EQ(nullptr, data); +} + TEST(UnionTest, NullValidation) { void* buf = nullptr; mojo::internal::BoundsChecker bounds_checker(buf, 0, 0); - EXPECT_TRUE(internal::PodUnion_Data::Validate(buf, &bounds_checker)); + EXPECT_TRUE(internal::PodUnion_Data::Validate(buf, &bounds_checker, false)); } TEST(UnionTest, OutOfAlignmentValidation) { @@ -154,7 +199,7 @@ TEST(UnionTest, OutOfAlignmentValidation) { reinterpret_cast<internal::PodUnion_Data*>(buf); mojo::internal::BoundsChecker bounds_checker(data, static_cast<uint32_t>(size), 0); - EXPECT_FALSE(internal::PodUnion_Data::Validate(buf, &bounds_checker)); + EXPECT_FALSE(internal::PodUnion_Data::Validate(buf, &bounds_checker, false)); free(raw_buf); } @@ -166,7 +211,8 @@ TEST(UnionTest, OOBValidation) { mojo::internal::BoundsChecker bounds_checker(data, static_cast<uint32_t>(size), 0); void* raw_buf = buf.Leak(); - EXPECT_FALSE(internal::PodUnion_Data::Validate(raw_buf, &bounds_checker)); + EXPECT_FALSE( + internal::PodUnion_Data::Validate(raw_buf, &bounds_checker, false)); free(raw_buf); } @@ -179,7 +225,8 @@ TEST(UnionTest, UnknownTagValidation) { mojo::internal::BoundsChecker bounds_checker(data, static_cast<uint32_t>(size), 0); void* raw_buf = buf.Leak(); - EXPECT_FALSE(internal::PodUnion_Data::Validate(raw_buf, &bounds_checker)); + EXPECT_FALSE( + internal::PodUnion_Data::Validate(raw_buf, &bounds_checker, false)); free(raw_buf); } @@ -222,10 +269,10 @@ TEST(UnionTest, StringSerialization) { String hello("hello world"); pod1->set_f_string(hello); - size_t size = GetSerializedSize_(pod1); + size_t size = GetSerializedSize_(pod1, false); mojo::internal::FixedBuffer buf(size); - internal::ObjectUnion_Data* data; - Serialize_(pod1.Pass(), &buf, &data); + internal::ObjectUnion_Data* data = nullptr; + SerializeUnion_(pod1.Pass(), &buf, &data, false); ObjectUnionPtr pod2; Deserialize_(data, &pod2); @@ -234,7 +281,7 @@ TEST(UnionTest, StringSerialization) { EXPECT_EQ(pod2->which(), ObjectUnion::Tag::F_STRING); } -TEST(UnionTest, StringValidationNull) { +TEST(UnionTest, NullStringValidation) { Environment environment; size_t size = sizeof(internal::ObjectUnion_Data); mojo::internal::FixedBuffer buf(size); @@ -244,11 +291,12 @@ TEST(UnionTest, StringValidationNull) { mojo::internal::BoundsChecker bounds_checker(data, static_cast<uint32_t>(size), 0); void* raw_buf = buf.Leak(); - EXPECT_FALSE(internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker)); + EXPECT_FALSE( + internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false)); free(raw_buf); } -TEST(UnionTest, StringValidationPointerOverflow) { +TEST(UnionTest, StringPointerOverflowValidation) { Environment environment; size_t size = sizeof(internal::ObjectUnion_Data); mojo::internal::FixedBuffer buf(size); @@ -258,11 +306,12 @@ TEST(UnionTest, StringValidationPointerOverflow) { mojo::internal::BoundsChecker bounds_checker(data, static_cast<uint32_t>(size), 0); void* raw_buf = buf.Leak(); - EXPECT_FALSE(internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker)); + EXPECT_FALSE( + internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false)); free(raw_buf); } -TEST(UnionTest, StringValidationValidateString) { +TEST(UnionTest, StringValidateOOB) { Environment environment; size_t size = 32; mojo::internal::FixedBuffer buf(size); @@ -277,8 +326,203 @@ TEST(UnionTest, StringValidationValidateString) { array_header->num_elements = 20; mojo::internal::BoundsChecker bounds_checker(data, 32, 0); void* raw_buf = buf.Leak(); - EXPECT_FALSE(internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker)); + EXPECT_FALSE( + internal::ObjectUnion_Data::Validate(raw_buf, &bounds_checker, false)); + free(raw_buf); +} + +// TODO(azani): Move back in array_unittest.cc when possible. +// Array tests +TEST(UnionTest, PodUnionInArray) { + SmallStructPtr small_struct(SmallStruct::New()); + small_struct->pod_union_array = Array<PodUnionPtr>(2); + small_struct->pod_union_array[0] = PodUnion::New(); + small_struct->pod_union_array[1] = PodUnion::New(); + + small_struct->pod_union_array[0]->set_f_int8(10); + small_struct->pod_union_array[1]->set_f_int16(12); + + EXPECT_EQ(10, small_struct->pod_union_array[0]->get_f_int8()); + EXPECT_EQ(12, small_struct->pod_union_array[1]->get_f_int16()); +} + +TEST(UnionTest, PodUnionInArraySerialization) { + Environment environment; + Array<PodUnionPtr> array(2); + array[0] = PodUnion::New(); + array[1] = PodUnion::New(); + + array[0]->set_f_int8(10); + array[1]->set_f_int16(12); + EXPECT_EQ(2U, array.size()); + + size_t size = GetSerializedSize_(array); + EXPECT_EQ(40U, size); + + mojo::internal::FixedBuffer buf(size); + mojo::internal::Array_Data<internal::PodUnion_Data>* data; + SerializeArray_<mojo::internal::ArrayValidateParams< + 0, false, mojo::internal::NoValidateParams>>(array.Pass(), &buf, &data); + + Array<PodUnionPtr> array2; + Deserialize_(data, &array2); + + EXPECT_EQ(2U, array2.size()); + + EXPECT_EQ(10, array2[0]->get_f_int8()); + EXPECT_EQ(12, array2[1]->get_f_int16()); +} + +TEST(UnionTest, PodUnionInArraySerializationWithNull) { + Environment environment; + Array<PodUnionPtr> array(2); + array[0] = PodUnion::New(); + + array[0]->set_f_int8(10); + EXPECT_EQ(2U, array.size()); + + size_t size = GetSerializedSize_(array); + EXPECT_EQ(40U, size); + + mojo::internal::FixedBuffer buf(size); + mojo::internal::Array_Data<internal::PodUnion_Data>* data; + SerializeArray_<mojo::internal::ArrayValidateParams< + 0, true, mojo::internal::NoValidateParams>>(array.Pass(), &buf, &data); + + Array<PodUnionPtr> array2; + Deserialize_(data, &array2); + + EXPECT_EQ(2U, array2.size()); + + EXPECT_EQ(10, array2[0]->get_f_int8()); + EXPECT_TRUE(array2[1].is_null()); +} + +// TODO(azani): Move back in struct_unittest.cc when possible. +// Struct tests +TEST(UnionTest, Clone_Union) { + Environment environment; + SmallStructPtr small_struct(SmallStruct::New()); + small_struct->pod_union = PodUnion::New(); + small_struct->pod_union->set_f_int8(10); + + SmallStructPtr clone = small_struct.Clone(); + EXPECT_EQ(10, clone->pod_union->get_f_int8()); +} + +// Serialization test of a struct with a union of plain old data. +TEST(UnionTest, Serialization_UnionOfPods) { + Environment environment; + SmallStructPtr small_struct(SmallStruct::New()); + small_struct->pod_union = PodUnion::New(); + small_struct->pod_union->set_f_int32(10); + + size_t size = GetSerializedSize_(small_struct); + + mojo::internal::FixedBuffer buf(size); + internal::SmallStruct_Data* data = nullptr; + Serialize_(small_struct.Pass(), &buf, &data); + + SmallStructPtr deserialized; + Deserialize_(data, &deserialized); + + EXPECT_EQ(10, deserialized->pod_union->get_f_int32()); +} + +// Serialization test of a struct with a union of structs. +TEST(UnionTest, Serialization_UnionOfObjects) { + Environment environment; + SmallObjStructPtr obj_struct(SmallObjStruct::New()); + obj_struct->obj_union = ObjectUnion::New(); + String hello("hello world"); + obj_struct->obj_union->set_f_string(hello); + + size_t size = GetSerializedSize_(obj_struct); + + mojo::internal::FixedBuffer buf(size); + internal::SmallObjStruct_Data* data = nullptr; + Serialize_(obj_struct.Pass(), &buf, &data); + + SmallObjStructPtr deserialized; + Deserialize_(data, &deserialized); + + EXPECT_EQ(hello, deserialized->obj_union->get_f_string()); +} + +// Validation test of a struct with a union. +TEST(UnionTest, Validation_UnionsInStruct) { + Environment environment; + SmallStructPtr small_struct(SmallStruct::New()); + small_struct->pod_union = PodUnion::New(); + small_struct->pod_union->set_f_int32(10); + + size_t size = GetSerializedSize_(small_struct); + + mojo::internal::FixedBuffer buf(size); + internal::SmallStruct_Data* data = nullptr; + Serialize_(small_struct.Pass(), &buf, &data); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, size, 0); + EXPECT_TRUE(internal::SmallStruct_Data::Validate(raw_buf, &bounds_checker)); + free(raw_buf); +} + +// Validation test of a struct union fails due to unknown union tag. +TEST(UnionTest, Validation_PodUnionInStruct_Failure) { + Environment environment; + SmallStructPtr small_struct(SmallStruct::New()); + small_struct->pod_union = PodUnion::New(); + small_struct->pod_union->set_f_int32(10); + + size_t size = GetSerializedSize_(small_struct); + + mojo::internal::FixedBuffer buf(size); + internal::SmallStruct_Data* data = nullptr; + Serialize_(small_struct.Pass(), &buf, &data); + data->pod_union.tag = static_cast<internal::PodUnion_Data::PodUnion_Tag>(100); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, size, 0); + EXPECT_FALSE(internal::SmallStruct_Data::Validate(raw_buf, &bounds_checker)); free(raw_buf); } + +// Validation fails due to non-nullable null union in struct. +TEST(UnionTest, Validation_NullUnion_Failure) { + Environment environment; + SmallStructNonNullableUnionPtr small_struct( + SmallStructNonNullableUnion::New()); + + size_t size = GetSerializedSize_(small_struct); + + mojo::internal::FixedBuffer buf(size); + internal::SmallStructNonNullableUnion_Data* data = + internal::SmallStructNonNullableUnion_Data::New(&buf); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, size, 0); + EXPECT_FALSE(internal::SmallStructNonNullableUnion_Data::Validate( + raw_buf, &bounds_checker)); + free(raw_buf); +} + +// Validation passes with nullable null union. +TEST(UnionTest, Validation_NullableUnion) { + Environment environment; + SmallStructPtr small_struct(SmallStruct::New()); + + size_t size = GetSerializedSize_(small_struct); + + mojo::internal::FixedBuffer buf(size); + internal::SmallStruct_Data* data = nullptr; + Serialize_(small_struct.Pass(), &buf, &data); + + void* raw_buf = buf.Leak(); + mojo::internal::BoundsChecker bounds_checker(data, size, 0); + EXPECT_TRUE(internal::SmallStruct_Data::Validate(raw_buf, &bounds_checker)); + free(raw_buf); +} + } // namespace test } // namespace mojo diff --git a/third_party/mojo/src/mojo/public/cpp/bindings/tests/validation_unittest.cc b/third_party/mojo/src/mojo/public/cpp/bindings/tests/validation_unittest.cc index 8139fe4..c15ff02 100644 --- a/third_party/mojo/src/mojo/public/cpp/bindings/tests/validation_unittest.cc +++ b/third_party/mojo/src/mojo/public/cpp/bindings/tests/validation_unittest.cc @@ -371,6 +371,16 @@ TEST_F(ValidationTest, Conformance) { RunValidationTests("conformance_", validators.GetHead()); } +// This test is similar to the Conformance test but for responses. +TEST_F(ValidationTest, ResponseConformance) { + DummyMessageReceiver dummy_receiver; + mojo::internal::FilterChain validators(&dummy_receiver); + validators.Append<mojo::internal::MessageHeaderValidator>(); + validators.Append<ConformanceTestInterface::ResponseValidator_>(); + + RunValidationTests("resp_conformance_", validators.GetHead()); +} + // Test that InterfacePtr<X> applies the correct validators and they don't // conflict with each other: // - MessageHeaderValidator diff --git a/third_party/mojo/src/mojo/public/go/application/application_impl.go b/third_party/mojo/src/mojo/public/go/application/application_impl.go new file mode 100644 index 0000000..43a1eb8 --- /dev/null +++ b/third_party/mojo/src/mojo/public/go/application/application_impl.go @@ -0,0 +1,167 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package application + +import ( + "log" + "sync" + + "mojo/public/go/bindings" + "mojo/public/go/system" + + "mojo/public/interfaces/application/application" + sp "mojo/public/interfaces/application/service_provider" + "mojo/public/interfaces/application/shell" +) + +// Delegate is an interface that your mojo application should implement. +// All methods are called from the same goroutine to make sure that order of +// calls matches the order of messages sent to underlying message pipe. +type Delegate interface { + // Initialize is called exactly once before any other method. + Initialize(ctx Context) + + // AcceptConnection is called when another application attempts to open + // a connection to this application. Close the connection if you no + // longer need it. + AcceptConnection(connection *Connection) + + // Quit is called to request the application shut itself down + // gracefully. + Quit() +} + +// Context is an interface to information about mojo application environment. +type Context interface { + // URL returns the URL the application was found at, after all mappings, + // resolution, and redirects. + URL() string + + // Args returns a list of initial configuration arguments, passed by the + // Shell. + Args() []string + + // ConnectToApplication requests a new connection to an application. You + // should pass a list of services you want to provide to the requested + // application. + ConnectToApplication(remoteURL string, providedServices ...ServiceFactory) *OutgoingConnection +} + +// ApplicationImpl is an utility class for communicating with the Shell, and +// providing Services to clients. +type ApplicationImpl struct { + shell *shell.ShellProxy + args []string + url string + // Pointer to the stub that runs this instance of ApplicationImpl. + runner *bindings.Stub + quitOnce sync.Once + + delegate Delegate + // Protects connections, that can be modified concurrently because of + // ConnectToApplication calls. + mu sync.Mutex + connections []*Connection +} + +// Run binds your mojo application to provided message pipe handle and runs it +// until the application is terminated. +func Run(delegate Delegate, applicationRequest system.MojoHandle) { + messagePipe := system.GetCore().AcquireNativeHandle(applicationRequest).ToMessagePipeHandle() + appRequest := application.ApplicationRequest{bindings.NewMessagePipeHandleOwner(messagePipe)} + impl := &ApplicationImpl{ + delegate: delegate, + } + stub := application.NewApplicationStub(appRequest, impl, bindings.GetAsyncWaiter()) + impl.runner = stub + for { + if err := stub.ServeRequest(); err != nil { + // TODO(rogulenko): don't log in case message pipe was closed + log.Println(err) + impl.RequestQuit() + break + } + } +} + +// Mojo application implementation. +func (impl *ApplicationImpl) Initialize(shellPointer shell.ShellPointer, args *[]string, url string) error { + impl.shell = shell.NewShellProxy(shellPointer, bindings.GetAsyncWaiter()) + if args != nil { + impl.args = *args + } + impl.url = url + impl.delegate.Initialize(impl) + return nil +} + +// Mojo application implementation. +func (impl *ApplicationImpl) AcceptConnection(requestorURL string, services *sp.ServiceProviderRequest, exposedServices *sp.ServiceProviderPointer, resolvedURL string) error { + connection := newConnection(requestorURL, services, exposedServices, resolvedURL) + impl.delegate.AcceptConnection(connection) + impl.addConnection(connection) + return nil +} + +// Mojo application implementation. +func (impl *ApplicationImpl) RequestQuit() error { + impl.quitOnce.Do(func() { + impl.delegate.Quit() + impl.mu.Lock() + for _, c := range impl.connections { + c.Close() + } + impl.mu.Unlock() + impl.shell.Close_proxy() + impl.runner.Close() + }) + return nil +} + +// Context implementaion. +func (impl *ApplicationImpl) URL() string { + return impl.url +} + +// Context implementaion. +func (impl *ApplicationImpl) Args() []string { + return impl.args +} + +// Context implementaion. +func (impl *ApplicationImpl) ConnectToApplication(remoteURL string, providedServices ...ServiceFactory) *OutgoingConnection { + servicesRequest, servicesPointer := sp.CreateMessagePipeForServiceProvider() + exposedServicesRequest, exposedServicesPointer := sp.CreateMessagePipeForServiceProvider() + if err := impl.shell.ConnectToApplication(remoteURL, &servicesRequest, &exposedServicesPointer); err != nil { + log.Printf("can't connect to %v: %v", remoteURL, err) + // In case of error message pipes sent through Shell are closed and + // the connection will work as if the remote application closed + // both ServiceProvider's pipes. + } + connection := newConnection(impl.url, &exposedServicesRequest, &servicesPointer, remoteURL) + impl.addConnection(connection) + return connection.ProvideServices(providedServices...) +} + +// addConnection appends connections slice by a provided connection, removing +// connections that have been closed. +func (impl *ApplicationImpl) addConnection(c *Connection) { + impl.mu.Lock() + i := 0 + for i < len(impl.connections) { + if impl.connections[i].isClosed { + last := len(impl.connections) - 1 + impl.connections[i] = impl.connections[last] + impl.connections[last] = nil + impl.connections = impl.connections[:last] + } else { + i++ + } + } + if !c.isClosed { + impl.connections = append(impl.connections, c) + } + impl.mu.Unlock() +} diff --git a/third_party/mojo/src/mojo/public/go/application/connection.go b/third_party/mojo/src/mojo/public/go/application/connection.go new file mode 100644 index 0000000..5b2c629 --- /dev/null +++ b/third_party/mojo/src/mojo/public/go/application/connection.go @@ -0,0 +1,174 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package application + +import ( + "log" + + "mojo/public/go/bindings" + "mojo/public/go/system" + + sp "mojo/public/interfaces/application/service_provider" +) + +type connectionInfo struct { + requestorURL string + connectionURL string +} + +// RequestorURL returns the URL of application that established the connection. +func (c *connectionInfo) RequestorURL() string { + return c.requestorURL +} + +// ConnectionURL returns the URL that was used by the source application to +// establish a connection to the destination application. +func (c *connectionInfo) ConnectionURL() string { + return c.connectionURL +} + +// ServiceRequest is an interface request for a specified mojo service. +type ServiceRequest interface { + // Name returns the name of requested mojo service. + Name() string + + // PassMessagePipe passes ownership of the underlying message pipe + // handle to the newly created handle object, invalidating the + // underlying handle object in the process. + PassMessagePipe() system.MessagePipeHandle +} + +// ServiceFactory provides implementation of a mojo service. +type ServiceFactory interface { + // Name returns the name of provided mojo service. + Name() string + + // Create binds an implementation of mojo service to the provided + // message pipe and runs it. + Create(pipe system.MessagePipeHandle) +} + +// Connection represents a connection to another application. An instance of +// this struct is passed to Delegate's AcceptConnection() function each time a +// connection is made to this application. +type Connection struct { + connectionInfo + // Request for local services. Is valid until ProvideServices is called. + servicesRequest *sp.ServiceProviderRequest + // Indicates that ProvideServices function was already called. + servicesProvided bool + localServices *bindings.Stub + outgoingConnection *OutgoingConnection + isClosed bool +} + +func newConnection(requestorURL string, services *sp.ServiceProviderRequest, exposedServices *sp.ServiceProviderPointer, resolvedURL string) *Connection { + info := connectionInfo{ + requestorURL, + resolvedURL, + } + var remoteServices *sp.ServiceProviderProxy + if exposedServices != nil { + remoteServices = sp.NewServiceProviderProxy(*exposedServices, bindings.GetAsyncWaiter()) + } + return &Connection{ + connectionInfo: info, + servicesRequest: services, + outgoingConnection: &OutgoingConnection{ + info, + remoteServices, + }, + } +} + +// ProvideServices starts a service provider on a separate goroutine that +// provides given services to the remote application. Returns a pointer to +// outgoing connection that can be used to connect to services provided by +// remote application. +// Panics if called more than once. +func (c *Connection) ProvideServices(services ...ServiceFactory) *OutgoingConnection { + if c.servicesProvided { + panic("ProvideServices can be called only once") + } + c.servicesProvided = true + if c.servicesRequest == nil { + return c.outgoingConnection + } + if len(services) == 0 { + c.servicesRequest.PassMessagePipe().Close() + return c.outgoingConnection + } + + provider := &serviceProviderImpl{ + make(map[string]ServiceFactory), + } + for _, service := range services { + provider.AddService(service) + } + c.localServices = sp.NewServiceProviderStub(*c.servicesRequest, provider, bindings.GetAsyncWaiter()) + go func() { + for { + if err := c.localServices.ServeRequest(); err != nil { + // TODO(rogulenko): don't log in case message pipe was closed + log.Println(err) + break + } + } + }() + return c.outgoingConnection +} + +// Close closes both incoming and outgoing parts of the connection. +func (c *Connection) Close() { + if c.servicesRequest != nil { + c.servicesRequest.Close() + } + if c.localServices != nil { + c.localServices.Close() + } + if c.outgoingConnection.remoteServices != nil { + c.outgoingConnection.remoteServices.Close_proxy() + } + c.isClosed = true +} + +// OutgoingConnection represents outgoing part of connection to another +// application. In order to close it close the |Connection| object that returned +// this |OutgoingConnection|. +type OutgoingConnection struct { + connectionInfo + remoteServices *sp.ServiceProviderProxy +} + +// ConnectToService asks remote application to provide a service through the +// message pipe endpoint supplied by the caller. +func (c *OutgoingConnection) ConnectToService(request ServiceRequest) { + pipe := request.PassMessagePipe() + if c.remoteServices == nil { + pipe.Close() + return + } + c.remoteServices.ConnectToService(request.Name(), pipe) +} + +// serviceProviderImpl is an implementation of mojo ServiceProvider interface. +type serviceProviderImpl struct { + factories map[string]ServiceFactory +} + +// Mojo ServiceProvider implementation. +func (sp *serviceProviderImpl) ConnectToService(name string, messagePipe system.MessagePipeHandle) error { + factory, ok := sp.factories[name] + if !ok { + messagePipe.Close() + return nil + } + factory.Create(messagePipe) + return nil +} + +func (sp *serviceProviderImpl) AddService(factory ServiceFactory) { + sp.factories[factory.Name()] = factory +} diff --git a/third_party/mojo/src/mojo/public/go/bindings/decoder.go b/third_party/mojo/src/mojo/public/go/bindings/decoder.go index 34a230e..8f25c0b 100644 --- a/third_party/mojo/src/mojo/public/go/bindings/decoder.go +++ b/third_party/mojo/src/mojo/public/go/bindings/decoder.go @@ -290,7 +290,6 @@ func (d *Decoder) ReadString() (string, error) { // ReadPointer reads a pointer and reassigns first unclaimed byte index if the // pointer is not null. func (d *Decoder) ReadPointer() (uint64, error) { - oldEnd := d.state().offset pointer, err := d.ReadUint64() if err != nil { return pointer, err @@ -299,7 +298,7 @@ func (d *Decoder) ReadPointer() (uint64, error) { return pointer, nil } - newEnd := uint64(oldEnd) + pointer + newEnd := uint64(d.state().offset-8) + pointer if newEnd >= uint64(len(d.buf)) { return 0, fmt.Errorf("trying to access out of range memory") } diff --git a/third_party/mojo/src/mojo/public/go/bindings/encoder.go b/third_party/mojo/src/mojo/public/go/bindings/encoder.go index 542cc21..e0a24fd 100644 --- a/third_party/mojo/src/mojo/public/go/bindings/encoder.go +++ b/third_party/mojo/src/mojo/public/go/bindings/encoder.go @@ -305,6 +305,8 @@ func (e *Encoder) WriteString(value string) error { // WritePointer writes a pointer to first unclaimed byte index. func (e *Encoder) WritePointer() error { + e.state().alignOffsetToBytes() + e.state().offset = align(e.state().offset, 8) return e.WriteUint64(uint64(e.end - e.state().offset)) } diff --git a/third_party/mojo/src/mojo/public/go/system/message_pipe.go b/third_party/mojo/src/mojo/public/go/system/message_pipe.go index 21b81ef..149b5be 100644 --- a/third_party/mojo/src/mojo/public/go/system/message_pipe.go +++ b/third_party/mojo/src/mojo/public/go/system/message_pipe.go @@ -49,7 +49,7 @@ func (h *messagePipe) WriteMessage(bytes []byte, handles []UntypedHandle, flags if len(handles) != 0 { rawHandles = make([]uint32, len(handles)) for i := 0; i < len(handles); i++ { - rawHandles[i] = uint32(handles[i].NativeHandle()) + rawHandles[i] = uint32(handles[i].ReleaseNativeHandle()) } } h.core.mu.Lock() diff --git a/third_party/mojo/src/mojo/public/interfaces/application/BUILD.gn b/third_party/mojo/src/mojo/public/interfaces/application/BUILD.gn index 5b15344..32a6987 100644 --- a/third_party/mojo/src/mojo/public/interfaces/application/BUILD.gn +++ b/third_party/mojo/src/mojo/public/interfaces/application/BUILD.gn @@ -12,8 +12,8 @@ mojom("application") { "shell.mojom", ] - # This base dir ensures that Dart's Mojo SDK can be imported with, e.g., + # The import_from ensures that Dart's Mojo SDK can be imported with, e.g., # import 'package:mojo/public/dart/core.dart' even when the Mojo SDK lives # somewhere else in the source tree. - base_dir = mojo_root + import_from = mojo_root } diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/BUILD.gn b/third_party/mojo/src/mojo/public/interfaces/bindings/BUILD.gn new file mode 100644 index 0000000..cf8a118 --- /dev/null +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/BUILD.gn @@ -0,0 +1,11 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("../../tools/bindings/mojom.gni") + +mojom("bindings") { + sources = [ + "interface_control_messages.mojom", + ] +} diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/interface_control_messages.mojom b/third_party/mojo/src/mojo/public/interfaces/bindings/interface_control_messages.mojom new file mode 100644 index 0000000..1ddccfc --- /dev/null +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/interface_control_messages.mojom @@ -0,0 +1,89 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module mojo.internal; + +// For each message pipe representing a user-defined interface, some control +// functions are provided at the same end of the message pipe as the +// user-defined interface, providing information about the user-defined +// interface and controlling behavior of the message pipe. + +//////////////////////////////////////////////////////////////////////////////// +// Run@0xFFFFFFFF(RunInput input) => (RunOutput? output); +// +// This control function runs the input command. If the command is not +// supported, |output| is set to null; otherwise |output| stores the result, +// whose type depends on the input. +// +// TODO(yzshen): Once union support is ready, switch the following definition +// to: +// struct RunMessageParams { +// RunInput input; +// }; +// union RunInput { +// QueryVersion query_version; +// }; +// +// struct RunResponseMessageParams { +// RunOutput? output; +// }; +// union RunOutput { +// QueryVersionResult query_version_result; +// }; + +const uint32 kRunMessageId = 0xFFFFFFFF; + +struct RunMessageParams { + // The reserved fields make the layout compatible with the RunInput union + // described above. + uint32 reserved0; // Must be set to 16. + uint32 reserved1; // Must be set to 0; + QueryVersion query_version; +}; + +struct RunResponseMessageParams { + // The reserved fields make the layout compatible with the RunOutput union + // described above. + uint32 reserved0; // Must be set to 16. + uint32 reserved1; // Must be set to 0. + QueryVersionResult query_version_result; +}; + +// Queries the max supported version of the user-defined interface. +struct QueryVersion { +}; +struct QueryVersionResult { + uint32 version; +}; + +//////////////////////////////////////////////////////////////////////////////// +// RunOrClosePipe@0xFFFFFFFE(RunOrClosePipeInput input); +// +// This control function runs the input command. If the operation fails or the +// command is not supported, the message pipe is closed. +// +// TODO(yzshen): Once union support is ready, switch the following definition +// to: +// struct RunOrClosePipeMessageParams { +// RunOrClosePipeInput input; +// }; +// union RunOrClosePipeInput { +// RequireVersion require_version; +// }; + +const uint32 kRunOrClosePipeMessageId = 0xFFFFFFFE; + +struct RunOrClosePipeMessageParams { + // The reserved fields make the layout compatible with the RunOrClosePipeInput + // union described above. + uint32 reserved0; // Must be set to 16. + uint32 reserved1; // Must be set to 0. + RequireVersion require_version; +}; + +// If the specified version of the user-defined interface is not supported, the +// function fails and the pipe is closed. +struct RequireVersion { + uint32 version; +}; diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/BUILD.gn b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/BUILD.gn index dabc3f6..572b58a 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/BUILD.gn +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/BUILD.gn @@ -18,7 +18,6 @@ mojom("test_interfaces") { "sample_service.mojom", "serialization_test_structs.mojom", "test_structs.mojom", - "test_unions.mojom", "validation_test_interfaces.mojom", ] } diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flags.data b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flag_combo.data index 6c9654b..6c9654b 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flags.data +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flag_combo.data diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flags.expected b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flag_combo.expected index 696c78d..696c78d 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flags.expected +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_invalid_flag_combo.expected diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_no_such_method.data b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_no_such_method.data new file mode 100644 index 0000000..a1b034c --- /dev/null +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_no_such_method.data @@ -0,0 +1,5 @@ +[dist4]message_header // num_bytes +[u4]2 // version number +[u4]9999 // There is no Method9999. +[u4]0 // flags +[anchr]message_header
\ No newline at end of file diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_no_such_method.expected b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_no_such_method.expected new file mode 100644 index 0000000..a32d895 --- /dev/null +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_msghdr_no_such_method.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags.data b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags.data new file mode 100644 index 0000000..07cf4c1 --- /dev/null +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags.data @@ -0,0 +1,6 @@ +[dist4]message_header // num_bytes +[u4]3 // version +[u4]0 // name +[u4]2 // flags: kMessageIsResponse is set in a request. +[u8]1 // request_id +[anchr]message_header diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags.expected b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags.expected new file mode 100644 index 0000000..696c78d --- /dev/null +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.data b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.data new file mode 100644 index 0000000..2b59165 --- /dev/null +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.data @@ -0,0 +1,7 @@ +[dist4]message_header // num_bytes +[u4]3 // version +[u4]0 // name +[u4]1 // flags: kMessageExpectsResponse is set in a request + // for a method that does not take a response. +[u8]1 // request_id +[anchr]message_header diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.expected b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.expected new file mode 100644 index 0000000..696c78d --- /dev/null +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd0_invalid_request_flags2.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.data b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.data new file mode 100644 index 0000000..1c80196 --- /dev/null +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.data @@ -0,0 +1,7 @@ +[dist4]message_header // num_bytes +[u4]3 // version +[u4]12 // name +[u4]0 // flags: kMessageExpectsResponse is not set but + // expected. +[u8]1 // request_id +[anchr]message_header diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.expected b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.expected new file mode 100644 index 0000000..696c78d --- /dev/null +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd12_invalid_request_flags.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.data b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.data new file mode 100644 index 0000000..d9ca38e --- /dev/null +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.data @@ -0,0 +1,6 @@ +[dist4]message_header // num_bytes +[u4]3 // version +[u4]12 // name +[u4]0 // flags: kMessageIsResponse is not set in a response. +[u8]1 // request_id +[anchr]message_header diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.expected b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.expected new file mode 100644 index 0000000..696c78d --- /dev/null +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags1.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.data b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.data new file mode 100644 index 0000000..7587fc5 --- /dev/null +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.data @@ -0,0 +1,6 @@ +[dist4]message_header // num_bytes +[u4]3 // version +[u4]12 // name +[u4]1 // flags: kMessageExpectsResponse is set in a response. +[u8]1 // request_id +[anchr]message_header diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.expected b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.expected new file mode 100644 index 0000000..696c78d --- /dev/null +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_invalid_response_flags2.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.data b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.data new file mode 100644 index 0000000..cc9cc8b --- /dev/null +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.data @@ -0,0 +1,6 @@ +[dist4]message_header // num_bytes +[u4]3 // version +[u4]11 // name: Method11 does not have a response message. +[u4]2 // flags: kMessageIsResponse +[u8]1 // request_id +[anchr]message_header diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.expected b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.expected new file mode 100644 index 0000000..65a48b3 --- /dev/null +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/data/validation/resp_conformance_msghdr_no_such_method.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD
\ No newline at end of file diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom index 52589a9..c3cd1b2a 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/sample_interfaces.mojom @@ -18,4 +18,5 @@ interface Provider { EchoStrings(string a, string b) => (string a, string b); EchoMessagePipeHandle(handle<message_pipe> a) => (handle<message_pipe> a); EchoEnum(Enum a) => (Enum a); + EchoInt(int32 a) => (int32 a); }; diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/test_unions.mojom b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/test_unions.mojom index eae8ce6..7250f65 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/test_unions.mojom +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/test_unions.mojom @@ -23,3 +23,22 @@ union ObjectUnion { int8 f_int8; string f_string; }; + +struct DummyStruct { + int8 f_int8; +}; + +struct SmallStruct { + DummyStruct? dummy_struct; + PodUnion? pod_union; + array<PodUnion>? pod_union_array; + array<DummyStruct>? s_array; +}; + +struct SmallStructNonNullableUnion { + PodUnion pod_union; +}; + +struct SmallObjStruct { + ObjectUnion obj_union; +}; diff --git a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom index 13d0b52..cd5e1aa 100644 --- a/third_party/mojo/src/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom +++ b/third_party/mojo/src/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom @@ -54,6 +54,7 @@ interface ConformanceTestInterface { Method9(array<array<handle?>>? param0); Method10(map<string, uint8> param0); Method11(StructG param0); + Method12(float param0) => (float param0); }; struct BasicStruct { diff --git a/third_party/mojo/src/mojo/public/js/validation_unittests.js b/third_party/mojo/src/mojo/public/js/validation_unittests.js index a04879c..4fdb5d4 100644 --- a/third_party/mojo/src/mojo/public/js/validation_unittests.js +++ b/third_party/mojo/src/mojo/public/js/validation_unittests.js @@ -225,11 +225,19 @@ define([ expect(testFiles.length).toBeGreaterThan(0); for (var i = 0; i < testFiles.length; i++) { - // TODO(hansmuller, yzshen): Temporarily skipping: - // - array pointer overflow tests; - // - struct versioning tests (tests with "mthd11" in the name). + // TODO(hansmuller) Temporarily skipping array pointer overflow tests + // because JS numbers are limited to 53 bits. + // TODO(yzshen) Skipping struct versioning tests (tests with "mthd11" + // in the name) because the feature is not supported in JS yet. + // TODO(rudominer): Temporarily skipping 'no-such-method', + // 'invalid_request_flags', and 'invalid_response_flags' until additional + // logic in *RequestValidator and *ResponseValidator is ported from + // cpp to js. if (testFiles[i].indexOf("overflow") != -1 || - testFiles[i].indexOf("mthd11") != -1) { + testFiles[i].indexOf("mthd11") != -1 || + testFiles[i].indexOf("no_such_method") != -1 || + testFiles[i].indexOf("invalid_request_flags") != -1 || + testFiles[i].indexOf("invalid_response_flags") != -1) { console.log("[Skipping " + testFiles[i] + "]"); continue; } @@ -257,12 +265,6 @@ define([ expect(testFiles.length).toBeGreaterThan(0); for (var i = 0; i < testFiles.length; i++) { - // TODO(hansmuller): Temporarily skipping array pointer overflow tests. - if (testFiles[i].indexOf("overflow") != -1) { - console.log("[Skipping " + testFiles[i] + "]"); - continue; - } - var testMessage = readTestMessage(testFiles[i]); var handles = new Array(testMessage.handleCount); var testMessagePipe = new core.createMessagePipe(); diff --git a/third_party/mojo/src/mojo/public/tools/BUILD.gn b/third_party/mojo/src/mojo/public/tools/BUILD.gn index 607ee7a..c8ea1e3 100644 --- a/third_party/mojo/src/mojo/public/tools/BUILD.gn +++ b/third_party/mojo/src/mojo/public/tools/BUILD.gn @@ -35,15 +35,25 @@ if (mojo_use_prebuilt_network_service) { copy("copy_network_service_apptests") { filename = "network_service_apptests.mojo" - if (is_android) { + if (defined(mojo_prebuilt_network_service_apptests_location) && + mojo_prebuilt_network_service_apptests_location != "") { sources = [ - "prebuilt/network_service_apptests/android-arm/$filename", + "$mojo_prebuilt_network_service_apptests_location", ] } else { - assert(is_linux) - sources = [ - "prebuilt/network_service_apptests/linux-x64/$filename", - ] + if (is_android) { + assert( + cpu_arch == "arm", + "Only arm version prebuilt netowrk_service_apptests.mojo is available.") + sources = [ + "prebuilt/network_service_apptests/android-arm/$filename", + ] + } else { + assert(is_linux) + sources = [ + "prebuilt/network_service_apptests/linux-x64/$filename", + ] + } } outputs = [ "$root_out_dir/$filename", diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl index 44d1428..fd6b643 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl @@ -46,7 +46,7 @@ class {{class_name}}_{{method.name}}_ForwardToCallback const {{class_name}}::{{method.name}}Callback& callback) : callback_(callback) { } - virtual bool Accept(mojo::Message* message) override; + bool Accept(mojo::Message* message) override; private: {{class_name}}::{{method.name}}Callback callback_; MOJO_DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ForwardToCallback); @@ -115,31 +115,33 @@ class {{class_name}}_{{method.name}}_ProxyToResponder : public {{class_name}}::{{method.name}}Callback::Runnable { public: virtual ~{{class_name}}_{{method.name}}_ProxyToResponder() { + // Is the Mojo application destroying the callback without running it + // and without first closing the pipe? + bool callback_was_dropped = responder_ && responder_->IsValid(); + // If the Callback was dropped then deleting the responder will close + // the pipe so the calling application knows to stop waiting for a reply. delete responder_; - // TODO(rudominer) DCHECK if |was_run_| is false and we don't have evidence - // that we are in a legitamte shutdown case such as the Connector detected - // an error or Close() was invoked. + MOJO_DCHECK(!callback_was_dropped) << "The callback passed to " + "{{class_name}}::{{method.name}}({%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}callback) " + "was never run."; } {{class_name}}_{{method.name}}_ProxyToResponder( uint64_t request_id, - mojo::MessageReceiver* responder) + mojo::MessageReceiverWithStatus* responder) : request_id_(request_id), - responder_(responder), - was_run_(false) { + responder_(responder) { } - virtual void Run({{interface_macros.declare_params("in_", method.response_parameters)}}) const override; + void Run({{interface_macros.declare_params("in_", method.response_parameters)}}) const override; private: uint64_t request_id_; - mutable mojo::MessageReceiver* responder_; - mutable bool was_run_; + mutable mojo::MessageReceiverWithStatus* responder_; MOJO_DISALLOW_COPY_AND_ASSIGN({{class_name}}_{{method.name}}_ProxyToResponder); }; void {{class_name}}_{{method.name}}_ProxyToResponder::Run( {{interface_macros.declare_params("in_", method.response_parameters)}}) const { - was_run_ = true; {{struct_macros.get_serialized_size(response_params_struct, "in_%s")}} mojo::internal::ResponseMessageBuilder builder( {{message_name}}, size, request_id_); @@ -159,6 +161,8 @@ void {{class_name}}_{{method.name}}_ProxyToResponder::Run( : sink_(nullptr) { } +{{class_name}}Stub::~{{interface.name}}Stub() {} + {#--- Stub definition #} bool {{class_name}}Stub::Accept(mojo::Message* message) { @@ -188,7 +192,7 @@ bool {{class_name}}Stub::Accept(mojo::Message* message) { } bool {{class_name}}Stub::AcceptWithResponder( - mojo::Message* message, mojo::MessageReceiver* responder) { + mojo::Message* message, mojo::MessageReceiverWithStatus* responder) { {%- if interface.methods %} switch (message->header()->name) { {%- for method in interface.methods %} @@ -227,16 +231,25 @@ bool {{class_name}}Stub::AcceptWithResponder( bool {{class_name}}RequestValidator::Accept(mojo::Message* message) { {%- if interface.methods %} + if (message->has_flag(mojo::internal::kMessageIsResponse)) { + ReportValidationError( + mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION); + return false; + } switch (message->header()->name) { {%- for method in interface.methods %} case internal::k{{class_name}}_{{method.name}}_Name: { {%- if method.response_parameters != None %} - if (!message->has_flag(mojo::internal::kMessageExpectsResponse)) - break; + if (!message->has_flag(mojo::internal::kMessageExpectsResponse)) { + ReportValidationError( + mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION); + return false; + } {%- else %} - if (message->has_flag(mojo::internal::kMessageExpectsResponse) || - message->has_flag(mojo::internal::kMessageIsResponse)) { - break; + if (message->has_flag(mojo::internal::kMessageExpectsResponse)) { + ReportValidationError( + mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION); + return false; } {%- endif %} mojo::internal::BoundsChecker bounds_checker( @@ -249,6 +262,12 @@ bool {{class_name}}RequestValidator::Accept(mojo::Message* message) { break; } {%- endfor %} + default: { + // Unrecognized message. + ReportValidationError( + mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD); + return false; + } } {%- endif %} @@ -264,11 +283,14 @@ bool {{class_name}}RequestValidator::Accept(mojo::Message* message) { bool {{class_name}}ResponseValidator::Accept(mojo::Message* message) { {%- if interface.methods %} + if (!message->has_flag(mojo::internal::kMessageIsResponse)) { + ReportValidationError( + mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION); + return false; + } switch (message->header()->name) { {%- for method in interface.methods if method.response_parameters != None %} case internal::k{{class_name}}_{{method.name}}_Name: { - if (!message->has_flag(mojo::internal::kMessageIsResponse)) - break; mojo::internal::BoundsChecker bounds_checker( message->payload(), message->payload_num_bytes(), message->handles()->size()); @@ -279,6 +301,12 @@ bool {{class_name}}ResponseValidator::Accept(mojo::Message* message) { break; } {%- endfor %} + default: { + // Unrecognized message. + ReportValidationError( + mojo::internal::VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD); + return false; + } } {%- endif %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl index 6b8e7c5..420f6e8 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl @@ -4,7 +4,7 @@ class {{interface.name}}Proxy : public {{interface.name}} { explicit {{interface.name}}Proxy(mojo::MessageReceiverWithResponder* receiver); {%- for method in interface.methods %} - virtual void {{method.name}}( + void {{method.name}}( {{interface_macros.declare_request_params("", method)}} ) override; {%- endfor %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl index 2239b69..29917ea 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_request_validator_declaration.tmpl @@ -2,5 +2,5 @@ class {{interface.name}}RequestValidator : public mojo::MessageFilter { public: explicit {{interface.name}}RequestValidator(mojo::MessageReceiver* sink = nullptr); - virtual bool Accept(mojo::Message* message) override; + bool Accept(mojo::Message* message) override; }; diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl index 801603d..5893bfd 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_response_validator_declaration.tmpl @@ -2,5 +2,5 @@ class {{interface.name}}ResponseValidator : public mojo::MessageFilter { public: explicit {{interface.name}}ResponseValidator(mojo::MessageReceiver* sink = nullptr); - virtual bool Accept(mojo::Message* message) override; + bool Accept(mojo::Message* message) override; }; diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl index afc6504..05a2495 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl @@ -1,12 +1,13 @@ -class {{interface.name}}Stub : public mojo::MessageReceiverWithResponder { +class {{interface.name}}Stub : public mojo::MessageReceiverWithResponderStatus { public: {{interface.name}}Stub(); + ~{{interface.name}}Stub() override; void set_sink({{interface.name}}* sink) { sink_ = sink; } {{interface.name}}* sink() { return sink_; } - virtual bool Accept(mojo::Message* message) override; - virtual bool AcceptWithResponder(mojo::Message* message, - mojo::MessageReceiver* responder) override; + bool Accept(mojo::Message* message) override; + bool AcceptWithResponder(mojo::Message* message, + mojo::MessageReceiverWithStatus* responder) override; private: {{interface.name}}* sink_; diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl index d9a398a..b4b4df8 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module-internal.h.tmpl @@ -50,15 +50,17 @@ class {{union.name}}_Data; #pragma pack(push, 1) +{#--- Unions must be declared first because they can be members of structs #} +{#--- Union class declarations #} +{% for union in unions %} +{% include "union_declaration.tmpl" %} +{%- endfor %} + {#--- Class declarations #} {% for struct in structs %} {% include "struct_declaration.tmpl" %} {%- endfor %} -{% for union in unions %} -{% include "union_declaration.tmpl" %} -{%- endfor %} - #pragma pack(pop) } // namespace internal diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl index 0312002..503e19d 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl @@ -63,6 +63,12 @@ typedef mojo::StructPtr<{{union.name}}> {{union.name}}Ptr; {% endif %} {%- endfor %} +{#--- Unions must be declared first because they can be members of structs #} +{#--- Unions #} +{% for union in unions %} +{% include "wrapper_union_class_declaration.tmpl" %} +{%- endfor %} + {#--- NOTE: Non-inlined structs may have pointers to inlined structs, so we #} {#--- need to fully define inlined structs ahead of the others. #} @@ -80,11 +86,6 @@ typedef mojo::StructPtr<{{union.name}}> {{union.name}}Ptr; {% endif %} {%- endfor %} -{#--- Unions #} -{% for union in unions %} -{% include "wrapper_union_class_declaration.tmpl" %} -{%- endfor %} - {#--- Interfaces -#} {% for interface in interfaces %} {% include "interface_declaration.tmpl" %} @@ -110,18 +111,17 @@ typedef mojo::StructPtr<{{union.name}}> {{union.name}}Ptr; {% include "interface_response_validator_declaration.tmpl" %} {%- endfor %} -{%- import "serialization_macros.tmpl" as serialization_macros %} {#--- Struct Serialization Helpers -#} {% if structs %} {% for struct in structs %} -{{ serialization_macros.declare_serialization(struct.name) }} +{% include "struct_serialization_declaration.tmpl" %} {%- endfor %} {%- endif %} {#--- Union Serialization Helpers -#} {% if unions %} {% for union in unions %} -{{ serialization_macros.declare_serialization(union.name) }} +{% include "union_serialization_declaration.tmpl" %} {%- endfor %} {%- endif %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/serialization_macros.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/serialization_macros.tmpl deleted file mode 100644 index 2be038c..0000000 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/serialization_macros.tmpl +++ /dev/null @@ -1,7 +0,0 @@ -{%- macro declare_serialization(name) %} -size_t GetSerializedSize_(const {{name}}Ptr& input); -void Serialize_({{name}}Ptr input, mojo::internal::Buffer* buffer, - internal::{{name}}_Data** output); -void Deserialize_(internal::{{name}}_Data* input, - {{name}}Ptr* output); -{%- endmacro %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl index 79a0bc4..32755cf 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_definition.tmpl @@ -11,17 +11,23 @@ {%- set kind = packed_field.field.kind %} {%- set wrapper_type = kind|cpp_wrapper_type %} {%- if not kind|is_nullable_kind %} +{%- if kind|is_union_kind %} + if (object->{{name}}.is_null()) { +{%- else %} if (!object->{{name}}.offset) { +{%- endif %} ReportValidationError( mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, "null {{name}} field in {{struct.name}} struct"); return false; } {%- endif %} +{%- if not kind|is_union_kind %} if (!mojo::internal::ValidateEncodedPointer(&object->{{name}}.offset)) { ReportValidationError(mojo::internal::VALIDATION_ERROR_ILLEGAL_POINTER); return false; } +{%- endif %} {%- if kind|is_array_kind or kind|is_string_kind %} if (!{{wrapper_type}}::Data_::Validate< {{kind|get_array_validate_params|indent(10)}}>( @@ -36,6 +42,9 @@ if (!{{kind|get_name_for_kind}}::Data_::Validate( mojo::internal::DecodePointerRaw(&object->{{name}}.offset), bounds_checker)) { +{%- elif kind|is_union_kind %} + if (!{{kind|get_name_for_kind}}::Data_::Validate( + &object->{{name}}, bounds_checker, true)) { {%- else %} if (!{{wrapper_type}}::Data_::Validate( mojo::internal::DecodePointerRaw(&object->{{name}}.offset), @@ -139,7 +148,7 @@ void {{class_name}}::EncodePointersAndHandles( std::vector<mojo::Handle>* handles) { MOJO_CHECK(header_.version == {{struct.versions[-1].version}}); {%- for pf in struct.packed.packed_fields_in_ordinal_order %} -{%- if pf.field.kind|is_object_kind %} +{%- if pf.field.kind|is_object_kind and not pf.field.kind|is_union_kind %} mojo::internal::Encode(&{{pf.field.name}}, handles); {%- elif pf.field.kind|is_any_handle_kind %} mojo::internal::EncodeHandle(&{{pf.field.name}}, handles); @@ -165,7 +174,9 @@ void {{class_name}}::DecodePointersAndHandles( if (header_.version < {{pf.min_version}}) return; {%- endif %} -{%- if kind|is_object_kind %} +{%- if kind|is_union_kind %} + // TODO(azani): Decode handles in union. +{%- elif kind|is_object_kind %} mojo::internal::Decode(&{{name}}, handles); {%- else %} mojo::internal::DecodeHandle(&{{name}}, handles); diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl index 03911b9..e8a625b 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl @@ -14,7 +14,11 @@ {%- macro get_serialized_size(struct, input_field_pattern) -%} size_t size = sizeof(internal::{{struct.name}}_Data); {%- for pf in struct.packed.packed_fields_in_ordinal_order if pf.field.kind|is_object_kind %} +{%- if pf.field.kind|is_union_kind %} + size += GetSerializedSize_({{input_field_pattern|format(pf.field.name)}}, true); +{%- else %} size += GetSerializedSize_({{input_field_pattern|format(pf.field.name)}}); +{%- endif %} {%- endfor %} {%- endmacro -%} @@ -46,12 +50,19 @@ {%- elif kind|is_map_kind %} mojo::SerializeMap_<{{kind.value_kind|get_map_validate_params|indent(24)}}>( mojo::internal::Forward({{input_field}}), {{buffer}}, &{{output}}->{{name}}.ptr); +{%- elif kind|is_union_kind %} + internal::{{kind.name}}_Data* {{name}}_ptr = &{{output}}->{{name}}; + SerializeUnion_(mojo::internal::Forward({{input_field}}), {{buffer}}, &{{name}}_ptr, true); {%- else %} Serialize_(mojo::internal::Forward({{input_field}}), {{buffer}}, &{{output}}->{{name}}.ptr); {%- endif %} {%- if not kind|is_nullable_kind %} MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( +{%- if kind|is_union_kind %} + {{output}}->{{name}}.is_null(), +{%- else %} !{{output}}->{{name}}.ptr, +{%- endif %} mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, "null {{name}} in {{struct_display_name}}"); {%- endif %} @@ -103,7 +114,11 @@ break; {%- endif %} {%- if kind|is_object_kind %} +{%- if kind|is_union_kind %} + Deserialize_(&{{input}}->{{name}}, &{{output_field}}); +{%- else %} Deserialize_({{input}}->{{name}}.ptr, &{{output_field}}); +{%- endif %} {%- elif kind|is_interface_kind or kind|is_interface_request_kind %} {{output_field}}.Bind(mojo::MakeScopedHandle(mojo::internal::FetchAndReset(&{{input}}->{{name}}))); {%- elif kind|is_any_handle_kind %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl new file mode 100644 index 0000000..604be86 --- /dev/null +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_declaration.tmpl @@ -0,0 +1,5 @@ +size_t GetSerializedSize_(const {{struct.name}}Ptr& input); +void Serialize_({{struct.name}}Ptr input, mojo::internal::Buffer* buffer, + internal::{{struct.name}}_Data** output); +void Deserialize_(internal::{{struct.name}}_Data* input, + {{struct.name}}Ptr* output); diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl index 6e6fe95..bc77c11 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/union_declaration.tmpl @@ -4,10 +4,22 @@ class {{class_name}} { public: + // Used to identify Mojom Union Data Classes. + typedef void MojomUnionDataType; static {{class_name}}* New(mojo::internal::Buffer* buf); + {{class_name}}(); + // Do nothing in the destructor since it won't be called. + ~{{class_name}}() {} static bool Validate(const void* data, - mojo::internal::BoundsChecker* bounds_checker); + mojo::internal::BoundsChecker* bounds_checker, + bool inlined); + + bool is_null() const { + return size == 0; + } + + void set_null(); enum class {{enum_name}} : uint32_t { {% for field in union.fields %} @@ -33,16 +45,12 @@ class {{class_name}} { uint64_t unknown; }; - uint32_t reserved; + uint32_t size; {{enum_name}} tag; Union_ data; void EncodePointersAndHandles(std::vector<mojo::Handle>* handles); void DecodePointersAndHandles(std::vector<mojo::Handle>* handles); - - private: - {{class_name}}(); - ~{{class_name}}() = delete; }; static_assert(sizeof({{class_name}}) == 16, "Bad sizeof({{class_name}})"); diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl index c9ea5d0..23e5cfd 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/union_definition.tmpl @@ -9,7 +9,8 @@ // static bool {{class_name}}::Validate(const void* data, - mojo::internal::BoundsChecker* bounds_checker) { + mojo::internal::BoundsChecker* bounds_checker, + bool inlined) { if (!data) { return true; } @@ -19,7 +20,10 @@ bool {{class_name}}::Validate(const void* data, return false; } - if (!bounds_checker->ClaimMemory(data, sizeof({{class_name}}))) { + // 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 && !bounds_checker->ClaimMemory(data, sizeof({{class_name}}))) { ReportValidationError( mojo::internal::VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE); return false; @@ -40,6 +44,12 @@ bool {{class_name}}::Validate(const void* data, } } +void {{class_name}}::set_null() { + size = 0U; + tag = static_cast<{{enum_name}}>(0); + data.unknown = 0U; +} + {{class_name}}::{{class_name}}() { } diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl new file mode 100644 index 0000000..404eb83 --- /dev/null +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_declaration.tmpl @@ -0,0 +1,5 @@ +size_t GetSerializedSize_(const {{union.name}}Ptr& input, bool inlined); +void SerializeUnion_({{union.name}}Ptr input, mojo::internal::Buffer* buffer, + internal::{{union.name}}_Data** output, bool inlined); +void Deserialize_(internal::{{union.name}}_Data* input, + {{union.name}}Ptr* output); diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl index 8b82f7f..f928eba 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/union_serialization_definition.tmpl @@ -1,8 +1,12 @@ -size_t GetSerializedSize_(const {{union.name}}Ptr& input) { +size_t GetSerializedSize_(const {{union.name}}Ptr& input, bool inlined) { + size_t size = 0U; + if (!inlined) { + size += sizeof(internal::{{union.name}}_Data); + } + if (!input) - return 0; + return size; - size_t size = sizeof(internal::{{union.name}}_Data); switch (input->which()) { {% for field in union.fields %} {% if field.kind|is_string_kind %} @@ -17,14 +21,17 @@ size_t GetSerializedSize_(const {{union.name}}Ptr& input) { return size; } - -void Serialize_({{union.name}}Ptr input, mojo::internal::Buffer* buf, - internal::{{union.name}}_Data** output) { +void SerializeUnion_({{union.name}}Ptr input, mojo::internal::Buffer* buf, + internal::{{union.name}}_Data** output, bool inlined) { + internal::{{union.name}}_Data* result = *output; if (input) { + if (!inlined) { + result = internal::{{union.name}}_Data::New(buf); + } mojo::internal::UnionAccessor<{{union.name}}> input_acc(input.get()); - internal::{{union.name}}_Data* result = - internal::{{union.name}}_Data::New(buf); // TODO(azani): Handle unknown and objects. + // Set the not-null flag. + result->size = 16; result->tag = input->which(); switch (input->which()) { {% for field in union.fields %} @@ -38,15 +45,17 @@ void Serialize_({{union.name}}Ptr input, mojo::internal::Buffer* buf, break; {%- endfor %} } - *output = result; + } else if (inlined) { + result->set_null(); } else { - *output = nullptr; + result = nullptr; } + *output = result; } void Deserialize_(internal::{{union.name}}_Data* input, {{union.name}}Ptr* output) { - if (input) { + if (input && !input->is_null()) { {{union.name}}Ptr result({{union.name}}::New()); mojo::internal::UnionAccessor<{{union.name}}> result_acc(result.get()); switch (input->tag) { diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl index da93e07..c171aa3 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/cpp_templates/wrapper_class_definition.tmpl @@ -18,7 +18,7 @@ {{struct.name}}Ptr {{struct.name}}::Clone() const { {{struct.name}}Ptr rv(New()); {%- for field in struct.fields %} -{%- if field.kind|is_struct_kind or field.kind|is_array_kind or field.kind|is_map_kind %} +{%- if field.kind|is_object_kind and not field.kind|is_string_kind %} rv->{{field.name}} = {{field.name}}.Clone(); {%- else %} rv->{{field.name}} = {{field.name}}; diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/module.lib.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/module.lib.tmpl index 21b643e..d6b869e 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/module.lib.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/module.lib.tmpl @@ -10,7 +10,7 @@ import 'package:mojo/public/dart/bindings.dart' as bindings; import 'package:mojo/public/dart/core.dart' as core; {%- for import in imports %} -import 'package:{{import.module.path}}.dart' as {{import.unique_name}}; +import 'package:{{import.rebased_path}}.dart' as {{import.unique_name}}; {%- endfor %} {%- include "module_definition.tmpl" %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/struct_definition.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/struct_definition.tmpl index 4d3f54d..6d9b653 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/struct_definition.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/dart_templates/struct_definition.tmpl @@ -74,9 +74,11 @@ if (decoder{{level+1}} == null) { {%- macro struct_def(struct) %} class {{struct|name}} extends bindings.Struct { - static const int kStructSize = {{struct.versions[-1].num_bytes}}; - static const bindings.StructDataHeader kDefaultStructInfo = - const bindings.StructDataHeader(kStructSize, {{struct.versions[-1].version}}); + static const List<bindings.StructDataHeader> kVersions = const [ +{%- for version in struct.versions %} + const bindings.StructDataHeader({{version.num_bytes}}, {{version.version}}){% if not loop.last %},{% endif %} +{%- endfor %} + ]; {#--- Enums #} {%- from "enum_definition.tmpl" import enum_def %} @@ -95,7 +97,7 @@ class {{struct|name}} extends bindings.Struct { {{packed_field.field.kind|dart_type}} {{packed_field.field|name}} = {{packed_field.field|default_value}}; {%- endfor %} - {{struct|name}}() : super(kStructSize); + {{struct|name}}() : super(kVersions.last.size); static {{struct|name}} deserialize(bindings.Message message) { return decode(new bindings.Decoder(message)); @@ -108,14 +110,24 @@ class {{struct|name}} extends bindings.Struct { {{struct|name}} result = new {{struct|name}}(); var mainDataHeader = decoder0.decodeStructDataHeader(); - if ((mainDataHeader.size < kStructSize) || - (mainDataHeader.version < {{struct.versions[-1].version}})) { - throw new bindings.MojoCodecError('Malformed header'); + if (mainDataHeader.version <= kVersions.last.version) { + // Scan in reverse order to optimize for more recent versions. + for (int i = kVersions.length - 1; i >= 0; --i) { + if (mainDataHeader.version >= kVersions[i].version) { + if (mainDataHeader.size != kVersions[i].size) + throw new bindings.MojoCodecError( + 'Header doesn\'t correspond to any known version.'); + } + } + } else if (mainDataHeader.size < kVersions.last.size) { + throw new bindings.MojoCodecError( + 'Message newer than the last known version cannot be shorter than ' + 'required by the last known version.'); } {%- for byte in struct.bytes %} {%- for packed_field in byte.packed_fields %} - { + if (mainDataHeader.version >= {{packed_field.min_version}}) { {{decode('result.' ~ packed_field.field|name, packed_field.field.kind, 8+packed_field.offset, packed_field.bit)|indent(6)}} } {%- endfor %} @@ -125,9 +137,9 @@ class {{struct|name}} extends bindings.Struct { void encode(bindings.Encoder encoder) { {%- if not struct.bytes %} - encoder.getStructEncoderAtOffset(kDefaultStructInfo); + encoder.getStructEncoderAtOffset(kVersions.last); {%- else %} - var encoder0 = encoder.getStructEncoderAtOffset(kDefaultStructInfo); + var encoder0 = encoder.getStructEncoderAtOffset(kVersions.last); {%- endif %} {%- for byte in struct.bytes %} {%- for packed_field in byte.packed_fields %} diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/go_templates/interface.tmpl b/third_party/mojo/src/mojo/public/tools/bindings/generators/go_templates/interface.tmpl index c96441e..25435e3 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/go_templates/interface.tmpl +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/go_templates/interface.tmpl @@ -42,9 +42,37 @@ type {{interface|name}} interface { {% endfor %} } +var {{interface|name(False)}}_Name = "{{interface.module.namespace|replace(".","::")}}::{{interface.name}}" + type {{interface|name}}Request bindings.InterfaceRequest + +func (r *{{interface|name}}Request) Name() string { + return {{interface|name(False)}}_Name +} + type {{interface|name}}Pointer bindings.InterfacePointer +func (p *{{interface|name}}Pointer) Name() string { + return {{interface|name(False)}}_Name +} + +type {{interface|name}}ServiceFactory struct{ + Delegate {{interface|name}}Factory +} + +type {{interface|name}}Factory interface { + Create(request {{interface|name}}Request) +} + +func (f *{{interface|name}}ServiceFactory) Name() string { + return {{interface|name(False)}}_Name +} + +func (f *{{interface|name}}ServiceFactory) Create(messagePipe system.MessagePipeHandle) { + request := {{interface|name}}Request{bindings.NewMessagePipeHandleOwner(messagePipe)} + f.Delegate.Create(request) +} + // CreateMessagePipeFor{{interface|name}} creates a message pipe for use with the // {{interface|name}} interface with a {{interface|name}}Request on one end and a {{interface|name}}Pointer on the other. func CreateMessagePipeFor{{interface|name}}() ({{interface|name}}Request, {{interface|name}}Pointer) { diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_cpp_generator.py index 8b7e219..f38aecb 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_cpp_generator.py +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_cpp_generator.py @@ -75,6 +75,8 @@ def GetCppType(kind): GetCppType(kind.key_kind), GetCppType(kind.value_kind)) if mojom.IsStructKind(kind): return "%s_Data*" % GetNameForKind(kind, internal=True) + if mojom.IsUnionKind(kind): + return "%s_Data" % GetNameForKind(kind, internal=True) if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind): return "mojo::MessagePipeHandle" if mojom.IsEnumKind(kind): @@ -91,7 +93,7 @@ def GetCppPodType(kind): def GetCppArrayArgWrapperType(kind): if mojom.IsEnumKind(kind): return GetNameForKind(kind) - if mojom.IsStructKind(kind): + if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind): return "%sPtr" % GetNameForKind(kind) if mojom.IsArrayKind(kind): return "mojo::Array<%s> " % GetCppArrayArgWrapperType(kind.kind) @@ -147,7 +149,7 @@ def GetCppResultWrapperType(kind): def GetCppWrapperType(kind): if mojom.IsEnumKind(kind): return GetNameForKind(kind) - if mojom.IsStructKind(kind): + if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind): return "%sPtr" % GetNameForKind(kind) if mojom.IsArrayKind(kind): return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind) @@ -206,6 +208,8 @@ def GetCppFieldType(kind): if mojom.IsStructKind(kind): return ("mojo::internal::StructPointer<%s_Data>" % GetNameForKind(kind, internal=True)) + if mojom.IsUnionKind(kind): + return "%s_Data" % GetNameForKind(kind, internal=True) if mojom.IsArrayKind(kind): return "mojo::internal::ArrayPointer<%s>" % GetCppType(kind.kind) if mojom.IsMapKind(kind): diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_dart_generator.py b/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_dart_generator.py index 2b562f2..fb1cfd4 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_dart_generator.py +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_dart_generator.py @@ -4,6 +4,7 @@ """Generates dart source files from a mojom.Module.""" +import os import re import mojom.generate.generator as generator @@ -373,10 +374,10 @@ class Generator(generator.Generator): 'struct_from_method': generator.GetStructFromMethod, } - def GetParameters(self): + def GetParameters(self, args): return { "namespace": self.module.namespace, - "imports": self.GetImports(), + "imports": self.GetImports(args), "kinds": self.module.kinds, "enums": self.module.enums, "module": self.module, @@ -387,14 +388,20 @@ class Generator(generator.Generator): } @UseJinja("dart_templates/module.lib.tmpl", filters=dart_filters) - def GenerateLibModule(self): - return self.GetParameters() + def GenerateLibModule(self, args): + return self.GetParameters(args) def GenerateFiles(self, args): - self.Write(self.GenerateLibModule(), + self.Write(self.GenerateLibModule(args), self.MatchMojomFilePath("%s.dart" % self.module.name)) - def GetImports(self): + def GetImports(self, args): + mojo_root_arg = next( + (x for x in args if x.startswith("--dart_mojo_root")), "") + (_, _, mojo_root_path) = mojo_root_arg.partition("=") + if not mojo_root_path.startswith("//"): + raise Exception("Malformed mojo SDK root: " + mojo_root_path) + mojo_root_path = mojo_root_path[2:] # strip // used_names = set() for each_import in self.module.imports: simple_name = each_import["module_name"].split(".")[0] @@ -410,6 +417,16 @@ class Generator(generator.Generator): used_names.add(unique_name) each_import["unique_name"] = unique_name + '_mojom' counter += 1 + + # At this point, a module's path is reletive to the root of the repo. + # However, imports of libraries from the Mojo SDK are always reletive to + # root of the Mojo SDK, which may be different from the root of the repo. + # This code uses the --dart_mojo_root argument to ensure that Mojo SDK + # imports are reletive to the Mojo SDK root. + path = each_import['module'].path + if os.path.commonprefix([mojo_root_path, path]) == mojo_root_path: + path = os.path.relpath(path, mojo_root_path) + each_import["rebased_path"] = path return self.module.imports def GetImportedInterfaces(self): diff --git a/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_go_generator.py b/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_go_generator.py index 9273186..f841b848 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_go_generator.py +++ b/third_party/mojo/src/mojo/public/tools/bindings/generators/mojom_go_generator.py @@ -263,6 +263,8 @@ def GetImports(module): if len(all_structs) > 0 or len(module.interfaces) > 0: _imports['fmt'] = 'fmt' _imports['mojo/public/go/bindings'] = 'bindings' + if len(module.interfaces) > 0: + _imports['mojo/public/go/system'] = 'system' if len(all_structs) > 0: _imports['sort'] = 'sort' diff --git a/third_party/mojo/src/mojo/public/tools/bindings/mojom.gni b/third_party/mojo/src/mojo/public/tools/bindings/mojom.gni index dd68501..6c03ea6 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/mojom.gni +++ b/third_party/mojo/src/mojo/public/tools/bindings/mojom.gni @@ -61,20 +61,21 @@ template("mojom") { "$generator_root/generators/cpp_templates/interface_request_validator_declaration.tmpl", "$generator_root/generators/cpp_templates/interface_response_validator_declaration.tmpl", "$generator_root/generators/cpp_templates/interface_stub_declaration.tmpl", + "$generator_root/generators/cpp_templates/module-internal.h.tmpl", "$generator_root/generators/cpp_templates/module.cc.tmpl", "$generator_root/generators/cpp_templates/module.h.tmpl", - "$generator_root/generators/cpp_templates/module-internal.h.tmpl", - "$generator_root/generators/cpp_templates/serialization_macros.tmpl", "$generator_root/generators/cpp_templates/struct_declaration.tmpl", "$generator_root/generators/cpp_templates/struct_definition.tmpl", - "$generator_root/generators/cpp_templates/struct_serialization_definition.tmpl", "$generator_root/generators/cpp_templates/struct_macros.tmpl", - "$generator_root/generators/cpp_templates/wrapper_class_declaration.tmpl", - "$generator_root/generators/cpp_templates/wrapper_class_definition.tmpl", + "$generator_root/generators/cpp_templates/struct_serialization_declaration.tmpl", + "$generator_root/generators/cpp_templates/struct_serialization_definition.tmpl", "$generator_root/generators/cpp_templates/union_declaration.tmpl", "$generator_root/generators/cpp_templates/union_definition.tmpl", + "$generator_root/generators/cpp_templates/union_serialization_declaration.tmpl", "$generator_root/generators/cpp_templates/union_serialization_definition.tmpl", "$generator_root/generators/cpp_templates/validation_macros.tmpl", + "$generator_root/generators/cpp_templates/wrapper_class_declaration.tmpl", + "$generator_root/generators/cpp_templates/wrapper_class_definition.tmpl", "$generator_root/generators/cpp_templates/wrapper_union_class_declaration.tmpl", "$generator_root/generators/cpp_templates/wrapper_union_class_definition.tmpl", "$generator_root/generators/dart_templates/enum_definition.tmpl", @@ -101,8 +102,8 @@ template("mojom") { "$generator_root/generators/js_templates/module.amd.tmpl", "$generator_root/generators/js_templates/module_definition.tmpl", "$generator_root/generators/js_templates/struct_definition.tmpl", - "$generator_root/generators/python_templates/module_macros.tmpl", "$generator_root/generators/python_templates/module.py.tmpl", + "$generator_root/generators/python_templates/module_macros.tmpl", "$generator_root/generators/mojom_cpp_generator.py", "$generator_root/generators/mojom_dart_generator.py", "$generator_root/generators/mojom_go_generator.py", @@ -180,6 +181,7 @@ template("mojom") { generator_js_outputs + generator_python_outputs args = [ "{{source}}", + "--dart_mojo_root=${mojo_root}", "--use_bundled_pylibs", "-d", rebase_path("//", root_build_dir), @@ -334,21 +336,21 @@ template("mojom") { output, ] - invoker_base_dir = "" - if (defined(invoker.base_dir)) { - invoker_base_dir = - rebase_path(invoker.base_dir, "$root_build_dir/../../", ".") + invoker_import_from = "" + if (defined(invoker.import_from)) { + invoker_import_from = + rebase_path(invoker.import_from, "$root_build_dir/../../", ".") } - rebase_base_dir = - rebase_path("$root_build_dir/gen/$invoker_base_dir", root_build_dir) + rebase_import_from = + rebase_path("$root_build_dir/gen/$invoker_import_from", root_build_dir) if (defined(invoker.sources)) { rebase_inputs = rebase_path(inputs, root_build_dir) } rebase_zip_inputs = rebase_path(zip_inputs, root_build_dir) rebase_output = rebase_path(output, root_build_dir) args = [ - "--base-dir=$rebase_base_dir", + "--base-dir=$rebase_import_from", "--zip-inputs=$rebase_zip_inputs", "--output=$rebase_output", ] diff --git a/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom/generate/pack.py b/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom/generate/pack.py index 4e522c9..9c72cfa 100644 --- a/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom/generate/pack.py +++ b/third_party/mojo/src/mojo/public/tools/bindings/pylib/mojom/generate/pack.py @@ -45,6 +45,8 @@ class PackedField(object): def GetSizeForKind(cls, kind): if isinstance(kind, (mojom.Array, mojom.Map, mojom.Struct)): return 8 + if isinstance(kind, mojom.Union): + return 16 if (isinstance(kind, mojom.Interface) or isinstance(kind, mojom.InterfaceRequest)): kind = mojom.MSGPIPE diff --git a/third_party/mojo/src/mojo/public/tools/dart_analyze.py b/third_party/mojo/src/mojo/public/tools/dart_analyze.py index c200783..f26ddbf 100755 --- a/third_party/mojo/src/mojo/public/tools/dart_analyze.py +++ b/third_party/mojo/src/mojo/public/tools/dart_analyze.py @@ -20,6 +20,9 @@ import tempfile import zipfile _ANALYZING_PATTERN = re.compile(r'^Analyzing \[') +_NO_ISSUES_FOUND_PATTERN = re.compile(r'^No issues found') +_PART_WARNINGS_PATTERN = re.compile( + r'.*is a part and can not|^Only libraries can be analyzed') _ERRORS_AND_WARNINGS_PATTERN = re.compile( r'^[0-9]+ errors? and [0-9]+ warnings? found.') _ERRORS_PATTERN = re.compile(r'^([0-9]+|No) (error|warning|issue)s? found.') @@ -59,6 +62,8 @@ def main(args): raw_lines.pop() filtered_lines = [i for i in raw_lines if ( not re.match(_ANALYZING_PATTERN, i) and + not re.match(_NO_ISSUES_FOUND_PATTERN, i) and + not re.match(_PART_WARNINGS_PATTERN, i) and not re.match(_ERRORS_AND_WARNINGS_PATTERN, i) and not re.match(_ERRORS_PATTERN, i))] for line in filtered_lines: diff --git a/third_party/mojo/src/mojo/public/tools/gn/zip.py b/third_party/mojo/src/mojo/public/tools/gn/zip.py index 506eb9e..a73f801 100755 --- a/third_party/mojo/src/mojo/public/tools/gn/zip.py +++ b/third_party/mojo/src/mojo/public/tools/gn/zip.py @@ -15,7 +15,7 @@ import zipfile def DoZip(inputs, zip_inputs, output, base_dir): files = [] - with zipfile.ZipFile(output, 'w') as outfile: + with zipfile.ZipFile(output, 'w', zipfile.ZIP_DEFLATED) as outfile: for f in inputs: file_name = os.path.relpath(f, base_dir) files.append(file_name) diff --git a/third_party/mojo/src/mojo/public/tools/mojom_fetcher/mojom_fetcher.py b/third_party/mojo/src/mojo/public/tools/mojom_fetcher/mojom_fetcher.py new file mode 100755 index 0000000..b0283e3 --- /dev/null +++ b/third_party/mojo/src/mojo/public/tools/mojom_fetcher/mojom_fetcher.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Tool to manage external mojom interfaces.""" + +import argparse +import errno +import logging +import os +import sys +import urllib2 + +# Local library +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), + "pylib")) +# Bindings library +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), + "..", "bindings", "pylib")) +# Requests library +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), + "..", "..", "..", "..", "third_party", + "requests", "src")) + +import requests + +from fetcher.repository import Repository +from fetcher.dependency import Dependency +from mojom.parse.parser import Parse, ParseError + + +class UrlRewriterException(Exception): + """Exception when processing URL rewrite rules.""" + pass + +class UrlRewriter(object): + """UrlRewriter rewrites URLs according to the provided mappings. + + Note that mappings are not followed transitively. If mappings contains + {"a": "b", "b": "c"}, then UrlRewriter.rewrite("a") will return "b", not "c". + """ + + def __init__(self, mappings): + self._mappings = mappings + for target in self._mappings.values(): + for source in self._mappings.keys(): + if source in target or target in source: + raise UrlRewriterException( + "%s and %s share a common subpath" % (source, target)) + + def rewrite(self, path): + for origin, destination in self._mappings.items(): + if path.startswith(origin): + return destination + path[len(origin):] + return path + + +class MojomFetcher(object): + def __init__(self, repository, url_rewriter): + self._repository = repository + self._url_rewriter = url_rewriter + + def _requests_get(self, url): + return requests.get(url, verify=True) + + def _os_makedirs(self, dirs): + try: + os.makedirs(dirs) + except OSError as e: + # The directory may already exist, we don't care. + if e.errno != errno.EEXIST: + raise + + def _open(self, f, mode="r"): + return open(f, mode) + + def _download_dependencies(self, dependencies): + """Takes the list of mojom dependencies and download the external ones. + Returns the number of successfully downloaded dependencies.""" + + downloaded = 0 + for dep in dependencies: + if self._maybe_download_dep(dep): + downloaded += 1 + return downloaded + + def _maybe_download_dep(self, dep): + if not dep.maybe_is_a_url(): + return False + + for candidate in dep.generate_candidate_urls(): + url = self._url_rewriter.rewrite(candidate) + response = self._requests_get("https://" + url) + if not response.ok: + # If we get an error, it just mean that this candidate URL is not + # correct. We must try the other ones before giving up. + logging.debug("Error while downloading %s (%s)", candidate, url) + continue + # This is an external dependency + directory = os.path.dirname(candidate) + full_directory = os.path.join(self._repository.get_external_directory(), + directory) + try: + self._os_makedirs(full_directory) + except OSError as e: + # The directory may already exist, we don't care. + if e.errno != errno.EEXIST: + raise + with self._open(os.path.join(self._repository.get_external_directory(), + candidate), "w") as f: + data = response.content + try: + Parse(data, candidate) + except ParseError: + logging.warn("File at %s is not a mojom", url) + break + f.write(data) + return True + return False + + def discover(self): + """Discover missing .mojom dependencies and download them.""" + while True: + missing_deps = self._repository.get_missing_dependencies() + downloaded = self._download_dependencies(missing_deps) + if downloaded == 0: + return 0 + + def get(self, dep): + dependency = Dependency(self._repository, ".", dep) + downloaded = self._download_dependencies([dependency]) + if downloaded != 0: + return self.discover() + else: + return -1 + + def update(self): + dependencies = [Dependency(self._repository, ".", f) + for f in self._repository.get_external_urls()] + # TODO(etiennej): We may want to suggest to the user to delete + # un-downloadable dependencies. + downloaded = self._download_dependencies(dependencies) + if downloaded != 0: + return self.discover() + else: + return -1 + +def _main(args): + if args.prefix_rewrite: + rewrite_rules = dict([x.split(':', 1) for x in args.prefix_rewrite]) + else: + rewrite_rules = {} + rewriter = UrlRewriter(rewrite_rules) + repository_path = os.path.abspath(args.repository_path) + repository = Repository(repository_path, args.external_dir) + fetcher = MojomFetcher(repository, rewriter) + if args.action == 'discover': + return fetcher.discover() + elif args.action == 'get': + return fetcher.get(args.url) + elif args.action == 'update': + return fetcher.update() + else: + logging.error("No matching action %s", args.action[0]) + return -1 + +def main(): + logging.basicConfig(level=logging.ERROR) + parser = argparse.ArgumentParser(description='Download mojom dependencies.') + parser.add_argument('--repository-path', type=str, action='store', + default='.', help='The path to the client repository.') + parser.add_argument('--external-dir', type=str, action='store', + default='external', + help='Directory for external interfaces') + parser.add_argument('--prefix-rewrite', type=str, action='append', + help='If present, "origin:destination" pairs. "origin" ' + 'prefixes will be rewritten as "destination". May be ' + 'used several times. Rewrites are not transitive.') + + subparsers = parser.add_subparsers(dest='action', help='action') + parser_get = subparsers.add_parser( + 'get', help='Get the specified URL and all its transitive dependencies') + parser_get.add_argument('url', type=str, nargs=1, + help='URL to download for get action') + subparsers.add_parser( + 'discover', + help='Recursively discover and download new external dependencies') + subparsers.add_parser('update', help='Update all external dependencies') + + args = parser.parse_args() + return _main(args) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/third_party/mojo/src/mojo/public/tools/mojom_fetcher/pylib/fetcher/__init__.py b/third_party/mojo/src/mojo/public/tools/mojom_fetcher/pylib/fetcher/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/third_party/mojo/src/mojo/public/tools/mojom_fetcher/pylib/fetcher/__init__.py diff --git a/third_party/mojo/src/mojo/public/tools/mojom_fetcher/pylib/fetcher/dependency.py b/third_party/mojo/src/mojo/public/tools/mojom_fetcher/pylib/fetcher/dependency.py new file mode 100644 index 0000000..a4716e4 --- /dev/null +++ b/third_party/mojo/src/mojo/public/tools/mojom_fetcher/pylib/fetcher/dependency.py @@ -0,0 +1,97 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import os + + +class Dependency(object): + """Dependency represents an import request from one mojom file to another. + """ + def __init__(self, repository, importer, imported): + self._repository = repository + self._importer_filename = os.path.normpath(importer) + self._imported_filename = os.path.normpath(imported) + + def __str__(self): + return str(self.__dict__) + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + def get_importer(self): + """Returns the name and full path of the file doing the import.""" + return self._importer_filename + + def get_imported(self): + """Returns the imported file (filename and path).""" + return self._imported_filename + + def is_sdk_dep(self): + """Returns whether this dependency is from the mojo SDK.""" + return (self._imported_filename.startswith("mojo/public/") or + self._imported_filename.startswith("//mojo/public/")) + + def _is_in_external(self): + """Returns whether this dependency is under the external directory.""" + common = os.path.commonprefix((self._repository.get_external_directory(), + self._importer_filename)) + return common == self._repository.get_external_directory() + + def maybe_is_a_url(self): + """Returns whether this dependency may be pointing to a downloadable + ressource.""" + if self._is_in_external() and not self.is_sdk_dep(): + # External dependencies may refer to other dependencies by relative path, + # so they can always be URLs. + return True + + base, _ = self._imported_filename.split(os.path.sep, 1) + if not '.' in base: + # There is no dot separator in the first part of the path; it cannot be a + # URL. + return False + return True + + def generate_candidate_urls(self): + """Generates possible paths where to download this dependency. It is + expected that at most one of them should work.""" + candidates = [] + + base, _ = self._imported_filename.split(os.path.sep, 1) + if '.' in base and not base.startswith('.'): + # This import may be an absolute URL path (without scheme). + candidates.append(self._imported_filename) + + # External dependencies may refer to other dependencies by relative path. + if self._is_in_external(): + directory = os.path.relpath(os.path.dirname(self._importer_filename), + self._repository.get_external_directory()) + + # This is to handle the case where external dependencies use + # imports relative to a directory upper in the directory structure. As we + # don't know which directory, we need to go through all of them. + while len(directory) > 0: + candidates.append(os.path.join(directory, self._imported_filename)) + directory = os.path.dirname(directory) + return candidates + + def get_search_path_for_dependency(self): + """Return all possible search paths for this dependency.""" + + # Root directory and external directory are always included. + search_paths = set([self._repository.get_repo_root_directory(), + self._repository.get_external_directory()]) + # Local import paths + search_paths.add(os.path.dirname(self._importer_filename)) + + if self._is_in_external(): + directory = os.path.dirname(self._importer_filename) + + # This is to handle the case where external dependencies use + # imports relative to a directory upper in the directory structure. As we + # don't know which directory, we need to go through all of them. + while self._repository.get_external_directory() in directory: + search_paths.add(directory) + directory = os.path.dirname(directory) + return search_paths diff --git a/third_party/mojo/src/mojo/public/tools/mojom_fetcher/pylib/fetcher/mojom_file.py b/third_party/mojo/src/mojo/public/tools/mojom_fetcher/pylib/fetcher/mojom_file.py new file mode 100644 index 0000000..ad8c969 --- /dev/null +++ b/third_party/mojo/src/mojo/public/tools/mojom_fetcher/pylib/fetcher/mojom_file.py @@ -0,0 +1,18 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +from fetcher.dependency import Dependency + + +class MojomFile(object): + """Mojom represents an interface file at a given location in the + repository.""" + def __init__(self, repository, name): + self.name = name + self._repository = repository + self.deps = [] + + def add_dependency(self, dependency): + """Declare a new dependency of this mojom.""" + self.deps.append(Dependency(self._repository, self.name, dependency)) diff --git a/third_party/mojo/src/mojo/public/tools/mojom_fetcher/pylib/fetcher/repository.py b/third_party/mojo/src/mojo/public/tools/mojom_fetcher/pylib/fetcher/repository.py new file mode 100644 index 0000000..dde99b2 --- /dev/null +++ b/third_party/mojo/src/mojo/public/tools/mojom_fetcher/pylib/fetcher/repository.py @@ -0,0 +1,88 @@ +# Copyright 2015 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import os + +from fetcher.dependency import Dependency +from mojom.parse.parser import Parse + + +class Repository(object): + """Repository represents a code repository on the local disc.""" + def __init__(self, root_dir, external_dir): + """root_dir represents the root of the repository; + external_dir is the relative path of the external directory within the + repository (so, relative to root_dir) + """ + self._root_dir = os.path.normpath(root_dir) + self._external_dir = external_dir + + def get_repo_root_directory(self): + return self._root_dir + + def get_external_directory(self): + return os.path.join(self._root_dir, self._external_dir) + + def _os_walk(self, root_directory): + # This method is included for dependency injection + return os.walk(root_directory) + + def _open(self, filename): + # This method is included for dependency injection + return open(filename) + + def _get_all_mojom_in_directory(self, root_directory): + mojoms = [] + for dirname, _, files in self._os_walk(root_directory): + for f in files: + if f.endswith(".mojom"): + mojoms.append(os.path.join(dirname,f)) + return mojoms + + def _resolve_dependencies(self, dependencies, mojoms): + """Resolve dependencies between discovered mojoms, so we know which are the + missing ones.""" + missing = [] + for dependency in dependencies: + found = False + for search_path in dependency.get_search_path_for_dependency(): + if os.path.normpath( + os.path.join(search_path, + dependency.get_imported())) in mojoms: + found = True + break + if not found: + missing.append(dependency) + return missing + + def get_missing_dependencies(self): + """get_missing_dependencies returns a set of dependencies that are required + by mojoms in this repository but not available. + """ + # Update the list of available mojoms in this repository. + mojoms = set(self._get_all_mojom_in_directory(self._root_dir)) + + # Find all declared dependencies + needed_deps = set([]) + for mojom in mojoms: + with self._open(mojom) as f: + source = f.read() + tree = Parse(source, mojom) + for dep in tree.import_list: + needed_deps.add(Dependency(self, dep.filename, dep.import_filename)) + + missing_deps = self._resolve_dependencies(needed_deps, mojoms) + + return missing_deps + + def get_external_urls(self): + """Get all external mojom files in this repository, by urls (without + scheme).""" + mojoms = set(self._get_all_mojom_in_directory( + self.get_external_directory())) + urls = [] + for mojom in mojoms: + urls.append(os.path.relpath(mojom, self.get_external_directory())) + return urls + diff --git a/third_party/mojo_services/src/accessibility/public/interfaces/BUILD.gn b/third_party/mojo_services/src/accessibility/public/interfaces/BUILD.gn index 2b29c1a..f2c1ba8 100644 --- a/third_party/mojo_services/src/accessibility/public/interfaces/BUILD.gn +++ b/third_party/mojo_services/src/accessibility/public/interfaces/BUILD.gn @@ -15,4 +15,6 @@ mojom("interfaces") { deps = [ "../../../geometry/public/interfaces", ] + + import_from = mojo_root } diff --git a/third_party/mojo_services/src/clipboard/public/interfaces/BUILD.gn b/third_party/mojo_services/src/clipboard/public/interfaces/BUILD.gn index 16fe33b..4edbf63 100644 --- a/third_party/mojo_services/src/clipboard/public/interfaces/BUILD.gn +++ b/third_party/mojo_services/src/clipboard/public/interfaces/BUILD.gn @@ -9,4 +9,6 @@ mojom("interfaces") { sources = [ "clipboard.mojom", ] + + import_from = mojo_root } diff --git a/third_party/mojo_services/src/content_handler/public/interfaces/BUILD.gn b/third_party/mojo_services/src/content_handler/public/interfaces/BUILD.gn index c58507a..ad1bb9a 100644 --- a/third_party/mojo_services/src/content_handler/public/interfaces/BUILD.gn +++ b/third_party/mojo_services/src/content_handler/public/interfaces/BUILD.gn @@ -23,4 +23,6 @@ mojom("interfaces") { ] mojo_sdk_deps = [ "mojo/public/interfaces/application" ] + + import_from = mojo_root } diff --git a/third_party/mojo_services/src/geometry/public/interfaces/BUILD.gn b/third_party/mojo_services/src/geometry/public/interfaces/BUILD.gn index 9047ee2..217eb27 100644 --- a/third_party/mojo_services/src/geometry/public/interfaces/BUILD.gn +++ b/third_party/mojo_services/src/geometry/public/interfaces/BUILD.gn @@ -9,4 +9,6 @@ mojom("interfaces") { sources = [ "geometry.mojom", ] + + import_from = mojo_root } diff --git a/third_party/mojo_services/src/gpu/public/interfaces/BUILD.gn b/third_party/mojo_services/src/gpu/public/interfaces/BUILD.gn index ec061df..b782146 100644 --- a/third_party/mojo_services/src/gpu/public/interfaces/BUILD.gn +++ b/third_party/mojo_services/src/gpu/public/interfaces/BUILD.gn @@ -19,4 +19,6 @@ mojom("interfaces") { deps = [ "../../../geometry/public/interfaces", ] + + import_from = mojo_root } diff --git a/third_party/mojo_services/src/http_server/public/interfaces/BUILD.gn b/third_party/mojo_services/src/http_server/public/interfaces/BUILD.gn index 79aa6a6..a02039e 100644 --- a/third_party/mojo_services/src/http_server/public/interfaces/BUILD.gn +++ b/third_party/mojo_services/src/http_server/public/interfaces/BUILD.gn @@ -24,4 +24,6 @@ mojom("interfaces") { deps = [ "$mojo_network_service_root/network/public/interfaces", ] + + import_from = mojo_root } diff --git a/third_party/mojo_services/src/input_events/public/interfaces/BUILD.gn b/third_party/mojo_services/src/input_events/public/interfaces/BUILD.gn index 5ef09eb..5f0f22d 100644 --- a/third_party/mojo_services/src/input_events/public/interfaces/BUILD.gn +++ b/third_party/mojo_services/src/input_events/public/interfaces/BUILD.gn @@ -17,4 +17,6 @@ mojom("interfaces") { deps = [ "../../../geometry/public/interfaces", ] + + import_from = mojo_root } diff --git a/third_party/mojo_services/src/input_events/public/interfaces/input_events.mojom b/third_party/mojo_services/src/input_events/public/interfaces/input_events.mojom index 0086265..9fa36303 100644 --- a/third_party/mojo_services/src/input_events/public/interfaces/input_events.mojom +++ b/third_party/mojo_services/src/input_events/public/interfaces/input_events.mojom @@ -70,6 +70,8 @@ struct Event { EventType action; // TODO(sky): parts of this should move to PointerData. EventFlags flags; + // Time the event was delivered. The time is in milliseconds and corresponds + // to the uptime of the machine. int64 time_stamp; KeyData? key_data; PointerData? pointer_data; diff --git a/third_party/mojo_services/src/native_viewport/public/interfaces/BUILD.gn b/third_party/mojo_services/src/native_viewport/public/interfaces/BUILD.gn index 86ff870..5603e17 100644 --- a/third_party/mojo_services/src/native_viewport/public/interfaces/BUILD.gn +++ b/third_party/mojo_services/src/native_viewport/public/interfaces/BUILD.gn @@ -18,4 +18,6 @@ mojom("interfaces") { "../../../input_events/public/interfaces", "../../../surfaces/public/interfaces:surface_id", ] + + import_from = mojo_root } diff --git a/third_party/mojo_services/src/navigation/public/interfaces/BUILD.gn b/third_party/mojo_services/src/navigation/public/interfaces/BUILD.gn index 19b9f8a..61158f3 100644 --- a/third_party/mojo_services/src/navigation/public/interfaces/BUILD.gn +++ b/third_party/mojo_services/src/navigation/public/interfaces/BUILD.gn @@ -21,4 +21,6 @@ mojom("interfaces") { deps = [ "$mojo_network_service_root/network/public/interfaces", ] + + import_from = mojo_root } diff --git a/third_party/mojo_services/src/surfaces/public/interfaces/BUILD.gn b/third_party/mojo_services/src/surfaces/public/interfaces/BUILD.gn index 7902a7a..36c8707 100644 --- a/third_party/mojo_services/src/surfaces/public/interfaces/BUILD.gn +++ b/third_party/mojo_services/src/surfaces/public/interfaces/BUILD.gn @@ -20,10 +20,14 @@ mojom("interfaces") { "../../../gpu/public/interfaces", "../../../native_viewport/public/interfaces", ] + + import_from = mojo_root } mojom("surface_id") { sources = [ "surface_id.mojom", ] + + import_from = mojo_root } diff --git a/third_party/mojo_services/src/view_manager/public/interfaces/BUILD.gn b/third_party/mojo_services/src/view_manager/public/interfaces/BUILD.gn index 177066b..eff5f62 100644 --- a/third_party/mojo_services/src/view_manager/public/interfaces/BUILD.gn +++ b/third_party/mojo_services/src/view_manager/public/interfaces/BUILD.gn @@ -22,4 +22,6 @@ mojom("interfaces") { "../../../native_viewport/public/interfaces", "../../../surfaces/public/interfaces:surface_id", ] + + import_from = mojo_root } diff --git a/third_party/mojo_services/src/window_manager/public/interfaces/BUILD.gn b/third_party/mojo_services/src/window_manager/public/interfaces/BUILD.gn index b97f069..3cf56dad 100644 --- a/third_party/mojo_services/src/window_manager/public/interfaces/BUILD.gn +++ b/third_party/mojo_services/src/window_manager/public/interfaces/BUILD.gn @@ -19,4 +19,6 @@ mojom("interfaces") { "../../../geometry/public/interfaces", "../../../input_events/public/interfaces", ] + + import_from = mojo_root } |