chromium/chromeos/ash/services/ime/decoder/decoder_engine.cc

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

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "chromeos/ash/services/ime/decoder/decoder_engine.h"

#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/callback_helpers.h"
#include "base/notreached.h"
#include "chromeos/ash/services/ime/constants.h"

namespace ash {
namespace ime {

namespace {

// A client delegate passed to the shared library in order for the
// shared library to send replies back to the engine.
class ClientDelegate : public ImeClientDelegate {
 public:
  // All replies from the shared library will be sent to both |remote| and
  // |callback|.
  ClientDelegate(const std::string& ime_spec,
                 mojo::PendingRemote<mojom::InputChannel> remote)
      : ime_spec_(ime_spec), client_remote_(std::move(remote)) {
    client_remote_.set_disconnect_handler(base::BindOnce(
        &ClientDelegate::OnDisconnected, base::Unretained(this)));
  }

  ~ClientDelegate() override {}

  void Unused1() override { NOTIMPLEMENTED(); }

  void Process(const uint8_t* data, size_t size) override {
    if (client_remote_ && client_remote_.is_bound()) {
      std::vector<uint8_t> msg(data, data + size);
      client_remote_->ProcessMessage(msg, base::DoNothing());
    }
  }

  void Destroy() override { delete this; }

 private:
  void OnDisconnected() {
    client_remote_.reset();
    LOG(ERROR) << "Client remote is disconnected." << ime_spec_;
  }

  // The ime specification which is unique in the scope of engine.
  std::string ime_spec_;

  // The InputChannel remote used to talk to the client.
  mojo::Remote<mojom::InputChannel> client_remote_;
};

}  // namespace

DecoderEngine::DecoderEngine(
    ImeCrosPlatform* platform,
    std::optional<ImeSharedLibraryWrapper::EntryPoints> entry_points) {
  if (!entry_points) {
    LOG(WARNING) << "DecoderEngine INIT INCOMPLETE.";
    return;
  }

  decoder_entry_points_ = *entry_points;
  decoder_entry_points_->init_proto_mode(platform);
}

DecoderEngine::~DecoderEngine() {
  if (!decoder_entry_points_) {
    return;
  }

  decoder_entry_points_->close_proto_mode();
}

bool DecoderEngine::BindRequest(
    const std::string& ime_spec,
    mojo::PendingReceiver<mojom::InputChannel> receiver,
    mojo::PendingRemote<mojom::InputChannel> remote,
    const std::vector<uint8_t>& extra) {
  // Activates an IME engine via the shared lib. Passing a |ClientDelegate| for
  // engine instance created by the shared lib to make safe calls on the client.
  if (decoder_entry_points_ &&
      decoder_entry_points_->proto_mode_supports(ime_spec.c_str()) &&
      decoder_entry_points_->proto_mode_activate_ime(
          ime_spec.c_str(), new ClientDelegate(ime_spec, std::move(remote)))) {
    decoder_channel_receivers_.Add(this, std::move(receiver));
    // TODO(https://crbug.com/837156): Registry connection error handler.
    return true;
  }
  return false;
}

void DecoderEngine::ProcessMessage(const std::vector<uint8_t>& message,
                                   ProcessMessageCallback callback) {
  // TODO(https://crbug.com/837156): Set a default protobuf message.
  std::vector<uint8_t> result;

  // Handle message via corresponding functions of loaded decoder.
  if (decoder_entry_points_) {
    decoder_entry_points_->proto_mode_process(message.data(), message.size());
  }

  std::move(callback).Run(result);
}

}  // namespace ime
}  // namespace ash