blob: 0cb7b4ae100200c9b8b385905eb32d0c3addf85f [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 <utility>
#include <vector>
#import <Foundation/Foundation.h>
#import "base/ios/block_types.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#import "base/mac/bind_objc_block.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#import "ios/web/public/web_state/js/crw_js_injection_evaluator.h"
#include "ios/web/public/web_thread.h"
#include "mojo/public/cpp/system/core.h"
#include "services/service_manager/public/interfaces/interface_provider.mojom.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace web {
namespace {
// Wraps an integer into |base::Value| as |Type::INTEGER|.
template <typename IntegerT>
std::unique_ptr<base::Value> ValueFromInteger(IntegerT handle) {
return base::MakeUnique<base::Value>(static_cast<int>(handle));
}
} // namespace
MojoFacade::MojoFacade(
service_manager::mojom::InterfaceProvider* interface_provider,
id<CRWJSInjectionEvaluator> script_evaluator)
: interface_provider_(interface_provider),
script_evaluator_(script_evaluator) {
DCHECK_CURRENTLY_ON(WebThread::UI);
DCHECK(interface_provider_);
DCHECK(script_evaluator_);
}
MojoFacade::~MojoFacade() {
DCHECK_CURRENTLY_ON(WebThread::UI);
}
std::string MojoFacade::HandleMojoMessage(
const std::string& mojo_message_as_json) {
DCHECK_CURRENTLY_ON(WebThread::UI);
std::string name;
std::unique_ptr<base::DictionaryValue> args;
GetMessageNameAndArguments(mojo_message_as_json, &name, &args);
std::unique_ptr<base::Value> result;
if (name == "Mojo.bindInterface") {
result = HandleMojoBindInterface(args.get());
} else if (name == "MojoHandle.close") {
result = HandleMojoHandleClose(args.get());
} else if (name == "Mojo.createMessagePipe") {
result = HandleMojoCreateMessagePipe(args.get());
} else if (name == "MojoHandle.writeMessage") {
result = HandleMojoHandleWriteMessage(args.get());
} else if (name == "MojoHandle.readMessage") {
result = HandleMojoHandleReadMessage(args.get());
} else if (name == "MojoHandle.watch") {
result = HandleMojoHandleWatch(args.get());
} else if (name == "MojoWatcher.cancel") {
result = HandleMojoWatcherCancel(args.get());
}
if (!result) {
return "";
}
std::string json_result;
base::JSONWriter::Write(*result, &json_result);
return json_result;
}
void MojoFacade::GetMessageNameAndArguments(
const std::string& mojo_message_as_json,
std::string* out_name,
std::unique_ptr<base::DictionaryValue>* out_args) {
int error_code = 0;
std::string error_message;
std::unique_ptr<base::Value> mojo_message_as_value(
base::JSONReader::ReadAndReturnError(mojo_message_as_json, false,
&error_code, &error_message));
CHECK(!error_code);
base::DictionaryValue* mojo_message = nullptr;
CHECK(mojo_message_as_value->GetAsDictionary(&mojo_message));
std::string name;
CHECK(mojo_message->GetString("name", &name));
base::DictionaryValue* args = nullptr;
CHECK(mojo_message->GetDictionary("args", &args));
*out_name = name;
*out_args = args->CreateDeepCopy();
}
std::unique_ptr<base::Value> MojoFacade::HandleMojoBindInterface(
const base::DictionaryValue* args) {
const base::Value* interface_name_as_value = nullptr;
CHECK(args->Get("interfaceName", &interface_name_as_value));
int raw_handle = 0;
CHECK(args->GetInteger("requestHandle", &raw_handle));
mojo::ScopedMessagePipeHandle handle(
static_cast<mojo::MessagePipeHandle>(raw_handle));
// By design interface_provider.getInterface either succeeds or crashes, so
// check if interface name is a valid string is intentionally omitted.
std::string interface_name_as_string;
interface_name_as_value->GetAsString(&interface_name_as_string);
interface_provider_->GetInterface(interface_name_as_string,
std::move(handle));
return nullptr;
}
std::unique_ptr<base::Value> MojoFacade::HandleMojoHandleClose(
const base::DictionaryValue* args) {
int handle = 0;
CHECK(args->GetInteger("handle", &handle));
mojo::Handle(handle).Close();
return nullptr;
}
std::unique_ptr<base::Value> MojoFacade::HandleMojoCreateMessagePipe(
base::DictionaryValue* args) {
mojo::ScopedMessagePipeHandle handle0, handle1;
MojoResult mojo_result = mojo::CreateMessagePipe(nullptr, &handle0, &handle1);
auto result = base::MakeUnique<base::DictionaryValue>();
result->SetInteger("result", mojo_result);
if (mojo_result == MOJO_RESULT_OK) {
result->SetInteger("handle0", handle0.release().value());
result->SetInteger("handle1", handle1.release().value());
}
return std::unique_ptr<base::Value>(result.release());
}
std::unique_ptr<base::Value> MojoFacade::HandleMojoHandleWriteMessage(
base::DictionaryValue* args) {
int handle = 0;
CHECK(args->GetInteger("handle", &handle));
base::ListValue* handles_list = nullptr;
CHECK(args->GetList("handles", &handles_list));
base::DictionaryValue* buffer = nullptr;
CHECK(args->GetDictionary("buffer", &buffer));
int flags = MOJO_WRITE_MESSAGE_FLAG_NONE;
std::vector<MojoHandle> handles(handles_list->GetSize());
for (size_t i = 0; i < handles_list->GetSize(); i++) {
int one_handle = 0;
handles_list->GetInteger(i, &one_handle);
handles[i] = one_handle;
}
std::vector<uint8_t> bytes(buffer->size());
for (size_t i = 0; i < buffer->size(); i++) {
int one_byte = 0;
buffer->GetInteger(base::IntToString(i), &one_byte);
bytes[i] = 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 ValueFromInteger(result);
}
std::unique_ptr<base::Value> MojoFacade::HandleMojoHandleReadMessage(
const base::DictionaryValue* args) {
const base::Value* handle_as_value = nullptr;
CHECK(args->Get("handle", &handle_as_value));
int handle_as_int = 0;
if (!handle_as_value->GetAsInteger(&handle_as_int)) {
handle_as_int = 0;
}
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);
auto result = base::MakeUnique<base::DictionaryValue>();
if (mojo_result == MOJO_RESULT_OK) {
auto handles_list = base::MakeUnique<base::ListValue>();
for (uint32_t i = 0; i < handles.size(); i++) {
handles_list->AppendInteger(handles[i].release().value());
}
result->Set("handles", std::move(handles_list));
auto buffer = base::MakeUnique<base::ListValue>();
for (uint32_t i = 0; i < bytes.size(); i++) {
buffer->AppendInteger(bytes[i]);
}
result->Set("buffer", std::move(buffer));
}
result->SetInteger("result", mojo_result);
return std::unique_ptr<base::Value>(result.release());
}
std::unique_ptr<base::Value> MojoFacade::HandleMojoHandleWatch(
const base::DictionaryValue* args) {
int handle = 0;
CHECK(args->GetInteger("handle", &handle));
int signals = 0;
CHECK(args->GetInteger("signals", &signals));
int callback_id;
CHECK(args->GetInteger("callbackId", &callback_id));
mojo::SimpleWatcher::ReadyCallback callback =
base::BindBlockArc(^(MojoResult result) {
NSString* script = [NSString
stringWithFormat:
@"Mojo.internal.watchCallbacksHolder.callCallback(%d, %d)",
callback_id, result];
[script_evaluator_ executeJavaScript:script completionHandler:nil];
});
auto watcher = base::MakeUnique<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 ValueFromInteger(last_watch_id_);
}
std::unique_ptr<base::Value> MojoFacade::HandleMojoWatcherCancel(
const base::DictionaryValue* args) {
int watch_id = 0;
CHECK(args->GetInteger("watchId", &watch_id));
watchers_.erase(watch_id);
return nullptr;
}
} // namespace web