Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(44)

Side by Side Diff: content/browser/android/java/gin_java_bridge_dispatcher_host.cc

Issue 772123002: [Android] Java Bridge: handle requests from Java Script on the background thread (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add the filter in more places, and make sure that the channel is present Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698