blob: 7f6bd9e9e35cc9b39378e067f96a92f56e15a550 [file] [log] [blame]
// Copyright 2018 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 "chromeos/services/ime/input_engine.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chromeos/services/ime/public/cpp/rulebased/engine.h"
namespace chromeos {
namespace ime {
namespace {
std::string GetIdFromImeSpec(const std::string& ime_spec) {
std::string prefix("m17n:");
return base::StartsWith(ime_spec, prefix, base::CompareCase::SENSITIVE)
? ime_spec.substr(prefix.length())
: base::EmptyString();
}
} // namespace
InputEngineContext::InputEngineContext(const std::string& ime) : ime_spec(ime) {
// The |ime_spec|'s format for rule based imes is: "m17n:<id>".
std::string id = GetIdFromImeSpec(ime_spec);
if (rulebased::Engine::IsImeSupported(id)) {
engine = std::make_unique<rulebased::Engine>();
engine->Activate(id);
}
}
InputEngineContext::~InputEngineContext() {}
InputEngine::InputEngine() {}
InputEngine::~InputEngine() {}
bool InputEngine::BindRequest(const std::string& ime_spec,
mojom::InputChannelRequest request,
mojom::InputChannelPtr client,
const std::vector<uint8_t>& extra) {
if (!IsImeSupported(ime_spec))
return false;
channel_bindings_.AddBinding(this, std::move(request),
std::make_unique<InputEngineContext>(ime_spec));
return true;
// TODO(https://crbug.com/837156): Registry connection error handler.
}
bool InputEngine::IsImeSupported(const std::string& ime_spec) {
return rulebased::Engine::IsImeSupported(GetIdFromImeSpec(ime_spec));
}
void InputEngine::ProcessText(const std::string& message,
ProcessTextCallback callback) {
auto& context = channel_bindings_.dispatch_context();
std::string result = Process(message, context.get());
std::move(callback).Run(result);
}
void InputEngine::ProcessMessage(const std::vector<uint8_t>& message,
ProcessMessageCallback callback) {
NOTIMPLEMENTED(); // Protobuf message is not used in the rulebased engine.
}
const std::string InputEngine::Process(const std::string& message,
const InputEngineContext* context) {
std::string ime_spec = context->ime_spec;
auto& engine = context->engine;
if (!engine)
return base::EmptyString();
const char* false_response = "{\"result\":false}";
// The request message is in JSON format as:
// {
// 'method': <string>, // 'reset' or 'keyEvent'
// 'type': <string>, // 'keydown' or 'keyup'
// 'code': <string>, // e.g. 'KeyA', 'Backspace', etc.
// 'shift': <boolean>,
// 'altgr': <boolean>,
// 'caps': <boolean>,
// 'ctrl': <boolean>,
// 'alt': <boolean>
// }
// TODO(shuchen): make parser/writer util class for the JSON-based protocol.
int error_code;
std::string error_string;
std::unique_ptr<base::Value> message_value =
base::JSONReader::ReadAndReturnError(message, base::JSON_PARSE_RFC,
&error_code, &error_string);
if (!message_value) {
LOG(ERROR) << "Read message error: " << error_code << "; " << error_string;
return false_response;
}
base::Value* method = message_value->FindKey("method");
if (!method)
return false_response;
const std::string& method_str = method->GetString();
if (method_str == "countKey") {
return std::to_string(engine->process_key_count());
}
if (method_str == "reset") {
engine->Reset();
return "{\"result\":true}";
}
if (method_str == "keyEvent") {
base::Value* type = message_value->FindKey("type");
if (!type || type->GetString() != "keydown")
return false_response;
}
base::Value* code = message_value->FindKey("code");
base::Value* shift = message_value->FindKey("shift");
base::Value* altgr = message_value->FindKey("altgr");
base::Value* caps = message_value->FindKey("caps");
if (!code || !shift || !altgr || !caps)
return false_response;
uint8_t modifiers = 0;
if (shift->GetBool())
modifiers |= rulebased::MODIFIER_SHIFT;
if (altgr->GetBool())
modifiers |= rulebased::MODIFIER_ALTGR;
if (caps->GetBool())
modifiers |= rulebased::MODIFIER_CAPSLOCK;
rulebased::ProcessKeyResult res =
engine->ProcessKey(code->GetString(), modifiers);
// The response message is in JSON format as:
// {
// 'result': <boolean>,
// 'operations': [
// {
// 'method': 'commitText',
// 'arguments': <string>
// }
// ]
// }
std::string response_str = "{\"result\":";
response_str += (res.key_handled ? "true" : "false");
if (res.commit_text.empty())
response_str += "}";
else
response_str +=
",\"operations\":[{\"method\":\"commitText\",\"arguments\":[\"" +
res.commit_text + "\"]}]}";
return response_str;
}
} // namespace ime
} // namespace chromeos