blob: e359b0d74e6b436358a80fb8180371c0d9790cad [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/web/webui/mojo_facade.h"
#include <stdint.h>
#include <limits>
#include <utility>
#include <vector>
#import <Foundation/Foundation.h>
#include "base/bind.h"
#import "base/ios/block_types.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/sys_string_conversions.h"
#include "base/values.h"
#include "ios/web/public/thread/web_thread.h"
#include "ios/web/public/web_state/web_state.h"
#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
#include "mojo/public/cpp/system/core.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace web {
MojoFacade::MojoFacade(WebState* web_state) : web_state_(web_state) {
DCHECK_CURRENTLY_ON(WebThread::UI);
DCHECK(web_state_);
}
MojoFacade::~MojoFacade() {
DCHECK_CURRENTLY_ON(WebThread::UI);
}
std::string MojoFacade::HandleMojoMessage(
const std::string& mojo_message_as_json) {
DCHECK_CURRENTLY_ON(WebThread::UI);
MessageNameAndArguments name_and_args =
GetMessageNameAndArguments(mojo_message_as_json);
base::Value result;
if (name_and_args.name == "Mojo.bindInterface") {
// HandleMojoBindInterface does not return a value.
HandleMojoBindInterface(std::move(name_and_args.args));
} else if (name_and_args.name == "MojoHandle.close") {
// HandleMojoHandleClose does not return a value.
HandleMojoHandleClose(std::move(name_and_args.args));
} else if (name_and_args.name == "Mojo.createMessagePipe") {
result = HandleMojoCreateMessagePipe(std::move(name_and_args.args));
} else if (name_and_args.name == "MojoHandle.writeMessage") {
result = HandleMojoHandleWriteMessage(std::move(name_and_args.args));
} else if (name_and_args.name == "MojoHandle.readMessage") {
result = HandleMojoHandleReadMessage(std::move(name_and_args.args));
} else if (name_and_args.name == "MojoHandle.watch") {
result = HandleMojoHandleWatch(std::move(name_and_args.args));
} else if (name_and_args.name == "MojoWatcher.cancel") {
// HandleMojoWatcherCancel does not return a value.
HandleMojoWatcherCancel(std::move(name_and_args.args));
}
if (result.is_none()) {
return std::string();
}
std::string json_result;
base::JSONWriter::Write(result, &json_result);
return json_result;
}
MojoFacade::MessageNameAndArguments MojoFacade::GetMessageNameAndArguments(
const std::string& mojo_message_as_json) {
base::JSONReader::ValueWithError value_with_error =
base::JSONReader::ReadAndReturnValueWithError(mojo_message_as_json,
base::JSON_PARSE_RFC);
CHECK(value_with_error.value);
CHECK(value_with_error.value->is_dict());
CHECK_EQ(value_with_error.error_code, base::JSONReader::JSON_NO_ERROR);
const std::string* name = value_with_error.value->FindStringKey("name");
CHECK(name);
base::Value* args = value_with_error.value->FindKeyOfType(
"args", base::Value::Type::DICTIONARY);
CHECK(args);
return {*name, std::move(*args)};
}
void MojoFacade::HandleMojoBindInterface(base::Value args) {
const std::string* interface_name = args.FindStringKey("interfaceName");
CHECK(interface_name);
base::Optional<int> raw_handle = args.FindIntKey("requestHandle");
CHECK(raw_handle.has_value());
mojo::ScopedMessagePipeHandle handle(
static_cast<mojo::MessagePipeHandle>(*raw_handle));
web_state_->GetInterfaceBinderForMainFrame()->BindInterface(
mojo::GenericPendingReceiver(*interface_name, std::move(handle)));
}
void MojoFacade::HandleMojoHandleClose(base::Value args) {
base::Optional<int> handle = args.FindIntKey("handle");
CHECK(handle.has_value());
mojo::Handle(*handle).Close();
}
base::Value MojoFacade::HandleMojoCreateMessagePipe(base::Value args) {
mojo::ScopedMessagePipeHandle handle0, handle1;
MojoResult mojo_result = mojo::CreateMessagePipe(nullptr, &handle0, &handle1);
base::Value result(base::Value::Type::DICTIONARY);
result.SetKey("result", base::Value(static_cast<int>(mojo_result)));
if (mojo_result == MOJO_RESULT_OK) {
result.SetKey("handle0",
base::Value(static_cast<int>(handle0.release().value())));
result.SetKey("handle1",
base::Value(static_cast<int>(handle1.release().value())));
}
return result;
}
base::Value MojoFacade::HandleMojoHandleWriteMessage(base::Value args) {
base::Optional<int> handle = args.FindIntKey("handle");
CHECK(handle.has_value());
const base::Value* handles_list =
args.FindKeyOfType("handles", base::Value::Type::LIST);
CHECK(handles_list);
const base::Value* buffer =
args.FindKeyOfType("buffer", base::Value::Type::DICTIONARY);
CHECK(buffer);
int flags = MOJO_WRITE_MESSAGE_FLAG_NONE;
const auto& handles_list_storage = handles_list->GetList();
std::vector<MojoHandle> handles(handles_list_storage.size());
for (size_t i = 0; i < handles_list_storage.size(); i++) {
int one_handle = handles_list_storage[i].GetInt();
handles[i] = one_handle;
}
std::vector<uint8_t> bytes(buffer->DictSize());
for (const auto& item : buffer->DictItems()) {
size_t index = std::numeric_limits<size_t>::max();
CHECK(base::StringToSizeT(item.first, &index));
CHECK(index < bytes.size());
int one_byte = item.second.GetInt();
bytes[index] = one_byte;
}
mojo::MessagePipeHandle message_pipe(static_cast<MojoHandle>(*handle));
MojoResult result =
mojo::WriteMessageRaw(message_pipe, bytes.data(), bytes.size(),
handles.data(), handles.size(), flags);
return base::Value(static_cast<int>(result));
}
base::Value MojoFacade::HandleMojoHandleReadMessage(base::Value args) {
base::Value* handle_as_value = args.FindKey("handle");
CHECK(handle_as_value);
int handle_as_int = 0;
if (handle_as_value->is_int()) {
handle_as_int = handle_as_value->GetInt();
}
int flags = MOJO_READ_MESSAGE_FLAG_NONE;
std::vector<uint8_t> bytes;
std::vector<mojo::ScopedHandle> handles;
mojo::MessagePipeHandle handle(static_cast<MojoHandle>(handle_as_int));
MojoResult mojo_result =
mojo::ReadMessageRaw(handle, &bytes, &handles, flags);
base::Value result(base::Value::Type::DICTIONARY);
if (mojo_result == MOJO_RESULT_OK) {
base::Value handles_list(base::Value::Type::LIST);
base::Value::ListStorage& handles_list_storage = handles_list.GetList();
for (uint32_t i = 0; i < handles.size(); i++) {
handles_list_storage.emplace_back(
static_cast<int>(handles[i].release().value()));
}
result.SetKey("handles", std::move(handles_list));
base::Value buffer(base::Value::Type::LIST);
base::Value::ListStorage& buffer_storage = buffer.GetList();
for (uint32_t i = 0; i < bytes.size(); i++) {
buffer_storage.emplace_back(bytes[i]);
}
result.SetKey("buffer", std::move(buffer));
}
result.SetKey("result", base::Value(static_cast<int>(mojo_result)));
return result;
}
base::Value MojoFacade::HandleMojoHandleWatch(base::Value args) {
base::Optional<int> handle = args.FindIntKey("handle");
CHECK(handle.has_value());
base::Optional<int> signals = args.FindIntKey("signals");
CHECK(signals.has_value());
base::Optional<int> callback_id = args.FindIntKey("callbackId");
CHECK(callback_id.has_value());
mojo::SimpleWatcher::ReadyCallback callback = base::BindRepeating(
^(int callback_id, MojoResult result) {
NSString* script = [NSString
stringWithFormat:
@"Mojo.internal.watchCallbacksHolder.callCallback(%d, %d)",
callback_id, result];
web_state_->ExecuteJavaScript(base::SysNSStringToUTF16(script));
},
*callback_id);
auto watcher = std::make_unique<mojo::SimpleWatcher>(
FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC);
watcher->Watch(static_cast<mojo::Handle>(*handle), *signals, callback);
watchers_.insert(std::make_pair(++last_watch_id_, std::move(watcher)));
return base::Value(last_watch_id_);
}
void MojoFacade::HandleMojoWatcherCancel(base::Value args) {
base::Optional<int> watch_id = args.FindIntKey("watchId");
CHECK(watch_id.has_value());
watchers_.erase(*watch_id);
}
} // namespace web