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