// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MOJO_CORE_USER_MESSAGE_IMPL_H_
#define MOJO_CORE_USER_MESSAGE_IMPL_H_

#include <memory>
#include <utility>
#include <vector>

#include "base/macros.h"
#include "base/optional.h"
#include "mojo/core/channel.h"
#include "mojo/core/dispatcher.h"
#include "mojo/core/ports/event.h"
#include "mojo/core/ports/name.h"
#include "mojo/core/ports/port_ref.h"
#include "mojo/core/ports/user_message.h"
#include "mojo/core/system_impl_export.h"
#include "mojo/public/c/system/message_pipe.h"
#include "mojo/public/c/system/types.h"

namespace mojo {
namespace core {

// UserMessageImpl is the sole implementation of ports::UserMessage used to
// attach message data to any ports::UserMessageEvent.
//
// A UserMessageImpl may be either serialized or unserialized. Unserialized
// instances are serialized lazily only when necessary, i.e., if and when
// Serialize() is called to obtain a serialized message for wire transfer.
class MOJO_SYSTEM_IMPL_EXPORT UserMessageImpl : public ports::UserMessage {
 public:
  static const TypeInfo kUserMessageTypeInfo;

  // Determines how ExtractSerializedHandles should behave when it encounters an
  // unrecoverable serialized handle.
  enum ExtractBadHandlePolicy {
    // Continue extracting handles upon encountering a bad handle. The bad
    // handle will be extracted with an invalid handle value.
    kSkip,

    // Abort the extraction process, leaving any valid serialized handles still
    // in the message.
    kAbort,
  };

  ~UserMessageImpl() override;

  // Creates a new ports::UserMessageEvent with an attached UserMessageImpl.
  static std::unique_ptr<ports::UserMessageEvent> CreateEventForNewMessage();

  // Creates a new ports::UserMessageEvent with an attached serialized
  // UserMessageImpl. May fail iff one or more |dispatchers| fails to serialize
  // (e.g. due to it being in an invalid state.)
  //
  // Upon success, MOJO_RESULT_OK is returned and the new UserMessageEvent is
  // stored in |*out_event|.
  static MojoResult CreateEventForNewSerializedMessage(
      uint32_t num_bytes,
      const Dispatcher::DispatcherInTransit* dispatchers,
      uint32_t num_dispatchers,
      std::unique_ptr<ports::UserMessageEvent>* out_event);

  // Creates a new UserMessageImpl from an existing serialized message buffer
  // which was read from a Channel. Takes ownership of |channel_message|.
  // |payload| and |payload_size| represent the range of bytes within
  // |channel_message| which should be parsed by this call.
  static std::unique_ptr<UserMessageImpl> CreateFromChannelMessage(
      ports::UserMessageEvent* message_event,
      Channel::MessagePtr channel_message,
      void* payload,
      size_t payload_size);

  // Extracts the serialized Channel::Message from the UserMessageEvent in
  // |event|. |event| must have a serialized UserMessageImpl instance attached.
  // |message_event| is serialized into the front of the message payload before
  // returning.
  static Channel::MessagePtr FinalizeEventMessage(
      std::unique_ptr<ports::UserMessageEvent> event);

  bool HasContext() const { return context_ != 0; }

  uintptr_t context() const { return context_; }

  bool IsSerialized() const {
    if (HasContext()) {
      DCHECK(!channel_message_);
      return false;
    }

    return !!channel_message_;
  }

  bool IsTransmittable() const { return !IsSerialized() || is_committed_; }

  void* user_payload() {
    DCHECK(IsSerialized());
    return user_payload_;
  }

  const void* user_payload() const {
    DCHECK(IsSerialized());
    return user_payload_;
  }

  size_t user_payload_size() const {
    DCHECK(IsSerialized());
    return user_payload_size_;
  }

  size_t user_payload_capacity() const;

  size_t num_handles() const;

  void set_source_node(const ports::NodeName& name) { source_node_ = name; }
  const ports::NodeName& source_node() const { return source_node_; }

