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.
5 #include "content/browser/android/java/gin_java_bridge_dispatcher_host.h"
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/android/java/gin_java_bound_object_delegate.h"
15 #include "content/browser/android/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 "content/public/browser/web_contents.h"
22 #include "ipc/ipc_message_utils.h"
24 #if !defined(OS_ANDROID)
25 #error "JavaBridge only supports OS_ANDROID"
31 // The JavaBridge needs to use a Java thread so the callback
32 // will happen on a thread with a prepared Looper.
33 class JavaBridgeThread
: public base::android::JavaHandlerThread
{
35 JavaBridgeThread() : base::android::JavaHandlerThread("JavaBridge") {
38 virtual ~JavaBridgeThread() {
43 base::LazyInstance
<JavaBridgeThread
> g_background_thread
=
44 LAZY_INSTANCE_INITIALIZER
;
48 GinJavaBridgeDispatcherHost::GinJavaBridgeDispatcherHost(
49 WebContents
* web_contents
,
50 jobject retained_object_set
)
51 : WebContentsObserver(web_contents
),
52 retained_object_set_(base::android::AttachCurrentThread(),
54 allow_object_contents_inspection_(true) {
55 DCHECK(retained_object_set
);
58 GinJavaBridgeDispatcherHost::~GinJavaBridgeDispatcherHost() {
61 void GinJavaBridgeDispatcherHost::RenderFrameCreated(
62 RenderFrameHost
* render_frame_host
) {
63 for (NamedObjectMap::const_iterator iter
= named_objects_
.begin();
64 iter
!= named_objects_
.end();
66 render_frame_host
->Send(new GinJavaBridgeMsg_AddNamedObject(
67 render_frame_host
->GetRoutingID(), iter
->first
, iter
->second
));
71 void GinJavaBridgeDispatcherHost::RenderFrameDeleted(
72 RenderFrameHost
* render_frame_host
) {
73 RemoveHolder(render_frame_host
,
74 GinJavaBoundObject::ObjectMap::iterator(&objects_
),
78 GinJavaBoundObject::ObjectID
GinJavaBridgeDispatcherHost::AddObject(
79 const base::android::JavaRef
<jobject
>& object
,
80 const base::android::JavaRef
<jclass
>& safe_annotation_clazz
,
82 RenderFrameHost
* holder
) {
83 DCHECK(is_named
|| holder
);
84 GinJavaBoundObject::ObjectID object_id
;
85 JNIEnv
* env
= base::android::AttachCurrentThread();
86 JavaObjectWeakGlobalRef
ref(env
, object
.obj());
88 object_id
= objects_
.Add(new scoped_refptr
<GinJavaBoundObject
>(
89 GinJavaBoundObject::CreateNamed(ref
, safe_annotation_clazz
)));
91 object_id
= objects_
.Add(new scoped_refptr
<GinJavaBoundObject
>(
92 GinJavaBoundObject::CreateTransient(
93 ref
, safe_annotation_clazz
, holder
)));
97 GinJavaBoundObject::ObjectID added_object_id
;
98 DCHECK(FindObjectId(object
, &added_object_id
));
99 DCHECK_EQ(object_id
, added_object_id
);
101 #endif // DCHECK_IS_ON
102 base::android::ScopedJavaLocalRef
<jobject
> retained_object_set
=
103 retained_object_set_
.get(env
);
104 if (!retained_object_set
.is_null()) {
105 JNI_Java_HashSet_add(env
, retained_object_set
, object
);
110 bool GinJavaBridgeDispatcherHost::FindObjectId(
111 const base::android::JavaRef
<jobject
>& object
,
112 GinJavaBoundObject::ObjectID
* object_id
) {
113 JNIEnv
* env
= base::android::AttachCurrentThread();
114 for (GinJavaBoundObject::ObjectMap::iterator
it(&objects_
); !it
.IsAtEnd();
116 if (env
->IsSameObject(
118 it
.GetCurrentValue()->get()->GetLocalRef(env
).obj())) {
119 *object_id
= it
.GetCurrentKey();
126 JavaObjectWeakGlobalRef
GinJavaBridgeDispatcherHost::GetObjectWeakRef(
127 GinJavaBoundObject::ObjectID object_id
) {
128 scoped_refptr
<GinJavaBoundObject
>* result
= objects_
.Lookup(object_id
);
129 scoped_refptr
<GinJavaBoundObject
> object(result
? *result
: NULL
);
131 return object
->GetWeakRef();
133 return JavaObjectWeakGlobalRef();
136 void GinJavaBridgeDispatcherHost::RemoveHolder(
137 RenderFrameHost
* holder
,
138 const GinJavaBoundObject::ObjectMap::iterator
& from
,
140 JNIEnv
* env
= base::android::AttachCurrentThread();
141 base::android::ScopedJavaLocalRef
<jobject
> retained_object_set
=
142 retained_object_set_
.get(env
);
144 for (GinJavaBoundObject::ObjectMap::iterator
it(from
);
145 !it
.IsAtEnd() && i
< count
;
147 scoped_refptr
<GinJavaBoundObject
> object(*it
.GetCurrentValue());
148 if (object
->IsNamed())
150 object
->RemoveHolder(holder
);
151 if (!object
->HasHolders()) {
152 if (!retained_object_set
.is_null()) {
153 JNI_Java_HashSet_remove(
154 env
, retained_object_set
, object
->GetLocalRef(env
));
156 objects_
.Remove(it
.GetCurrentKey());
161 void GinJavaBridgeDispatcherHost::AddNamedObject(
162 const std::string
& name
,
163 const base::android::JavaRef
<jobject
>& object
,
164 const base::android::JavaRef
<jclass
>& safe_annotation_clazz
) {
165 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
166 GinJavaBoundObject::ObjectID object_id
;
167 NamedObjectMap::iterator iter
= named_objects_
.find(name
);
168 bool existing_object
= FindObjectId(object
, &object_id
);
169 if (existing_object
&& iter
!= named_objects_
.end() &&
170 iter
->second
== object_id
) {
174 if (iter
!= named_objects_
.end()) {
175 RemoveNamedObject(iter
->first
);
177 if (existing_object
) {
178 (*objects_
.Lookup(object_id
))->AddName();
180 object_id
= AddObject(object
, safe_annotation_clazz
, true, NULL
);
182 named_objects_
[name
] = object_id
;
184 web_contents()->SendToAllFrames(
185 new GinJavaBridgeMsg_AddNamedObject(MSG_ROUTING_NONE
, name
, object_id
));
188 void GinJavaBridgeDispatcherHost::RemoveNamedObject(
189 const std::string
& name
) {
190 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
191 NamedObjectMap::iterator iter
= named_objects_
.find(name
);
192 if (iter
== named_objects_
.end())
195 // |name| may come from |named_objects_|. Make a copy of name so that if
196 // |name| is from |named_objects_| it'll be valid after the remove below.
197 const std::string
copied_name(name
);
199 scoped_refptr
<GinJavaBoundObject
> object(*objects_
.Lookup(iter
->second
));
200 named_objects_
.erase(iter
);
201 object
->RemoveName();
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
));
216 web_contents()->SendToAllFrames(
217 new GinJavaBridgeMsg_RemoveNamedObject(MSG_ROUTING_NONE
, copied_name
));
220 void GinJavaBridgeDispatcherHost::SetAllowObjectContentsInspection(bool allow
) {
221 allow_object_contents_inspection_
= allow
;
224 void GinJavaBridgeDispatcherHost::DocumentAvailableInMainFrame() {
225 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
226 // Called when the window object has been cleared in the main frame.
227 // That means, all sub-frames have also been cleared, so only named
229 JNIEnv
* env
= base::android::AttachCurrentThread();
230 base::android::ScopedJavaLocalRef
<jobject
> retained_object_set
=
231 retained_object_set_
.get(env
);
232 if (!retained_object_set
.is_null()) {
233 JNI_Java_HashSet_clear(env
, retained_object_set
);
236 // We also need to add back the named objects we have so far as they
237 // should survive navigations.
238 for (GinJavaBoundObject::ObjectMap::iterator
it(&objects_
); !it
.IsAtEnd();
240 scoped_refptr
<GinJavaBoundObject
> object(*it
.GetCurrentValue());
241 if (object
->IsNamed()) {
242 if (!retained_object_set
.is_null()) {
243 JNI_Java_HashSet_add(
244 env
, retained_object_set
, object
->GetLocalRef(env
));
247 objects_
.Remove(it
.GetCurrentKey());
254 // TODO(mnaganov): Implement passing of a parameter into sync message handlers.
255 class MessageForwarder
: public IPC::Sender
{
257 MessageForwarder(GinJavaBridgeDispatcherHost
* gjbdh
,
258 RenderFrameHost
* render_frame_host
)
259 : gjbdh_(gjbdh
), render_frame_host_(render_frame_host
) {}
260 void OnGetMethods(GinJavaBoundObject::ObjectID object_id
,
261 IPC::Message
* reply_msg
) {
262 gjbdh_
->OnGetMethods(render_frame_host_
,
266 void OnHasMethod(GinJavaBoundObject::ObjectID object_id
,
267 const std::string
& method_name
,
268 IPC::Message
* reply_msg
) {
269 gjbdh_
->OnHasMethod(render_frame_host_
,
274 void OnInvokeMethod(GinJavaBoundObject::ObjectID object_id
,
275 const std::string
& method_name
,
276 const base::ListValue
& arguments
,
277 IPC::Message
* reply_msg
) {
278 gjbdh_
->OnInvokeMethod(render_frame_host_
,
284 virtual bool Send(IPC::Message
* msg
) OVERRIDE
{
289 GinJavaBridgeDispatcherHost
* gjbdh_
;
290 RenderFrameHost
* render_frame_host_
;
295 bool GinJavaBridgeDispatcherHost::OnMessageReceived(
296 const IPC::Message
& message
,
297 RenderFrameHost
* render_frame_host
) {
298 DCHECK(render_frame_host
);
300 MessageForwarder
forwarder(this, render_frame_host
);
301 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(GinJavaBridgeDispatcherHost
, message
,
303 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_GetMethods
,
305 MessageForwarder::OnGetMethods
)
306 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_HasMethod
,
308 MessageForwarder::OnHasMethod
)
309 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_InvokeMethod
,
311 MessageForwarder::OnInvokeMethod
)
312 IPC_MESSAGE_HANDLER(GinJavaBridgeHostMsg_ObjectWrapperDeleted
,
313 OnObjectWrapperDeleted
)
314 IPC_MESSAGE_UNHANDLED(handled
= false)
315 IPC_END_MESSAGE_MAP()
321 class IsValidRenderFrameHostHelper
322 : public base::RefCounted
<IsValidRenderFrameHostHelper
> {
324 explicit IsValidRenderFrameHostHelper(RenderFrameHost
* rfh_to_match
)
325 : rfh_to_match_(rfh_to_match
), rfh_found_(false) {}
327 bool rfh_found() { return rfh_found_
; }
329 void OnFrame(RenderFrameHost
* rfh
) {
330 if (rfh_to_match_
== rfh
) rfh_found_
= true;
334 friend class base::RefCounted
<IsValidRenderFrameHostHelper
>;
336 ~IsValidRenderFrameHostHelper() {}
338 RenderFrameHost
* rfh_to_match_
;
341 DISALLOW_COPY_AND_ASSIGN(IsValidRenderFrameHostHelper
);
346 bool GinJavaBridgeDispatcherHost::IsValidRenderFrameHost(
347 RenderFrameHost
* render_frame_host
) {
348 scoped_refptr
<IsValidRenderFrameHostHelper
> helper
=
349 new IsValidRenderFrameHostHelper(render_frame_host
);
350 web_contents()->ForEachFrame(
351 base::Bind(&IsValidRenderFrameHostHelper::OnFrame
, helper
));
352 return helper
->rfh_found();
355 void GinJavaBridgeDispatcherHost::SendReply(
356 RenderFrameHost
* render_frame_host
,
357 IPC::Message
* reply_msg
) {
358 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
359 if (IsValidRenderFrameHost(render_frame_host
)) {
360 render_frame_host
->Send(reply_msg
);
366 void GinJavaBridgeDispatcherHost::OnGetMethods(
367 RenderFrameHost
* render_frame_host
,
368 GinJavaBoundObject::ObjectID object_id
,
369 IPC::Message
* reply_msg
) {
370 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
371 DCHECK(render_frame_host
);
372 if (!allow_object_contents_inspection_
) {
373 IPC::WriteParam(reply_msg
, std::set
<std::string
>());
374 render_frame_host
->Send(reply_msg
);
377 scoped_refptr
<GinJavaBoundObject
> object(*objects_
.Lookup(object_id
));
379 LOG(ERROR
) << "WebView: Unknown object: " << object_id
;
380 IPC::WriteParam(reply_msg
, std::set
<std::string
>());
381 render_frame_host
->Send(reply_msg
);
384 base::PostTaskAndReplyWithResult(
385 g_background_thread
.Get().message_loop()->message_loop_proxy(),
387 base::Bind(&GinJavaBoundObject::GetMethodNames
, object
),
388 base::Bind(&GinJavaBridgeDispatcherHost::SendMethods
,
394 void GinJavaBridgeDispatcherHost::SendMethods(
395 RenderFrameHost
* render_frame_host
,
396 IPC::Message
* reply_msg
,
397 const std::set
<std::string
>& method_names
) {
398 IPC::WriteParam(reply_msg
, method_names
);
399 SendReply(render_frame_host
, reply_msg
);
402 void GinJavaBridgeDispatcherHost::OnHasMethod(
403 RenderFrameHost
* render_frame_host
,
404 GinJavaBoundObject::ObjectID object_id
,
405 const std::string
& method_name
,
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
));
411 LOG(ERROR
) << "WebView: Unknown object: " << object_id
;
412 IPC::WriteParam(reply_msg
, false);
413 render_frame_host
->Send(reply_msg
);
416 base::PostTaskAndReplyWithResult(
417 g_background_thread
.Get().message_loop()->message_loop_proxy(),
419 base::Bind(&GinJavaBoundObject::HasMethod
, object
, method_name
),
420 base::Bind(&GinJavaBridgeDispatcherHost::SendHasMethodReply
,
426 void GinJavaBridgeDispatcherHost::SendHasMethodReply(
427 RenderFrameHost
* render_frame_host
,
428 IPC::Message
* reply_msg
,
430 IPC::WriteParam(reply_msg
, result
);
431 SendReply(render_frame_host
, reply_msg
);
434 void GinJavaBridgeDispatcherHost::OnInvokeMethod(
435 RenderFrameHost
* render_frame_host
,
436 GinJavaBoundObject::ObjectID object_id
,
437 const std::string
& method_name
,
438 const base::ListValue
& arguments
,
439 IPC::Message
* reply_msg
) {
440 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
441 DCHECK(render_frame_host
);
442 scoped_refptr
<GinJavaBoundObject
> object(*objects_
.Lookup(object_id
));
444 LOG(ERROR
) << "WebView: Unknown object: " << object_id
;
445 base::ListValue result
;
446 result
.Append(base::Value::CreateNullValue());
447 IPC::WriteParam(reply_msg
, result
);
448 IPC::WriteParam(reply_msg
, kGinJavaBridgeUnknownObjectId
);
449 render_frame_host
->Send(reply_msg
);
452 scoped_refptr
<GinJavaMethodInvocationHelper
> result
=
453 new GinJavaMethodInvocationHelper(
454 make_scoped_ptr(new GinJavaBoundObjectDelegate(object
))
455 .PassAs
<GinJavaMethodInvocationHelper::ObjectDelegate
>(),
459 g_background_thread
.Get()
461 ->message_loop_proxy()
464 base::Bind(&GinJavaMethodInvocationHelper::Invoke
, result
),
466 &GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult
,
473 void GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult(
474 RenderFrameHost
* render_frame_host
,
475 IPC::Message
* reply_msg
,
476 scoped_refptr
<GinJavaMethodInvocationHelper
> result
) {
477 if (result
->HoldsPrimitiveResult()) {
478 IPC::WriteParam(reply_msg
, result
->GetPrimitiveResult());
479 IPC::WriteParam(reply_msg
, result
->GetInvocationError());
480 SendReply(render_frame_host
, reply_msg
);
482 ProcessMethodInvocationObjectResult(render_frame_host
, reply_msg
, result
);
486 void GinJavaBridgeDispatcherHost::ProcessMethodInvocationObjectResult(
487 RenderFrameHost
* render_frame_host
,
488 IPC::Message
* reply_msg
,
489 scoped_refptr
<GinJavaMethodInvocationHelper
> result
) {
490 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
491 if (!IsValidRenderFrameHost(render_frame_host
)) {
495 base::ListValue wrapped_result
;
496 if (!result
->GetObjectResult().is_null()) {
497 GinJavaBoundObject::ObjectID returned_object_id
;
498 if (FindObjectId(result
->GetObjectResult(), &returned_object_id
)) {
499 (*objects_
.Lookup(returned_object_id
))->AddHolder(render_frame_host
);
501 returned_object_id
= AddObject(result
->GetObjectResult(),
502 result
->GetSafeAnnotationClass(),
506 wrapped_result
.Append(
507 GinJavaBridgeValue::CreateObjectIDValue(returned_object_id
).release());
509 wrapped_result
.Append(base::Value::CreateNullValue());
511 IPC::WriteParam(reply_msg
, wrapped_result
);
512 IPC::WriteParam(reply_msg
, result
->GetInvocationError());
513 render_frame_host
->Send(reply_msg
);
516 void GinJavaBridgeDispatcherHost::OnObjectWrapperDeleted(
517 RenderFrameHost
* render_frame_host
,
518 GinJavaBoundObject::ObjectID object_id
) {
519 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
520 DCHECK(render_frame_host
);
521 if (objects_
.Lookup(object_id
)) {
522 GinJavaBoundObject::ObjectMap::iterator
iter(&objects_
);
523 while (!iter
.IsAtEnd() && iter
.GetCurrentKey() != object_id
)
525 DCHECK(!iter
.IsAtEnd());
526 RemoveHolder(render_frame_host
, iter
, 1);
530 } // namespace content