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