1 // Copyright (c) 2012 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. 4 5 #include "content/browser/renderer_host/java/java_bridge_dispatcher_host.h" 6 7 #include "base/android/java_handler_thread.h" 8 #include "base/bind.h" 9 #include "base/lazy_instance.h" 10 #include "content/browser/renderer_host/java/java_bridge_channel_host.h" 11 #include "content/browser/renderer_host/render_view_host_impl.h" 12 #include "content/child/child_process.h" 13 #include "content/child/npapi/npobject_stub.h" 14 #include "content/child/npapi/npobject_util.h" // For CreateNPVariantParam() 15 #include "content/common/java_bridge_messages.h" 16 #include "content/public/browser/browser_thread.h" 17 #include "content/public/browser/render_process_host.h" 18 #include "third_party/WebKit/public/web/WebBindings.h" 19 20 #if !defined(OS_ANDROID) 21 #error "JavaBridge only supports OS_ANDROID" 22 #endif 23 24 namespace content { 25 26 namespace { 27 // The JavaBridge needs to use a Java thread so the callback 28 // will happen on a thread with a prepared Looper. 29 class JavaBridgeThread : public base::android::JavaHandlerThread { 30 public: 31 JavaBridgeThread() : base::android::JavaHandlerThread("JavaBridge") { 32 Start(); 33 } 34 virtual ~JavaBridgeThread() { 35 Stop(); 36 } 37 }; 38 39 void CleanUpStubs(const std::vector<base::WeakPtr<NPObjectStub> > & stubs) { 40 for (size_t i = 0; i < stubs.size(); ++i) { 41 if (stubs[i]) { 42 stubs[i]->DeleteSoon(); 43 } 44 } 45 } 46 47 base::LazyInstance<JavaBridgeThread> g_background_thread = 48 LAZY_INSTANCE_INITIALIZER; 49 } // namespace 50 51 JavaBridgeDispatcherHost::JavaBridgeDispatcherHost( 52 RenderViewHost* render_view_host) 53 : render_view_host_(render_view_host) { 54 } 55 56 JavaBridgeDispatcherHost::~JavaBridgeDispatcherHost() { 57 g_background_thread.Get().message_loop()->PostTask( 58 FROM_HERE, 59 base::Bind(&CleanUpStubs, stubs_)); 60 } 61 62 void JavaBridgeDispatcherHost::AddNamedObject(const base::string16& name, 63 NPObject* object) { 64 NPVariant_Param variant_param; 65 CreateNPVariantParam(object, &variant_param); 66 67 Send(new JavaBridgeMsg_AddNamedObject( 68 render_view_host_->GetRoutingID(), name, variant_param)); 69 } 70 71 void JavaBridgeDispatcherHost::RemoveNamedObject(const base::string16& name) { 72 // On receipt of this message, the JavaBridgeDispatcher will drop its 73 // reference to the corresponding proxy object. When the last reference is 74 // removed, the proxy object will delete its NPObjectProxy, which will cause 75 // the NPObjectStub to be deleted, which will drop its reference to the 76 // original NPObject. 77 Send(new JavaBridgeMsg_RemoveNamedObject( 78 render_view_host_->GetRoutingID(), name)); 79 } 80 81 void JavaBridgeDispatcherHost::RenderViewDeleted() { 82 render_view_host_ = NULL; 83 } 84 85 void JavaBridgeDispatcherHost::OnGetChannelHandle(IPC::Message* reply_msg) { 86 g_background_thread.Get().message_loop()->PostTask( 87 FROM_HERE, 88 base::Bind(&JavaBridgeDispatcherHost::GetChannelHandle, this, reply_msg)); 89 } 90 91 void JavaBridgeDispatcherHost::Send(IPC::Message* msg) { 92 if (render_view_host_) { 93 render_view_host_->Send(msg); 94 return; 95 } 96 97 delete msg; 98 } 99 100 void JavaBridgeDispatcherHost::GetChannelHandle(IPC::Message* reply_msg) { 101 // The channel creates the channel handle based on the renderer ID we passed 102 // to GetJavaBridgeChannelHost() and, on POSIX, the file descriptor used by 103 // the underlying channel. 104 JavaBridgeHostMsg_GetChannelHandle::WriteReplyParams( 105 reply_msg, 106 channel_->channel_handle()); 107 108 BrowserThread::PostTask( 109 BrowserThread::UI, 110 FROM_HERE, 111 base::Bind(&JavaBridgeDispatcherHost::Send, this, reply_msg)); 112 } 113 114 void JavaBridgeDispatcherHost::CreateNPVariantParam(NPObject* object, 115 NPVariant_Param* param) { 116 // The JavaBridgeChannelHost needs to be created on the background thread, as 117 // that is where Java objects will live, and CreateNPVariantParam() needs the 118 // channel to create the NPObjectStub. To avoid blocking here until the 119 // channel is ready, create the NPVariant_Param by hand, then post a message 120 // to the background thread to set up the channel and create the corresponding 121 // NPObjectStub. Post that message before doing any IPC, to make sure that 122 // the channel and object proxies are ready before responses are received 123 // from the renderer. 124 125 // Create an NPVariantParam suitable for serialization over IPC from our 126 // NPVariant. See CreateNPVariantParam() in npobject_utils. 127 param->type = NPVARIANT_PARAM_SENDER_OBJECT_ROUTING_ID; 128 int route_id = JavaBridgeChannelHost::ThreadsafeGenerateRouteID(); 129 param->npobject_routing_id = route_id; 130 131 blink::WebBindings::retainObject(object); 132 g_background_thread.Get().message_loop()->PostTask( 133 FROM_HERE, 134 base::Bind(&JavaBridgeDispatcherHost::CreateObjectStub, this, object, 135 render_view_host_->GetProcess()->GetID(), route_id)); 136 } 137 138 void JavaBridgeDispatcherHost::CreateObjectStub(NPObject* object, 139 int render_process_id, 140 int route_id) { 141 DCHECK_EQ(g_background_thread.Get().message_loop(), 142 base::MessageLoop::current()); 143 if (!channel_.get()) { 144 channel_ = JavaBridgeChannelHost::GetJavaBridgeChannelHost( 145 render_process_id, 146 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)); 147 } 148 149 // In a typical scenario, the lifetime of each NPObjectStub is governed by 150 // that of the NPObjectProxy in the renderer, via the channel. However, 151 // we cannot guaranteed that the renderer always terminates cleanly 152 // (crashes / sometimes just unavoidable). We keep a weak reference to 153 // it now and schedule a delete on it when this host is getting deleted. 154 155 // Pass 0 for the containing window, as it's only used by plugins to pump the 156 // window message queue when a method on a renderer-side object causes a 157 // dialog to be displayed, and the Java Bridge does not need this 158 // functionality. The page URL is also not required. 159 stubs_.push_back((new NPObjectStub( 160 object, channel_.get(), route_id, 0, GURL()))->AsWeakPtr()); 161 162 // The NPObjectStub takes a reference to the NPObject. Release the ref added 163 // in CreateNPVariantParam(). 164 blink::WebBindings::releaseObject(object); 165 } 166 167 } // namespace content 168