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