Process Alt-Svc headers.
[chromium-blink-merge.git] / content / browser / android / java / gin_java_bridge_dispatcher_host.cc
blobd217988366ca3ec72b0fe5895f722b05b3e4c48f
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 DCHECK(!named_objects_.empty());
48 if (!web_contents()->GetRenderProcessHost()->GetChannel()) return;
50 DCHECK(!GinJavaBridgeMessageFilter::FromHost(this, false));
51 scoped_refptr<GinJavaBridgeMessageFilter> filter =
52 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 scoped_refptr<GinJavaBridgeMessageFilter> filter =
63 GinJavaBridgeMessageFilter::FromHost(this, false);
64 if (filter) {
65 filter->AddRoutingIdForHost(this, render_frame_host);
66 } else if (!named_objects_.empty()) {
67 InstallFilterAndRegisterAllRoutingIds();
69 for (NamedObjectMap::const_iterator iter = named_objects_.begin();
70 iter != named_objects_.end();
71 ++iter) {
72 render_frame_host->Send(new GinJavaBridgeMsg_AddNamedObject(
73 render_frame_host->GetRoutingID(), iter->first, iter->second));
77 void GinJavaBridgeDispatcherHost::WebContentsDestroyed() {
78 scoped_refptr<GinJavaBridgeMessageFilter> filter =
79 GinJavaBridgeMessageFilter::FromHost(this, false);
80 if (filter)
81 filter->RemoveHost(this);
84 GinJavaBoundObject::ObjectID GinJavaBridgeDispatcherHost::AddObject(
85 const base::android::JavaRef<jobject>& object,
86 const base::android::JavaRef<jclass>& safe_annotation_clazz,
87 bool is_named,
88 int32 holder) {
89 // Can be called on any thread. Calls come from the UI thread via
90 // AddNamedObject, and from the background thread, when injected Java
91 // object's method returns a Java object.
92 DCHECK(is_named || holder);
93 JNIEnv* env = base::android::AttachCurrentThread();
94 JavaObjectWeakGlobalRef ref(env, object.obj());
95 scoped_refptr<GinJavaBoundObject> new_object =
96 is_named ? GinJavaBoundObject::CreateNamed(ref, safe_annotation_clazz)
97 : GinJavaBoundObject::CreateTransient(ref, safe_annotation_clazz,
98 holder);
99 GinJavaBoundObject::ObjectID object_id = next_object_id_++;
101 base::AutoLock locker(objects_lock_);
102 objects_[object_id] = new_object;
104 #if DCHECK_IS_ON()
106 GinJavaBoundObject::ObjectID added_object_id;
107 DCHECK(FindObjectId(object, &added_object_id));
108 DCHECK_EQ(object_id, added_object_id);
110 #endif // DCHECK_IS_ON()
111 base::android::ScopedJavaLocalRef<jobject> retained_object_set =
112 retained_object_set_.get(env);
113 if (!retained_object_set.is_null()) {
114 base::AutoLock locker(objects_lock_);
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 // Can be called on any thread.
124 JNIEnv* env = base::android::AttachCurrentThread();
125 base::AutoLock locker(objects_lock_);
126 for (const auto& pair : objects_) {
127 if (env->IsSameObject(
128 object.obj(),
129 pair.second->GetLocalRef(env).obj())) {
130 *object_id = pair.first;
131 return true;
134 return false;
137 JavaObjectWeakGlobalRef GinJavaBridgeDispatcherHost::GetObjectWeakRef(
138 GinJavaBoundObject::ObjectID object_id) {
139 scoped_refptr<GinJavaBoundObject> object = FindObject(object_id);
140 if (object.get())
141 return object->GetWeakRef();
142 else
143 return JavaObjectWeakGlobalRef();
146 JavaObjectWeakGlobalRef
147 GinJavaBridgeDispatcherHost::RemoveHolderAndAdvanceLocked(
148 int32 holder,
149 ObjectMap::iterator* iter_ptr) {
150 objects_lock_.AssertAcquired();
151 JavaObjectWeakGlobalRef result;
152 scoped_refptr<GinJavaBoundObject> object((*iter_ptr)->second);
153 bool object_erased = false;
154 if (!object->IsNamed()) {
155 object->RemoveHolder(holder);
156 if (!object->HasHolders()) {
157 result = object->GetWeakRef();
158 objects_.erase((*iter_ptr)++);
159 object_erased = true;
162 if (!object_erased) {
163 ++(*iter_ptr);
165 return result;
168 void GinJavaBridgeDispatcherHost::RemoveFromRetainedObjectSetLocked(
169 const JavaObjectWeakGlobalRef& ref) {
170 objects_lock_.AssertAcquired();
171 JNIEnv* env = base::android::AttachCurrentThread();
172 base::android::ScopedJavaLocalRef<jobject> retained_object_set =
173 retained_object_set_.get(env);
174 if (!retained_object_set.is_null()) {
175 JNI_Java_HashSet_remove(env, retained_object_set, ref.get(env));
179 void GinJavaBridgeDispatcherHost::AddNamedObject(
180 const std::string& name,
181 const base::android::JavaRef<jobject>& object,
182 const base::android::JavaRef<jclass>& safe_annotation_clazz) {
183 DCHECK_CURRENTLY_ON(BrowserThread::UI);
184 GinJavaBoundObject::ObjectID object_id;
185 NamedObjectMap::iterator iter = named_objects_.find(name);
186 bool existing_object = FindObjectId(object, &object_id);
187 if (existing_object && iter != named_objects_.end() &&
188 iter->second == object_id) {
189 // Nothing to do.
190 return;
192 if (iter != named_objects_.end()) {
193 RemoveNamedObject(iter->first);
195 if (existing_object) {
196 base::AutoLock locker(objects_lock_);
197 objects_[object_id]->AddName();
198 } else {
199 object_id = AddObject(object, safe_annotation_clazz, true, 0);
201 named_objects_[name] = object_id;
203 scoped_refptr<GinJavaBridgeMessageFilter> filter =
204 GinJavaBridgeMessageFilter::FromHost(this, false);
205 if (!filter)
206 InstallFilterAndRegisterAllRoutingIds();
207 web_contents()->SendToAllFrames(
208 new GinJavaBridgeMsg_AddNamedObject(MSG_ROUTING_NONE, name, object_id));
211 void GinJavaBridgeDispatcherHost::RemoveNamedObject(
212 const std::string& name) {
213 DCHECK_CURRENTLY_ON(BrowserThread::UI);
214 NamedObjectMap::iterator iter = named_objects_.find(name);
215 if (iter == named_objects_.end())
216 return;
218 // |name| may come from |named_objects_|. Make a copy of name so that if
219 // |name| is from |named_objects_| it'll be valid after the remove below.
220 const std::string copied_name(name);
223 base::AutoLock locker(objects_lock_);
224 objects_[iter->second]->RemoveName();
226 named_objects_.erase(iter);
228 // As the object isn't going to be removed from the JavaScript side until the
229 // next page reload, calls to it must still work, thus we should continue to
230 // hold it. All the transient objects and removed named objects will be purged
231 // during the cleansing caused by DocumentAvailableInMainFrame event.
233 web_contents()->SendToAllFrames(
234 new GinJavaBridgeMsg_RemoveNamedObject(MSG_ROUTING_NONE, copied_name));
237 void GinJavaBridgeDispatcherHost::SetAllowObjectContentsInspection(bool allow) {
238 if (!JavaBridgeThread::CurrentlyOn()) {
239 JavaBridgeThread::GetTaskRunner()->PostTask(
240 FROM_HERE,
241 base::Bind(
242 &GinJavaBridgeDispatcherHost::SetAllowObjectContentsInspection,
243 this, allow));
244 return;
246 allow_object_contents_inspection_ = allow;
249 void GinJavaBridgeDispatcherHost::DocumentAvailableInMainFrame() {
250 DCHECK_CURRENTLY_ON(BrowserThread::UI);
251 // Called when the window object has been cleared in the main frame.
252 // That means, all sub-frames have also been cleared, so only named
253 // objects survived.
254 JNIEnv* env = base::android::AttachCurrentThread();
255 base::android::ScopedJavaLocalRef<jobject> retained_object_set =
256 retained_object_set_.get(env);
257 base::AutoLock locker(objects_lock_);
258 if (!retained_object_set.is_null()) {
259 JNI_Java_HashSet_clear(env, retained_object_set);
261 auto iter = objects_.begin();
262 while (iter != objects_.end()) {
263 if (iter->second->IsNamed()) {
264 if (!retained_object_set.is_null()) {
265 JNI_Java_HashSet_add(
266 env, retained_object_set, iter->second->GetLocalRef(env));
268 ++iter;
269 } else {
270 objects_.erase(iter++);
275 scoped_refptr<GinJavaBoundObject> GinJavaBridgeDispatcherHost::FindObject(
276 GinJavaBoundObject::ObjectID object_id) {
277 // Can be called on any thread.
278 base::AutoLock locker(objects_lock_);
279 auto iter = objects_.find(object_id);
280 if (iter != objects_.end())
281 return iter->second;
282 LOG(ERROR) << "WebView: Unknown object: " << object_id;
283 return nullptr;
286 void GinJavaBridgeDispatcherHost::OnGetMethods(
287 GinJavaBoundObject::ObjectID object_id,
288 std::set<std::string>* returned_method_names) {
289 DCHECK(JavaBridgeThread::CurrentlyOn());
290 if (!allow_object_contents_inspection_)
291 return;
292 scoped_refptr<GinJavaBoundObject> object = FindObject(object_id);
293 if (object.get())
294 *returned_method_names = object->GetMethodNames();
297 void GinJavaBridgeDispatcherHost::OnHasMethod(
298 GinJavaBoundObject::ObjectID object_id,
299 const std::string& method_name,
300 bool* result) {
301 DCHECK(JavaBridgeThread::CurrentlyOn());
302 scoped_refptr<GinJavaBoundObject> object = FindObject(object_id);
303 if (object.get())
304 *result = object->HasMethod(method_name);
307 void GinJavaBridgeDispatcherHost::OnInvokeMethod(
308 int routing_id,
309 GinJavaBoundObject::ObjectID object_id,
310 const std::string& method_name,
311 const base::ListValue& arguments,
312 base::ListValue* wrapped_result,
313 content::GinJavaBridgeError* error_code) {
314 DCHECK(JavaBridgeThread::CurrentlyOn());
315 DCHECK(routing_id != MSG_ROUTING_NONE);
316 scoped_refptr<GinJavaBoundObject> object = FindObject(object_id);
317 if (!object.get()) {
318 wrapped_result->Append(base::Value::CreateNullValue());
319 *error_code = kGinJavaBridgeUnknownObjectId;
320 return;
322 scoped_refptr<GinJavaMethodInvocationHelper> result =
323 new GinJavaMethodInvocationHelper(
324 make_scoped_ptr(new GinJavaBoundObjectDelegate(object)),
325 method_name,
326 arguments);
327 result->Init(this);
328 result->Invoke();
329 *error_code = result->GetInvocationError();
330 if (result->HoldsPrimitiveResult()) {
331 scoped_ptr<base::ListValue> result_copy(
332 result->GetPrimitiveResult().DeepCopy());
333 wrapped_result->Swap(result_copy.get());
334 } else if (!result->GetObjectResult().is_null()) {
335 GinJavaBoundObject::ObjectID returned_object_id;
336 if (FindObjectId(result->GetObjectResult(), &returned_object_id)) {
337 base::AutoLock locker(objects_lock_);
338 objects_[returned_object_id]->AddHolder(routing_id);
339 } else {
340 returned_object_id = AddObject(result->GetObjectResult(),
341 result->GetSafeAnnotationClass(),
342 false,
343 routing_id);
345 wrapped_result->Append(
346 GinJavaBridgeValue::CreateObjectIDValue(
347 returned_object_id).release());
348 } else {
349 wrapped_result->Append(base::Value::CreateNullValue());
353 void GinJavaBridgeDispatcherHost::OnObjectWrapperDeleted(
354 int routing_id,
355 GinJavaBoundObject::ObjectID object_id) {
356 DCHECK(JavaBridgeThread::CurrentlyOn());
357 DCHECK(routing_id != MSG_ROUTING_NONE);
358 base::AutoLock locker(objects_lock_);
359 auto iter = objects_.find(object_id);
360 if (iter == objects_.end())
361 return;
362 JavaObjectWeakGlobalRef ref =
363 RemoveHolderAndAdvanceLocked(routing_id, &iter);
364 if (!ref.is_empty()) {
365 RemoveFromRetainedObjectSetLocked(ref);
369 } // namespace content