Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / android / java / gin_java_bridge_dispatcher_host.cc
blob0a08896b09253e210e72897d278359c46e603edd
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/jni_android.h"
8 #include "base/android/scoped_java_ref.h"
9 #include "content/browser/android/java/gin_java_bound_object_delegate.h"
10 #include "content/browser/android/java/gin_java_bridge_message_filter.h"
11 #include "content/browser/android/java/java_bridge_thread.h"
12 #include "content/browser/android/java/jni_helper.h"
13 #include "content/common/android/gin_java_bridge_value.h"
14 #include "content/common/android/hash_set.h"
15 #include "content/common/gin_java_bridge_messages.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/render_frame_host.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "content/public/browser/web_contents.h"
21 #if !defined(OS_ANDROID)
22 #error "JavaBridge only supports OS_ANDROID"
23 #endif
25 namespace content {
27 GinJavaBridgeDispatcherHost::GinJavaBridgeDispatcherHost(
28 WebContents* web_contents,
29 jobject retained_object_set)
30 : WebContentsObserver(web_contents),
31 next_object_id_(1),
32 retained_object_set_(base::android::AttachCurrentThread(),
33 retained_object_set),
34 allow_object_contents_inspection_(true) {
35 DCHECK(retained_object_set);
38 GinJavaBridgeDispatcherHost::~GinJavaBridgeDispatcherHost() {
41 // GinJavaBridgeDispatcherHost gets created earlier than RenderProcessHost
42 // is initialized. So we postpone installing the message filter until we know
43 // that the RPH is in a good shape. Also, message filter installation is
44 // postponed until the first named object is created.
45 void GinJavaBridgeDispatcherHost::InstallFilterAndRegisterAllRoutingIds() {
46 DCHECK_CURRENTLY_ON(BrowserThread::UI);
47 if (named_objects_.empty() ||
48 !web_contents()->GetRenderProcessHost()->GetChannel()) {
49 return;
52 auto filter = GinJavaBridgeMessageFilter::FromHost(this, true);
53 // ForEachFrame is synchronous.
54 web_contents()->ForEachFrame(
55 base::Bind(&GinJavaBridgeMessageFilter::AddRoutingIdForHost, filter,
56 base::Unretained(this)));
59 void GinJavaBridgeDispatcherHost::RenderFrameCreated(
60 RenderFrameHost* render_frame_host) {
61 DCHECK_CURRENTLY_ON(BrowserThread::UI);
62 if (auto filter = GinJavaBridgeMessageFilter::FromHost(this, false)) {
63 filter->AddRoutingIdForHost(this, render_frame_host);
64 } else {
65 InstallFilterAndRegisterAllRoutingIds();
67 for (NamedObjectMap::const_iterator iter = named_objects_.begin();
68 iter != named_objects_.end();
69 ++iter) {
70 render_frame_host->Send(new GinJavaBridgeMsg_AddNamedObject(
71 render_frame_host->GetRoutingID(), iter->first, iter->second));
75 void GinJavaBridgeDispatcherHost::WebContentsDestroyed() {
76 scoped_refptr<GinJavaBridgeMessageFilter> filter =
77 GinJavaBridgeMessageFilter::FromHost(this, false);
78 if (filter)
79 filter->RemoveHost(this);
82 GinJavaBoundObject::ObjectID GinJavaBridgeDispatcherHost::AddObject(
83 const base::android::JavaRef<jobject>& object,
84 const base::android::JavaRef<jclass>& safe_annotation_clazz,
85 bool is_named,
86 int32 holder) {
87 // Can be called on any thread. Calls come from the UI thread via
88 // AddNamedObject, and from the background thread, when injected Java
89 // object's method returns a Java object.
90 DCHECK(is_named || holder);
91 JNIEnv* env = base::android::AttachCurrentThread();
92 JavaObjectWeakGlobalRef ref(env, object.obj());
93 scoped_refptr<GinJavaBoundObject> new_object =
94 is_named ? GinJavaBoundObject::CreateNamed(ref, safe_annotation_clazz)
95 : GinJavaBoundObject::CreateTransient(ref, safe_annotation_clazz,
96 holder);
97 GinJavaBoundObject::ObjectID object_id = next_object_id_++;
99 base::AutoLock locker(objects_lock_);
100 objects_[object_id] = new_object;
102 #if DCHECK_IS_ON()
104 GinJavaBoundObject::ObjectID added_object_id;
105 DCHECK(FindObjectId(object, &added_object_id));
106 DCHECK_EQ(object_id, added_object_id);
108 #endif // DCHECK_IS_ON()
109 base::android::ScopedJavaLocalRef<jobject> retained_object_set =
110 retained_object_set_.get(env);
111 if (!retained_object_set.is_null()) {
112 base::AutoLock locker(objects_lock_);
113 JNI_Java_HashSet_add(env, retained_object_set, object);
115 return object_id;
118 bool GinJavaBridgeDispatcherHost::FindObjectId(
119 const base::android::JavaRef<jobject>& object,
120 GinJavaBoundObject::ObjectID* object_id) {
121 // Can be called on any thread.
122 JNIEnv* env = base::android::AttachCurrentThread();
123 base::AutoLock locker(objects_lock_);
124 for (const auto& pair : objects_) {
125 if (env->IsSameObject(
126 object.obj(),
127 pair.second->GetLocalRef(env).obj())) {
128 *object_id = pair.first;
129 return true;
132 return false;
135 JavaObjectWeakGlobalRef GinJavaBridgeDispatcherHost::GetObjectWeakRef(
136 GinJavaBoundObject::ObjectID object_id) {
137 scoped_refptr<GinJavaBoundObject> object = FindObject(object_id);
138 if (object.get())
139 return object->GetWeakRef();
140 else
141 return JavaObjectWeakGlobalRef();
144 JavaObjectWeakGlobalRef
145 GinJavaBridgeDispatcherHost::RemoveHolderAndAdvanceLocked(
146 int32 holder,
147 ObjectMap::iterator* iter_ptr) {
148 objects_lock_.AssertAcquired();
149 JavaObjectWeakGlobalRef result;
150 scoped_refptr<GinJavaBoundObject> object((*iter_ptr)->second);
151 bool object_erased = false;
152 if (!object->IsNamed()) {
153 object->RemoveHolder(holder);
154 if (!object->HasHolders()) {
155 result = object->GetWeakRef();
156 objects_.erase((*iter_ptr)++);
157 object_erased = true;
160 if (!object_erased) {
161 ++(*iter_ptr);
163 return result;
166 void GinJavaBridgeDispatcherHost::RemoveFromRetainedObjectSetLocked(
167 const JavaObjectWeakGlobalRef& ref) {
168 objects_lock_.AssertAcquired();
169 JNIEnv* env = base::android::AttachCurrentThread();
170 base::android::ScopedJavaLocalRef<jobject> retained_object_set =
171 retained_object_set_.get(env);
172 if (!retained_object_set.is_null()) {
173 JNI_Java_HashSet_remove(env, retained_object_set, ref.get(env));
177 void GinJavaBridgeDispatcherHost::AddNamedObject(
178 const std::string& name,
179 const base::android::JavaRef<jobject>& object,
180 const base::android::JavaRef<jclass>& safe_annotation_clazz) {
181 DCHECK_CURRENTLY_ON(BrowserThread::UI);
182 GinJavaBoundObject::ObjectID object_id;
183 NamedObjectMap::iterator iter = named_objects_.find(name);
184 bool existing_object = FindObjectId(object, &object_id);
185 if (existing_object && iter != named_objects_.end() &&
186 iter->second == object_id) {
187 // Nothing to do.
188 return;
190 if (iter != named_objects_.end()) {
191 RemoveNamedObject(iter->first);
193 if (existing_object) {
194 base::AutoLock locker(objects_lock_);
195 objects_[object_id]->AddName();
196 } else {
197 object_id = AddObject(object, safe_annotation_clazz, true, 0);
199 named_objects_[name] = object_id;
201 // As GinJavaBridgeDispatcherHost can be created later than WebContents has
202 // notified the observers about new RenderFrame, it is necessary to ensure
203 // here that all render frame IDs are registered with the filter.
204 InstallFilterAndRegisterAllRoutingIds();
205 web_contents()->SendToAllFrames(
206 new GinJavaBridgeMsg_AddNamedObject(MSG_ROUTING_NONE, name, object_id));
209 void GinJavaBridgeDispatcherHost::RemoveNamedObject(
210 const std::string& name) {
211 DCHECK_CURRENTLY_ON(BrowserThread::UI);
212 NamedObjectMap::iterator iter = named_objects_.find(name);
213 if (iter == named_objects_.end())
214 return;
216 // |name| may come from |named_objects_|. Make a copy of name so that if
217 // |name| is from |named_objects_| it'll be valid after the remove below.
218 const std::string copied_name(name);
221 base::AutoLock locker(objects_lock_);
222 objects_[iter->second]->RemoveName();
224 named_objects_.erase(iter);
226 // As the object isn't going to be removed from the JavaScript side until the
227 // next page reload, calls to it must still work, thus we should continue to
228 // hold it. All the transient objects and removed named objects will be purged
229 // during the cleansing caused by DocumentAvailableInMainFrame event.
231 web_contents()->SendToAllFrames(
232 new GinJavaBridgeMsg_RemoveNamedObject(MSG_ROUTING_NONE, copied_name));
235 void GinJavaBridgeDispatcherHost::SetAllowObjectContentsInspection(bool allow) {
236 if (!JavaBridgeThread::CurrentlyOn()) {
237 JavaBridgeThread::GetTaskRunner()->PostTask(
238 FROM_HERE,
239 base::Bind(
240 &GinJavaBridgeDispatcherHost::SetAllowObjectContentsInspection,
241 this, allow));
242 return;
244 allow_object_contents_inspection_ = allow;
247 void GinJavaBridgeDispatcherHost::DocumentAvailableInMainFrame() {
248 DCHECK_CURRENTLY_ON(BrowserThread::UI);
249 // Called when the window object has been cleared in the main frame.
250 // That means, all sub-frames have also been cleared, so only named
251 // objects survived.
252 JNIEnv* env = base::android::AttachCurrentThread();
253 base::android::ScopedJavaLocalRef<jobject> retained_object_set =
254 retained_object_set_.get(env);
255 base::AutoLock locker(objects_lock_);
256 if (!retained_object_set.is_null()) {
257 JNI_Java_HashSet_clear(env, retained_object_set);
259 auto iter = objects_.begin();
260 while (iter != objects_.end()) {
261 if (iter->second->IsNamed()) {
262 if (!retained_object_set.is_null()) {
263 JNI_Java_HashSet_add(
264 env, retained_object_set, iter->second->GetLocalRef(env));
266 ++iter;
267 } else {
268 objects_.erase(iter++);
273 scoped_refptr<GinJavaBoundObject> GinJavaBridgeDispatcherHost::FindObject(
274 GinJavaBoundObject::ObjectID object_id) {
275 // Can be called on any thread.
276 base::AutoLock locker(objects_lock_);
277 auto iter = objects_.find(object_id);
278 if (iter != objects_.end())
279 return iter->second;
280 LOG(ERROR) << "WebView: Unknown object: " << object_id;
281 return nullptr;
284 void GinJavaBridgeDispatcherHost::OnGetMethods(
285 GinJavaBoundObject::ObjectID object_id,
286 std::set<std::string>* returned_method_names) {
287 DCHECK(JavaBridgeThread::CurrentlyOn());
288 if (!allow_object_contents_inspection_)
289 return;
290 scoped_refptr<GinJavaBoundObject> object = FindObject(object_id);
291 if (object.get())
292 *returned_method_names = object->GetMethodNames();
295 void GinJavaBridgeDispatcherHost::OnHasMethod(
296 GinJavaBoundObject::ObjectID object_id,
297 const std::string& method_name,
298 bool* result) {
299 DCHECK(JavaBridgeThread::CurrentlyOn());
300 scoped_refptr<GinJavaBoundObject> object = FindObject(object_id);
301 if (object.get())
302 *result = object->HasMethod(method_name);
305 void GinJavaBridgeDispatcherHost::OnInvokeMethod(
306 int routing_id,
307 GinJavaBoundObject::ObjectID object_id,
308 const std::string& method_name,
309 const base::ListValue& arguments,
310 base::ListValue* wrapped_result,
311 content::GinJavaBridgeError* error_code) {
312 DCHECK(JavaBridgeThread::CurrentlyOn());
313 DCHECK(routing_id != MSG_ROUTING_NONE);
314 scoped_refptr<GinJavaBoundObject> object = FindObject(object_id);
315 if (!object.get()) {
316 wrapped_result->Append(base::Value::CreateNullValue());
317 *error_code = kGinJavaBridgeUnknownObjectId;
318 return;
320 scoped_refptr<GinJavaMethodInvocationHelper> result =
321 new GinJavaMethodInvocationHelper(
322 make_scoped_ptr(new GinJavaBoundObjectDelegate(object)),
323 method_name,
324 arguments);
325 result->Init(this);
326 result->Invoke();
327 *error_code = result->GetInvocationError();
328 if (result->HoldsPrimitiveResult()) {
329 scoped_ptr<base::ListValue> result_copy(
330 result->GetPrimitiveResult().DeepCopy());
331 wrapped_result->Swap(result_copy.get());
332 } else if (!result->GetObjectResult().is_null()) {
333 GinJavaBoundObject::ObjectID returned_object_id;
334 if (FindObjectId(result->GetObjectResult(), &returned_object_id)) {
335 base::AutoLock locker(objects_lock_);
336 objects_[returned_object_id]->AddHolder(routing_id);
337 } else {
338 returned_object_id = AddObject(result->GetObjectResult(),
339 result->GetSafeAnnotationClass(),
340 false,
341 routing_id);
343 wrapped_result->Append(
344 GinJavaBridgeValue::CreateObjectIDValue(
345 returned_object_id).release());
346 } else {
347 wrapped_result->Append(base::Value::CreateNullValue());
351 void GinJavaBridgeDispatcherHost::OnObjectWrapperDeleted(
352 int routing_id,
353 GinJavaBoundObject::ObjectID object_id) {
354 DCHECK(JavaBridgeThread::CurrentlyOn());
355 DCHECK(routing_id != MSG_ROUTING_NONE);
356 base::AutoLock locker(objects_lock_);
357 auto iter = objects_.find(object_id);
358 if (iter == objects_.end())
359 return;
360 JavaObjectWeakGlobalRef ref =
361 RemoveHolderAndAdvanceLocked(routing_id, &iter);
362 if (!ref.is_empty()) {
363 RemoveFromRetainedObjectSetLocked(ref);
367 } // namespace content