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 |