Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(91)

Side by Side Diff: ios/web/webui/mojo_facade.mm

Issue 1956113002: [ios Mojo] iOS facade class for Mojo API. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698