• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/android/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/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"
23 
24 #if !defined(OS_ANDROID)
25 #error "JavaBridge only supports OS_ANDROID"
26 #endif
27 
28 namespace content {
29 
30 namespace {
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 {
34  public:
JavaBridgeThread()35   JavaBridgeThread() : base::android::JavaHandlerThread("JavaBridge") {
36     Start();
37   }
~JavaBridgeThread()38   virtual ~JavaBridgeThread() {
39     Stop();
40   }
41 };
42 
43 base::LazyInstance<JavaBridgeThread> g_background_thread =
44     LAZY_INSTANCE_INITIALIZER;
45 
46 }  // namespace
47 
GinJavaBridgeDispatcherHost(WebContents * web_contents,jobject retained_object_set)48 GinJavaBridgeDispatcherHost::GinJavaBridgeDispatcherHost(
49     WebContents* web_contents,
50     jobject retained_object_set)
51     : WebContentsObserver(web_contents),
52       retained_object_set_(base::android::AttachCurrentThread(),
53                            retained_object_set),
54       allow_object_contents_inspection_(true) {
55   DCHECK(retained_object_set);
56 }
57 
~GinJavaBridgeDispatcherHost()58 GinJavaBridgeDispatcherHost::~GinJavaBridgeDispatcherHost() {
59   DCHECK(pending_replies_.empty());
60 }
61 
RenderFrameCreated(RenderFrameHost * render_frame_host)62 void GinJavaBridgeDispatcherHost::RenderFrameCreated(
63     RenderFrameHost* render_frame_host) {
64   for (NamedObjectMap::const_iterator iter = named_objects_.begin();
65        iter != named_objects_.end();
66        ++iter) {
67     render_frame_host->Send(new GinJavaBridgeMsg_AddNamedObject(
68         render_frame_host->GetRoutingID(), iter->first, iter->second));
69   }
70 }
71 
RenderFrameDeleted(RenderFrameHost * render_frame_host)72 void GinJavaBridgeDispatcherHost::RenderFrameDeleted(
73     RenderFrameHost* render_frame_host) {
74   DCHECK_CURRENTLY_ON(BrowserThread::UI);
75   IPC::Message* reply_msg = TakePendingReply(render_frame_host);
76   if (reply_msg != NULL) {
77     base::ListValue result;
78     result.Append(base::Value::CreateNullValue());
79     IPC::WriteParam(reply_msg, result);
80     IPC::WriteParam(reply_msg, kGinJavaBridgeRenderFrameDeleted);
81     render_frame_host->Send(reply_msg);
82   }
83   RemoveHolder(render_frame_host,
84                GinJavaBoundObject::ObjectMap::iterator(&objects_),
85                objects_.size());
86 }
87 
AddObject(const base::android::JavaRef<jobject> & object,const base::android::JavaRef<jclass> & safe_annotation_clazz,bool is_named,RenderFrameHost * holder)88 GinJavaBoundObject::ObjectID GinJavaBridgeDispatcherHost::AddObject(
89     const base::android::JavaRef<jobject>& object,
90     const base::android::JavaRef<jclass>& safe_annotation_clazz,
91     bool is_named,
92     RenderFrameHost* holder) {
93   DCHECK(is_named || holder);
94   GinJavaBoundObject::ObjectID object_id;
95   JNIEnv* env = base::android::AttachCurrentThread();
96   JavaObjectWeakGlobalRef ref(env, object.obj());
97   if (is_named) {
98     object_id = objects_.Add(new scoped_refptr<GinJavaBoundObject>(
99         GinJavaBoundObject::CreateNamed(ref, safe_annotation_clazz)));
100   } else {
101     object_id = objects_.Add(new scoped_refptr<GinJavaBoundObject>(
102         GinJavaBoundObject::CreateTransient(
103             ref, safe_annotation_clazz, holder)));
104   }
105 #if DCHECK_IS_ON
106   {
107     GinJavaBoundObject::ObjectID added_object_id;
108     DCHECK(FindObjectId(object, &added_object_id));
109     DCHECK_EQ(object_id, added_object_id);
110   }
111 #endif  // DCHECK_IS_ON
112   base::android::ScopedJavaLocalRef<jobject> retained_object_set =
113         retained_object_set_.get(env);
114   if (!retained_object_set.is_null()) {
115     JNI_Java_HashSet_add(env, retained_object_set, object);
116   }
117   return object_id;
118 }
119 
FindObjectId(const base::android::JavaRef<jobject> & object,GinJavaBoundObject::ObjectID * object_id)120 bool GinJavaBridgeDispatcherHost::FindObjectId(
121     const base::android::JavaRef<jobject>& object,
122     GinJavaBoundObject::ObjectID* object_id) {
123   JNIEnv* env = base::android::AttachCurrentThread();
124   for (GinJavaBoundObject::ObjectMap::iterator it(&objects_); !it.IsAtEnd();
125        it.Advance()) {
126     if (env->IsSameObject(
127             object.obj(),
128             it.GetCurrentValue()->get()->GetLocalRef(env).obj())) {
129       *object_id = it.GetCurrentKey();
130       return true;
131     }
132   }
133   return false;
134 }
135 
GetObjectWeakRef(GinJavaBoundObject::ObjectID object_id)136 JavaObjectWeakGlobalRef GinJavaBridgeDispatcherHost::GetObjectWeakRef(
137     GinJavaBoundObject::ObjectID object_id) {
138   scoped_refptr<GinJavaBoundObject>* result = objects_.Lookup(object_id);
139   scoped_refptr<GinJavaBoundObject> object(result ? *result : NULL);
140   if (object.get())
141     return object->GetWeakRef();
142   else
143     return JavaObjectWeakGlobalRef();
144 }
145 
RemoveHolder(RenderFrameHost * holder,const GinJavaBoundObject::ObjectMap::iterator & from,size_t count)146 void GinJavaBridgeDispatcherHost::RemoveHolder(
147     RenderFrameHost* holder,
148     const GinJavaBoundObject::ObjectMap::iterator& from,
149     size_t count) {
150   JNIEnv* env = base::android::AttachCurrentThread();
151   base::android::ScopedJavaLocalRef<jobject> retained_object_set =
152       retained_object_set_.get(env);
153   size_t i = 0;
154   for (GinJavaBoundObject::ObjectMap::iterator it(from);
155        !it.IsAtEnd() && i < count;
156        it.Advance(), ++i) {
157     scoped_refptr<GinJavaBoundObject> object(*it.GetCurrentValue());
158     if (object->IsNamed())
159       continue;
160     object->RemoveHolder(holder);
161     if (!object->HasHolders()) {
162       if (!retained_object_set.is_null()) {
163         JNI_Java_HashSet_remove(
164             env, retained_object_set, object->GetLocalRef(env));
165       }
166       objects_.Remove(it.GetCurrentKey());
167     }
168   }
169 }
170 
AddNamedObject(const std::string & name,const base::android::JavaRef<jobject> & object,const base::android::JavaRef<jclass> & safe_annotation_clazz)171 void GinJavaBridgeDispatcherHost::AddNamedObject(
172     const std::string& name,
173     const base::android::JavaRef<jobject>& object,
174     const base::android::JavaRef<jclass>& safe_annotation_clazz) {
175   DCHECK_CURRENTLY_ON(BrowserThread::UI);
176   GinJavaBoundObject::ObjectID object_id;
177   NamedObjectMap::iterator iter = named_objects_.find(name);
178   bool existing_object = FindObjectId(object, &object_id);
179   if (existing_object && iter != named_objects_.end() &&
180       iter->second == object_id) {
181     // Nothing to do.
182     return;
183   }
184   if (iter != named_objects_.end()) {
185     RemoveNamedObject(iter->first);
186   }
187   if (existing_object) {
188     (*objects_.Lookup(object_id))->AddName();
189   } else {
190     object_id = AddObject(object, safe_annotation_clazz, true, NULL);
191   }
192   named_objects_[name] = object_id;
193 
194   web_contents()->SendToAllFrames(
195       new GinJavaBridgeMsg_AddNamedObject(MSG_ROUTING_NONE, name, object_id));
196 }
197 
RemoveNamedObject(const std::string & name)198 void GinJavaBridgeDispatcherHost::RemoveNamedObject(
199     const std::string& name) {
200   DCHECK_CURRENTLY_ON(BrowserThread::UI);
201   NamedObjectMap::iterator iter = named_objects_.find(name);
202   if (iter == named_objects_.end())
203     return;
204 
205   // |name| may come from |named_objects_|. Make a copy of name so that if
206   // |name| is from |named_objects_| it'll be valid after the remove below.
207   const std::string copied_name(name);
208 
209   scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(iter->second));
210   named_objects_.erase(iter);
211   object->RemoveName();
212 
213   // Not erasing from the objects map, as we can still receive method
214   // invocation requests for this object, and they should work until the
215   // java object is gone.
216   if (!object->IsNamed()) {
217     JNIEnv* env = base::android::AttachCurrentThread();
218     base::android::ScopedJavaLocalRef<jobject> retained_object_set =
219         retained_object_set_.get(env);
220     if (!retained_object_set.is_null()) {
221       JNI_Java_HashSet_remove(
222           env, retained_object_set, object->GetLocalRef(env));
223     }
224   }
225 
226   web_contents()->SendToAllFrames(
227       new GinJavaBridgeMsg_RemoveNamedObject(MSG_ROUTING_NONE, copied_name));
228 }
229 
SetAllowObjectContentsInspection(bool allow)230 void GinJavaBridgeDispatcherHost::SetAllowObjectContentsInspection(bool allow) {
231   allow_object_contents_inspection_ = allow;
232 }
233 
DocumentAvailableInMainFrame()234 void GinJavaBridgeDispatcherHost::DocumentAvailableInMainFrame() {
235   DCHECK_CURRENTLY_ON(BrowserThread::UI);
236   // Called when the window object has been cleared in the main frame.
237   // That means, all sub-frames have also been cleared, so only named
238   // objects survived.
239   JNIEnv* env = base::android::AttachCurrentThread();
240   base::android::ScopedJavaLocalRef<jobject> retained_object_set =
241       retained_object_set_.get(env);
242   if (!retained_object_set.is_null()) {
243     JNI_Java_HashSet_clear(env, retained_object_set);
244   }
245 
246   // We also need to add back the named objects we have so far as they
247   // should survive navigations.
248   for (GinJavaBoundObject::ObjectMap::iterator it(&objects_); !it.IsAtEnd();
249        it.Advance()) {
250     scoped_refptr<GinJavaBoundObject> object(*it.GetCurrentValue());
251     if (object->IsNamed()) {
252       if (!retained_object_set.is_null()) {
253         JNI_Java_HashSet_add(
254             env, retained_object_set, object->GetLocalRef(env));
255       }
256     } else {
257       objects_.Remove(it.GetCurrentKey());
258     }
259   }
260 }
261 
262 namespace {
263 
264 // TODO(mnaganov): Implement passing of a parameter into sync message handlers.
265 class MessageForwarder : public IPC::Sender {
266  public:
MessageForwarder(GinJavaBridgeDispatcherHost * gjbdh,RenderFrameHost * render_frame_host)267   MessageForwarder(GinJavaBridgeDispatcherHost* gjbdh,
268                    RenderFrameHost* render_frame_host)
269       : gjbdh_(gjbdh), render_frame_host_(render_frame_host) {}
OnGetMethods(GinJavaBoundObject::ObjectID object_id,IPC::Message * reply_msg)270   void OnGetMethods(GinJavaBoundObject::ObjectID object_id,
271                     IPC::Message* reply_msg) {
272     gjbdh_->OnGetMethods(render_frame_host_,
273                          object_id,
274                          reply_msg);
275   }
OnHasMethod(GinJavaBoundObject::ObjectID object_id,const std::string & method_name,IPC::Message * reply_msg)276   void OnHasMethod(GinJavaBoundObject::ObjectID object_id,
277                    const std::string& method_name,
278                    IPC::Message* reply_msg) {
279     gjbdh_->OnHasMethod(render_frame_host_,
280                         object_id,
281                         method_name,
282                         reply_msg);
283   }
OnInvokeMethod(GinJavaBoundObject::ObjectID object_id,const std::string & method_name,const base::ListValue & arguments,IPC::Message * reply_msg)284   void OnInvokeMethod(GinJavaBoundObject::ObjectID object_id,
285                       const std::string& method_name,
286                       const base::ListValue& arguments,
287                       IPC::Message* reply_msg) {
288     gjbdh_->OnInvokeMethod(render_frame_host_,
289                            object_id,
290                            method_name,
291                            arguments,
292                            reply_msg);
293   }
Send(IPC::Message * msg)294   virtual bool Send(IPC::Message* msg) OVERRIDE {
295     NOTREACHED();
296     return false;
297   }
298  private:
299   GinJavaBridgeDispatcherHost* gjbdh_;
300   RenderFrameHost* render_frame_host_;
301 };
302 
303 }
304 
OnMessageReceived(const IPC::Message & message,RenderFrameHost * render_frame_host)305 bool GinJavaBridgeDispatcherHost::OnMessageReceived(
306     const IPC::Message& message,
307     RenderFrameHost* render_frame_host) {
308   DCHECK(render_frame_host);
309   bool handled = true;
310   MessageForwarder forwarder(this, render_frame_host);
311   IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(GinJavaBridgeDispatcherHost, message,
312                                    render_frame_host)
313     IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_GetMethods,
314                                     &forwarder,
315                                     MessageForwarder::OnGetMethods)
316     IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_HasMethod,
317                                     &forwarder,
318                                     MessageForwarder::OnHasMethod)
319     IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_InvokeMethod,
320                                     &forwarder,
321                                     MessageForwarder::OnInvokeMethod)
322     IPC_MESSAGE_HANDLER(GinJavaBridgeHostMsg_ObjectWrapperDeleted,
323                         OnObjectWrapperDeleted)
324     IPC_MESSAGE_UNHANDLED(handled = false)
325   IPC_END_MESSAGE_MAP()
326   return handled;
327 }
328 
329 namespace {
330 
331 class IsValidRenderFrameHostHelper
332     : public base::RefCounted<IsValidRenderFrameHostHelper> {
333  public:
IsValidRenderFrameHostHelper(RenderFrameHost * rfh_to_match)334   explicit IsValidRenderFrameHostHelper(RenderFrameHost* rfh_to_match)
335       : rfh_to_match_(rfh_to_match), rfh_found_(false) {}
336 
rfh_found()337   bool rfh_found() { return rfh_found_; }
338 
OnFrame(RenderFrameHost * rfh)339   void OnFrame(RenderFrameHost* rfh) {
340     if (rfh_to_match_ == rfh) rfh_found_ = true;
341   }
342 
343  private:
344   friend class base::RefCounted<IsValidRenderFrameHostHelper>;
345 
~IsValidRenderFrameHostHelper()346   ~IsValidRenderFrameHostHelper() {}
347 
348   RenderFrameHost* rfh_to_match_;
349   bool rfh_found_;
350 
351   DISALLOW_COPY_AND_ASSIGN(IsValidRenderFrameHostHelper);
352 };
353 
354 }  // namespace
355 
IsValidRenderFrameHost(RenderFrameHost * render_frame_host)356 bool GinJavaBridgeDispatcherHost::IsValidRenderFrameHost(
357     RenderFrameHost* render_frame_host) {
358   scoped_refptr<IsValidRenderFrameHostHelper> helper =
359       new IsValidRenderFrameHostHelper(render_frame_host);
360   web_contents()->ForEachFrame(
361       base::Bind(&IsValidRenderFrameHostHelper::OnFrame, helper));
362   return helper->rfh_found();
363 }
364 
OnGetMethods(RenderFrameHost * render_frame_host,GinJavaBoundObject::ObjectID object_id,IPC::Message * reply_msg)365 void GinJavaBridgeDispatcherHost::OnGetMethods(
366     RenderFrameHost* render_frame_host,
367     GinJavaBoundObject::ObjectID object_id,
368     IPC::Message* reply_msg) {
369   DCHECK_CURRENTLY_ON(BrowserThread::UI);
370   DCHECK(render_frame_host);
371   if (!allow_object_contents_inspection_) {
372     IPC::WriteParam(reply_msg, std::set<std::string>());
373     render_frame_host->Send(reply_msg);
374     return;
375   }
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, std::set<std::string>());
380     render_frame_host->Send(reply_msg);
381     return;
382   }
383   DCHECK(!HasPendingReply(render_frame_host));
384   pending_replies_[render_frame_host] = reply_msg;
385   base::PostTaskAndReplyWithResult(
386       g_background_thread.Get().message_loop()->message_loop_proxy(),
387       FROM_HERE,
388       base::Bind(&GinJavaBoundObject::GetMethodNames, object),
389       base::Bind(&GinJavaBridgeDispatcherHost::SendMethods,
390                  AsWeakPtr(),
391                  render_frame_host));
392 }
393 
SendMethods(RenderFrameHost * render_frame_host,const std::set<std::string> & method_names)394 void GinJavaBridgeDispatcherHost::SendMethods(
395     RenderFrameHost* render_frame_host,
396     const std::set<std::string>& method_names) {
397   IPC::Message* reply_msg = TakePendingReply(render_frame_host);
398   if (!reply_msg) {
399     return;
400   }
401   IPC::WriteParam(reply_msg, method_names);
402   render_frame_host->Send(reply_msg);
403 }
404 
OnHasMethod(RenderFrameHost * render_frame_host,GinJavaBoundObject::ObjectID object_id,const std::string & method_name,IPC::Message * reply_msg)405 void GinJavaBridgeDispatcherHost::OnHasMethod(
406     RenderFrameHost* render_frame_host,
407     GinJavaBoundObject::ObjectID object_id,
408     const std::string& method_name,
409     IPC::Message* reply_msg) {
410   DCHECK_CURRENTLY_ON(BrowserThread::UI);
411   DCHECK(render_frame_host);
412   scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id));
413   if (!object) {
414     LOG(ERROR) << "WebView: Unknown object: " << object_id;
415     IPC::WriteParam(reply_msg, false);
416     render_frame_host->Send(reply_msg);
417     return;
418   }
419   DCHECK(!HasPendingReply(render_frame_host));
420   pending_replies_[render_frame_host] = reply_msg;
421   base::PostTaskAndReplyWithResult(
422       g_background_thread.Get().message_loop()->message_loop_proxy(),
423       FROM_HERE,
424       base::Bind(&GinJavaBoundObject::HasMethod, object, method_name),
425       base::Bind(&GinJavaBridgeDispatcherHost::SendHasMethodReply,
426                  AsWeakPtr(),
427                  render_frame_host));
428 }
429 
SendHasMethodReply(RenderFrameHost * render_frame_host,bool result)430 void GinJavaBridgeDispatcherHost::SendHasMethodReply(
431     RenderFrameHost* render_frame_host,
432     bool result) {
433   IPC::Message* reply_msg = TakePendingReply(render_frame_host);
434   if (!reply_msg) {
435     return;
436   }
437   IPC::WriteParam(reply_msg, result);
438   render_frame_host->Send(reply_msg);
439 }
440 
OnInvokeMethod(RenderFrameHost * render_frame_host,GinJavaBoundObject::ObjectID object_id,const std::string & method_name,const base::ListValue & arguments,IPC::Message * reply_msg)441 void GinJavaBridgeDispatcherHost::OnInvokeMethod(
442     RenderFrameHost* render_frame_host,
443     GinJavaBoundObject::ObjectID object_id,
444     const std::string& method_name,
445     const base::ListValue& arguments,
446     IPC::Message* reply_msg) {
447   DCHECK_CURRENTLY_ON(BrowserThread::UI);
448   DCHECK(render_frame_host);
449   scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id));
450   if (!object) {
451     LOG(ERROR) << "WebView: Unknown object: " << object_id;
452     base::ListValue result;
453     result.Append(base::Value::CreateNullValue());
454     IPC::WriteParam(reply_msg, result);
455     IPC::WriteParam(reply_msg, kGinJavaBridgeUnknownObjectId);
456     render_frame_host->Send(reply_msg);
457     return;
458   }
459   DCHECK(!HasPendingReply(render_frame_host));
460   pending_replies_[render_frame_host] = reply_msg;
461   scoped_refptr<GinJavaMethodInvocationHelper> result =
462       new GinJavaMethodInvocationHelper(
463           make_scoped_ptr(new GinJavaBoundObjectDelegate(object))
464               .PassAs<GinJavaMethodInvocationHelper::ObjectDelegate>(),
465           method_name,
466           arguments);
467   result->Init(this);
468   g_background_thread.Get()
469       .message_loop()
470       ->message_loop_proxy()
471       ->PostTaskAndReply(
472           FROM_HERE,
473           base::Bind(&GinJavaMethodInvocationHelper::Invoke, result),
474           base::Bind(
475               &GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult,
476               AsWeakPtr(),
477               render_frame_host,
478               result));
479 }
480 
ProcessMethodInvocationResult(RenderFrameHost * render_frame_host,scoped_refptr<GinJavaMethodInvocationHelper> result)481 void GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult(
482     RenderFrameHost* render_frame_host,
483     scoped_refptr<GinJavaMethodInvocationHelper> result) {
484   if (result->HoldsPrimitiveResult()) {
485     IPC::Message* reply_msg = TakePendingReply(render_frame_host);
486     if (!reply_msg) {
487       return;
488     }
489     IPC::WriteParam(reply_msg, result->GetPrimitiveResult());
490     IPC::WriteParam(reply_msg, result->GetInvocationError());
491     render_frame_host->Send(reply_msg);
492   } else {
493     ProcessMethodInvocationObjectResult(render_frame_host, result);
494   }
495 }
496 
ProcessMethodInvocationObjectResult(RenderFrameHost * render_frame_host,scoped_refptr<GinJavaMethodInvocationHelper> result)497 void GinJavaBridgeDispatcherHost::ProcessMethodInvocationObjectResult(
498     RenderFrameHost* render_frame_host,
499     scoped_refptr<GinJavaMethodInvocationHelper> result) {
500   DCHECK_CURRENTLY_ON(BrowserThread::UI);
501 
502   if (!IsValidRenderFrameHost(render_frame_host)) {
503     // In this case, we must've already sent the reply when the render frame
504     // was destroyed.
505     DCHECK(!HasPendingReply(render_frame_host));
506     return;
507   }
508 
509   base::ListValue wrapped_result;
510   if (!result->GetObjectResult().is_null()) {
511     GinJavaBoundObject::ObjectID returned_object_id;
512     if (FindObjectId(result->GetObjectResult(), &returned_object_id)) {
513       (*objects_.Lookup(returned_object_id))->AddHolder(render_frame_host);
514     } else {
515       returned_object_id = AddObject(result->GetObjectResult(),
516                                      result->GetSafeAnnotationClass(),
517                                      false,
518                                      render_frame_host);
519     }
520     wrapped_result.Append(
521         GinJavaBridgeValue::CreateObjectIDValue(
522             returned_object_id).release());
523   } else {
524     wrapped_result.Append(base::Value::CreateNullValue());
525   }
526   IPC::Message* reply_msg = TakePendingReply(render_frame_host);
527   if (!reply_msg) {
528     return;
529   }
530   IPC::WriteParam(reply_msg, wrapped_result);
531   IPC::WriteParam(reply_msg, result->GetInvocationError());
532   render_frame_host->Send(reply_msg);
533 }
534 
OnObjectWrapperDeleted(RenderFrameHost * render_frame_host,GinJavaBoundObject::ObjectID object_id)535 void GinJavaBridgeDispatcherHost::OnObjectWrapperDeleted(
536     RenderFrameHost* render_frame_host,
537     GinJavaBoundObject::ObjectID object_id) {
538   DCHECK_CURRENTLY_ON(BrowserThread::UI);
539   DCHECK(render_frame_host);
540   if (objects_.Lookup(object_id)) {
541     GinJavaBoundObject::ObjectMap::iterator iter(&objects_);
542     while (!iter.IsAtEnd() && iter.GetCurrentKey() != object_id)
543       iter.Advance();
544     DCHECK(!iter.IsAtEnd());
545     RemoveHolder(render_frame_host, iter, 1);
546   }
547 }
548 
TakePendingReply(RenderFrameHost * render_frame_host)549 IPC::Message* GinJavaBridgeDispatcherHost::TakePendingReply(
550     RenderFrameHost* render_frame_host) {
551   if (!IsValidRenderFrameHost(render_frame_host)) {
552     DCHECK(!HasPendingReply(render_frame_host));
553     return NULL;
554   }
555 
556   PendingReplyMap::iterator it = pending_replies_.find(render_frame_host);
557   // There may be no pending reply if we're called from RenderFrameDeleted and
558   // we already sent the reply through the regular route.
559   if (it == pending_replies_.end()) {
560     return NULL;
561   }
562 
563   IPC::Message* reply_msg = it->second;
564   pending_replies_.erase(it);
565   return reply_msg;
566 }
567 
HasPendingReply(RenderFrameHost * render_frame_host) const568 bool GinJavaBridgeDispatcherHost::HasPendingReply(
569     RenderFrameHost* render_frame_host) const {
570   return pending_replies_.find(render_frame_host) != pending_replies_.end();
571 }
572 
573 }  // namespace content
574