// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h" #include "base/android/jni_android.h" #include "base/android/jni_helper.h" #include "base/android/scoped_java_ref.h" #include "base/bind.h" #include "base/logging.h" #include "base/strings/utf_string_conversions.h" #include "content/browser/renderer_host/java/java_bound_object.h" #include "content/browser/renderer_host/java/java_bridge_dispatcher_host.h" #include "content/common/android/hash_set.h" #include "content/public/browser/browser_thread.h" #include "third_party/WebKit/public/web/WebBindings.h" namespace content { JavaBridgeDispatcherHostManager::JavaBridgeDispatcherHostManager( WebContents* web_contents) : WebContentsObserver(web_contents) { } JavaBridgeDispatcherHostManager::~JavaBridgeDispatcherHostManager() { for (ObjectMap::iterator iter = objects_.begin(); iter != objects_.end(); ++iter) { blink::WebBindings::releaseObject(iter->second); } DCHECK_EQ(0U, instances_.size()); } void JavaBridgeDispatcherHostManager::AddNamedObject(const base::string16& name, NPObject* object) { // Record this object in a map so that we can add it into RenderViewHosts // created later. The JavaBridgeDispatcherHost instances will take a // reference to the object, but we take one too, because this method can be // called before there are any such instances. blink::WebBindings::retainObject(object); objects_[name] = object; for (InstanceMap::iterator iter = instances_.begin(); iter != instances_.end(); ++iter) { iter->second->AddNamedObject(name, object); } } void JavaBridgeDispatcherHostManager::SetRetainedObjectSet( const JavaObjectWeakGlobalRef& retained_object_set) { // It's an error to replace the retained_object_set_ after it's been set, // so we check that it hasn't already been here. // TODO(benm): It'd be better to pass the set in the constructor to avoid // the chance of this happening; but that's tricky as this get's constructed // before ContentViewCore (which owns the set). Best solution may be to move // ownership of the JavaBridgerDispatchHostManager from WebContents to // ContentViewCore? JNIEnv* env = base::android::AttachCurrentThread(); base::android::ScopedJavaLocalRef new_retained_object_set = retained_object_set.get(env); base::android::ScopedJavaLocalRef current_retained_object_set = retained_object_set_.get(env); if (!env->IsSameObject(new_retained_object_set.obj(), current_retained_object_set.obj())) { DCHECK(current_retained_object_set.is_null()); retained_object_set_ = retained_object_set; } } void JavaBridgeDispatcherHostManager::RemoveNamedObject( const base::string16& name) { ObjectMap::iterator iter = objects_.find(name); if (iter == objects_.end()) { return; } blink::WebBindings::releaseObject(iter->second); objects_.erase(iter); for (InstanceMap::iterator iter = instances_.begin(); iter != instances_.end(); ++iter) { iter->second->RemoveNamedObject(name); } } void JavaBridgeDispatcherHostManager::OnGetChannelHandle( RenderViewHost* render_view_host, IPC::Message* reply_msg) { instances_[render_view_host]->OnGetChannelHandle(reply_msg); } void JavaBridgeDispatcherHostManager::RenderViewCreated( RenderViewHost* render_view_host) { // Creates a JavaBridgeDispatcherHost for the specified RenderViewHost and // adds all currently registered named objects to the new instance. scoped_refptr instance = new JavaBridgeDispatcherHost(render_view_host); for (ObjectMap::const_iterator iter = objects_.begin(); iter != objects_.end(); ++iter) { instance->AddNamedObject(iter->first, iter->second); } instances_[render_view_host] = instance; } void JavaBridgeDispatcherHostManager::RenderViewDeleted( RenderViewHost* render_view_host) { if (!instances_.count(render_view_host)) // Needed for tests. return; instances_[render_view_host]->RenderViewDeleted(); instances_.erase(render_view_host); } void JavaBridgeDispatcherHostManager::DocumentAvailableInMainFrame() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // Called when the window object has been cleared in the main frame. JNIEnv* env = base::android::AttachCurrentThread(); base::android::ScopedJavaLocalRef retained_object_set = retained_object_set_.get(env); if (!retained_object_set.is_null()) { JNI_Java_HashSet_clear(env, retained_object_set); // We also need to add back the named objects we have so far as they // should survive navigations. ObjectMap::iterator it = objects_.begin(); for (; it != objects_.end(); ++it) { JNI_Java_HashSet_add(env, retained_object_set, JavaBoundObject::GetJavaObject(it->second)); } } } void JavaBridgeDispatcherHostManager::JavaBoundObjectCreated( const base::android::JavaRef& object) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); JNIEnv* env = base::android::AttachCurrentThread(); base::android::ScopedJavaLocalRef retained_object_set = retained_object_set_.get(env); if (!retained_object_set.is_null()) { JNI_Java_HashSet_add(env, retained_object_set, object); } } void JavaBridgeDispatcherHostManager::JavaBoundObjectDestroyed( const base::android::JavaRef& object) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); JNIEnv* env = base::android::AttachCurrentThread(); base::android::ScopedJavaLocalRef retained_object_set = retained_object_set_.get(env); if (!retained_object_set.is_null()) { JNI_Java_HashSet_remove(env, retained_object_set, object); } } } // namespace content