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

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: Addressed Sami's comments 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 // 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() {
Sami 2014/12/09 11:33:51 nit: can this be a const method?
mnaganov (inactive) 2014/12/09 12:37:11 Done.
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());
Sami 2014/12/09 11:33:51 Would it make sense to DCHECK that GetCurrentRouti
mnaganov (inactive) 2014/12/09 12:37:11 Absolutely! Done.
441 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id)); 436 scoped_refptr<GinJavaBoundObject> object = FindObject(object_id);
442 if (!object.get()) { 437 if (!object.get()) {
443 LOG(ERROR) << "WebView: Unknown object: " << object_id; 438 LOG(ERROR) << "WebView: Unknown object: " << object_id;
444 base::ListValue result; 439 wrapped_result->Append(base::Value::CreateNullValue());
445 result.Append(base::Value::CreateNullValue()); 440 *error_code = kGinJavaBridgeUnknownObjectId;
446 IPC::WriteParam(reply_msg, result);
447 IPC::WriteParam(reply_msg, kGinJavaBridgeUnknownObjectId);
448 render_frame_host->Send(reply_msg);
449 return; 441 return;
450 } 442 }
451 DCHECK(!HasPendingReply(render_frame_host));
452 pending_replies_[render_frame_host] = reply_msg;
453 scoped_refptr<GinJavaMethodInvocationHelper> result = 443 scoped_refptr<GinJavaMethodInvocationHelper> result =
454 new GinJavaMethodInvocationHelper( 444 new GinJavaMethodInvocationHelper(
455 make_scoped_ptr(new GinJavaBoundObjectDelegate(object)), 445 make_scoped_ptr(new GinJavaBoundObjectDelegate(object)),
456 method_name, 446 method_name,
457 arguments); 447 arguments);
458 result->Init(this); 448 result->Init(this);
459 g_background_thread.Get() 449 result->Invoke();
460 .message_loop() 450 *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()) { 451 if (result->HoldsPrimitiveResult()) {
476 IPC::Message* reply_msg = TakePendingReply(render_frame_host); 452 scoped_ptr<base::ListValue> result_copy(
477 if (!reply_msg) { 453 result->GetPrimitiveResult().DeepCopy());
478 return; 454 wrapped_result->Swap(result_copy.get());
479 } 455 } 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; 456 GinJavaBoundObject::ObjectID returned_object_id;
503 if (FindObjectId(result->GetObjectResult(), &returned_object_id)) { 457 if (FindObjectId(result->GetObjectResult(), &returned_object_id)) {
504 (*objects_.Lookup(returned_object_id))->AddHolder(render_frame_host); 458 base::AutoLock locker(objects_lock_);
459 objects_[returned_object_id]->AddHolder(GetCurrentRoutingID());
505 } else { 460 } else {
506 returned_object_id = AddObject(result->GetObjectResult(), 461 returned_object_id = AddObject(result->GetObjectResult(),
507 result->GetSafeAnnotationClass(), 462 result->GetSafeAnnotationClass(),
508 false, 463 false,
509 render_frame_host); 464 GetCurrentRoutingID());
510 } 465 }
511 wrapped_result.Append( 466 wrapped_result->Append(
512 GinJavaBridgeValue::CreateObjectIDValue( 467 GinJavaBridgeValue::CreateObjectIDValue(
513 returned_object_id).release()); 468 returned_object_id).release());
514 } else { 469 } else {
515 wrapped_result.Append(base::Value::CreateNullValue()); 470 wrapped_result->Append(base::Value::CreateNullValue());
516 } 471 }
517 IPC::Message* reply_msg = TakePendingReply(render_frame_host); 472 }
518 if (!reply_msg) { 473
474 void GinJavaBridgeDispatcherHost::OnObjectWrapperDeleted(
475 GinJavaBoundObject::ObjectID object_id) {
476 DCHECK(JavaBridgeThread::CurrentlyOn());
477 base::AutoLock locker(objects_lock_);
478 auto iter = objects_.find(object_id);
479 if (iter == objects_.end())
519 return; 480 return;
520 } 481 JavaObjectWeakGlobalRef ref =
521 IPC::WriteParam(reply_msg, wrapped_result); 482 RemoveHolderAndAdvanceLocked(GetCurrentRoutingID(), &iter);
522 IPC::WriteParam(reply_msg, result->GetInvocationError()); 483 if (!ref.is_empty()) {
523 render_frame_host->Send(reply_msg); 484 RemoveFromRetainedObjectSetLocked(ref);
524 } 485 }
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 } 486 }
563 487
564 } // namespace content 488 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698