Implement SSLKEYLOGFILE for OpenSSL.
[chromium-blink-merge.git] / content / browser / android / java / gin_java_bridge_dispatcher_host.cc
blobca63276b5b051713a4cfe8032920aaf4b3eb16e3
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.
5 #include "content/browser/android/java/gin_java_bridge_dispatcher_host.h"
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"
24 #if !defined(OS_ANDROID)
25 #error "JavaBridge only supports OS_ANDROID"
26 #endif
28 namespace content {
30 namespace {
31 // The JavaBridge needs to use a Java thread so the callback
32 // will happen on a thread with a prepared Looper.
33 class JavaBridgeThread : public base::android::JavaHandlerThread {
34 public:
35 JavaBridgeThread() : base::android::JavaHandlerThread("JavaBridge") {
36 Start();
38 virtual ~JavaBridgeThread() {
39 Stop();
43 base::LazyInstance<JavaBridgeThread> g_background_thread =
44 LAZY_INSTANCE_INITIALIZER;
46 } // namespace
48 GinJavaBridgeDispatcherHost::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);
58 GinJavaBridgeDispatcherHost::~GinJavaBridgeDispatcherHost() {
59 DCHECK(pending_replies_.empty());
62 void GinJavaBridgeDispatcherHost::RenderFrameCreated(
63 RenderFrameHost* render_frame_host) {
64 for (NamedObjectMap::const_iterator iter = named_objects_.begin();
65 iter != named_objects_.end();
66 ++iter) {
67 render_frame_host->Send(new GinJavaBridgeMsg_AddNamedObject(
68 render_frame_host->GetRoutingID(), iter->first, iter->second));
72 void GinJavaBridgeDispatcherHost::RenderFrameDeleted(
73 RenderFrameHost* render_frame_host) {
74 DCHECK_CURRENTLY_ON(BrowserThread::UI);
75 IPC::Message* reply_msg = TakePendingReply(render_frame_host);
76 if (reply_msg != NULL) {
77 base::ListValue result;
78 result.Append(base::Value::CreateNullValue());
79 IPC::WriteParam(reply_msg, result);
80 IPC::WriteParam(reply_msg, kGinJavaBridgeRenderFrameDeleted);
81 render_frame_host->Send(reply_msg);
83 RemoveHolder(render_frame_host,
84 GinJavaBoundObject::ObjectMap::iterator(&objects_),
85 objects_.size());
88 GinJavaBoundObject::ObjectID GinJavaBridgeDispatcherHost::AddObject(
89 const base::android::JavaRef<jobject>& object,
90 const base::android::JavaRef<jclass>& safe_annotation_clazz,
91 bool is_named,
92 RenderFrameHost* holder) {
93 DCHECK(is_named || holder);
94 GinJavaBoundObject::ObjectID object_id;
95 JNIEnv* env = base::android::AttachCurrentThread();
96 JavaObjectWeakGlobalRef ref(env, object.obj());
97 if (is_named) {
98 object_id = objects_.Add(new scoped_refptr<GinJavaBoundObject>(
99 GinJavaBoundObject::CreateNamed(ref, safe_annotation_clazz)));
100 } else {
101 object_id = objects_.Add(new scoped_refptr<GinJavaBoundObject>(
102 GinJavaBoundObject::CreateTransient(
103 ref, safe_annotation_clazz, holder)));
105 #if DCHECK_IS_ON
107 GinJavaBoundObject::ObjectID added_object_id;
108 DCHECK(FindObjectId(object, &added_object_id));
109 DCHECK_EQ(object_id, added_object_id);
111 #endif // DCHECK_IS_ON
112 base::android::ScopedJavaLocalRef<jobject> retained_object_set =
113 retained_object_set_.get(env);
114 if (!retained_object_set.is_null()) {
115 JNI_Java_HashSet_add(env, retained_object_set, object);
117 return object_id;
120 bool GinJavaBridgeDispatcherHost::FindObjectId(
121 const base::android::JavaRef<jobject>& object,
122 GinJavaBoundObject::ObjectID* object_id) {
123 JNIEnv* env = base::android::AttachCurrentThread();
124 for (GinJavaBoundObject::ObjectMap::iterator it(&objects_); !it.IsAtEnd();
125 it.Advance()) {
126 if (env->IsSameObject(
127 object.obj(),
128 it.GetCurrentValue()->get()->GetLocalRef(env).obj())) {
129 *object_id = it.GetCurrentKey();
130 return true;
133 return false;
136 JavaObjectWeakGlobalRef GinJavaBridgeDispatcherHost::GetObjectWeakRef(
137 GinJavaBoundObject::ObjectID object_id) {
138 scoped_refptr<GinJavaBoundObject>* result = objects_.Lookup(object_id);
139 scoped_refptr<GinJavaBoundObject> object(result ? *result : NULL);
140 if (object.get())
141 return object->GetWeakRef();
142 else
143 return JavaObjectWeakGlobalRef();
146 void GinJavaBridgeDispatcherHost::RemoveHolder(
147 RenderFrameHost* holder,
148 const GinJavaBoundObject::ObjectMap::iterator& from,
149 size_t count) {
150 JNIEnv* env = base::android::AttachCurrentThread();
151 base::android::ScopedJavaLocalRef<jobject> retained_object_set =
152 retained_object_set_.get(env);
153 size_t i = 0;
154 for (GinJavaBoundObject::ObjectMap::iterator it(from);
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));
166 objects_.Remove(it.GetCurrentKey());
171 void GinJavaBridgeDispatcherHost::AddNamedObject(
172 const std::string& name,
173 const base::android::JavaRef<jobject>& object,
174 const base::android::JavaRef<jclass>& safe_annotation_clazz) {
175 DCHECK_CURRENTLY_ON(BrowserThread::UI);
176 GinJavaBoundObject::ObjectID object_id;
177 NamedObjectMap::iterator iter = named_objects_.find(name);
178 bool existing_object = FindObjectId(object, &object_id);
179 if (existing_object && iter != named_objects_.end() &&
180 iter->second == object_id) {
181 // Nothing to do.
182 return;
184 if (iter != named_objects_.end()) {
185 RemoveNamedObject(iter->first);
187 if (existing_object) {
188 (*objects_.Lookup(object_id))->AddName();
189 } else {
190 object_id = AddObject(object, safe_annotation_clazz, true, NULL);
192 named_objects_[name] = object_id;
194 web_contents()->SendToAllFrames(
195 new GinJavaBridgeMsg_AddNamedObject(MSG_ROUTING_NONE, name, object_id));
198 void GinJavaBridgeDispatcherHost::RemoveNamedObject(
199 const std::string& name) {
200 DCHECK_CURRENTLY_ON(BrowserThread::UI);
201 NamedObjectMap::iterator iter = named_objects_.find(name);
202 if (iter == named_objects_.end())
203 return;
205 // |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.
207 const std::string copied_name(name);
209 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(iter->second));
210 named_objects_.erase(iter);
211 object->RemoveName();
213 // Not erasing from the objects map, as we can still receive method
214 // invocation requests for this object, and they should work until the
215 // java object is gone.
216 if (!object->IsNamed()) {
217 JNIEnv* env = base::android::AttachCurrentThread();
218 base::android::ScopedJavaLocalRef<jobject> retained_object_set =
219 retained_object_set_.get(env);
220 if (!retained_object_set.is_null()) {
221 JNI_Java_HashSet_remove(
222 env, retained_object_set, object->GetLocalRef(env));
226 web_contents()->SendToAllFrames(
227 new GinJavaBridgeMsg_RemoveNamedObject(MSG_ROUTING_NONE, copied_name));
230 void GinJavaBridgeDispatcherHost::SetAllowObjectContentsInspection(bool allow) {
231 allow_object_contents_inspection_ = allow;
234 void GinJavaBridgeDispatcherHost::DocumentAvailableInMainFrame() {
235 DCHECK_CURRENTLY_ON(BrowserThread::UI);
236 // Called when the window object has been cleared in the main frame.
237 // That means, all sub-frames have also been cleared, so only named
238 // objects survived.
239 JNIEnv* env = base::android::AttachCurrentThread();
240 base::android::ScopedJavaLocalRef<jobject> retained_object_set =
241 retained_object_set_.get(env);
242 if (!retained_object_set.is_null()) {
243 JNI_Java_HashSet_clear(env, retained_object_set);
246 // We also need to add back the named objects we have so far as they
247 // should survive navigations.
248 for (GinJavaBoundObject::ObjectMap::iterator it(&objects_); !it.IsAtEnd();
249 it.Advance()) {
250 scoped_refptr<GinJavaBoundObject> object(*it.GetCurrentValue());
251 if (object->IsNamed()) {
252 if (!retained_object_set.is_null()) {
253 JNI_Java_HashSet_add(
254 env, retained_object_set, object->GetLocalRef(env));
256 } else {
257 objects_.Remove(it.GetCurrentKey());
262 namespace {
264 // TODO(mnaganov): Implement passing of a parameter into sync message handlers.
265 class MessageForwarder : public IPC::Sender {
266 public:
267 MessageForwarder(GinJavaBridgeDispatcherHost* gjbdh,
268 RenderFrameHost* render_frame_host)
269 : gjbdh_(gjbdh), render_frame_host_(render_frame_host) {}
270 void OnGetMethods(GinJavaBoundObject::ObjectID object_id,
271 IPC::Message* reply_msg) {
272 gjbdh_->OnGetMethods(render_frame_host_,
273 object_id,
274 reply_msg);
276 void OnHasMethod(GinJavaBoundObject::ObjectID object_id,
277 const std::string& method_name,
278 IPC::Message* reply_msg) {
279 gjbdh_->OnHasMethod(render_frame_host_,
280 object_id,
281 method_name,
282 reply_msg);
284 void OnInvokeMethod(GinJavaBoundObject::ObjectID object_id,
285 const std::string& method_name,
286 const base::ListValue& arguments,
287 IPC::Message* reply_msg) {
288 gjbdh_->OnInvokeMethod(render_frame_host_,
289 object_id,
290 method_name,
291 arguments,
292 reply_msg);
294 virtual bool Send(IPC::Message* msg) OVERRIDE {
295 NOTREACHED();
296 return false;
298 private:
299 GinJavaBridgeDispatcherHost* gjbdh_;
300 RenderFrameHost* render_frame_host_;
305 bool GinJavaBridgeDispatcherHost::OnMessageReceived(
306 const IPC::Message& message,
307 RenderFrameHost* render_frame_host) {
308 DCHECK(render_frame_host);
309 bool handled = true;
310 MessageForwarder forwarder(this, render_frame_host);
311 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(GinJavaBridgeDispatcherHost, message,
312 render_frame_host)
313 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_GetMethods,
314 &forwarder,
315 MessageForwarder::OnGetMethods)
316 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_HasMethod,
317 &forwarder,
318 MessageForwarder::OnHasMethod)
319 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_InvokeMethod,
320 &forwarder,
321 MessageForwarder::OnInvokeMethod)
322 IPC_MESSAGE_HANDLER(GinJavaBridgeHostMsg_ObjectWrapperDeleted,
323 OnObjectWrapperDeleted)
324 IPC_MESSAGE_UNHANDLED(handled = false)
325 IPC_END_MESSAGE_MAP()
326 return handled;
329 namespace {
331 class IsValidRenderFrameHostHelper
332 : public base::RefCounted<IsValidRenderFrameHostHelper> {
333 public:
334 explicit IsValidRenderFrameHostHelper(RenderFrameHost* rfh_to_match)
335 : rfh_to_match_(rfh_to_match), rfh_found_(false) {}
337 bool rfh_found() { return rfh_found_; }
339 void OnFrame(RenderFrameHost* rfh) {
340 if (rfh_to_match_ == rfh) rfh_found_ = true;
343 private:
344 friend class base::RefCounted<IsValidRenderFrameHostHelper>;
346 ~IsValidRenderFrameHostHelper() {}
348 RenderFrameHost* rfh_to_match_;
349 bool rfh_found_;
351 DISALLOW_COPY_AND_ASSIGN(IsValidRenderFrameHostHelper);
354 } // namespace
356 bool GinJavaBridgeDispatcherHost::IsValidRenderFrameHost(
357 RenderFrameHost* render_frame_host) {
358 scoped_refptr<IsValidRenderFrameHostHelper> helper =
359 new IsValidRenderFrameHostHelper(render_frame_host);
360 web_contents()->ForEachFrame(
361 base::Bind(&IsValidRenderFrameHostHelper::OnFrame, helper));
362 return helper->rfh_found();
365 void GinJavaBridgeDispatcherHost::OnGetMethods(
366 RenderFrameHost* render_frame_host,
367 GinJavaBoundObject::ObjectID object_id,
368 IPC::Message* reply_msg) {
369 DCHECK_CURRENTLY_ON(BrowserThread::UI);
370 DCHECK(render_frame_host);
371 if (!allow_object_contents_inspection_) {
372 IPC::WriteParam(reply_msg, std::set<std::string>());
373 render_frame_host->Send(reply_msg);
374 return;
376 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id));
377 if (!object) {
378 LOG(ERROR) << "WebView: Unknown object: " << object_id;
379 IPC::WriteParam(reply_msg, std::set<std::string>());
380 render_frame_host->Send(reply_msg);
381 return;
383 DCHECK(!HasPendingReply(render_frame_host));
384 pending_replies_[render_frame_host] = reply_msg;
385 base::PostTaskAndReplyWithResult(
386 g_background_thread.Get().message_loop()->message_loop_proxy(),
387 FROM_HERE,
388 base::Bind(&GinJavaBoundObject::GetMethodNames, object),
389 base::Bind(&GinJavaBridgeDispatcherHost::SendMethods,
390 AsWeakPtr(),
391 render_frame_host));
394 void GinJavaBridgeDispatcherHost::SendMethods(
395 RenderFrameHost* render_frame_host,
396 const std::set<std::string>& method_names) {
397 IPC::Message* reply_msg = TakePendingReply(render_frame_host);
398 if (!reply_msg) {
399 return;
401 IPC::WriteParam(reply_msg, method_names);
402 render_frame_host->Send(reply_msg);
405 void GinJavaBridgeDispatcherHost::OnHasMethod(
406 RenderFrameHost* render_frame_host,
407 GinJavaBoundObject::ObjectID object_id,
408 const std::string& method_name,
409 IPC::Message* reply_msg) {
410 DCHECK_CURRENTLY_ON(BrowserThread::UI);
411 DCHECK(render_frame_host);
412 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id));
413 if (!object) {
414 LOG(ERROR) << "WebView: Unknown object: " << object_id;
415 IPC::WriteParam(reply_msg, false);
416 render_frame_host->Send(reply_msg);
417 return;
419 DCHECK(!HasPendingReply(render_frame_host));
420 pending_replies_[render_frame_host] = reply_msg;
421 base::PostTaskAndReplyWithResult(
422 g_background_thread.Get().message_loop()->message_loop_proxy(),
423 FROM_HERE,
424 base::Bind(&GinJavaBoundObject::HasMethod, object, method_name),
425 base::Bind(&GinJavaBridgeDispatcherHost::SendHasMethodReply,
426 AsWeakPtr(),
427 render_frame_host));
430 void GinJavaBridgeDispatcherHost::SendHasMethodReply(
431 RenderFrameHost* render_frame_host,
432 bool result) {
433 IPC::Message* reply_msg = TakePendingReply(render_frame_host);
434 if (!reply_msg) {
435 return;
437 IPC::WriteParam(reply_msg, result);
438 render_frame_host->Send(reply_msg);
441 void GinJavaBridgeDispatcherHost::OnInvokeMethod(
442 RenderFrameHost* render_frame_host,
443 GinJavaBoundObject::ObjectID object_id,
444 const std::string& method_name,
445 const base::ListValue& arguments,
446 IPC::Message* reply_msg) {
447 DCHECK_CURRENTLY_ON(BrowserThread::UI);
448 DCHECK(render_frame_host);
449 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id));
450 if (!object) {
451 LOG(ERROR) << "WebView: Unknown object: " << object_id;
452 base::ListValue result;
453 result.Append(base::Value::CreateNullValue());
454 IPC::WriteParam(reply_msg, result);
455 IPC::WriteParam(reply_msg, kGinJavaBridgeUnknownObjectId);
456 render_frame_host->Send(reply_msg);
457 return;
459 DCHECK(!HasPendingReply(render_frame_host));
460 pending_replies_[render_frame_host] = reply_msg;
461 scoped_refptr<GinJavaMethodInvocationHelper> result =
462 new GinJavaMethodInvocationHelper(
463 make_scoped_ptr(new GinJavaBoundObjectDelegate(object))
464 .PassAs<GinJavaMethodInvocationHelper::ObjectDelegate>(),
465 method_name,
466 arguments);
467 result->Init(this);
468 g_background_thread.Get()
469 .message_loop()
470 ->message_loop_proxy()
471 ->PostTaskAndReply(
472 FROM_HERE,
473 base::Bind(&GinJavaMethodInvocationHelper::Invoke, result),
474 base::Bind(
475 &GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult,
476 AsWeakPtr(),
477 render_frame_host,
478 result));
481 void GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult(
482 RenderFrameHost* render_frame_host,
483 scoped_refptr<GinJavaMethodInvocationHelper> result) {
484 if (result->HoldsPrimitiveResult()) {
485 IPC::Message* reply_msg = TakePendingReply(render_frame_host);
486 if (!reply_msg) {
487 return;
489 IPC::WriteParam(reply_msg, result->GetPrimitiveResult());
490 IPC::WriteParam(reply_msg, result->GetInvocationError());
491 render_frame_host->Send(reply_msg);
492 } else {
493 ProcessMethodInvocationObjectResult(render_frame_host, result);
497 void GinJavaBridgeDispatcherHost::ProcessMethodInvocationObjectResult(
498 RenderFrameHost* render_frame_host,
499 scoped_refptr<GinJavaMethodInvocationHelper> result) {
500 DCHECK_CURRENTLY_ON(BrowserThread::UI);
502 if (!IsValidRenderFrameHost(render_frame_host)) {
503 // In this case, we must've already sent the reply when the render frame
504 // was destroyed.
505 DCHECK(!HasPendingReply(render_frame_host));
506 return;
509 base::ListValue wrapped_result;
510 if (!result->GetObjectResult().is_null()) {
511 GinJavaBoundObject::ObjectID returned_object_id;
512 if (FindObjectId(result->GetObjectResult(), &returned_object_id)) {
513 (*objects_.Lookup(returned_object_id))->AddHolder(render_frame_host);
514 } else {
515 returned_object_id = AddObject(result->GetObjectResult(),
516 result->GetSafeAnnotationClass(),
517 false,
518 render_frame_host);
520 wrapped_result.Append(
521 GinJavaBridgeValue::CreateObjectIDValue(
522 returned_object_id).release());
523 } else {
524 wrapped_result.Append(base::Value::CreateNullValue());
526 IPC::Message* reply_msg = TakePendingReply(render_frame_host);
527 if (!reply_msg) {
528 return;
530 IPC::WriteParam(reply_msg, wrapped_result);
531 IPC::WriteParam(reply_msg, result->GetInvocationError());
532 render_frame_host->Send(reply_msg);
535 void GinJavaBridgeDispatcherHost::OnObjectWrapperDeleted(
536 RenderFrameHost* render_frame_host,
537 GinJavaBoundObject::ObjectID object_id) {
538 DCHECK_CURRENTLY_ON(BrowserThread::UI);
539 DCHECK(render_frame_host);
540 if (objects_.Lookup(object_id)) {
541 GinJavaBoundObject::ObjectMap::iterator iter(&objects_);
542 while (!iter.IsAtEnd() && iter.GetCurrentKey() != object_id)
543 iter.Advance();
544 DCHECK(!iter.IsAtEnd());
545 RemoveHolder(render_frame_host, iter, 1);
549 IPC::Message* GinJavaBridgeDispatcherHost::TakePendingReply(
550 RenderFrameHost* render_frame_host) {
551 if (!IsValidRenderFrameHost(render_frame_host)) {
552 DCHECK(!HasPendingReply(render_frame_host));
553 return NULL;
556 PendingReplyMap::iterator it = pending_replies_.find(render_frame_host);
557 // There may be no pending reply if we're called from RenderFrameDeleted and
558 // we already sent the reply through the regular route.
559 if (it == pending_replies_.end()) {
560 return NULL;
563 IPC::Message* reply_msg = it->second;
564 pending_replies_.erase(it);
565 return reply_msg;
568 bool GinJavaBridgeDispatcherHost::HasPendingReply(
569 RenderFrameHost* render_frame_host) const {
570 return pending_replies_.find(render_frame_host) != pending_replies_.end();
573 } // namespace content