gin_java_bridge_dispatcher_host.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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 3// found in the LICENSE file. 4 5#include "content/browser/android/java/gin_java_bridge_dispatcher_host.h" 6 7#include "base/android/java_handler_thread.h" 8#include "base/android/jni_android.h" 9#include "base/android/scoped_java_ref.h" 10#include "base/lazy_instance.h" 11#include "base/strings/string_number_conversions.h" 12#include "base/strings/utf_string_conversions.h" 13#include "base/task_runner_util.h" 14#include "content/browser/android/java/gin_java_bound_object_delegate.h" 15#include "content/browser/android/java/jni_helper.h" 16#include "content/common/android/gin_java_bridge_value.h" 17#include "content/common/android/hash_set.h" 18#include "content/common/gin_java_bridge_messages.h" 19#include "content/public/browser/browser_thread.h" 20#include "content/public/browser/render_frame_host.h" 21#include "content/public/browser/web_contents.h" 22#include "ipc/ipc_message_utils.h" 23 24#if !defined(OS_ANDROID) 25#error "JavaBridge only supports OS_ANDROID" 26#endif 27 28namespace content { 29 30namespace { 31// The JavaBridge needs to use a Java thread so the callback 32// will happen on a thread with a prepared Looper. 33class JavaBridgeThread : public base::android::JavaHandlerThread { 34 public: 35 JavaBridgeThread() : base::android::JavaHandlerThread("JavaBridge") { 36 Start(); 37 } 38 virtual ~JavaBridgeThread() { 39 Stop(); 40 } 41}; 42 43base::LazyInstance<JavaBridgeThread> g_background_thread = 44 LAZY_INSTANCE_INITIALIZER; 45 46} // namespace 47 48GinJavaBridgeDispatcherHost::GinJavaBridgeDispatcherHost( 49 WebContents* web_contents, 50 jobject retained_object_set) 51 : WebContentsObserver(web_contents), 52 retained_object_set_(base::android::AttachCurrentThread(), 53 retained_object_set), 54 allow_object_contents_inspection_(true) { 55 DCHECK(retained_object_set); 56} 57 58GinJavaBridgeDispatcherHost::~GinJavaBridgeDispatcherHost() { 59} 60 61void GinJavaBridgeDispatcherHost::RenderFrameCreated( 62 RenderFrameHost* render_frame_host) { 63 for (NamedObjectMap::const_iterator iter = named_objects_.begin(); 64 iter != named_objects_.end(); 65 ++iter) { 66 render_frame_host->Send(new GinJavaBridgeMsg_AddNamedObject( 67 render_frame_host->GetRoutingID(), iter->first, iter->second)); 68 } 69} 70 71void GinJavaBridgeDispatcherHost::RenderFrameDeleted( 72 RenderFrameHost* render_frame_host) { 73 RemoveHolder(render_frame_host, 74 GinJavaBoundObject::ObjectMap::iterator(&objects_), 75 objects_.size()); 76} 77 78GinJavaBoundObject::ObjectID GinJavaBridgeDispatcherHost::AddObject( 79 const base::android::JavaRef<jobject>& object, 80 const base::android::JavaRef<jclass>& safe_annotation_clazz, 81 bool is_named, 82 RenderFrameHost* holder) { 83 DCHECK(is_named || holder); 84 GinJavaBoundObject::ObjectID object_id; 85 JNIEnv* env = base::android::AttachCurrentThread(); 86 JavaObjectWeakGlobalRef ref(env, object.obj()); 87 if (is_named) { 88 object_id = objects_.Add(new scoped_refptr<GinJavaBoundObject>( 89 GinJavaBoundObject::CreateNamed(ref, safe_annotation_clazz))); 90 } else { 91 object_id = objects_.Add(new scoped_refptr<GinJavaBoundObject>( 92 GinJavaBoundObject::CreateTransient( 93 ref, safe_annotation_clazz, holder))); 94 } 95#if DCHECK_IS_ON 96 { 97 GinJavaBoundObject::ObjectID added_object_id; 98 DCHECK(FindObjectId(object, &added_object_id)); 99 DCHECK_EQ(object_id, added_object_id); 100 } 101#endif // DCHECK_IS_ON 102 base::android::ScopedJavaLocalRef<jobject> retained_object_set = 103 retained_object_set_.get(env); 104 if (!retained_object_set.is_null()) { 105 JNI_Java_HashSet_add(env, retained_object_set, object); 106 } 107 return object_id; 108} 109 110bool GinJavaBridgeDispatcherHost::FindObjectId( 111 const base::android::JavaRef<jobject>& object, 112 GinJavaBoundObject::ObjectID* object_id) { 113 JNIEnv* env = base::android::AttachCurrentThread(); 114 for (GinJavaBoundObject::ObjectMap::iterator it(&objects_); !it.IsAtEnd(); 115 it.Advance()) { 116 if (env->IsSameObject( 117 object.obj(), 118 it.GetCurrentValue()->get()->GetLocalRef(env).obj())) { 119 *object_id = it.GetCurrentKey(); 120 return true; 121 } 122 } 123 return false; 124} 125 126JavaObjectWeakGlobalRef GinJavaBridgeDispatcherHost::GetObjectWeakRef( 127 GinJavaBoundObject::ObjectID object_id) { 128 scoped_refptr<GinJavaBoundObject>* result = objects_.Lookup(object_id); 129 scoped_refptr<GinJavaBoundObject> object(result ? *result : NULL); 130 if (object.get()) 131 return object->GetWeakRef(); 132 else 133 return JavaObjectWeakGlobalRef(); 134} 135 136void GinJavaBridgeDispatcherHost::RemoveHolder( 137 RenderFrameHost* holder, 138 const GinJavaBoundObject::ObjectMap::iterator& from, 139 size_t count) { 140 JNIEnv* env = base::android::AttachCurrentThread(); 141 base::android::ScopedJavaLocalRef<jobject> retained_object_set = 142 retained_object_set_.get(env); 143 size_t i = 0; 144 for (GinJavaBoundObject::ObjectMap::iterator it(from); 145 !it.IsAtEnd() && i < count; 146 it.Advance(), ++i) { 147 scoped_refptr<GinJavaBoundObject> object(*it.GetCurrentValue()); 148 if (object->IsNamed()) 149 continue; 150 object->RemoveHolder(holder); 151 if (!object->HasHolders()) { 152 if (!retained_object_set.is_null()) { 153 JNI_Java_HashSet_remove( 154 env, retained_object_set, object->GetLocalRef(env)); 155 } 156 objects_.Remove(it.GetCurrentKey()); 157 } 158 } 159} 160 161void GinJavaBridgeDispatcherHost::AddNamedObject( 162 const std::string& name, 163 const base::android::JavaRef<jobject>& object, 164 const base::android::JavaRef<jclass>& safe_annotation_clazz) { 165 DCHECK_CURRENTLY_ON(BrowserThread::UI); 166 GinJavaBoundObject::ObjectID object_id; 167 NamedObjectMap::iterator iter = named_objects_.find(name); 168 bool existing_object = FindObjectId(object, &object_id); 169 if (existing_object && iter != named_objects_.end() && 170 iter->second == object_id) { 171 // Nothing to do. 172 return; 173 } 174 if (iter != named_objects_.end()) { 175 RemoveNamedObject(iter->first); 176 } 177 if (existing_object) { 178 (*objects_.Lookup(object_id))->AddName(); 179 } else { 180 object_id = AddObject(object, safe_annotation_clazz, true, NULL); 181 } 182 named_objects_[name] = object_id; 183 184 web_contents()->SendToAllFrames( 185 new GinJavaBridgeMsg_AddNamedObject(MSG_ROUTING_NONE, name, object_id)); 186} 187 188void GinJavaBridgeDispatcherHost::RemoveNamedObject( 189 const std::string& name) { 190 DCHECK_CURRENTLY_ON(BrowserThread::UI); 191 NamedObjectMap::iterator iter = named_objects_.find(name); 192 if (iter == named_objects_.end()) 193 return; 194 195 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(iter->second)); 196 named_objects_.erase(iter); 197 object->RemoveName(); 198 199 // Not erasing from the objects map, as we can still receive method 200 // invocation requests for this object, and they should work until the 201 // java object is gone. 202 if (!object->IsNamed()) { 203 JNIEnv* env = base::android::AttachCurrentThread(); 204 base::android::ScopedJavaLocalRef<jobject> retained_object_set = 205 retained_object_set_.get(env); 206 if (!retained_object_set.is_null()) { 207 JNI_Java_HashSet_remove( 208 env, retained_object_set, object->GetLocalRef(env)); 209 } 210 } 211 212 web_contents()->SendToAllFrames( 213 new GinJavaBridgeMsg_RemoveNamedObject(MSG_ROUTING_NONE, name)); 214} 215 216void GinJavaBridgeDispatcherHost::SetAllowObjectContentsInspection(bool allow) { 217 allow_object_contents_inspection_ = allow; 218} 219 220void GinJavaBridgeDispatcherHost::DocumentAvailableInMainFrame() { 221 DCHECK_CURRENTLY_ON(BrowserThread::UI); 222 // Called when the window object has been cleared in the main frame. 223 // That means, all sub-frames have also been cleared, so only named 224 // objects survived. 225 JNIEnv* env = base::android::AttachCurrentThread(); 226 base::android::ScopedJavaLocalRef<jobject> retained_object_set = 227 retained_object_set_.get(env); 228 if (!retained_object_set.is_null()) { 229 JNI_Java_HashSet_clear(env, retained_object_set); 230 } 231 232 // We also need to add back the named objects we have so far as they 233 // should survive navigations. 234 for (GinJavaBoundObject::ObjectMap::iterator it(&objects_); !it.IsAtEnd(); 235 it.Advance()) { 236 scoped_refptr<GinJavaBoundObject> object(*it.GetCurrentValue()); 237 if (object->IsNamed()) { 238 if (!retained_object_set.is_null()) { 239 JNI_Java_HashSet_add( 240 env, retained_object_set, object->GetLocalRef(env)); 241 } 242 } else { 243 objects_.Remove(it.GetCurrentKey()); 244 } 245 } 246} 247 248namespace { 249 250// TODO(mnaganov): Implement passing of a parameter into sync message handlers. 251class MessageForwarder : public IPC::Sender { 252 public: 253 MessageForwarder(GinJavaBridgeDispatcherHost* gjbdh, 254 RenderFrameHost* render_frame_host) 255 : gjbdh_(gjbdh), render_frame_host_(render_frame_host) {} 256 void OnGetMethods(GinJavaBoundObject::ObjectID object_id, 257 IPC::Message* reply_msg) { 258 gjbdh_->OnGetMethods(render_frame_host_, 259 object_id, 260 reply_msg); 261 } 262 void OnHasMethod(GinJavaBoundObject::ObjectID object_id, 263 const std::string& method_name, 264 IPC::Message* reply_msg) { 265 gjbdh_->OnHasMethod(render_frame_host_, 266 object_id, 267 method_name, 268 reply_msg); 269 } 270 void OnInvokeMethod(GinJavaBoundObject::ObjectID object_id, 271 const std::string& method_name, 272 const base::ListValue& arguments, 273 IPC::Message* reply_msg) { 274 gjbdh_->OnInvokeMethod(render_frame_host_, 275 object_id, 276 method_name, 277 arguments, 278 reply_msg); 279 } 280 virtual bool Send(IPC::Message* msg) OVERRIDE { 281 NOTREACHED(); 282 return false; 283 } 284 private: 285 GinJavaBridgeDispatcherHost* gjbdh_; 286 RenderFrameHost* render_frame_host_; 287}; 288 289} 290 291bool GinJavaBridgeDispatcherHost::OnMessageReceived( 292 const IPC::Message& message, 293 RenderFrameHost* render_frame_host) { 294 DCHECK(render_frame_host); 295 bool handled = true; 296 MessageForwarder forwarder(this, render_frame_host); 297 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(GinJavaBridgeDispatcherHost, message, 298 render_frame_host) 299 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_GetMethods, 300 &forwarder, 301 MessageForwarder::OnGetMethods) 302 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_HasMethod, 303 &forwarder, 304 MessageForwarder::OnHasMethod) 305 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_InvokeMethod, 306 &forwarder, 307 MessageForwarder::OnInvokeMethod) 308 IPC_MESSAGE_HANDLER(GinJavaBridgeHostMsg_ObjectWrapperDeleted, 309 OnObjectWrapperDeleted) 310 IPC_MESSAGE_UNHANDLED(handled = false) 311 IPC_END_MESSAGE_MAP() 312 return handled; 313} 314 315namespace { 316 317class IsValidRenderFrameHostHelper 318 : public base::RefCounted<IsValidRenderFrameHostHelper> { 319 public: 320 explicit IsValidRenderFrameHostHelper(RenderFrameHost* rfh_to_match) 321 : rfh_to_match_(rfh_to_match), rfh_found_(false) {} 322 323 bool rfh_found() { return rfh_found_; } 324 325 void OnFrame(RenderFrameHost* rfh) { 326 if (rfh_to_match_ == rfh) rfh_found_ = true; 327 } 328 329 private: 330 friend class base::RefCounted<IsValidRenderFrameHostHelper>; 331 332 ~IsValidRenderFrameHostHelper() {} 333 334 RenderFrameHost* rfh_to_match_; 335 bool rfh_found_; 336 337 DISALLOW_COPY_AND_ASSIGN(IsValidRenderFrameHostHelper); 338}; 339 340} // namespace 341 342bool GinJavaBridgeDispatcherHost::IsValidRenderFrameHost( 343 RenderFrameHost* render_frame_host) { 344 scoped_refptr<IsValidRenderFrameHostHelper> helper = 345 new IsValidRenderFrameHostHelper(render_frame_host); 346 web_contents()->ForEachFrame( 347 base::Bind(&IsValidRenderFrameHostHelper::OnFrame, helper)); 348 return helper->rfh_found(); 349} 350 351void GinJavaBridgeDispatcherHost::SendReply( 352 RenderFrameHost* render_frame_host, 353 IPC::Message* reply_msg) { 354 DCHECK_CURRENTLY_ON(BrowserThread::UI); 355 if (IsValidRenderFrameHost(render_frame_host)) { 356 render_frame_host->Send(reply_msg); 357 } else { 358 delete reply_msg; 359 } 360} 361 362void GinJavaBridgeDispatcherHost::OnGetMethods( 363 RenderFrameHost* render_frame_host, 364 GinJavaBoundObject::ObjectID object_id, 365 IPC::Message* reply_msg) { 366 DCHECK_CURRENTLY_ON(BrowserThread::UI); 367 DCHECK(render_frame_host); 368 if (!allow_object_contents_inspection_) { 369 IPC::WriteParam(reply_msg, std::set<std::string>()); 370 render_frame_host->Send(reply_msg); 371 return; 372 } 373 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id)); 374 if (!object) { 375 LOG(ERROR) << "WebView: Unknown object: " << object_id; 376 IPC::WriteParam(reply_msg, std::set<std::string>()); 377 render_frame_host->Send(reply_msg); 378 return; 379 } 380 base::PostTaskAndReplyWithResult( 381 g_background_thread.Get().message_loop()->message_loop_proxy(), 382 FROM_HERE, 383 base::Bind(&GinJavaBoundObject::GetMethodNames, object), 384 base::Bind(&GinJavaBridgeDispatcherHost::SendMethods, 385 AsWeakPtr(), 386 render_frame_host, 387 reply_msg)); 388} 389 390void GinJavaBridgeDispatcherHost::SendMethods( 391 RenderFrameHost* render_frame_host, 392 IPC::Message* reply_msg, 393 const std::set<std::string>& method_names) { 394 IPC::WriteParam(reply_msg, method_names); 395 SendReply(render_frame_host, reply_msg); 396} 397 398void GinJavaBridgeDispatcherHost::OnHasMethod( 399 RenderFrameHost* render_frame_host, 400 GinJavaBoundObject::ObjectID object_id, 401 const std::string& method_name, 402 IPC::Message* reply_msg) { 403 DCHECK_CURRENTLY_ON(BrowserThread::UI); 404 DCHECK(render_frame_host); 405 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id)); 406 if (!object) { 407 LOG(ERROR) << "WebView: Unknown object: " << object_id; 408 IPC::WriteParam(reply_msg, false); 409 render_frame_host->Send(reply_msg); 410 return; 411 } 412 base::PostTaskAndReplyWithResult( 413 g_background_thread.Get().message_loop()->message_loop_proxy(), 414 FROM_HERE, 415 base::Bind(&GinJavaBoundObject::HasMethod, object, method_name), 416 base::Bind(&GinJavaBridgeDispatcherHost::SendHasMethodReply, 417 AsWeakPtr(), 418 render_frame_host, 419 reply_msg)); 420} 421 422void GinJavaBridgeDispatcherHost::SendHasMethodReply( 423 RenderFrameHost* render_frame_host, 424 IPC::Message* reply_msg, 425 bool result) { 426 IPC::WriteParam(reply_msg, result); 427 SendReply(render_frame_host, reply_msg); 428} 429 430void GinJavaBridgeDispatcherHost::OnInvokeMethod( 431 RenderFrameHost* render_frame_host, 432 GinJavaBoundObject::ObjectID object_id, 433 const std::string& method_name, 434 const base::ListValue& arguments, 435 IPC::Message* reply_msg) { 436 DCHECK_CURRENTLY_ON(BrowserThread::UI); 437 DCHECK(render_frame_host); 438 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id)); 439 if (!object) { 440 LOG(ERROR) << "WebView: Unknown object: " << object_id; 441 base::ListValue result; 442 result.Append(base::Value::CreateNullValue()); 443 IPC::WriteParam(reply_msg, result); 444 IPC::WriteParam(reply_msg, kGinJavaBridgeUnknownObjectId); 445 render_frame_host->Send(reply_msg); 446 return; 447 } 448 scoped_refptr<GinJavaMethodInvocationHelper> result = 449 new GinJavaMethodInvocationHelper( 450 make_scoped_ptr(new GinJavaBoundObjectDelegate(object)) 451 .PassAs<GinJavaMethodInvocationHelper::ObjectDelegate>(), 452 method_name, 453 arguments); 454 result->Init(this); 455 g_background_thread.Get() 456 .message_loop() 457 ->message_loop_proxy() 458 ->PostTaskAndReply( 459 FROM_HERE, 460 base::Bind(&GinJavaMethodInvocationHelper::Invoke, result), 461 base::Bind( 462 &GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult, 463 AsWeakPtr(), 464 render_frame_host, 465 reply_msg, 466 result)); 467} 468 469void GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult( 470 RenderFrameHost* render_frame_host, 471 IPC::Message* reply_msg, 472 scoped_refptr<GinJavaMethodInvocationHelper> result) { 473 if (result->HoldsPrimitiveResult()) { 474 IPC::WriteParam(reply_msg, result->GetPrimitiveResult()); 475 IPC::WriteParam(reply_msg, result->GetInvocationError()); 476 SendReply(render_frame_host, reply_msg); 477 } else { 478 ProcessMethodInvocationObjectResult(render_frame_host, reply_msg, result); 479 } 480} 481 482void GinJavaBridgeDispatcherHost::ProcessMethodInvocationObjectResult( 483 RenderFrameHost* render_frame_host, 484 IPC::Message* reply_msg, 485 scoped_refptr<GinJavaMethodInvocationHelper> result) { 486 DCHECK_CURRENTLY_ON(BrowserThread::UI); 487 if (!IsValidRenderFrameHost(render_frame_host)) { 488 delete reply_msg; 489 return; 490 } 491 base::ListValue wrapped_result; 492 if (!result->GetObjectResult().is_null()) { 493 GinJavaBoundObject::ObjectID returned_object_id; 494 if (FindObjectId(result->GetObjectResult(), &returned_object_id)) { 495 (*objects_.Lookup(returned_object_id))->AddHolder(render_frame_host); 496 } else { 497 returned_object_id = AddObject(result->GetObjectResult(), 498 result->GetSafeAnnotationClass(), 499 false, 500 render_frame_host); 501 } 502 wrapped_result.Append( 503 GinJavaBridgeValue::CreateObjectIDValue(returned_object_id).release()); 504 } else { 505 wrapped_result.Append(base::Value::CreateNullValue()); 506 } 507 IPC::WriteParam(reply_msg, wrapped_result); 508 IPC::WriteParam(reply_msg, result->GetInvocationError()); 509 render_frame_host->Send(reply_msg); 510} 511 512void GinJavaBridgeDispatcherHost::OnObjectWrapperDeleted( 513 RenderFrameHost* render_frame_host, 514 GinJavaBoundObject::ObjectID object_id) { 515 DCHECK_CURRENTLY_ON(BrowserThread::UI); 516 DCHECK(render_frame_host); 517 if (objects_.Lookup(object_id)) { 518 GinJavaBoundObject::ObjectMap::iterator iter(&objects_); 519 while (!iter.IsAtEnd() && iter.GetCurrentKey() != object_id) 520 iter.Advance(); 521 DCHECK(!iter.IsAtEnd()); 522 RemoveHolder(render_frame_host, iter, 1); 523 } 524} 525 526} // namespace content 527