| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/android/java/gin_java_bridge_dispatcher_host.h" | 5 #include "content/browser/android/java/gin_java_bridge_dispatcher_host.h" |
| 6 | 6 |
| 7 #include "base/android/java_handler_thread.h" | 7 #include "base/android/java_handler_thread.h" |
| 8 #include "base/android/jni_android.h" | 8 #include "base/android/jni_android.h" |
| 9 #include "base/android/scoped_java_ref.h" | 9 #include "base/android/scoped_java_ref.h" |
| 10 #include "base/atomic_sequence_num.h" |
| 10 #include "base/lazy_instance.h" | 11 #include "base/lazy_instance.h" |
| 12 #include "base/pickle.h" |
| 11 #include "base/strings/string_number_conversions.h" | 13 #include "base/strings/string_number_conversions.h" |
| 12 #include "base/strings/utf_string_conversions.h" | 14 #include "base/strings/utf_string_conversions.h" |
| 13 #include "base/task_runner_util.h" | 15 #include "base/task_runner_util.h" |
| 14 #include "content/browser/android/java/gin_java_bound_object_delegate.h" | 16 #include "content/browser/android/java/gin_java_bound_object_delegate.h" |
| 15 #include "content/browser/android/java/jni_helper.h" | 17 #include "content/browser/android/java/jni_helper.h" |
| 16 #include "content/common/android/gin_java_bridge_value.h" | 18 #include "content/common/android/gin_java_bridge_value.h" |
| 17 #include "content/common/android/hash_set.h" | 19 #include "content/common/android/hash_set.h" |
| 18 #include "content/common/gin_java_bridge_messages.h" | 20 #include "content/common/gin_java_bridge_messages.h" |
| 19 #include "content/public/browser/browser_thread.h" | 21 #include "content/public/browser/browser_thread.h" |
| 20 #include "content/public/browser/render_frame_host.h" | 22 #include "content/public/browser/render_frame_host.h" |
| 23 #include "content/public/browser/render_process_host.h" |
| 21 #include "content/public/browser/web_contents.h" | 24 #include "content/public/browser/web_contents.h" |
| 22 #include "ipc/ipc_message_utils.h" | 25 #include "ipc/ipc_message_utils.h" |
| 23 | 26 |
| 24 #if !defined(OS_ANDROID) | 27 #if !defined(OS_ANDROID) |
| 25 #error "JavaBridge only supports OS_ANDROID" | 28 #error "JavaBridge only supports OS_ANDROID" |
| 26 #endif | 29 #endif |
| 27 | 30 |
| 28 namespace content { | 31 namespace content { |
| 29 | 32 |
| 30 namespace { | 33 namespace { |
| 31 // The JavaBridge needs to use a Java thread so the callback | 34 // The JavaBridge needs to use a Java thread so the callback |
| 32 // will happen on a thread with a prepared Looper. | 35 // will happen on a thread with a prepared Looper. |
| 33 class JavaBridgeThread : public base::android::JavaHandlerThread { | 36 class JavaBridgeThread : public base::android::JavaHandlerThread { |
| 34 public: | 37 public: |
| 35 JavaBridgeThread() : base::android::JavaHandlerThread("JavaBridge") { | 38 JavaBridgeThread() : base::android::JavaHandlerThread("JavaBridge") { |
| 36 Start(); | 39 Start(); |
| 37 } | 40 } |
| 38 virtual ~JavaBridgeThread() { | 41 virtual ~JavaBridgeThread() { |
| 39 Stop(); | 42 Stop(); |
| 40 } | 43 } |
| 44 static bool CurrentlyOn(); |
| 41 }; | 45 }; |
| 42 | 46 |
| 43 base::LazyInstance<JavaBridgeThread> g_background_thread = | 47 base::LazyInstance<JavaBridgeThread> g_background_thread = |
| 44 LAZY_INSTANCE_INITIALIZER; | 48 LAZY_INSTANCE_INITIALIZER; |
| 45 | 49 |
| 50 // static |
| 51 bool JavaBridgeThread::CurrentlyOn() { |
| 52 return base::MessageLoop::current() == |
| 53 g_background_thread.Get().message_loop(); |
| 54 } |
| 55 |
| 56 // Object IDs are globally unique, so we can figure out the right |
| 57 // GinJavaBridgeDispatcherHost when dispatching messages on the background |
| 58 // thread. |
| 59 base::StaticAtomicSequenceNumber g_next_object_id; |
| 60 |
| 46 } // namespace | 61 } // namespace |
| 47 | 62 |
| 48 GinJavaBridgeDispatcherHost::GinJavaBridgeDispatcherHost( | 63 GinJavaBridgeDispatcherHost::GinJavaBridgeDispatcherHost( |
| 49 WebContents* web_contents, | 64 WebContents* web_contents, |
| 50 jobject retained_object_set) | 65 jobject retained_object_set) |
| 51 : WebContentsObserver(web_contents), | 66 : WebContentsObserver(web_contents), |
| 67 BrowserMessageFilter(GinJavaBridgeMsgStart), |
| 68 browser_filter_added_(false), |
| 52 retained_object_set_(base::android::AttachCurrentThread(), | 69 retained_object_set_(base::android::AttachCurrentThread(), |
| 53 retained_object_set), | 70 retained_object_set), |
| 54 allow_object_contents_inspection_(true) { | 71 allow_object_contents_inspection_(true), |
| 72 current_routing_id_(MSG_ROUTING_NONE) { |
| 55 DCHECK(retained_object_set); | 73 DCHECK(retained_object_set); |
| 56 } | 74 } |
| 57 | 75 |
| 58 GinJavaBridgeDispatcherHost::~GinJavaBridgeDispatcherHost() { | 76 GinJavaBridgeDispatcherHost::~GinJavaBridgeDispatcherHost() { |
| 59 DCHECK(pending_replies_.empty()); | 77 } |
| 78 |
| 79 void GinJavaBridgeDispatcherHost::TryAddingBrowserFilter() { |
| 80 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 81 // GinJavaBridgeDispatcherHost gets created earlier than RenderProcessHost |
| 82 // is initalized. So we postpone installing the message filter until we know |
| 83 // that the RPH is in a good shape. |
| 84 // Transient objects can only appear after named objects were added. Thus, |
| 85 // we can wait until we have one, to avoid installing unnecessary filters. |
| 86 if (!browser_filter_added_ && |
| 87 web_contents()->GetRenderProcessHost()->GetChannel() && |
| 88 !named_objects_.empty()) { |
| 89 web_contents()->GetRenderProcessHost()->AddFilter(this); |
| 90 browser_filter_added_ = true; |
| 91 } |
| 60 } | 92 } |
| 61 | 93 |
| 62 void GinJavaBridgeDispatcherHost::RenderFrameCreated( | 94 void GinJavaBridgeDispatcherHost::RenderFrameCreated( |
| 63 RenderFrameHost* render_frame_host) { | 95 RenderFrameHost* render_frame_host) { |
| 96 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 97 TryAddingBrowserFilter(); |
| 64 for (NamedObjectMap::const_iterator iter = named_objects_.begin(); | 98 for (NamedObjectMap::const_iterator iter = named_objects_.begin(); |
| 65 iter != named_objects_.end(); | 99 iter != named_objects_.end(); |
| 66 ++iter) { | 100 ++iter) { |
| 67 render_frame_host->Send(new GinJavaBridgeMsg_AddNamedObject( | 101 render_frame_host->Send(new GinJavaBridgeMsg_AddNamedObject( |
| 68 render_frame_host->GetRoutingID(), iter->first, iter->second)); | 102 render_frame_host->GetRoutingID(), iter->first, iter->second)); |
| 69 } | 103 } |
| 70 } | 104 } |
| 71 | 105 |
| 72 void GinJavaBridgeDispatcherHost::RenderFrameDeleted( | 106 void GinJavaBridgeDispatcherHost::RenderFrameDeleted( |
| 73 RenderFrameHost* render_frame_host) { | 107 RenderFrameHost* render_frame_host) { |
| 74 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 108 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 75 IPC::Message* reply_msg = TakePendingReply(render_frame_host); | 109 TryAddingBrowserFilter(); |
| 76 if (reply_msg != NULL) { | 110 base::AutoLock locker(objects_lock_); |
| 77 base::ListValue result; | 111 auto iter = objects_.begin(); |
| 78 result.Append(base::Value::CreateNullValue()); | 112 while (iter != objects_.end()) { |
| 79 IPC::WriteParam(reply_msg, result); | 113 JavaObjectWeakGlobalRef ref = |
| 80 IPC::WriteParam(reply_msg, kGinJavaBridgeRenderFrameDeleted); | 114 RemoveHolderAndAdvanceLocked(render_frame_host->GetRoutingID(), &iter); |
| 81 render_frame_host->Send(reply_msg); | 115 if (!ref.is_empty()) { |
| 116 RemoveFromRetainedObjectSetLocked(ref); |
| 117 } |
| 82 } | 118 } |
| 83 RemoveHolder(render_frame_host, | |
| 84 GinJavaBoundObject::ObjectMap::iterator(&objects_), | |
| 85 objects_.size()); | |
| 86 } | 119 } |
| 87 | 120 |
| 88 GinJavaBoundObject::ObjectID GinJavaBridgeDispatcherHost::AddObject( | 121 GinJavaBoundObject::ObjectID GinJavaBridgeDispatcherHost::AddObject( |
| 89 const base::android::JavaRef<jobject>& object, | 122 const base::android::JavaRef<jobject>& object, |
| 90 const base::android::JavaRef<jclass>& safe_annotation_clazz, | 123 const base::android::JavaRef<jclass>& safe_annotation_clazz, |
| 91 bool is_named, | 124 bool is_named, |
| 92 RenderFrameHost* holder) { | 125 int32 holder) { |
| 126 // Can be called on any thread. Calls come from the UI thread via |
| 127 // AddNamedObject, and from the background thread, when injected Java |
| 128 // object's method returns a Java object. |
| 93 DCHECK(is_named || holder); | 129 DCHECK(is_named || holder); |
| 94 GinJavaBoundObject::ObjectID object_id; | |
| 95 JNIEnv* env = base::android::AttachCurrentThread(); | 130 JNIEnv* env = base::android::AttachCurrentThread(); |
| 96 JavaObjectWeakGlobalRef ref(env, object.obj()); | 131 JavaObjectWeakGlobalRef ref(env, object.obj()); |
| 97 if (is_named) { | 132 scoped_refptr<GinJavaBoundObject> new_object = |
| 98 object_id = objects_.Add(new scoped_refptr<GinJavaBoundObject>( | 133 is_named ? GinJavaBoundObject::CreateNamed(ref, safe_annotation_clazz) |
| 99 GinJavaBoundObject::CreateNamed(ref, safe_annotation_clazz))); | 134 : GinJavaBoundObject::CreateTransient(ref, safe_annotation_clazz, |
| 100 } else { | 135 holder); |
| 101 object_id = objects_.Add(new scoped_refptr<GinJavaBoundObject>( | 136 // Note that we are abusing the fact that StaticAtomicSequenceNumber |
| 102 GinJavaBoundObject::CreateTransient( | 137 // uses Atomic32 as a counter, so it is guaranteed that it will not |
| 103 ref, safe_annotation_clazz, holder))); | 138 // overflow our int32 IDs. IDs start from 1. |
| 139 GinJavaBoundObject::ObjectID object_id = g_next_object_id.GetNext() + 1; |
| 140 { |
| 141 base::AutoLock locker(objects_lock_); |
| 142 objects_[object_id] = new_object; |
| 104 } | 143 } |
| 105 #if DCHECK_IS_ON | 144 #if DCHECK_IS_ON |
| 106 { | 145 { |
| 107 GinJavaBoundObject::ObjectID added_object_id; | 146 GinJavaBoundObject::ObjectID added_object_id; |
| 108 DCHECK(FindObjectId(object, &added_object_id)); | 147 DCHECK(FindObjectId(object, &added_object_id)); |
| 109 DCHECK_EQ(object_id, added_object_id); | 148 DCHECK_EQ(object_id, added_object_id); |
| 110 } | 149 } |
| 111 #endif // DCHECK_IS_ON | 150 #endif // DCHECK_IS_ON |
| 112 base::android::ScopedJavaLocalRef<jobject> retained_object_set = | 151 base::android::ScopedJavaLocalRef<jobject> retained_object_set = |
| 113 retained_object_set_.get(env); | 152 retained_object_set_.get(env); |
| 114 if (!retained_object_set.is_null()) { | 153 if (!retained_object_set.is_null()) { |
| 154 base::AutoLock locker(objects_lock_); |
| 115 JNI_Java_HashSet_add(env, retained_object_set, object); | 155 JNI_Java_HashSet_add(env, retained_object_set, object); |
| 116 } | 156 } |
| 117 return object_id; | 157 return object_id; |
| 118 } | 158 } |
| 119 | 159 |
| 120 bool GinJavaBridgeDispatcherHost::FindObjectId( | 160 bool GinJavaBridgeDispatcherHost::FindObjectId( |
| 121 const base::android::JavaRef<jobject>& object, | 161 const base::android::JavaRef<jobject>& object, |
| 122 GinJavaBoundObject::ObjectID* object_id) { | 162 GinJavaBoundObject::ObjectID* object_id) { |
| 163 // Can be called on any thread. |
| 123 JNIEnv* env = base::android::AttachCurrentThread(); | 164 JNIEnv* env = base::android::AttachCurrentThread(); |
| 124 for (GinJavaBoundObject::ObjectMap::iterator it(&objects_); !it.IsAtEnd(); | 165 base::AutoLock locker(objects_lock_); |
| 125 it.Advance()) { | 166 for (auto pair : objects_) { |
| 126 if (env->IsSameObject( | 167 if (env->IsSameObject( |
| 127 object.obj(), | 168 object.obj(), |
| 128 it.GetCurrentValue()->get()->GetLocalRef(env).obj())) { | 169 pair.second->GetLocalRef(env).obj())) { |
| 129 *object_id = it.GetCurrentKey(); | 170 *object_id = pair.first; |
| 130 return true; | 171 return true; |
| 131 } | 172 } |
| 132 } | 173 } |
| 133 return false; | 174 return false; |
| 134 } | 175 } |
| 135 | 176 |
| 136 JavaObjectWeakGlobalRef GinJavaBridgeDispatcherHost::GetObjectWeakRef( | 177 JavaObjectWeakGlobalRef GinJavaBridgeDispatcherHost::GetObjectWeakRef( |
| 137 GinJavaBoundObject::ObjectID object_id) { | 178 GinJavaBoundObject::ObjectID object_id) { |
| 138 scoped_refptr<GinJavaBoundObject>* result = objects_.Lookup(object_id); | 179 scoped_refptr<GinJavaBoundObject> object = FindObject(object_id); |
| 139 scoped_refptr<GinJavaBoundObject> object(result ? *result : NULL); | |
| 140 if (object.get()) | 180 if (object.get()) |
| 141 return object->GetWeakRef(); | 181 return object->GetWeakRef(); |
| 142 else | 182 else |
| 143 return JavaObjectWeakGlobalRef(); | 183 return JavaObjectWeakGlobalRef(); |
| 144 } | 184 } |
| 145 | 185 |
| 146 void GinJavaBridgeDispatcherHost::RemoveHolder( | 186 JavaObjectWeakGlobalRef |
| 147 RenderFrameHost* holder, | 187 GinJavaBridgeDispatcherHost::RemoveHolderAndAdvanceLocked( |
| 148 const GinJavaBoundObject::ObjectMap::iterator& from, | 188 int32 holder, |
| 149 size_t count) { | 189 ObjectMap::iterator* iter_ptr) { |
| 190 objects_lock_.AssertAcquired(); |
| 191 JavaObjectWeakGlobalRef result; |
| 192 scoped_refptr<GinJavaBoundObject> object((*iter_ptr)->second); |
| 193 if (!object->IsNamed()) { |
| 194 object->RemoveHolder(holder); |
| 195 if (!object->HasHolders()) { |
| 196 result = object->GetWeakRef(); |
| 197 objects_.erase((*iter_ptr)++); |
| 198 } |
| 199 } else { |
| 200 ++(*iter_ptr); |
| 201 } |
| 202 return result; |
| 203 } |
| 204 |
| 205 void GinJavaBridgeDispatcherHost::RemoveFromRetainedObjectSetLocked( |
| 206 const JavaObjectWeakGlobalRef& ref) { |
| 207 objects_lock_.AssertAcquired(); |
| 150 JNIEnv* env = base::android::AttachCurrentThread(); | 208 JNIEnv* env = base::android::AttachCurrentThread(); |
| 151 base::android::ScopedJavaLocalRef<jobject> retained_object_set = | 209 base::android::ScopedJavaLocalRef<jobject> retained_object_set = |
| 152 retained_object_set_.get(env); | 210 retained_object_set_.get(env); |
| 153 size_t i = 0; | 211 if (!retained_object_set.is_null()) { |
| 154 for (GinJavaBoundObject::ObjectMap::iterator it(from); | 212 JNI_Java_HashSet_remove(env, retained_object_set, ref.get(env)); |
| 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 } | 213 } |
| 169 } | 214 } |
| 170 | 215 |
| 171 void GinJavaBridgeDispatcherHost::AddNamedObject( | 216 void GinJavaBridgeDispatcherHost::AddNamedObject( |
| 172 const std::string& name, | 217 const std::string& name, |
| 173 const base::android::JavaRef<jobject>& object, | 218 const base::android::JavaRef<jobject>& object, |
| 174 const base::android::JavaRef<jclass>& safe_annotation_clazz) { | 219 const base::android::JavaRef<jclass>& safe_annotation_clazz) { |
| 175 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 220 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 176 GinJavaBoundObject::ObjectID object_id; | 221 GinJavaBoundObject::ObjectID object_id; |
| 177 NamedObjectMap::iterator iter = named_objects_.find(name); | 222 NamedObjectMap::iterator iter = named_objects_.find(name); |
| 178 bool existing_object = FindObjectId(object, &object_id); | 223 bool existing_object = FindObjectId(object, &object_id); |
| 179 if (existing_object && iter != named_objects_.end() && | 224 if (existing_object && iter != named_objects_.end() && |
| 180 iter->second == object_id) { | 225 iter->second == object_id) { |
| 181 // Nothing to do. | 226 // Nothing to do. |
| 182 return; | 227 return; |
| 183 } | 228 } |
| 184 if (iter != named_objects_.end()) { | 229 if (iter != named_objects_.end()) { |
| 185 RemoveNamedObject(iter->first); | 230 RemoveNamedObject(iter->first); |
| 186 } | 231 } |
| 187 if (existing_object) { | 232 if (existing_object) { |
| 188 (*objects_.Lookup(object_id))->AddName(); | 233 base::AutoLock locker(objects_lock_); |
| 234 objects_[object_id]->AddName(); |
| 189 } else { | 235 } else { |
| 190 object_id = AddObject(object, safe_annotation_clazz, true, NULL); | 236 object_id = AddObject(object, safe_annotation_clazz, true, 0); |
| 191 } | 237 } |
| 192 named_objects_[name] = object_id; | 238 named_objects_[name] = object_id; |
| 193 | 239 |
| 240 TryAddingBrowserFilter(); |
| 194 web_contents()->SendToAllFrames( | 241 web_contents()->SendToAllFrames( |
| 195 new GinJavaBridgeMsg_AddNamedObject(MSG_ROUTING_NONE, name, object_id)); | 242 new GinJavaBridgeMsg_AddNamedObject(MSG_ROUTING_NONE, name, object_id)); |
| 196 } | 243 } |
| 197 | 244 |
| 198 void GinJavaBridgeDispatcherHost::RemoveNamedObject( | 245 void GinJavaBridgeDispatcherHost::RemoveNamedObject( |
| 199 const std::string& name) { | 246 const std::string& name) { |
| 200 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 247 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 201 NamedObjectMap::iterator iter = named_objects_.find(name); | 248 NamedObjectMap::iterator iter = named_objects_.find(name); |
| 202 if (iter == named_objects_.end()) | 249 if (iter == named_objects_.end()) |
| 203 return; | 250 return; |
| 204 | 251 |
| 205 // |name| may come from |named_objects_|. Make a copy of name so that if | 252 // |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. | 253 // |name| is from |named_objects_| it'll be valid after the remove below. |
| 207 const std::string copied_name(name); | 254 const std::string copied_name(name); |
| 208 | 255 |
| 209 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(iter->second)); | 256 { |
| 257 base::AutoLock locker(objects_lock_); |
| 258 objects_[iter->second]->RemoveName(); |
| 259 } |
| 210 named_objects_.erase(iter); | 260 named_objects_.erase(iter); |
| 211 object->RemoveName(); | |
| 212 | 261 |
| 213 // As the object isn't going to be removed from the JavaScript side until the | 262 // As the object isn't going to be removed from the JavaScript side until the |
| 214 // next page reload, calls to it must still work, thus we should continue to | 263 // next page reload, calls to it must still work, thus we should continue to |
| 215 // hold it. All the transient objects and removed named objects will be purged | 264 // hold it. All the transient objects and removed named objects will be purged |
| 216 // during the cleansing caused by DocumentAvailableInMainFrame event. | 265 // during the cleansing caused by DocumentAvailableInMainFrame event. |
| 217 | 266 |
| 218 web_contents()->SendToAllFrames( | 267 web_contents()->SendToAllFrames( |
| 219 new GinJavaBridgeMsg_RemoveNamedObject(MSG_ROUTING_NONE, copied_name)); | 268 new GinJavaBridgeMsg_RemoveNamedObject(MSG_ROUTING_NONE, copied_name)); |
| 220 } | 269 } |
| 221 | 270 |
| 222 void GinJavaBridgeDispatcherHost::SetAllowObjectContentsInspection(bool allow) { | 271 void GinJavaBridgeDispatcherHost::SetAllowObjectContentsInspection(bool allow) { |
| 272 if (!JavaBridgeThread::CurrentlyOn()) { |
| 273 g_background_thread.Get().message_loop()->task_runner()->PostTask( |
| 274 FROM_HERE, |
| 275 base::Bind( |
| 276 &GinJavaBridgeDispatcherHost::SetAllowObjectContentsInspection, |
| 277 this, allow)); |
| 278 return; |
| 279 } |
| 223 allow_object_contents_inspection_ = allow; | 280 allow_object_contents_inspection_ = allow; |
| 224 } | 281 } |
| 225 | 282 |
| 226 void GinJavaBridgeDispatcherHost::DocumentAvailableInMainFrame() { | 283 void GinJavaBridgeDispatcherHost::DocumentAvailableInMainFrame() { |
| 227 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 284 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 228 // Called when the window object has been cleared in the main frame. | 285 // Called when the window object has been cleared in the main frame. |
| 229 // That means, all sub-frames have also been cleared, so only named | 286 // That means, all sub-frames have also been cleared, so only named |
| 230 // objects survived. | 287 // objects survived. |
| 288 TryAddingBrowserFilter(); |
| 231 JNIEnv* env = base::android::AttachCurrentThread(); | 289 JNIEnv* env = base::android::AttachCurrentThread(); |
| 232 base::android::ScopedJavaLocalRef<jobject> retained_object_set = | 290 base::android::ScopedJavaLocalRef<jobject> retained_object_set = |
| 233 retained_object_set_.get(env); | 291 retained_object_set_.get(env); |
| 292 base::AutoLock locker(objects_lock_); |
| 234 if (!retained_object_set.is_null()) { | 293 if (!retained_object_set.is_null()) { |
| 235 JNI_Java_HashSet_clear(env, retained_object_set); | 294 JNI_Java_HashSet_clear(env, retained_object_set); |
| 236 } | 295 } |
| 237 | 296 auto iter = objects_.begin(); |
| 238 // We also need to add back the named objects we have so far as they | 297 while (iter != objects_.end()) { |
| 239 // should survive navigations. | 298 if (iter->second->IsNamed()) { |
| 240 for (GinJavaBoundObject::ObjectMap::iterator it(&objects_); !it.IsAtEnd(); | |
| 241 it.Advance()) { | |
| 242 scoped_refptr<GinJavaBoundObject> object(*it.GetCurrentValue()); | |
| 243 if (object->IsNamed()) { | |
| 244 if (!retained_object_set.is_null()) { | 299 if (!retained_object_set.is_null()) { |
| 245 JNI_Java_HashSet_add( | 300 JNI_Java_HashSet_add( |
| 246 env, retained_object_set, object->GetLocalRef(env)); | 301 env, retained_object_set, iter->second->GetLocalRef(env)); |
| 247 } | 302 } |
| 303 ++iter; |
| 248 } else { | 304 } else { |
| 249 objects_.Remove(it.GetCurrentKey()); | 305 objects_.erase(iter++); |
| 250 } | 306 } |
| 251 } | 307 } |
| 252 } | 308 } |
| 253 | 309 |
| 254 namespace { | 310 base::TaskRunner* GinJavaBridgeDispatcherHost::OverrideTaskRunnerForMessage( |
| 255 | 311 const IPC::Message& message) { |
| 256 // TODO(mnaganov): Implement passing of a parameter into sync message handlers. | 312 GinJavaBoundObject::ObjectID object_id = 0; |
| 257 class MessageForwarder : public IPC::Sender { | 313 // TODO(mnaganov): It's very sad that we have a BrowserMessageFilter per |
| 258 public: | 314 // WebView instance. We should redesign to have a filter per RPH. |
| 259 MessageForwarder(GinJavaBridgeDispatcherHost* gjbdh, | 315 // Check, if the object ID in the message is known to this host. If not, |
| 260 RenderFrameHost* render_frame_host) | 316 // this is a message for some other host. As all our IPC messages from the |
| 261 : gjbdh_(gjbdh), render_frame_host_(render_frame_host) {} | 317 // renderer start with object ID, we just fetch it directly from the |
| 262 void OnGetMethods(GinJavaBoundObject::ObjectID object_id, | 318 // message, considering sync and async messages separately. |
| 263 IPC::Message* reply_msg) { | 319 switch (message.type()) { |
| 264 gjbdh_->OnGetMethods(render_frame_host_, | 320 case GinJavaBridgeHostMsg_GetMethods::ID: |
| 265 object_id, | 321 case GinJavaBridgeHostMsg_HasMethod::ID: |
| 266 reply_msg); | 322 case GinJavaBridgeHostMsg_InvokeMethod::ID: { |
| 323 DCHECK(message.is_sync()); |
| 324 PickleIterator message_reader = |
| 325 IPC::SyncMessage::GetDataIterator(&message); |
| 326 if (!IPC::ReadParam(&message, &message_reader, &object_id)) |
| 327 return NULL; |
| 328 break; |
| 329 } |
| 330 case GinJavaBridgeHostMsg_ObjectWrapperDeleted::ID: { |
| 331 DCHECK(!message.is_sync()); |
| 332 PickleIterator message_reader(message); |
| 333 if (!IPC::ReadParam(&message, &message_reader, &object_id)) |
| 334 return NULL; |
| 335 break; |
| 336 } |
| 267 } | 337 } |
| 268 void OnHasMethod(GinJavaBoundObject::ObjectID object_id, | 338 { |
| 269 const std::string& method_name, | 339 base::AutoLock locker(objects_lock_); |
| 270 IPC::Message* reply_msg) { | 340 if (objects_.find(object_id) != objects_.end()) { |
| 271 gjbdh_->OnHasMethod(render_frame_host_, | 341 return g_background_thread.Get().message_loop()->task_runner().get(); |
| 272 object_id, | 342 } |
| 273 method_name, | |
| 274 reply_msg); | |
| 275 } | 343 } |
| 276 void OnInvokeMethod(GinJavaBoundObject::ObjectID object_id, | 344 return NULL; |
| 277 const std::string& method_name, | |
| 278 const base::ListValue& arguments, | |
| 279 IPC::Message* reply_msg) { | |
| 280 gjbdh_->OnInvokeMethod(render_frame_host_, | |
| 281 object_id, | |
| 282 method_name, | |
| 283 arguments, | |
| 284 reply_msg); | |
| 285 } | |
| 286 virtual bool Send(IPC::Message* msg) override { | |
| 287 NOTREACHED(); | |
| 288 return false; | |
| 289 } | |
| 290 private: | |
| 291 GinJavaBridgeDispatcherHost* gjbdh_; | |
| 292 RenderFrameHost* render_frame_host_; | |
| 293 }; | |
| 294 | |
| 295 } | 345 } |
| 296 | 346 |
| 297 bool GinJavaBridgeDispatcherHost::OnMessageReceived( | 347 bool GinJavaBridgeDispatcherHost::OnMessageReceived( |
| 298 const IPC::Message& message, | 348 const IPC::Message& message) { |
| 299 RenderFrameHost* render_frame_host) { | 349 // We can get here As WebContentsObserver also has OnMessageReceived, |
| 300 DCHECK(render_frame_host); | 350 // or because we have not provided a task runner in |
| 351 // OverrideTaskRunnerForMessage. In either case, just bail out. |
| 352 if (!JavaBridgeThread::CurrentlyOn()) |
| 353 return false; |
| 354 SetCurrentRoutingID(message.routing_id()); |
| 301 bool handled = true; | 355 bool handled = true; |
| 302 MessageForwarder forwarder(this, render_frame_host); | 356 IPC_BEGIN_MESSAGE_MAP(GinJavaBridgeDispatcherHost, message) |
| 303 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(GinJavaBridgeDispatcherHost, message, | 357 IPC_MESSAGE_HANDLER(GinJavaBridgeHostMsg_GetMethods, OnGetMethods) |
| 304 render_frame_host) | 358 IPC_MESSAGE_HANDLER(GinJavaBridgeHostMsg_HasMethod, OnHasMethod) |
| 305 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_GetMethods, | 359 IPC_MESSAGE_HANDLER(GinJavaBridgeHostMsg_InvokeMethod, OnInvokeMethod) |
| 306 &forwarder, | |
| 307 MessageForwarder::OnGetMethods) | |
| 308 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_HasMethod, | |
| 309 &forwarder, | |
| 310 MessageForwarder::OnHasMethod) | |
| 311 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_InvokeMethod, | |
| 312 &forwarder, | |
| 313 MessageForwarder::OnInvokeMethod) | |
| 314 IPC_MESSAGE_HANDLER(GinJavaBridgeHostMsg_ObjectWrapperDeleted, | 360 IPC_MESSAGE_HANDLER(GinJavaBridgeHostMsg_ObjectWrapperDeleted, |
| 315 OnObjectWrapperDeleted) | 361 OnObjectWrapperDeleted) |
| 316 IPC_MESSAGE_UNHANDLED(handled = false) | 362 IPC_MESSAGE_UNHANDLED(handled = false) |
| 317 IPC_END_MESSAGE_MAP() | 363 IPC_END_MESSAGE_MAP() |
| 364 SetCurrentRoutingID(MSG_ROUTING_NONE); |
| 318 return handled; | 365 return handled; |
| 319 } | 366 } |
| 320 | 367 |
| 321 namespace { | 368 void GinJavaBridgeDispatcherHost::OnDestruct() const { |
| 369 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| 370 delete this; |
| 371 } else { |
| 372 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this); |
| 373 } |
| 374 } |
| 322 | 375 |
| 323 class IsValidRenderFrameHostHelper | 376 int GinJavaBridgeDispatcherHost::GetCurrentRoutingID() { |
| 324 : public base::RefCounted<IsValidRenderFrameHostHelper> { | 377 DCHECK(JavaBridgeThread::CurrentlyOn()); |
| 325 public: | 378 return current_routing_id_; |
| 326 explicit IsValidRenderFrameHostHelper(RenderFrameHost* rfh_to_match) | 379 } |
| 327 : rfh_to_match_(rfh_to_match), rfh_found_(false) {} | |
| 328 | 380 |
| 329 bool rfh_found() { return rfh_found_; } | 381 void GinJavaBridgeDispatcherHost::SetCurrentRoutingID(int32 routing_id) { |
| 382 DCHECK(JavaBridgeThread::CurrentlyOn()); |
| 383 current_routing_id_ = routing_id; |
| 384 } |
| 330 | 385 |
| 331 void OnFrame(RenderFrameHost* rfh) { | 386 scoped_refptr<GinJavaBoundObject> GinJavaBridgeDispatcherHost::FindObject( |
| 332 if (rfh_to_match_ == rfh) rfh_found_ = true; | 387 GinJavaBoundObject::ObjectID object_id) { |
| 333 } | 388 // Can be called on any thread. |
| 334 | 389 base::AutoLock locker(objects_lock_); |
| 335 private: | 390 auto iter = objects_.find(object_id); |
| 336 friend class base::RefCounted<IsValidRenderFrameHostHelper>; | 391 if (iter != objects_.end()) |
| 337 | 392 return iter->second; |
| 338 ~IsValidRenderFrameHostHelper() {} | 393 return NULL; |
| 339 | |
| 340 RenderFrameHost* rfh_to_match_; | |
| 341 bool rfh_found_; | |
| 342 | |
| 343 DISALLOW_COPY_AND_ASSIGN(IsValidRenderFrameHostHelper); | |
| 344 }; | |
| 345 | |
| 346 } // namespace | |
| 347 | |
| 348 bool GinJavaBridgeDispatcherHost::IsValidRenderFrameHost( | |
| 349 RenderFrameHost* render_frame_host) { | |
| 350 scoped_refptr<IsValidRenderFrameHostHelper> helper = | |
| 351 new IsValidRenderFrameHostHelper(render_frame_host); | |
| 352 web_contents()->ForEachFrame( | |
| 353 base::Bind(&IsValidRenderFrameHostHelper::OnFrame, helper)); | |
| 354 return helper->rfh_found(); | |
| 355 } | 394 } |
| 356 | 395 |
| 357 void GinJavaBridgeDispatcherHost::OnGetMethods( | 396 void GinJavaBridgeDispatcherHost::OnGetMethods( |
| 358 RenderFrameHost* render_frame_host, | |
| 359 GinJavaBoundObject::ObjectID object_id, | 397 GinJavaBoundObject::ObjectID object_id, |
| 360 IPC::Message* reply_msg) { | 398 std::set<std::string>* returned_method_names) { |
| 361 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 399 DCHECK(JavaBridgeThread::CurrentlyOn()); |
| 362 DCHECK(render_frame_host); | 400 if (!allow_object_contents_inspection_) |
| 363 if (!allow_object_contents_inspection_) { | |
| 364 IPC::WriteParam(reply_msg, std::set<std::string>()); | |
| 365 render_frame_host->Send(reply_msg); | |
| 366 return; | 401 return; |
| 402 scoped_refptr<GinJavaBoundObject> object = FindObject(object_id); |
| 403 if (object.get()) { |
| 404 *returned_method_names = object->GetMethodNames(); |
| 405 } else { |
| 406 LOG(ERROR) << "WebView: Unknown object: " << object_id; |
| 367 } | 407 } |
| 368 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id)); | |
| 369 if (!object.get()) { | |
| 370 LOG(ERROR) << "WebView: Unknown object: " << object_id; | |
| 371 IPC::WriteParam(reply_msg, std::set<std::string>()); | |
| 372 render_frame_host->Send(reply_msg); | |
| 373 return; | |
| 374 } | |
| 375 DCHECK(!HasPendingReply(render_frame_host)); | |
| 376 pending_replies_[render_frame_host] = reply_msg; | |
| 377 base::PostTaskAndReplyWithResult( | |
| 378 g_background_thread.Get().message_loop()->message_loop_proxy().get(), | |
| 379 FROM_HERE, | |
| 380 base::Bind(&GinJavaBoundObject::GetMethodNames, object), | |
| 381 base::Bind(&GinJavaBridgeDispatcherHost::SendMethods, | |
| 382 AsWeakPtr(), | |
| 383 render_frame_host)); | |
| 384 } | |
| 385 | |
| 386 void GinJavaBridgeDispatcherHost::SendMethods( | |
| 387 RenderFrameHost* render_frame_host, | |
| 388 const std::set<std::string>& method_names) { | |
| 389 IPC::Message* reply_msg = TakePendingReply(render_frame_host); | |
| 390 if (!reply_msg) { | |
| 391 return; | |
| 392 } | |
| 393 IPC::WriteParam(reply_msg, method_names); | |
| 394 render_frame_host->Send(reply_msg); | |
| 395 } | 408 } |
| 396 | 409 |
| 397 void GinJavaBridgeDispatcherHost::OnHasMethod( | 410 void GinJavaBridgeDispatcherHost::OnHasMethod( |
| 398 RenderFrameHost* render_frame_host, | |
| 399 GinJavaBoundObject::ObjectID object_id, | 411 GinJavaBoundObject::ObjectID object_id, |
| 400 const std::string& method_name, | 412 const std::string& method_name, |
| 401 IPC::Message* reply_msg) { | 413 bool* result) { |
| 402 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 414 DCHECK(JavaBridgeThread::CurrentlyOn()); |
| 403 DCHECK(render_frame_host); | 415 scoped_refptr<GinJavaBoundObject> object = FindObject(object_id); |
| 404 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id)); | 416 if (object.get()) { |
| 405 if (!object.get()) { | 417 *result = object->HasMethod(method_name); |
| 418 } else { |
| 406 LOG(ERROR) << "WebView: Unknown object: " << object_id; | 419 LOG(ERROR) << "WebView: Unknown object: " << object_id; |
| 407 IPC::WriteParam(reply_msg, false); | |
| 408 render_frame_host->Send(reply_msg); | |
| 409 return; | |
| 410 } | 420 } |
| 411 DCHECK(!HasPendingReply(render_frame_host)); | |
| 412 pending_replies_[render_frame_host] = reply_msg; | |
| 413 base::PostTaskAndReplyWithResult( | |
| 414 g_background_thread.Get().message_loop()->message_loop_proxy().get(), | |
| 415 FROM_HERE, | |
| 416 base::Bind(&GinJavaBoundObject::HasMethod, object, method_name), | |
| 417 base::Bind(&GinJavaBridgeDispatcherHost::SendHasMethodReply, | |
| 418 AsWeakPtr(), | |
| 419 render_frame_host)); | |
| 420 } | |
| 421 | |
| 422 void GinJavaBridgeDispatcherHost::SendHasMethodReply( | |
| 423 RenderFrameHost* render_frame_host, | |
| 424 bool result) { | |
| 425 IPC::Message* reply_msg = TakePendingReply(render_frame_host); | |
| 426 if (!reply_msg) { | |
| 427 return; | |
| 428 } | |
| 429 IPC::WriteParam(reply_msg, result); | |
| 430 render_frame_host->Send(reply_msg); | |
| 431 } | 421 } |
| 432 | 422 |
| 433 void GinJavaBridgeDispatcherHost::OnInvokeMethod( | 423 void GinJavaBridgeDispatcherHost::OnInvokeMethod( |
| 434 RenderFrameHost* render_frame_host, | |
| 435 GinJavaBoundObject::ObjectID object_id, | 424 GinJavaBoundObject::ObjectID object_id, |
| 436 const std::string& method_name, | 425 const std::string& method_name, |
| 437 const base::ListValue& arguments, | 426 const base::ListValue& arguments, |
| 438 IPC::Message* reply_msg) { | 427 base::ListValue* wrapped_result, |
| 439 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 428 content::GinJavaBridgeError* error_code) { |
| 440 DCHECK(render_frame_host); | 429 DCHECK(JavaBridgeThread::CurrentlyOn()); |
| 441 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id)); | 430 scoped_refptr<GinJavaBoundObject> object = FindObject(object_id); |
| 442 if (!object.get()) { | 431 if (!object.get()) { |
| 443 LOG(ERROR) << "WebView: Unknown object: " << object_id; | 432 LOG(ERROR) << "WebView: Unknown object: " << object_id; |
| 444 base::ListValue result; | 433 wrapped_result->Append(base::Value::CreateNullValue()); |
| 445 result.Append(base::Value::CreateNullValue()); | 434 *error_code = kGinJavaBridgeUnknownObjectId; |
| 446 IPC::WriteParam(reply_msg, result); | |
| 447 IPC::WriteParam(reply_msg, kGinJavaBridgeUnknownObjectId); | |
| 448 render_frame_host->Send(reply_msg); | |
| 449 return; | 435 return; |
| 450 } | 436 } |
| 451 DCHECK(!HasPendingReply(render_frame_host)); | |
| 452 pending_replies_[render_frame_host] = reply_msg; | |
| 453 scoped_refptr<GinJavaMethodInvocationHelper> result = | 437 scoped_refptr<GinJavaMethodInvocationHelper> result = |
| 454 new GinJavaMethodInvocationHelper( | 438 new GinJavaMethodInvocationHelper( |
| 455 make_scoped_ptr(new GinJavaBoundObjectDelegate(object)), | 439 make_scoped_ptr(new GinJavaBoundObjectDelegate(object)), |
| 456 method_name, | 440 method_name, |
| 457 arguments); | 441 arguments); |
| 458 result->Init(this); | 442 result->Init(this); |
| 459 g_background_thread.Get() | 443 result->Invoke(); |
| 460 .message_loop() | 444 *error_code = result->GetInvocationError(); |
| 461 ->message_loop_proxy() | |
| 462 ->PostTaskAndReply( | |
| 463 FROM_HERE, | |
| 464 base::Bind(&GinJavaMethodInvocationHelper::Invoke, result), | |
| 465 base::Bind( | |
| 466 &GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult, | |
| 467 AsWeakPtr(), | |
| 468 render_frame_host, | |
| 469 result)); | |
| 470 } | |
| 471 | |
| 472 void GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult( | |
| 473 RenderFrameHost* render_frame_host, | |
| 474 scoped_refptr<GinJavaMethodInvocationHelper> result) { | |
| 475 if (result->HoldsPrimitiveResult()) { | 445 if (result->HoldsPrimitiveResult()) { |
| 476 IPC::Message* reply_msg = TakePendingReply(render_frame_host); | 446 scoped_ptr<base::ListValue> result_copy( |
| 477 if (!reply_msg) { | 447 result->GetPrimitiveResult().DeepCopy()); |
| 478 return; | 448 wrapped_result->Swap(result_copy.get()); |
| 479 } | 449 } else if (!result->GetObjectResult().is_null()) { |
| 480 IPC::WriteParam(reply_msg, result->GetPrimitiveResult()); | |
| 481 IPC::WriteParam(reply_msg, result->GetInvocationError()); | |
| 482 render_frame_host->Send(reply_msg); | |
| 483 } else { | |
| 484 ProcessMethodInvocationObjectResult(render_frame_host, result); | |
| 485 } | |
| 486 } | |
| 487 | |
| 488 void GinJavaBridgeDispatcherHost::ProcessMethodInvocationObjectResult( | |
| 489 RenderFrameHost* render_frame_host, | |
| 490 scoped_refptr<GinJavaMethodInvocationHelper> result) { | |
| 491 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 492 | |
| 493 if (!IsValidRenderFrameHost(render_frame_host)) { | |
| 494 // In this case, we must've already sent the reply when the render frame | |
| 495 // was destroyed. | |
| 496 DCHECK(!HasPendingReply(render_frame_host)); | |
| 497 return; | |
| 498 } | |
| 499 | |
| 500 base::ListValue wrapped_result; | |
| 501 if (!result->GetObjectResult().is_null()) { | |
| 502 GinJavaBoundObject::ObjectID returned_object_id; | 450 GinJavaBoundObject::ObjectID returned_object_id; |
| 503 if (FindObjectId(result->GetObjectResult(), &returned_object_id)) { | 451 if (FindObjectId(result->GetObjectResult(), &returned_object_id)) { |
| 504 (*objects_.Lookup(returned_object_id))->AddHolder(render_frame_host); | 452 base::AutoLock locker(objects_lock_); |
| 453 objects_[returned_object_id]->AddHolder(GetCurrentRoutingID()); |
| 505 } else { | 454 } else { |
| 506 returned_object_id = AddObject(result->GetObjectResult(), | 455 returned_object_id = AddObject(result->GetObjectResult(), |
| 507 result->GetSafeAnnotationClass(), | 456 result->GetSafeAnnotationClass(), |
| 508 false, | 457 false, |
| 509 render_frame_host); | 458 GetCurrentRoutingID()); |
| 510 } | 459 } |
| 511 wrapped_result.Append( | 460 wrapped_result->Append( |
| 512 GinJavaBridgeValue::CreateObjectIDValue( | 461 GinJavaBridgeValue::CreateObjectIDValue( |
| 513 returned_object_id).release()); | 462 returned_object_id).release()); |
| 514 } else { | 463 } else { |
| 515 wrapped_result.Append(base::Value::CreateNullValue()); | 464 wrapped_result->Append(base::Value::CreateNullValue()); |
| 516 } | 465 } |
| 517 IPC::Message* reply_msg = TakePendingReply(render_frame_host); | |
| 518 if (!reply_msg) { | |
| 519 return; | |
| 520 } | |
| 521 IPC::WriteParam(reply_msg, wrapped_result); | |
| 522 IPC::WriteParam(reply_msg, result->GetInvocationError()); | |
| 523 render_frame_host->Send(reply_msg); | |
| 524 } | 466 } |
| 525 | 467 |
| 526 void GinJavaBridgeDispatcherHost::OnObjectWrapperDeleted( | 468 void GinJavaBridgeDispatcherHost::OnObjectWrapperDeleted( |
| 527 RenderFrameHost* render_frame_host, | |
| 528 GinJavaBoundObject::ObjectID object_id) { | 469 GinJavaBoundObject::ObjectID object_id) { |
| 529 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 470 DCHECK(JavaBridgeThread::CurrentlyOn()); |
| 530 DCHECK(render_frame_host); | 471 base::AutoLock locker(objects_lock_); |
| 531 if (objects_.Lookup(object_id)) { | 472 auto iter = objects_.find(object_id); |
| 532 GinJavaBoundObject::ObjectMap::iterator iter(&objects_); | 473 if (iter == objects_.end()) |
| 533 while (!iter.IsAtEnd() && iter.GetCurrentKey() != object_id) | 474 return; |
| 534 iter.Advance(); | 475 JavaObjectWeakGlobalRef ref = |
| 535 DCHECK(!iter.IsAtEnd()); | 476 RemoveHolderAndAdvanceLocked(GetCurrentRoutingID(), &iter); |
| 536 RemoveHolder(render_frame_host, iter, 1); | 477 if (!ref.is_empty()) { |
| 478 RemoveFromRetainedObjectSetLocked(ref); |
| 537 } | 479 } |
| 538 } | 480 } |
| 539 | 481 |
| 540 IPC::Message* GinJavaBridgeDispatcherHost::TakePendingReply( | |
| 541 RenderFrameHost* render_frame_host) { | |
| 542 if (!IsValidRenderFrameHost(render_frame_host)) { | |
| 543 DCHECK(!HasPendingReply(render_frame_host)); | |
| 544 return NULL; | |
| 545 } | |
| 546 | |
| 547 PendingReplyMap::iterator it = pending_replies_.find(render_frame_host); | |
| 548 // There may be no pending reply if we're called from RenderFrameDeleted and | |
| 549 // we already sent the reply through the regular route. | |
| 550 if (it == pending_replies_.end()) { | |
| 551 return NULL; | |
| 552 } | |
| 553 | |
| 554 IPC::Message* reply_msg = it->second; | |
| 555 pending_replies_.erase(it); | |
| 556 return reply_msg; | |
| 557 } | |
| 558 | |
| 559 bool GinJavaBridgeDispatcherHost::HasPendingReply( | |
| 560 RenderFrameHost* render_frame_host) const { | |
| 561 return pending_replies_.find(render_frame_host) != pending_replies_.end(); | |
| 562 } | |
| 563 | |
| 564 } // namespace content | 482 } // namespace content |
| OLD | NEW |