Chromium Code Reviews| Index: ios/web/webui/mojo_facade.mm |
| diff --git a/ios/web/webui/mojo_facade.mm b/ios/web/webui/mojo_facade.mm |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..fed5bcc025d60061d8e761eb929a0eb79fb0a423 |
| --- /dev/null |
| +++ b/ios/web/webui/mojo_facade.mm |
| @@ -0,0 +1,327 @@ |
| +// 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" |
| + |
| +#import <Foundation/Foundation.h> |
| + |
| +#include "base/ios/block_types.h" |
| +#include "base/json/json_reader.h" |
| +#include "base/json/json_writer.h" |
| +#include "base/mac/scoped_block.h" |
| +#include "base/memory/singleton.h" |
| +#include "base/strings/string_number_conversions.h" |
| +#include "base/strings/sys_string_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/c/system/core.h" |
| +#include "services/shell/public/interfaces/interface_provider.mojom.h" |
| + |
| +namespace web { |
| + |
| +namespace { |
| + |
| +// Wraps Mojo integer into |base::Value| as |TYPE_INTEGER|. |
| +template <typename MojoIntegerT> |
| +std::unique_ptr<base::Value> ValueFromMojoInteger(MojoIntegerT handle) { |
| + return std::unique_ptr<base::Value>( |
| + new base::FundamentalValue(static_cast<int>(handle))); |
| +} |
| + |
| +// Singleton which holds contexts for all currently active watches. Must be used |
| +// only on UI thread. |
| +class WatchCallbackHolder { |
| + public: |
| + static WatchCallbackHolder* GetInstance() { |
| + DCHECK_CURRENTLY_ON(WebThread::UI); |
| + return base::Singleton<WatchCallbackHolder>::get(); |
| + } |
| + |
| + // Adds |callback_block| which must be executed when the Watch callback fires. |
| + // Returns context which can be passed to |Run| to execute the block. Callers |
| + // are responsible for removing contexts which are no longer active by calling |
| + // |Remove|. |
| + uintptr_t Add(void (^callback_block)(void)) { |
| + DCHECK_CURRENTLY_ON(WebThread::UI); |
| + callbacks_[++last_watch_context_].reset([callback_block copy]); |
| + return last_watch_context_; |
| + } |
| + |
| + // Executes the callback previously added by calling |Add|. |watch_context| is |
| + // the context returned from |Add|. Calling this method for a context which |
| + // has been previously removed is no-op. |
| + void Run(uintptr_t watch_context) { |
| + DCHECK_CURRENTLY_ON(WebThread::UI); |
| + ProceduralBlock callback = callbacks_[watch_context]; |
| + if (callback) |
| + callback(); |
| + } |
| + |
| + // Removes previously added callback. |watch_context| is the context returned |
| + // from |Add|. |
| + void Remove(uintptr_t watch_context) { |
| + DCHECK_CURRENTLY_ON(WebThread::UI); |
| + DCHECK(callbacks_.find(watch_context) != callbacks_.end()); |
| + callbacks_.erase(watch_context); |
| + } |
| + |
| + private: |
| + WatchCallbackHolder() : last_watch_context_(0) {} |
| + ~WatchCallbackHolder() { |
| + // Clients are responsible for removing all callbacks before shutdown. |
| + DCHECK(callbacks_.empty()); |
| + } |
| + |
| + // Required by base::Singleton. |
| + friend struct base::DefaultSingletonTraits<WatchCallbackHolder>; |
| + |
| + // Context for the last added callback. Context for the next callback will be |
| + // |last_watch_context_ + 1|. |
| + uintptr_t last_watch_context_; |
| + // Callbacks added via |Add| method. |
| + std::map<uintptr_t, base::mac::ScopedBlock<ProceduralBlock>> callbacks_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(WatchCallbackHolder); |
| +}; |
| + |
| +// Callback for |MojoWatch| call. Called when: |
| +// - watched signal has been satisfied; |
| +// - it became known that none of the watched signals will ever be satisfied; |
| +// - handle was closed; |
| +void MojoWatchCallback(uintptr_t watch_context, |
| + MojoResult result, |
| + struct MojoHandleSignalsState signals_state, |
| + MojoWatchNotificationFlags flags) { |
| + WatchCallbackHolder::GetInstance()->Run(watch_context); |
| +} |
| + |
| +} // namespace |
| + |
| +MojoFacade::MojoFacade(shell::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); |
| + for (uintptr_t context : watch_contexts_) |
| + WatchCallbackHolder::GetInstance()->Remove(context); |
| +} |
| + |
| +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 == "service_provider.connectToService") { |
|
Ken Rockot(use gerrit already)
2016/05/12 21:13:05
I mean I guess efficiency is already tossed out be
Eugene But (OOO till 7-30)
2016/05/13 02:54:26
I doubt that you will see any performance differen
|
| + result = HandleServiceProviderConnectToService(args.get()); |
| + } else if (name == "core.close") { |
| + result = HandleCoreClose(args.get()); |
| + } else if (name == "core.createMessagePipe") { |
| + result = HandleCoreCreateMessagePipe(args.get()); |
| + } else if (name == "core.writeMessage") { |
| + result = HandleCoreWriteMessage(args.get()); |
| + } else if (name == "core.readMessage") { |
| + result = HandleCoreReadMessage(args.get()); |
| + } else if (name == "support.watch") { |
| + result = HandleSupportWatch(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::HandleServiceProviderConnectToService( |
| + const base::DictionaryValue* args) { |
| + const base::Value* service_name_as_value = nullptr; |
| + CHECK(args->Get("serviceName", &service_name_as_value)); |
| + |
| + // By design service_provider.connectToService either succeeds or crashes, so |
| + // check if service name is a valid string is intentionally omitted. |
| + std::string service_name_as_string; |
| + service_name_as_value->GetAsString(&service_name_as_string); |
| + |
| + mojo::MessagePipe pipe; |
| + interface_provider_->GetInterface(mojo::String::From(service_name_as_string), |
| + std::move(pipe.handle0)); |
| + |
| + return ValueFromMojoInteger(pipe.handle1.release().value()); |
| +} |
| + |
| +std::unique_ptr<base::Value> MojoFacade::HandleCoreClose( |
| + const base::DictionaryValue* args) { |
| + int handle = 0; |
| + CHECK(args->GetInteger("handle", &handle)); |
| + |
| + return ValueFromMojoInteger(MojoClose(handle)); |
| +} |
| + |
| +std::unique_ptr<base::Value> MojoFacade::HandleCoreCreateMessagePipe( |
| + base::DictionaryValue* args) { |
| + const base::Value* options_as_value = nullptr; |
| + CHECK(args->Get("optionsDict", &options_as_value)); |
| + |
| + std::unique_ptr<MojoCreateMessagePipeOptions> options; |
| + if (options_as_value->IsType(base::Value::TYPE_DICTIONARY)) { |
| + // Extract options and reset |options| pointer when this codepath is hit. |
| + NOTIMPLEMENTED(); |
| + } else { |
| + CHECK(options_as_value->IsType(base::Value::TYPE_NULL)); |
| + } |
| + |
| + MojoHandle handle0; |
| + MojoHandle handle1; |
| + MojoCreateMessagePipe(options.get(), &handle0, &handle1); |
| + |
| + std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue); |
| + result->SetInteger("handle0", handle0); |
| + result->SetInteger("handle1", handle1); |
| + return std::unique_ptr<base::Value>(result.release()); |
| +} |
| + |
| +std::unique_ptr<base::Value> MojoFacade::HandleCoreWriteMessage( |
| + 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)); |
| + |
| + const base::Value* flags_as_value = nullptr; |
| + CHECK(args->Get("flags", &flags_as_value)); |
| + |
| + int flags = MOJO_WRITE_MESSAGE_FLAG_NONE; |
| + if (!flags_as_value->GetAsInteger(&flags)) { |
| + 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; |
| + } |
| + |
| + MojoResult result = |
| + MojoWriteMessage(static_cast<MojoHandle>(handle), bytes.data(), |
| + bytes.size(), handles.data(), handles.size(), flags); |
| + |
| + return ValueFromMojoInteger(result); |
| +} |
| + |
| +std::unique_ptr<base::Value> MojoFacade::HandleCoreReadMessage( |
| + 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; |
| + } |
| + |
| + const base::Value* flags_as_value = nullptr; |
| + CHECK(args->Get("flags", &flags_as_value)); |
| + |
| + int flags = MOJO_READ_MESSAGE_FLAG_NONE; |
| + if (!flags_as_value->GetAsInteger(&flags)) { |
| + flags = MOJO_READ_MESSAGE_FLAG_NONE; |
| + } |
| + |
| + uint32_t num_bytes = 0; |
| + uint32_t num_handles = 0; |
| + MojoResult mojo_result = |
| + MojoReadMessage(static_cast<MojoHandle>(handle_as_int), nullptr, |
| + &num_bytes, nullptr, &num_handles, flags); |
| + std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue); |
| + |
| + if (mojo_result == MOJO_RESULT_RESOURCE_EXHAUSTED) { |
| + std::vector<uint8_t> bytes(num_bytes); |
| + std::vector<MojoHandle> handles(num_handles); |
| + mojo_result = |
| + MojoReadMessage(static_cast<MojoHandle>(handle_as_int), bytes.data(), |
| + &num_bytes, handles.data(), &num_handles, flags); |
| + |
| + base::ListValue* handles_list = new base::ListValue; |
| + for (uint32_t i = 0; i < num_handles; i++) { |
| + handles_list->AppendInteger(handles[i]); |
| + } |
| + result->Set("handles", std::unique_ptr<base::Value>(handles_list)); |
| + |
| + base::ListValue* buffer = new base::ListValue; |
| + for (uint32_t i = 0; i < num_bytes; i++) { |
| + buffer->AppendInteger(bytes[i]); |
| + } |
| + result->Set("buffer", std::unique_ptr<base::Value>(buffer)); |
| + } |
| + result->SetInteger("result", mojo_result); |
| + |
| + return std::unique_ptr<base::Value>(result.release()); |
| +} |
| + |
| +std::unique_ptr<base::Value> MojoFacade::HandleSupportWatch( |
| + const base::DictionaryValue* args) { |
| + int handle = 0; |
| + CHECK(args->GetInteger("handle", &handle)); |
| + int signals = 0; |
| + CHECK(args->GetInteger("signals", &signals)); |
| + std::string callback; |
| + CHECK(args->GetString("callback", &callback_id)); |
| + |
| + uintptr_t context = WatchCallbackHolder::GetInstance()->Add(^{ |
| + [script_evaluator_ executeJavaScript:base::SysUTF8ToNSString(callback) |
| + completionHandler:nil]; |
| + }); |
| + |
| + watch_contexts_.push_back(context); |
| + |
| + return ValueFromMojoInteger( |
| + MojoWatch(handle, signals, &MojoWatchCallback, context)); |
| +} |
| + |
| +} // namespace web |