Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #import "ios/web/webui/mojo_facade.h" | |
| 6 | |
| 7 #import <Foundation/Foundation.h> | |
| 8 | |
| 9 #include "base/ios/block_types.h" | |
| 10 #include "base/json/json_reader.h" | |
| 11 #include "base/json/json_writer.h" | |
| 12 #include "base/mac/scoped_block.h" | |
| 13 #include "base/memory/singleton.h" | |
| 14 #include "base/strings/string_number_conversions.h" | |
| 15 #include "base/strings/sys_string_conversions.h" | |
| 16 #include "base/values.h" | |
| 17 #import "ios/web/public/web_state/js/crw_js_injection_evaluator.h" | |
| 18 #include "ios/web/public/web_thread.h" | |
| 19 #include "mojo/public/c/system/core.h" | |
| 20 #include "services/shell/public/interfaces/interface_provider.mojom.h" | |
| 21 | |
| 22 namespace web { | |
| 23 | |
| 24 namespace { | |
| 25 | |
| 26 // Wraps Mojo integer into |base::Value| as |TYPE_INTEGER|. | |
|
Ken Rockot(use gerrit already)
2016/05/12 21:13:06
Can you make this comment explicit? Wraps a MojoRe
Eugene But (OOO till 7-30)
2016/05/13 02:54:26
This method now takes also int, so changed comment
| |
| 27 template <typename MojoIntegerT> | |
| 28 std::unique_ptr<base::Value> ValueFromMojoInteger(MojoIntegerT handle) { | |
| 29 return std::unique_ptr<base::Value>( | |
| 30 new base::FundamentalValue(static_cast<int>(handle))); | |
| 31 } | |
| 32 | |
| 33 // Singleton which holds contexts for all currently active watches. Must be used | |
| 34 // only on UI thread. | |
| 35 class WatchCallbackHolder { | |
| 36 public: | |
| 37 static WatchCallbackHolder* GetInstance() { | |
| 38 DCHECK_CURRENTLY_ON(WebThread::UI); | |
| 39 return base::Singleton<WatchCallbackHolder>::get(); | |
| 40 } | |
| 41 | |
| 42 // Adds |callback_block| which must be executed when the Watch callback fires. | |
| 43 // Returns context which can be passed to |Run| to execute the block. Callers | |
| 44 // are responsible for removing contexts which are no longer active by calling | |
| 45 // |Remove|. | |
| 46 uintptr_t Add(void (^callback_block)(void)) { | |
| 47 DCHECK_CURRENTLY_ON(WebThread::UI); | |
| 48 callbacks_[++last_watch_context_].reset([callback_block copy]); | |
| 49 return last_watch_context_; | |
| 50 } | |
| 51 | |
| 52 // Executes the callback previously added by calling |Add|. |watch_context| is | |
| 53 // the context returned from |Add|. Calling this method for a context which | |
| 54 // has been previously removed is no-op. | |
| 55 void Run(uintptr_t watch_context) { | |
| 56 DCHECK_CURRENTLY_ON(WebThread::UI); | |
| 57 ProceduralBlock callback = callbacks_[watch_context]; | |
| 58 if (callback) | |
| 59 callback(); | |
| 60 } | |
| 61 | |
| 62 // Removes previously added callback. |watch_context| is the context returned | |
| 63 // from |Add|. | |
| 64 void Remove(uintptr_t watch_context) { | |
| 65 DCHECK_CURRENTLY_ON(WebThread::UI); | |
| 66 DCHECK(callbacks_.find(watch_context) != callbacks_.end()); | |
| 67 callbacks_.erase(watch_context); | |
| 68 } | |
| 69 | |
| 70 private: | |
| 71 WatchCallbackHolder() : last_watch_context_(0) {} | |
| 72 ~WatchCallbackHolder() { | |
| 73 // Clients are responsible for removing all callbacks before shutdown. | |
| 74 DCHECK(callbacks_.empty()); | |
| 75 } | |
| 76 | |
| 77 // Required by base::Singleton. | |
| 78 friend struct base::DefaultSingletonTraits<WatchCallbackHolder>; | |
| 79 | |
| 80 // Context for the last added callback. Context for the next callback will be | |
| 81 // |last_watch_context_ + 1|. | |
| 82 uintptr_t last_watch_context_; | |
| 83 // Callbacks added via |Add| method. | |
| 84 std::map<uintptr_t, base::mac::ScopedBlock<ProceduralBlock>> callbacks_; | |
| 85 | |
| 86 DISALLOW_COPY_AND_ASSIGN(WatchCallbackHolder); | |
| 87 }; | |
| 88 | |
| 89 // Callback for |MojoWatch| call. Called when: | |
| 90 // - watched signal has been satisfied; | |
| 91 // - it became known that none of the watched signals will ever be satisfied; | |
| 92 // - handle was closed; | |
| 93 void MojoWatchCallback(uintptr_t watch_context, | |
| 94 MojoResult result, | |
| 95 struct MojoHandleSignalsState signals_state, | |
| 96 MojoWatchNotificationFlags flags) { | |
| 97 WatchCallbackHolder::GetInstance()->Run(watch_context); | |
| 98 } | |
| 99 | |
| 100 } // namespace | |
| 101 | |
| 102 MojoFacade::MojoFacade(shell::mojom::InterfaceProvider* interface_provider, | |
| 103 id<CRWJSInjectionEvaluator> script_evaluator) | |
| 104 : interface_provider_(interface_provider), | |
| 105 script_evaluator_(script_evaluator) { | |
| 106 DCHECK_CURRENTLY_ON(WebThread::UI); | |
| 107 DCHECK(interface_provider_); | |
| 108 DCHECK(script_evaluator_); | |
| 109 } | |
| 110 | |
| 111 MojoFacade::~MojoFacade() { | |
| 112 DCHECK_CURRENTLY_ON(WebThread::UI); | |
| 113 for (uintptr_t context : watch_contexts_) | |
| 114 WatchCallbackHolder::GetInstance()->Remove(context); | |
| 115 } | |
| 116 | |
| 117 std::string MojoFacade::HandleMojoMessage( | |
| 118 const std::string& mojo_message_as_json) { | |
| 119 DCHECK_CURRENTLY_ON(WebThread::UI); | |
| 120 std::string name; | |
| 121 std::unique_ptr<base::DictionaryValue> args; | |
| 122 GetMessageNameAndArguments(mojo_message_as_json, &name, &args); | |
| 123 | |
| 124 std::unique_ptr<base::Value> result; | |
| 125 if (name == "service_provider.connectToService") { | |
| 126 result = HandleServiceProviderConnectToService(args.get()); | |
| 127 } else if (name == "core.close") { | |
| 128 result = HandleCoreClose(args.get()); | |
| 129 } else if (name == "core.createMessagePipe") { | |
| 130 result = HandleCoreCreateMessagePipe(args.get()); | |
| 131 } else if (name == "core.writeMessage") { | |
| 132 result = HandleCoreWriteMessage(args.get()); | |
| 133 } else if (name == "core.readMessage") { | |
| 134 result = HandleCoreReadMessage(args.get()); | |
| 135 } else if (name == "support.watch") { | |
| 136 result = HandleSupportWatch(args.get()); | |
| 137 } | |
| 138 | |
| 139 if (!result) { | |
| 140 return ""; | |
| 141 } | |
| 142 | |
| 143 std::string json_result; | |
| 144 base::JSONWriter::Write(*result, &json_result); | |
| 145 return json_result; | |
| 146 } | |
| 147 | |
| 148 void MojoFacade::GetMessageNameAndArguments( | |
| 149 const std::string& mojo_message_as_json, | |
| 150 std::string* out_name, | |
| 151 std::unique_ptr<base::DictionaryValue>* out_args) { | |
| 152 int error_code = 0; | |
| 153 std::string error_message; | |
| 154 std::unique_ptr<base::Value> mojo_message_as_value( | |
| 155 base::JSONReader::ReadAndReturnError(mojo_message_as_json, false, | |
| 156 &error_code, &error_message)); | |
| 157 CHECK(!error_code); | |
| 158 base::DictionaryValue* mojo_message = nullptr; | |
| 159 CHECK(mojo_message_as_value->GetAsDictionary(&mojo_message)); | |
| 160 | |
| 161 std::string name; | |
| 162 CHECK(mojo_message->GetString("name", &name)); | |
| 163 | |
| 164 base::DictionaryValue* args = nullptr; | |
| 165 CHECK(mojo_message->GetDictionary("args", &args)); | |
| 166 | |
| 167 *out_name = name; | |
| 168 *out_args = args->CreateDeepCopy(); | |
| 169 } | |
| 170 | |
| 171 std::unique_ptr<base::Value> MojoFacade::HandleServiceProviderConnectToService( | |
| 172 const base::DictionaryValue* args) { | |
| 173 const base::Value* service_name_as_value = nullptr; | |
| 174 CHECK(args->Get("serviceName", &service_name_as_value)); | |
| 175 | |
| 176 // By design service_provider.connectToService either succeeds or crashes, so | |
| 177 // check if service name is a valid string is intentionally omitted. | |
| 178 std::string service_name_as_string; | |
| 179 service_name_as_value->GetAsString(&service_name_as_string); | |
| 180 | |
| 181 mojo::MessagePipe pipe; | |
| 182 interface_provider_->GetInterface(mojo::String::From(service_name_as_string), | |
| 183 std::move(pipe.handle0)); | |
| 184 | |
| 185 return ValueFromMojoInteger(pipe.handle1.release().value()); | |
| 186 } | |
| 187 | |
| 188 std::unique_ptr<base::Value> MojoFacade::HandleCoreClose( | |
| 189 const base::DictionaryValue* args) { | |
| 190 int handle = 0; | |
| 191 CHECK(args->GetInteger("handle", &handle)); | |
| 192 | |
| 193 return ValueFromMojoInteger(MojoClose(handle)); | |
| 194 } | |
| 195 | |
| 196 std::unique_ptr<base::Value> MojoFacade::HandleCoreCreateMessagePipe( | |
| 197 base::DictionaryValue* args) { | |
| 198 const base::Value* options_as_value = nullptr; | |
| 199 CHECK(args->Get("optionsDict", &options_as_value)); | |
| 200 | |
| 201 std::unique_ptr<MojoCreateMessagePipeOptions> options; | |
| 202 if (options_as_value->IsType(base::Value::TYPE_DICTIONARY)) { | |
| 203 // Extract options and reset |options| pointer when this codepath is hit. | |
| 204 NOTIMPLEMENTED(); | |
| 205 } else { | |
| 206 CHECK(options_as_value->IsType(base::Value::TYPE_NULL)); | |
| 207 } | |
| 208 | |
| 209 MojoHandle handle0; | |
| 210 MojoHandle handle1; | |
| 211 MojoCreateMessagePipe(options.get(), &handle0, &handle1); | |
| 212 | |
| 213 std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue); | |
| 214 result->SetInteger("handle0", handle0); | |
| 215 result->SetInteger("handle1", handle1); | |
| 216 return std::unique_ptr<base::Value>(result.release()); | |
| 217 } | |
| 218 | |
| 219 std::unique_ptr<base::Value> MojoFacade::HandleCoreWriteMessage( | |
| 220 base::DictionaryValue* args) { | |
| 221 int handle = 0; | |
| 222 CHECK(args->GetInteger("handle", &handle)); | |
| 223 | |
| 224 base::ListValue* handles_list = nullptr; | |
| 225 CHECK(args->GetList("handles", &handles_list)); | |
| 226 | |
| 227 base::DictionaryValue* buffer = nullptr; | |
| 228 CHECK(args->GetDictionary("buffer", &buffer)); | |
| 229 | |
| 230 const base::Value* flags_as_value = nullptr; | |
| 231 CHECK(args->Get("flags", &flags_as_value)); | |
| 232 | |
| 233 int flags = MOJO_WRITE_MESSAGE_FLAG_NONE; | |
| 234 if (!flags_as_value->GetAsInteger(&flags)) { | |
| 235 flags = MOJO_WRITE_MESSAGE_FLAG_NONE; | |
| 236 } | |
| 237 | |
| 238 std::vector<MojoHandle> handles(handles_list->GetSize()); | |
| 239 for (size_t i = 0; i < handles_list->GetSize(); i++) { | |
| 240 int one_handle = 0; | |
| 241 handles_list->GetInteger(i, &one_handle); | |
| 242 handles[i] = one_handle; | |
| 243 } | |
| 244 | |
| 245 std::vector<uint8_t> bytes(buffer->size()); | |
| 246 for (size_t i = 0; i < buffer->size(); i++) { | |
| 247 int one_byte = 0; | |
| 248 buffer->GetInteger(base::IntToString(i), &one_byte); | |
| 249 bytes[i] = one_byte; | |
| 250 } | |
| 251 | |
| 252 MojoResult result = | |
| 253 MojoWriteMessage(static_cast<MojoHandle>(handle), bytes.data(), | |
| 254 bytes.size(), handles.data(), handles.size(), flags); | |
| 255 | |
| 256 return ValueFromMojoInteger(result); | |
| 257 } | |
| 258 | |
| 259 std::unique_ptr<base::Value> MojoFacade::HandleCoreReadMessage( | |
| 260 const base::DictionaryValue* args) { | |
| 261 const base::Value* handle_as_value = nullptr; | |
| 262 CHECK(args->Get("handle", &handle_as_value)); | |
| 263 int handle_as_int = 0; | |
| 264 if (!handle_as_value->GetAsInteger(&handle_as_int)) { | |
| 265 handle_as_int = 0; | |
| 266 } | |
| 267 | |
| 268 const base::Value* flags_as_value = nullptr; | |
| 269 CHECK(args->Get("flags", &flags_as_value)); | |
| 270 | |
| 271 int flags = MOJO_READ_MESSAGE_FLAG_NONE; | |
| 272 if (!flags_as_value->GetAsInteger(&flags)) { | |
| 273 flags = MOJO_READ_MESSAGE_FLAG_NONE; | |
| 274 } | |
| 275 | |
| 276 uint32_t num_bytes = 0; | |
| 277 uint32_t num_handles = 0; | |
| 278 MojoResult mojo_result = | |
| 279 MojoReadMessage(static_cast<MojoHandle>(handle_as_int), nullptr, | |
| 280 &num_bytes, nullptr, &num_handles, flags); | |
| 281 std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue); | |
| 282 | |
| 283 if (mojo_result == MOJO_RESULT_RESOURCE_EXHAUSTED) { | |
| 284 std::vector<uint8_t> bytes(num_bytes); | |
| 285 std::vector<MojoHandle> handles(num_handles); | |
| 286 mojo_result = | |
| 287 MojoReadMessage(static_cast<MojoHandle>(handle_as_int), bytes.data(), | |
| 288 &num_bytes, handles.data(), &num_handles, flags); | |
| 289 | |
| 290 base::ListValue* handles_list = new base::ListValue; | |
| 291 for (uint32_t i = 0; i < num_handles; i++) { | |
| 292 handles_list->AppendInteger(handles[i]); | |
| 293 } | |
| 294 result->Set("handles", std::unique_ptr<base::Value>(handles_list)); | |
| 295 | |
| 296 base::ListValue* buffer = new base::ListValue; | |
| 297 for (uint32_t i = 0; i < num_bytes; i++) { | |
| 298 buffer->AppendInteger(bytes[i]); | |
| 299 } | |
| 300 result->Set("buffer", std::unique_ptr<base::Value>(buffer)); | |
| 301 } | |
| 302 result->SetInteger("result", mojo_result); | |
| 303 | |
| 304 return std::unique_ptr<base::Value>(result.release()); | |
| 305 } | |
| 306 | |
| 307 std::unique_ptr<base::Value> MojoFacade::HandleSupportWatch( | |
| 308 const base::DictionaryValue* args) { | |
| 309 int handle = 0; | |
| 310 CHECK(args->GetInteger("handle", &handle)); | |
| 311 int signals = 0; | |
| 312 CHECK(args->GetInteger("signals", &signals)); | |
| 313 std::string callback; | |
| 314 CHECK(args->GetString("callback", &callback)); | |
|
Ken Rockot(use gerrit already)
2016/05/12 21:13:06
Am I understanding correctly that we take the call
Eugene But (OOO till 7-30)
2016/05/13 02:54:26
Yes. But I changed this to callback ID, as I need
| |
| 315 | |
| 316 uintptr_t context = WatchCallbackHolder::GetInstance()->Add(^{ | |
| 317 [script_evaluator_ executeJavaScript:base::SysUTF8ToNSString(callback) | |
| 318 completionHandler:nil]; | |
| 319 }); | |
| 320 | |
| 321 watch_contexts_.push_back(context); | |
| 322 | |
| 323 return ValueFromMojoInteger( | |
| 324 MojoWatch(handle, signals, &MojoWatchCallback, context)); | |
| 325 } | |
| 326 | |
| 327 } // namespace web | |
| OLD | NEW |