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

Side by Side Diff: content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.cc

Issue 345753003: [Android] Java Bridge with Gin: implement Java Bridge dispatcher (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Use an enum for passing error codes Created 6 years, 6 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2014 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 #include "content/browser/renderer_host/java/gin_java_bridge_dispatcher_host.h"
6
7 #include "base/android/java_handler_thread.h"
8 #include "base/android/jni_android.h"
9 #include "base/android/scoped_java_ref.h"
10 #include "base/lazy_instance.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/task_runner_util.h"
14 #include "content/browser/renderer_host/java/gin_java_bound_object_delegate.h"
15 #include "content/browser/renderer_host/java/jni_helper.h"
16 #include "content/common/android/gin_java_bridge_value.h"
17 #include "content/common/android/hash_set.h"
18 #include "content/common/gin_java_bridge_messages.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/render_frame_host.h"
21 #include "ipc/ipc_message_utils.h"
22
23 #if !defined(OS_ANDROID)
24 #error "JavaBridge only supports OS_ANDROID"
25 #endif
26
27 namespace content {
28
29 namespace {
30 // The JavaBridge needs to use a Java thread so the callback
31 // will happen on a thread with a prepared Looper.
32 class JavaBridgeThread : public base::android::JavaHandlerThread {
33 public:
34 JavaBridgeThread() : base::android::JavaHandlerThread("JavaBridge") {
35 Start();
36 }
37 virtual ~JavaBridgeThread() {
38 Stop();
39 }
40 };
41
42 base::LazyInstance<JavaBridgeThread> g_background_thread =
43 LAZY_INSTANCE_INITIALIZER;
44
45 } // namespace
46
47 GinJavaBridgeDispatcherHost::GinJavaBridgeDispatcherHost(
48 WebContents* web_contents,
49 jobject retained_object_set)
50 : WebContentsObserver(web_contents),
51 retained_object_set_(base::android::AttachCurrentThread(),
52 retained_object_set),
53 allow_object_contents_inspection_(true) {
54 DCHECK(retained_object_set);
55 }
56
57 GinJavaBridgeDispatcherHost::~GinJavaBridgeDispatcherHost() {
58 }
59
60 void GinJavaBridgeDispatcherHost::RenderFrameCreated(
61 RenderFrameHost* render_frame_host) {
62 renderers_.insert(render_frame_host);
63 for (NamedObjectMap::const_iterator iter = named_objects_.begin();
64 iter != named_objects_.end();
65 ++iter) {
66 render_frame_host->Send(new GinJavaBridgeMsg_AddNamedObject(
67 render_frame_host->GetRoutingID(), iter->first, iter->second));
68 }
69 }
70
71 void GinJavaBridgeDispatcherHost::RenderFrameDeleted(
72 RenderFrameHost* render_frame_host) {
73 renderers_.erase(render_frame_host);
74 RemoveHolder(render_frame_host,
75 GinJavaBoundObject::ObjectMap::iterator(&objects_),
76 objects_.size());
77 }
78
79 GinJavaBoundObject::ObjectID GinJavaBridgeDispatcherHost::AddObject(
80 const base::android::JavaRef<jobject>& object,
81 const base::android::JavaRef<jclass>& safe_annotation_clazz,
82 bool is_named,
83 RenderFrameHost* holder) {
84 DCHECK(is_named || holder);
85 GinJavaBoundObject::ObjectID object_id;
86 JNIEnv* env = base::android::AttachCurrentThread();
87 JavaObjectWeakGlobalRef ref(env, object.obj());
88 if (is_named) {
89 object_id = objects_.Add(new scoped_refptr<GinJavaBoundObject>(
90 GinJavaBoundObject::CreateNamed(ref, safe_annotation_clazz)));
91 } else {
92 object_id = objects_.Add(new scoped_refptr<GinJavaBoundObject>(
93 GinJavaBoundObject::CreateTransient(
94 ref, safe_annotation_clazz, holder)));
95 }
96 #if DCHECK_IS_ON
97 {
98 GinJavaBoundObject::ObjectID added_object_id;
99 DCHECK(FindObjectId(object, &added_object_id));
100 DCHECK_EQ(object_id, added_object_id);
101 }
102 #endif // DCHECK_IS_ON
103 base::android::ScopedJavaLocalRef<jobject> retained_object_set =
104 retained_object_set_.get(env);
105 if (!retained_object_set.is_null()) {
106 JNI_Java_HashSet_add(env, retained_object_set, object);
107 }
108 return object_id;
109 }
110
111 bool GinJavaBridgeDispatcherHost::FindObjectId(
112 const base::android::JavaRef<jobject>& object,
113 GinJavaBoundObject::ObjectID* object_id) {
114 JNIEnv* env = base::android::AttachCurrentThread();
115 for (GinJavaBoundObject::ObjectMap::iterator it(&objects_); !it.IsAtEnd();
116 it.Advance()) {
117 if (env->IsSameObject(
118 object.obj(),
119 it.GetCurrentValue()->get()->GetLocalRef(env).obj())) {
120 *object_id = it.GetCurrentKey();
121 return true;
122 }
123 }
124 return false;
125 }
126
127 JavaObjectWeakGlobalRef GinJavaBridgeDispatcherHost::GetObjectWeakRef(
128 GinJavaBoundObject::ObjectID object_id) {
129 scoped_refptr<GinJavaBoundObject>* result = objects_.Lookup(object_id);
130 scoped_refptr<GinJavaBoundObject> object(result ? *result : NULL);
131 if (object.get())
132 return object->GetWeakRef();
133 else
134 return JavaObjectWeakGlobalRef();
135 }
136
137 void GinJavaBridgeDispatcherHost::RemoveHolder(
138 RenderFrameHost* holder,
139 const GinJavaBoundObject::ObjectMap::iterator& from,
140 size_t count) {
141 JNIEnv* env = base::android::AttachCurrentThread();
142 base::android::ScopedJavaLocalRef<jobject> retained_object_set =
143 retained_object_set_.get(env);
144 size_t i = 0;
145 for (GinJavaBoundObject::ObjectMap::iterator it(from);
146 !it.IsAtEnd() && i < count;
147 it.Advance(), ++i) {
148 scoped_refptr<GinJavaBoundObject> object(*it.GetCurrentValue());
149 if (object->IsNamed())
150 continue;
151 object->RemoveHolder(holder);
152 if (!object->HasHolders()) {
153 if (!retained_object_set.is_null()) {
154 JNI_Java_HashSet_remove(
155 env, retained_object_set, object->GetLocalRef(env));
156 }
157 objects_.Remove(it.GetCurrentKey());
158 }
159 }
160 }
161
162 void GinJavaBridgeDispatcherHost::AddNamedObject(
163 const std::string& name,
164 const base::android::JavaRef<jobject>& object,
165 const base::android::JavaRef<jclass>& safe_annotation_clazz) {
166 DCHECK_CURRENTLY_ON(BrowserThread::UI);
167 GinJavaBoundObject::ObjectID object_id;
168 NamedObjectMap::iterator iter = named_objects_.find(name);
169 bool existing_object = FindObjectId(object, &object_id);
170 if (existing_object && iter != named_objects_.end() &&
171 iter->second == object_id) {
172 // Nothing to do.
173 return;
174 }
175 if (iter != named_objects_.end()) {
176 RemoveNamedObject(iter->first);
177 }
178 if (existing_object) {
179 (*objects_.Lookup(object_id))->AddName();
180 } else {
181 object_id = AddObject(object, safe_annotation_clazz, true, NULL);
182 }
183 named_objects_[name] = object_id;
184
185 for (RendererSet::iterator iter = renderers_.begin();
186 iter != renderers_.end(); ++iter) {
187 (*iter)->Send(new GinJavaBridgeMsg_AddNamedObject(
188 (*iter)->GetRoutingID(), name, object_id));
189 }
190 }
191
192 void GinJavaBridgeDispatcherHost::RemoveNamedObject(
193 const std::string& name) {
194 DCHECK_CURRENTLY_ON(BrowserThread::UI);
195 NamedObjectMap::iterator iter = named_objects_.find(name);
196 if (iter == named_objects_.end())
197 return;
198
199 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(iter->second));
200 named_objects_.erase(iter);
201 object->RemoveName();
202
203 // Not erasing from the objects map, as we can still receive method
204 // invocation requests for this object, and they should work until the
205 // java object is gone.
206 if (!object->IsNamed()) {
207 JNIEnv* env = base::android::AttachCurrentThread();
208 base::android::ScopedJavaLocalRef<jobject> retained_object_set =
209 retained_object_set_.get(env);
210 if (!retained_object_set.is_null()) {
211 JNI_Java_HashSet_remove(
212 env, retained_object_set, object->GetLocalRef(env));
213 }
214 }
215
216 for (RendererSet::iterator iter = renderers_.begin();
217 iter != renderers_.end(); ++iter) {
218 (*iter)->Send(new GinJavaBridgeMsg_RemoveNamedObject(
219 (*iter)->GetRoutingID(), name));
220 }
221 }
222
223 void GinJavaBridgeDispatcherHost::SetAllowObjectContentsInspection(bool allow) {
224 allow_object_contents_inspection_ = allow;
225 }
226
227 void GinJavaBridgeDispatcherHost::DocumentAvailableInMainFrame() {
228 DCHECK_CURRENTLY_ON(BrowserThread::UI);
229 // Called when the window object has been cleared in the main frame.
230 // That means, all sub-frames have also been cleared, so only named
231 // objects survived.
232 JNIEnv* env = base::android::AttachCurrentThread();
233 base::android::ScopedJavaLocalRef<jobject> retained_object_set =
234 retained_object_set_.get(env);
235 if (!retained_object_set.is_null()) {
236 JNI_Java_HashSet_clear(env, retained_object_set);
237 }
238
239 // We also need to add back the named objects we have so far as they
240 // should survive navigations.
241 for (GinJavaBoundObject::ObjectMap::iterator it(&objects_); !it.IsAtEnd();
242 it.Advance()) {
243 scoped_refptr<GinJavaBoundObject> object(*it.GetCurrentValue());
244 if (object->IsNamed()) {
245 if (!retained_object_set.is_null()) {
246 JNI_Java_HashSet_add(
247 env, retained_object_set, object->GetLocalRef(env));
248 }
249 } else {
250 objects_.Remove(it.GetCurrentKey());
251 }
252 }
253 }
254
255 namespace {
256
257 // TODO(mnaganov): Implement passing of a parameter into sync message handlers.
258 class MessageForwarder : public IPC::Sender {
259 public:
260 MessageForwarder(GinJavaBridgeDispatcherHost* gjbdh,
261 RenderFrameHost* render_frame_host)
262 : gjbdh_(gjbdh), render_frame_host_(render_frame_host) {}
263 void OnGetMethods(GinJavaBoundObject::ObjectID object_id,
264 IPC::Message* reply_msg) {
265 gjbdh_->OnGetMethods(render_frame_host_,
266 object_id,
267 reply_msg);
268 }
269 void OnHasMethod(GinJavaBoundObject::ObjectID object_id,
270 const std::string& method_name,
271 IPC::Message* reply_msg) {
272 gjbdh_->OnHasMethod(render_frame_host_,
273 object_id,
274 method_name,
275 reply_msg);
276 }
277 void OnInvokeMethod(GinJavaBoundObject::ObjectID object_id,
278 const std::string& method_name,
279 const base::ListValue& arguments,
280 IPC::Message* reply_msg) {
281 gjbdh_->OnInvokeMethod(render_frame_host_,
282 object_id,
283 method_name,
284 arguments,
285 reply_msg);
286 }
287 virtual bool Send(IPC::Message* msg) OVERRIDE {
288 NOTREACHED();
289 return false;
290 }
291 private:
292 GinJavaBridgeDispatcherHost* gjbdh_;
293 RenderFrameHost* render_frame_host_;
294 };
295
296 }
297
298 bool GinJavaBridgeDispatcherHost::OnMessageReceived(
299 const IPC::Message& message,
300 RenderFrameHost* render_frame_host) {
301 DCHECK(render_frame_host);
302 bool handled = true;
303 MessageForwarder forwarder(this, render_frame_host);
304 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(GinJavaBridgeDispatcherHost, message,
305 render_frame_host)
306 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_GetMethods,
307 &forwarder,
308 MessageForwarder::OnGetMethods)
309 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_HasMethod,
310 &forwarder,
311 MessageForwarder::OnHasMethod)
312 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_InvokeMethod,
313 &forwarder,
314 MessageForwarder::OnInvokeMethod)
315 IPC_MESSAGE_HANDLER(GinJavaBridgeHostMsg_ObjectWrapperDeleted,
316 OnObjectWrapperDeleted)
317 IPC_MESSAGE_UNHANDLED(handled = false)
318 IPC_END_MESSAGE_MAP()
319 return handled;
320 }
321
322 void GinJavaBridgeDispatcherHost::SendReply(
323 RenderFrameHost* render_frame_host,
324 IPC::Message* reply_msg) {
325 DCHECK_CURRENTLY_ON(BrowserThread::UI);
326 if (renderers_.find(render_frame_host) != renderers_.end()) {
327 render_frame_host->Send(reply_msg);
328 } else {
329 delete reply_msg;
330 }
331 }
332
333 void GinJavaBridgeDispatcherHost::OnGetMethods(
334 RenderFrameHost* render_frame_host,
335 GinJavaBoundObject::ObjectID object_id,
336 IPC::Message* reply_msg) {
337 DCHECK_CURRENTLY_ON(BrowserThread::UI);
338 DCHECK(render_frame_host);
339 if (!allow_object_contents_inspection_) {
340 IPC::WriteParam(reply_msg, std::set<std::string>());
341 render_frame_host->Send(reply_msg);
342 return;
343 }
344 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id));
345 if (!object) {
346 LOG(ERROR) << "WebView: Unknown object: " << object_id;
347 IPC::WriteParam(reply_msg, std::set<std::string>());
348 render_frame_host->Send(reply_msg);
349 return;
350 }
351 base::PostTaskAndReplyWithResult(
352 g_background_thread.Get().message_loop()->message_loop_proxy(),
353 FROM_HERE,
354 base::Bind(&GinJavaBoundObject::GetMethodNames, object),
355 base::Bind(&GinJavaBridgeDispatcherHost::SendMethods,
356 AsWeakPtr(),
357 render_frame_host,
358 reply_msg));
359 }
360
361 void GinJavaBridgeDispatcherHost::SendMethods(
362 RenderFrameHost* render_frame_host,
363 IPC::Message* reply_msg,
364 const std::set<std::string>& method_names) {
365 IPC::WriteParam(reply_msg, method_names);
366 SendReply(render_frame_host, reply_msg);
367 }
368
369 void GinJavaBridgeDispatcherHost::OnHasMethod(
370 RenderFrameHost* render_frame_host,
371 GinJavaBoundObject::ObjectID object_id,
372 const std::string& method_name,
373 IPC::Message* reply_msg) {
374 DCHECK_CURRENTLY_ON(BrowserThread::UI);
375 DCHECK(render_frame_host);
376 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id));
377 if (!object) {
378 LOG(ERROR) << "WebView: Unknown object: " << object_id;
379 IPC::WriteParam(reply_msg, false);
380 render_frame_host->Send(reply_msg);
381 return;
382 }
383 base::PostTaskAndReplyWithResult(
384 g_background_thread.Get().message_loop()->message_loop_proxy(),
385 FROM_HERE,
386 base::Bind(&GinJavaBoundObject::HasMethod, object, method_name),
387 base::Bind(&GinJavaBridgeDispatcherHost::SendHasMethodReply,
388 AsWeakPtr(),
389 render_frame_host,
390 reply_msg));
391 }
392
393 void GinJavaBridgeDispatcherHost::SendHasMethodReply(
394 RenderFrameHost* render_frame_host,
395 IPC::Message* reply_msg,
396 bool result) {
397 IPC::WriteParam(reply_msg, result);
398 SendReply(render_frame_host, reply_msg);
399 }
400
401 void GinJavaBridgeDispatcherHost::OnInvokeMethod(
402 RenderFrameHost* render_frame_host,
403 GinJavaBoundObject::ObjectID object_id,
404 const std::string& method_name,
405 const base::ListValue& arguments,
406 IPC::Message* reply_msg) {
407 DCHECK_CURRENTLY_ON(BrowserThread::UI);
408 DCHECK(render_frame_host);
409 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id));
410 if (!object) {
411 LOG(ERROR) << "WebView: Unknown object: " << object_id;
412 base::ListValue result;
413 result.Append(base::Value::CreateNullValue());
414 IPC::WriteParam(reply_msg, result);
415 IPC::WriteParam(reply_msg, kGinJavaBridgeUnknownObjectId);
416 render_frame_host->Send(reply_msg);
417 return;
418 }
419 scoped_refptr<GinJavaMethodInvocationHelper> result =
420 new GinJavaMethodInvocationHelper(
421 make_scoped_ptr(new GinJavaBoundObjectDelegate(object))
422 .PassAs<GinJavaMethodInvocationHelper::ObjectDelegate>(),
423 method_name,
424 arguments);
425 result->Init(this);
426 g_background_thread.Get()
427 .message_loop()
428 ->message_loop_proxy()
429 ->PostTaskAndReply(
430 FROM_HERE,
431 base::Bind(&GinJavaMethodInvocationHelper::Invoke, result),
432 base::Bind(
433 &GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult,
434 AsWeakPtr(),
435 render_frame_host,
436 reply_msg,
437 result));
438 }
439
440 void GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult(
441 RenderFrameHost* render_frame_host,
442 IPC::Message* reply_msg,
443 scoped_refptr<GinJavaMethodInvocationHelper> result) {
444 if (result->HoldsPrimitiveResult()) {
445 IPC::WriteParam(reply_msg, result->GetPrimitiveResult());
446 IPC::WriteParam(reply_msg, result->GetInvocationError());
447 SendReply(render_frame_host, reply_msg);
448 } else {
449 ProcessMethodInvocationObjectResult(render_frame_host, reply_msg, result);
450 }
451 }
452
453 void GinJavaBridgeDispatcherHost::ProcessMethodInvocationObjectResult(
454 RenderFrameHost* render_frame_host,
455 IPC::Message* reply_msg,
456 scoped_refptr<GinJavaMethodInvocationHelper> result) {
457 DCHECK_CURRENTLY_ON(BrowserThread::UI);
458 if (renderers_.find(render_frame_host) == renderers_.end()) {
459 delete reply_msg;
460 return;
461 }
462 base::ListValue wrapped_result;
463 if (!result->GetObjectResult().is_null()) {
464 GinJavaBoundObject::ObjectID returned_object_id;
465 if (FindObjectId(result->GetObjectResult(), &returned_object_id)) {
466 (*objects_.Lookup(returned_object_id))->AddHolder(render_frame_host);
467 } else {
468 returned_object_id = AddObject(result->GetObjectResult(),
469 result->GetSafeAnnotationClass(),
470 false,
471 render_frame_host);
472 }
473 wrapped_result.Append(
474 GinJavaBridgeValue::CreateObjectIDValue(returned_object_id).release());
475 } else {
476 wrapped_result.Append(base::Value::CreateNullValue());
477 }
478 IPC::WriteParam(reply_msg, wrapped_result);
479 IPC::WriteParam(reply_msg, result->GetInvocationError());
480 render_frame_host->Send(reply_msg);
481 }
482
483 void GinJavaBridgeDispatcherHost::OnObjectWrapperDeleted(
484 RenderFrameHost* render_frame_host,
485 GinJavaBoundObject::ObjectID object_id) {
486 DCHECK_CURRENTLY_ON(BrowserThread::UI);
487 DCHECK(render_frame_host);
488 if (objects_.Lookup(object_id)) {
489 GinJavaBoundObject::ObjectMap::iterator iter(&objects_);
490 while (!iter.IsAtEnd() && iter.GetCurrentKey() != object_id)
491 iter.Advance();
492 DCHECK(!iter.IsAtEnd());
493 RemoveHolder(render_frame_host, iter, 1);
494 }
495 }
496
497 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698