  MojoResult SetContext(uintptr_t context,
                        MojoMessageContextSerializer serializer,
                        MojoMessageContextDestructor destructor);
  MojoResult AppendData(uint32_t additional_payload_size,
                        const MojoHandle* handles,
                        uint32_t num_handles);
  MojoResult CommitSize();

  // If this message is not already serialized, this serializes it.
  MojoResult SerializeIfNecessary();

  // Extracts handles from this (serialized) message.
  //
  // Returns |MOJO_RESULT_OK|
  // if sucessful, |MOJO_RESULT_FAILED_PRECONDITION| if this isn't a serialized
  // message, |MOJO_RESULT_NOT_FOUND| if all serialized handles have already
  // been extracted, or |MOJO_RESULT_ABORTED| if one or more handles failed
  // extraction.
  //
  // On success, |handles| is populated with |num_handles()| extracted handles,
  // whose ownership is thereby transferred to the caller.
  MojoResult ExtractSerializedHandles(ExtractBadHandlePolicy bad_handle_policy,
                                      MojoHandle* handles);

  // Forces all handle serialization to fail. Serialization can fail in
  // production for a few different reasons (e.g. file descriptor exhaustion
  // when duping data pipe buffer handles) which may be difficult to control in
  // testing environments. This forces the common serialization code path to
  // always behave as if the underlying implementation signaled failure,
  // allowing tests to exercise those cases.
  static void FailHandleSerializationForTesting(bool fail);

 private:
  // Creates an unserialized UserMessageImpl with an associated |context| and
  // |thunks|. If the message is ever going to be routed to another node (see
  // |WillBeRoutedExternally()| below), it will be serialized at that time using
  // operations provided by |thunks|.
  UserMessageImpl(ports::UserMessageEvent* message_event);

  // Creates a serialized UserMessageImpl backed by an existing Channel::Message
  // object. |header| and |user_payload| must be pointers into
  // |channel_message|'s own storage, and |user_payload_size| is the number of
  // bytes comprising the user message contents at |user_payload|.
  UserMessageImpl(ports::UserMessageEvent* message_event,
                  Channel::MessagePtr channel_message,
                  void* header,
                  size_t header_size,
                  void* user_payload,
                  size_t user_payload_size);

  // UserMessage:
  bool WillBeRoutedExternally() override;
  size_t GetSizeIfSerialized() const override;

  // The event which owns this serialized message. Not owned.
  ports::UserMessageEvent* const message_event_;

  // Unserialized message state.
  uintptr_t context_ = 0;
  MojoMessageContextSerializer context_serializer_ = nullptr;
  MojoMessageContextDestructor context_destructor_ = nullptr;

  // Serialized message contents. May be null if this is not a serialized
  // message.
  Channel::MessagePtr channel_message_;

  // Indicates whether any handles serialized within |channel_message_| have
  // yet to be extracted.
  bool has_serialized_handles_ = false;

  // Indicates whether the serialized message's contents (if any) have been
  // committed yet.
  bool is_committed_ = false;

  // Only valid if |channel_message_| is non-null. |header_| is the address
  // of the UserMessageImpl's internal MessageHeader structure within the
  // serialized message buffer. |user_payload_| is the address of the first byte
  // after any serialized dispatchers, with the payload comprising the remaining
  // |user_payload_size_| bytes of the message.
  void* header_ = nullptr;
  size_t header_size_ = 0;
  void* user_payload_ = nullptr;
  size_t user_payload_size_ = 0;

  // Handles which have been attached to the serialized message but which have
  // not yet been serialized.
  std::vector<Dispatcher::DispatcherInTransit> pending_handle_attachments_;

  // The node name from which this message was received, iff it came from
  // out-of-process and the source is known.
  ports::NodeName source_node_ = ports::kInvalidNodeName;

  DISALLOW_COPY_AND_ASSIGN(UserMessageImpl);
};

}  // namespace core
}  // namespace mojo

#endif  // MOJO_CORE_USER_MESSAGE_IMPL_H_