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/values.h" | |
| 16 #import "ios/web/public/web_state/js/crw_js_injection_evaluator.h" | |
| 17 #include "ios/web/public/web_thread.h" | |
| 18 #include "mojo/public/c/system/core.h" | |
| 19 #include "services/shell/public/interfaces/interface_provider.mojom.h" | |
| 20 | |
| 21 namespace web { | |
| 22 | |
| 23 namespace { | |
| 24 | |
| 25 // Wraps Mojo integer into |base::Value| as |TYPE_INTEGER|. | |
| 26 template <typename MojoIntegerT> | |
| 27 std::unique_ptr<base::Value> ValueFromMojoInteger(MojoIntegerT handle) { | |
| 28 return std::unique_ptr<base::Value>( | |
| 29 new base::FundamentalValue(static_cast<int>(handle))); | |
| 30 } | |
| 31 | |
| 32 // Singleton which holds contexts for all currently active watches. Must be used | |
| 33 // only on UI thread. | |
| 34 class WatchCallbackHolder { | |
| 35 public: | |
| 36 static WatchCallbackHolder* GetInstance() { | |
| 37 DCHECK_CURRENTLY_ON(WebThread::UI); | |
| 38 return base::Singleton<WatchCallbackHolder>::get(); | |
| 39 } | |
| 40 | |
| 41 // Adds |callback_block| which must be executed when the Watch callback fires. | |
| 42 // Returns context which can be passed to |Run| to execute the block. Callers | |
| 43 // are responsible for removing contexts which are no longer active by calling | |
| 44 // |Remove|. | |
| 45 uintptr_t Add(void (^callback_block)(void)) { | |
| 46 DCHECK_CURRENTLY_ON(WebThread::UI); | |
| 47 callbacks_[++last_watch_context_].reset([callback_block copy]); | |
| 48 return last_watch_context_; | |
| 49 } | |
| 50 | |
| 51 // Executes the callback previously added by calling |Add|. |watch_context| is | |
| 52 // the context returned from |Add|. Calling this method for a context which | |
| 53 // has been previously removed is no-op. | |
| 54 void Run(uintptr_t watch_context) { | |
| 55 DCHECK_CURRENTLY_ON(WebThread::UI); | |
| 56 ProceduralBlock callback = callbacks_[watch_context]; | |
| 57 if (callback) | |
| 58 callback(); | |
| 59 } | |
| 60 | |
| 61 // Removes previously added callback. |watch_context| is the context returned | |
| 62 // from |Add|. | |
| 63 void Remove(uintptr_t watch_context) { | |
| 64 DCHECK_CURRENTLY_ON(WebThread::UI); | |
| 65 DCHECK(callbacks_.find(watch_context) != callbacks_.end()); | |
| 66 callbacks_.erase(watch_context); | |
| 67 } | |
| 68 | |
| 69 private: | |
| 70 WatchCallbackHolder() : last_watch_context_(0) {} | |
| 71 ~WatchCallbackHolder() { | |
| 72 // Clients are responsible for removing all callbacks before shutdown. | |
| 73 DCHECK(callbacks_.empty()); | |
| 74 } | |
| 75 | |
| 76 // Required by base::Singleton. | |
| 77 friend struct base::DefaultSingletonTraits<WatchCallbackHolder>; | |
| 78 | |
| 79 // Context for the last added callback. Context for the next callback will be | |
| 80 // |last_watch_context_ + 1|. | |
| 81 uintptr_t last_watch_context_; | |
| 82 // Callbacks added via |Add| method. | |
| 83 std::map<uintptr_t, base::mac::ScopedBlock<ProceduralBlock>> callbacks_; | |
| 84 | |
| 85 DISALLOW_COPY_AND_ASSIGN(WatchCallbackHolder); | |
| 86 }; | |
| 87 | |
| 88 // Callback for |MojoWatch| call. Called when: | |
| 89 // - watched signal has been satisfied; | |
| 90 // - it became known that none of the watched signals will ever be satisfied; | |
| 91 // - handle was closed; | |
| 92 void MojoWatchCallback(uintptr_t watch_context, | |
| 93 MojoResult result, | |
| 94 struct MojoHandleSignalsState signals_state, | |
| 95 MojoWatchNotificationFlags flags) { | |
| 96 WatchCallbackHolder::GetInstance()->Run(watch_context); | |
| 97 } | |
| 98 | |
| 99 } // namespace | |
| 100 | |
| 101 MojoFacade::MojoFacade(shell::mojom::InterfaceProvider* interface_provider, | |
| 102 id<CRWJSInjectionEvaluator> script_evaluator) | |
| 103 : interface_provider_(interface_provider), | |
| 104 script_evaluator_(script_evaluator) { | |
| 105 DCHECK_CURRENTLY_ON(WebThread::UI); | |
| 106 DCHECK(interface_provider_); | |
| 107 DCHECK(script_evaluator_); | |
| 108 } | |
| 109 | |
| 110 MojoFacade::~MojoFacade() { | |
| 111 DCHECK_CURRENTLY_ON(WebThread::UI); | |
| 112 for (uintptr_t context : watch_contexts_) | |
| 113 WatchCallbackHolder::GetInstance()->Remove(context); | |
| 114 } | |
| 115 | |
| 116 std::string MojoFacade::HandleMojoMessage( | |
| 117 const std::string& mojo_message_as_json) { | |
| 118 DCHECK_CURRENTLY_ON(WebThread::UI); | |
| 119 std::string name; | |
| 120 std::unique_ptr<base::DictionaryValue> args; | |
| 121 if (!GetMessageNameAndArguments(mojo_message_as_json, &name, &args)) { | |
| 122 return ""; | |
|
Eugene But (OOO till 7-30)
2016/05/07 00:07:52
On iOS we fail gracefully if JS message is malform
Ken Rockot(use gerrit already)
2016/05/09 15:44:38
What do you mean by "any page can send these messa
Eugene But (OOO till 7-30)
2016/05/09 16:08:10
A lot of stuff on iOS is implemented in JavaScript
Eugene But (OOO till 7-30)
2016/05/10 02:00:56
Replaced returns with CHECKs
| |
| 123 } | |
| 124 | |
| 125 std::unique_ptr<base::Value> result; | |
| 126 if (name == "service_provider.connectToService") { | |
| 127 result = HandleServiceProviderConnectToService(args.get()); | |
| 128 } else if (name == "core.close") { | |
| 129 result = HandleCoreClose(args.get()); | |
| 130 } else if (name == "core.createMessagePipe") { | |
| 131 result = HandleCoreCreateMessagePipe(args.get()); | |
| 132 } else if (name == "core.writeMessage") { | |
| 133 result = HandleCoreWriteMessage(args.get()); | |
| 134 } else if (name == "core.readMessage") { | |
| 135 result = HandleCoreReadMessage(args.get()); | |
| 136 } else if (name == "support.watch") { | |
| 137 result = HandleSupportWatch(args.get()); | |
| 138 } | |
| 139 | |
| 140 if (!result) { | |
| 141 return ""; | |
| 142 } | |
| 143 | |
| 144 std::string json_result; | |
| 145 base::JSONWriter::Write(*result, &json_result); | |
| 146 return json_result; | |
| 147 } | |
| 148 | |
| 149 bool MojoFacade::GetMessageNameAndArguments( | |
| 150 const std::string& mojo_message_as_json, | |
| 151 std::string* out_name, | |
| 152 std::unique_ptr<base::DictionaryValue>* out_args) { | |
| 153 int error_code = 0; | |
| 154 std::string error_message; | |
| 155 std::unique_ptr<base::Value> mojo_message_as_value( | |
| 156 base::JSONReader::ReadAndReturnError(mojo_message_as_json, false, | |
| 157 &error_code, &error_message)); | |
| 158 if (error_code) { | |
| 159 DLOG(WARNING) << "JSON parse error: " << error_message; | |
| 160 return false; | |
| 161 } | |
| 162 base::DictionaryValue* mojo_message = nullptr; | |
| 163 if (!mojo_message_as_value->GetAsDictionary(&mojo_message)) { | |
| 164 DLOG(WARNING) << "mojo message is not a dictionary"; | |
| 165 return false; | |
| 166 } | |
| 167 | |
| 168 std::string name; | |
| 169 if (!mojo_message->GetString("name", &name)) { | |
| 170 DLOG(WARNING) << "missing 'name' key in mojo message"; | |
| 171 return false; | |
| 172 } | |
| 173 | |
| 174 base::DictionaryValue* args = nullptr; | |
| 175 if (!mojo_message->GetDictionary("args", &args)) { | |
| 176 DLOG(WARNING) << "missing 'args' key in mojo message"; | |
| 177 return false; | |
| 178 } | |
| 179 | |
| 180 *out_name = name; | |
| 181 *out_args = args->CreateDeepCopy(); | |
| 182 return true; | |
| 183 } | |
| 184 | |
| 185 std::unique_ptr<base::Value> MojoFacade::HandleServiceProviderConnectToService( | |
| 186 const base::DictionaryValue* args) { | |
| 187 const base::Value* service_name_as_value = nullptr; | |
| 188 if (!args->Get("serviceName", &service_name_as_value)) { | |
| 189 DLOG(WARNING) << "missing 'serviceName' key in mojo message"; | |
| 190 return nullptr; | |
| 191 } | |
| 192 | |
| 193 // By design service_provider.connectToService either succeeds or crashes, so | |
| 194 // check if service name is a valid string is intentionally omitted. | |
| 195 std::string service_name_as_string; | |
| 196 service_name_as_value->GetAsString(&service_name_as_string); | |
| 197 | |
| 198 mojo::MessagePipe pipe; | |
| 199 interface_provider_->GetInterface(mojo::String::From(service_name_as_string), | |
| 200 std::move(pipe.handle0)); | |
| 201 | |
| 202 return ValueFromMojoInteger(pipe.handle1.release().value()); | |
| 203 } | |
| 204 | |
| 205 std::unique_ptr<base::Value> MojoFacade::HandleCoreClose( | |
| 206 const base::DictionaryValue* args) { | |
| 207 int handle = 0; | |
| 208 if (!args->GetInteger("handle", &handle)) { | |
| 209 DLOG(WARNING) << "missing 'handle' key in mojo message"; | |
| 210 return nullptr; | |
| 211 } | |
| 212 | |
| 213 return ValueFromMojoInteger(MojoClose(handle)); | |
| 214 } | |
| 215 | |
| 216 std::unique_ptr<base::Value> MojoFacade::HandleCoreCreateMessagePipe( | |
| 217 base::DictionaryValue* args) { | |
| 218 const base::Value* options_as_value = nullptr; | |
| 219 if (!args->Get("optionsDict", &options_as_value)) { | |
| 220 DLOG(WARNING) << "missing 'optionsDict' key in mojo message"; | |
| 221 return nullptr; | |
| 222 } | |
| 223 | |
| 224 std::unique_ptr<MojoCreateMessagePipeOptions> options; | |
| 225 if (options_as_value->IsType(base::Value::TYPE_DICTIONARY)) { | |
| 226 // Extract options and reset |options| pointer when this codepath is hit. | |
| 227 NOTIMPLEMENTED(); | |
| 228 } else if (!options_as_value->IsType(base::Value::TYPE_NULL)) { | |
| 229 DLOG(WARNING) << "'optionsDict' is neither a dict nor null"; | |
| 230 return nullptr; | |
| 231 } | |
| 232 | |
| 233 MojoHandle handle0; | |
| 234 MojoHandle handle1; | |
| 235 MojoCreateMessagePipe(options.get(), &handle0, &handle1); | |
| 236 | |
| 237 std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue); | |
| 238 result->SetInteger("handle0", handle0); | |
| 239 result->SetInteger("handle1", handle1); | |
| 240 return std::unique_ptr<base::Value>(result.release()); | |
| 241 } | |
| 242 | |
| 243 std::unique_ptr<base::Value> MojoFacade::HandleCoreWriteMessage( | |
| 244 base::DictionaryValue* args) { | |
| 245 int handle = 0; | |
| 246 if (!args->GetInteger("handle", &handle)) { | |
| 247 DLOG(WARNING) << "missing 'handle' key in mojo message"; | |
| 248 return nullptr; | |
| 249 } | |
| 250 | |
| 251 base::ListValue* handles_list = nullptr; | |
| 252 if (!args->GetList("handles", &handles_list)) { | |
| 253 DLOG(WARNING) << "missing 'handles' key in mojo message"; | |
| 254 return nullptr; | |
| 255 } | |
| 256 | |
| 257 base::DictionaryValue* buffer = nullptr; | |
| 258 if (!args->GetDictionary("buffer", &buffer)) { | |
| 259 DLOG(WARNING) << "missing 'buffer' key in mojo message"; | |
| 260 return nullptr; | |
| 261 } | |
| 262 | |
| 263 const base::Value* flags_as_value = nullptr; | |
| 264 if (!args->Get("flags", &flags_as_value)) { | |
| 265 DLOG(WARNING) << "missing 'flags' key in mojo message"; | |
| 266 return nullptr; | |
| 267 } | |
| 268 | |
| 269 int flags = MOJO_WRITE_MESSAGE_FLAG_NONE; | |
| 270 if (!flags_as_value->GetAsInteger(&flags)) { | |
| 271 flags = MOJO_WRITE_MESSAGE_FLAG_NONE; | |
| 272 } | |
| 273 | |
| 274 std::vector<MojoHandle> handles(handles_list->GetSize()); | |
| 275 for (size_t i = 0; i < handles_list->GetSize(); i++) { | |
| 276 int one_handle = 0; | |
| 277 handles_list->GetInteger(i, &one_handle); | |
| 278 handles[i] = one_handle; | |
| 279 } | |
| 280 | |
| 281 std::vector<uint8_t> bytes(buffer->size()); | |
| 282 for (size_t i = 0; i < buffer->size(); i++) { | |
| 283 int one_byte = 0; | |
| 284 buffer->GetInteger(base::IntToString(i), &one_byte); | |
| 285 bytes[i] = one_byte; | |
| 286 } | |
| 287 | |
| 288 MojoResult result = | |
| 289 MojoWriteMessage(static_cast<MojoHandle>(handle), bytes.data(), | |
| 290 bytes.size(), handles.data(), handles.size(), flags); | |
| 291 | |
| 292 return ValueFromMojoInteger(result); | |
| 293 } | |
| 294 | |
| 295 std::unique_ptr<base::Value> MojoFacade::HandleCoreReadMessage( | |
| 296 const base::DictionaryValue* args) { | |
| 297 const base::Value* handle_as_value = nullptr; | |
| 298 if (!args->Get("handle", &handle_as_value)) { | |
| 299 DLOG(WARNING) << "missing 'handle' key in mojo message"; | |
| 300 return nullptr; | |
| 301 } | |
| 302 int handle_as_int = 0; | |
| 303 if (!handle_as_value->GetAsInteger(&handle_as_int)) { | |
| 304 handle_as_int = 0; | |
| 305 } | |
| 306 | |
| 307 const base::Value* flags_as_value = nullptr; | |
| 308 if (!args->Get("flags", &flags_as_value)) { | |
| 309 DLOG(WARNING) << "missing 'flags' key in mojo message"; | |
| 310 return nullptr; | |
| 311 } | |
| 312 | |
| 313 int flags = MOJO_READ_MESSAGE_FLAG_NONE; | |
| 314 if (!flags_as_value->GetAsInteger(&flags)) { | |
| 315 flags = MOJO_READ_MESSAGE_FLAG_NONE; | |
| 316 } | |
| 317 | |
| 318 uint32_t num_bytes = 0; | |
| 319 uint32_t num_handles = 0; | |
| 320 MojoResult mojo_result = | |
| 321 MojoReadMessage(static_cast<MojoHandle>(handle_as_int), nullptr, | |
| 322 &num_bytes, nullptr, &num_handles, flags); | |
| 323 std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue); | |
| 324 | |
| 325 if (mojo_result == MOJO_RESULT_RESOURCE_EXHAUSTED) { | |
| 326 std::vector<uint8_t> bytes(num_bytes); | |
| 327 std::vector<MojoHandle> handles(num_handles); | |
| 328 mojo_result = | |
| 329 MojoReadMessage(static_cast<MojoHandle>(handle_as_int), bytes.data(), | |
| 330 &num_bytes, handles.data(), &num_handles, flags); | |
| 331 | |
| 332 base::ListValue* handles_list = new base::ListValue; | |
| 333 for (uint32_t i = 0; i < num_handles; i++) { | |
| 334 handles_list->AppendInteger(handles[i]); | |
| 335 } | |
| 336 result->Set("handles", std::unique_ptr<base::Value>(handles_list)); | |
| 337 | |
| 338 base::ListValue* buffer = new base::ListValue; | |
| 339 for (uint32_t i = 0; i < num_bytes; i++) { | |
| 340 buffer->AppendInteger(bytes[i]); | |
| 341 } | |
| 342 result->Set("buffer", std::unique_ptr<base::Value>(buffer)); | |
| 343 } | |
| 344 result->SetInteger("result", mojo_result); | |
| 345 | |
| 346 return std::unique_ptr<base::Value>(result.release()); | |
| 347 } | |
| 348 | |
| 349 std::unique_ptr<base::Value> MojoFacade::HandleSupportWatch( | |
| 350 const base::DictionaryValue* args) { | |
| 351 int handle = 0; | |
| 352 if (!args->GetInteger("handle", &handle)) { | |
| 353 DLOG(WARNING) << "missing 'handle' key in mojo message"; | |
| 354 return nullptr; | |
| 355 } | |
| 356 int signals = 0; | |
| 357 if (!args->GetInteger("signals", &signals)) { | |
| 358 DLOG(WARNING) << "missing 'signals' key in mojo message"; | |
| 359 return nullptr; | |
| 360 } | |
| 361 int callback_id = 0; | |
| 362 if (!args->GetInteger("callbackId", &callback_id)) { | |
| 363 DLOG(WARNING) << "missing 'callbackId' key in mojo message"; | |
| 364 return nullptr; | |
| 365 } | |
| 366 | |
| 367 uintptr_t context = WatchCallbackHolder::GetInstance()->Add(^{ | |
| 368 NSString* signal_watch = [NSString | |
| 369 stringWithFormat:@"__crWeb.mojo.mojoWatchSignal(%d);", callback_id]; | |
| 370 [script_evaluator_ executeJavaScript:signal_watch completionHandler:nil]; | |
| 371 }); | |
| 372 | |
| 373 watch_contexts_.push_back(context); | |
| 374 | |
| 375 return ValueFromMojoInteger( | |
| 376 MojoWatch(handle, signals, &MojoWatchCallback, context)); | |
| 377 } | |
| 378 | |
| 379 } // namespace web | |
| OLD | NEW |