Battery Status API: add UMA logging for Linux.
[chromium-blink-merge.git] / content / browser / android / java / gin_java_bridge_dispatcher_host.cc
blob74ccaa21aaf6b0352edd3d971c6d1630524292ae
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() {
61 void 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));
71 void GinJavaBridgeDispatcherHost::RenderFrameDeleted(
72 RenderFrameHost* render_frame_host) {
73 RemoveHolder(render_frame_host,
74 GinJavaBoundObject::ObjectMap::iterator(&objects_),
75 objects_.size());
78 GinJavaBoundObject::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)));
95 #if DCHECK_IS_ON
97 GinJavaBoundObject::ObjectID added_object_id;
98 DCHECK(FindObjectId(object, &added_object_id));
99 DCHECK_EQ(object_id, added_object_id);
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);
107 return object_id;
110 bool 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;
123 return false;
126 JavaObjectWeakGlobalRef 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();
136 void 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));
156 objects_.Remove(it.GetCurrentKey());
161 void 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;
174 if (iter != named_objects_.end()) {
175 RemoveNamedObject(iter->first);
177 if (existing_object) {
178 (*objects_.Lookup(object_id))->AddName();
179 } else {
180 object_id = AddObject(object, safe_annotation_clazz, true, NULL);
182 named_objects_[name] = object_id;
184 web_contents()->SendToAllFrames(
185 new GinJavaBridgeMsg_AddNamedObject(MSG_ROUTING_NONE, name, object_id));
188 void 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;
195 // |name| may come from |named_objects_|. Make a copy of name so that if
196 // |name| is from |named_objects_| it'll be valid after the remove below.
197 const std::string copied_name(name);
199 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(iter->second));
200 named_objects_.erase(iter);
201 object->RemoveName();
203 // Not erasing from the objects map, as we can still receive method
204 // invocation requests for this object, and they should work until the
205 // java object is gone.
206 if (!object->IsNamed()) {
207 JNIEnv* env = base::android::AttachCurrentThread();
208 base::android::ScopedJavaLocalRef<jobject> retained_object_set =
209 retained_object_set_.get(env);
210 if (!retained_object_set.is_null()) {
211 JNI_Java_HashSet_remove(
212 env, retained_object_set, object->GetLocalRef(env));
216 web_contents()->SendToAllFrames(
217 new GinJavaBridgeMsg_RemoveNamedObject(MSG_ROUTING_NONE, copied_name));
220 void GinJavaBridgeDispatcherHost::SetAllowObjectContentsInspection(bool allow) {
221 allow_object_contents_inspection_ = allow;
224 void GinJavaBridgeDispatcherHost::DocumentAvailableInMainFrame() {
225 DCHECK_CURRENTLY_ON(BrowserThread::UI);
226 // Called when the window object has been cleared in the main frame.
227 // That means, all sub-frames have also been cleared, so only named
228 // objects survived.
229 JNIEnv* env = base::android::AttachCurrentThread();
230 base::android::ScopedJavaLocalRef<jobject> retained_object_set =
231 retained_object_set_.get(env);
232 if (!retained_object_set.is_null()) {
233 JNI_Java_HashSet_clear(env, retained_object_set);
236 // We also need to add back the named objects we have so far as they
237 // should survive navigations.
238 for (GinJavaBoundObject::ObjectMap::iterator it(&objects_); !it.IsAtEnd();
239 it.Advance()) {
240 scoped_refptr<GinJavaBoundObject> object(*it.GetCurrentValue());
241 if (object->IsNamed()) {
242 if (!retained_object_set.is_null()) {
243 JNI_Java_HashSet_add(
244 env, retained_object_set, object->GetLocalRef(env));
246 } else {
247 objects_.Remove(it.GetCurrentKey());
252 namespace {
254 // TODO(mnaganov): Implement passing of a parameter into sync message handlers.
255 class MessageForwarder : public IPC::Sender {
256 public:
257 MessageForwarder(GinJavaBridgeDispatcherHost* gjbdh,
258 RenderFrameHost* render_frame_host)
259 : gjbdh_(gjbdh), render_frame_host_(render_frame_host) {}
260 void OnGetMethods(GinJavaBoundObject::ObjectID object_id,
261 IPC::Message* reply_msg) {
262 gjbdh_->OnGetMethods(render_frame_host_,
263 object_id,
264 reply_msg);
266 void OnHasMethod(GinJavaBoundObject::ObjectID object_id,
267 const std::string& method_name,
268 IPC::Message* reply_msg) {
269 gjbdh_->OnHasMethod(render_frame_host_,
270 object_id,
271 method_name,
272 reply_msg);
274 void OnInvokeMethod(GinJavaBoundObject::ObjectID object_id,
275 const std::string& method_name,
276 const base::ListValue& arguments,
277 IPC::Message* reply_msg) {
278 gjbdh_->OnInvokeMethod(render_frame_host_,
279 object_id,
280 method_name,
281 arguments,
282 reply_msg);
284 virtual bool Send(IPC::Message* msg) OVERRIDE {
285 NOTREACHED();
286 return false;
288 private:
289 GinJavaBridgeDispatcherHost* gjbdh_;
290 RenderFrameHost* render_frame_host_;
295 bool GinJavaBridgeDispatcherHost::OnMessageReceived(
296 const IPC::Message& message,
297 RenderFrameHost* render_frame_host) {
298 DCHECK(render_frame_host);
299 bool handled = true;
300 MessageForwarder forwarder(this, render_frame_host);
301 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(GinJavaBridgeDispatcherHost, message,
302 render_frame_host)
303 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_GetMethods,
304 &forwarder,
305 MessageForwarder::OnGetMethods)
306 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_HasMethod,
307 &forwarder,
308 MessageForwarder::OnHasMethod)
309 IPC_MESSAGE_FORWARD_DELAY_REPLY(GinJavaBridgeHostMsg_InvokeMethod,
310 &forwarder,
311 MessageForwarder::OnInvokeMethod)
312 IPC_MESSAGE_HANDLER(GinJavaBridgeHostMsg_ObjectWrapperDeleted,
313 OnObjectWrapperDeleted)
314 IPC_MESSAGE_UNHANDLED(handled = false)
315 IPC_END_MESSAGE_MAP()
316 return handled;
319 namespace {
321 class IsValidRenderFrameHostHelper
322 : public base::RefCounted<IsValidRenderFrameHostHelper> {
323 public:
324 explicit IsValidRenderFrameHostHelper(RenderFrameHost* rfh_to_match)
325 : rfh_to_match_(rfh_to_match), rfh_found_(false) {}
327 bool rfh_found() { return rfh_found_; }
329 void OnFrame(RenderFrameHost* rfh) {
330 if (rfh_to_match_ == rfh) rfh_found_ = true;
333 private:
334 friend class base::RefCounted<IsValidRenderFrameHostHelper>;
336 ~IsValidRenderFrameHostHelper() {}
338 RenderFrameHost* rfh_to_match_;
339 bool rfh_found_;
341 DISALLOW_COPY_AND_ASSIGN(IsValidRenderFrameHostHelper);
344 } // namespace
346 bool GinJavaBridgeDispatcherHost::IsValidRenderFrameHost(
347 RenderFrameHost* render_frame_host) {
348 scoped_refptr<IsValidRenderFrameHostHelper> helper =
349 new IsValidRenderFrameHostHelper(render_frame_host);
350 web_contents()->ForEachFrame(
351 base::Bind(&IsValidRenderFrameHostHelper::OnFrame, helper));
352 return helper->rfh_found();
355 void GinJavaBridgeDispatcherHost::SendReply(
356 RenderFrameHost* render_frame_host,
357 IPC::Message* reply_msg) {
358 DCHECK_CURRENTLY_ON(BrowserThread::UI);
359 if (IsValidRenderFrameHost(render_frame_host)) {
360 render_frame_host->Send(reply_msg);
361 } else {
362 delete reply_msg;
366 void GinJavaBridgeDispatcherHost::OnGetMethods(
367 RenderFrameHost* render_frame_host,
368 GinJavaBoundObject::ObjectID object_id,
369 IPC::Message* reply_msg) {
370 DCHECK_CURRENTLY_ON(BrowserThread::UI);
371 DCHECK(render_frame_host);
372 if (!allow_object_contents_inspection_) {
373 IPC::WriteParam(reply_msg, std::set<std::string>());
374 render_frame_host->Send(reply_msg);
375 return;
377 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id));
378 if (!object) {
379 LOG(ERROR) << "WebView: Unknown object: " << object_id;
380 IPC::WriteParam(reply_msg, std::set<std::string>());
381 render_frame_host->Send(reply_msg);
382 return;
384 base::PostTaskAndReplyWithResult(
385 g_background_thread.Get().message_loop()->message_loop_proxy(),
386 FROM_HERE,
387 base::Bind(&GinJavaBoundObject::GetMethodNames, object),
388 base::Bind(&GinJavaBridgeDispatcherHost::SendMethods,
389 AsWeakPtr(),
390 render_frame_host,
391 reply_msg));
394 void GinJavaBridgeDispatcherHost::SendMethods(
395 RenderFrameHost* render_frame_host,
396 IPC::Message* reply_msg,
397 const std::set<std::string>& method_names) {
398 IPC::WriteParam(reply_msg, method_names);
399 SendReply(render_frame_host, reply_msg);
402 void GinJavaBridgeDispatcherHost::OnHasMethod(
403 RenderFrameHost* render_frame_host,
404 GinJavaBoundObject::ObjectID object_id,
405 const std::string& method_name,
406 IPC::Message* reply_msg) {
407 DCHECK_CURRENTLY_ON(BrowserThread::UI);
408 DCHECK(render_frame_host);
409 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id));
410 if (!object) {
411 LOG(ERROR) << "WebView: Unknown object: " << object_id;
412 IPC::WriteParam(reply_msg, false);
413 render_frame_host->Send(reply_msg);
414 return;
416 base::PostTaskAndReplyWithResult(
417 g_background_thread.Get().message_loop()->message_loop_proxy(),
418 FROM_HERE,
419 base::Bind(&GinJavaBoundObject::HasMethod, object, method_name),
420 base::Bind(&GinJavaBridgeDispatcherHost::SendHasMethodReply,
421 AsWeakPtr(),
422 render_frame_host,
423 reply_msg));
426 void GinJavaBridgeDispatcherHost::SendHasMethodReply(
427 RenderFrameHost* render_frame_host,
428 IPC::Message* reply_msg,
429 bool result) {
430 IPC::WriteParam(reply_msg, result);
431 SendReply(render_frame_host, reply_msg);
434 void GinJavaBridgeDispatcherHost::OnInvokeMethod(
435 RenderFrameHost* render_frame_host,
436 GinJavaBoundObject::ObjectID object_id,
437 const std::string& method_name,
438 const base::ListValue& arguments,
439 IPC::Message* reply_msg) {
440 DCHECK_CURRENTLY_ON(BrowserThread::UI);
441 DCHECK(render_frame_host);
442 scoped_refptr<GinJavaBoundObject> object(*objects_.Lookup(object_id));
443 if (!object) {
444 LOG(ERROR) << "WebView: Unknown object: " << object_id;
445 base::ListValue result;
446 result.Append(base::Value::CreateNullValue());
447 IPC::WriteParam(reply_msg, result);
448 IPC::WriteParam(reply_msg, kGinJavaBridgeUnknownObjectId);
449 render_frame_host->Send(reply_msg);
450 return;
452 scoped_refptr<GinJavaMethodInvocationHelper> result =
453 new GinJavaMethodInvocationHelper(
454 make_scoped_ptr(new GinJavaBoundObjectDelegate(object))
455 .PassAs<GinJavaMethodInvocationHelper::ObjectDelegate>(),
456 method_name,
457 arguments);
458 result->Init(this);
459 g_background_thread.Get()
460 .message_loop()
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 reply_msg,
470 result));
473 void GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult(
474 RenderFrameHost* render_frame_host,
475 IPC::Message* reply_msg,
476 scoped_refptr<GinJavaMethodInvocationHelper> result) {
477 if (result->HoldsPrimitiveResult()) {
478 IPC::WriteParam(reply_msg, result->GetPrimitiveResult());
479 IPC::WriteParam(reply_msg, result->GetInvocationError());
480 SendReply(render_frame_host, reply_msg);
481 } else {
482 ProcessMethodInvocationObjectResult(render_frame_host, reply_msg, result);
486 void GinJavaBridgeDispatcherHost::ProcessMethodInvocationObjectResult(
487 RenderFrameHost* render_frame_host,
488 IPC::Message* reply_msg,
489 scoped_refptr<GinJavaMethodInvocationHelper> result) {
490 DCHECK_CURRENTLY_ON(BrowserThread::UI);
491 if (!IsValidRenderFrameHost(render_frame_host)) {
492 delete reply_msg;
493 return;
495 base::ListValue wrapped_result;
496 if (!result->GetObjectResult().is_null()) {
497 GinJavaBoundObject::ObjectID returned_object_id;
498 if (FindObjectId(result->GetObjectResult(), &returned_object_id)) {
499 (*objects_.Lookup(returned_object_id))->AddHolder(render_frame_host);
500 } else {
501 returned_object_id = AddObject(result->GetObjectResult(),
502 result->GetSafeAnnotationClass(),
503 false,
504 render_frame_host);
506 wrapped_result.Append(
507 GinJavaBridgeValue::CreateObjectIDValue(returned_object_id).release());
508 } else {
509 wrapped_result.Append(base::Value::CreateNullValue());
511 IPC::WriteParam(reply_msg, wrapped_result);
512 IPC::WriteParam(reply_msg, result->GetInvocationError());
513 render_frame_host->Send(reply_msg);
516 void GinJavaBridgeDispatcherHost::OnObjectWrapperDeleted(
517 RenderFrameHost* render_frame_host,
518 GinJavaBoundObject::ObjectID object_id) {
519 DCHECK_CURRENTLY_ON(BrowserThread::UI);
520 DCHECK(render_frame_host);
521 if (objects_.Lookup(object_id)) {
522 GinJavaBoundObject::ObjectMap::iterator iter(&objects_);
523 while (!iter.IsAtEnd() && iter.GetCurrentKey() != object_id)
524 iter.Advance();
525 DCHECK(!iter.IsAtEnd());
526 RemoveHolder(render_frame_host, iter, 1);
530 } // namespace content