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 } // 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 | |
OLD | NEW |