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.
5 #include "xwalk/runtime/browser/android/xwalk_contents_io_thread_client_impl.h"
11 #include "base/android/jni_helper.h"
12 #include "base/android/jni_string.h"
13 #include "base/lazy_instance.h"
14 #include "base/memory/linked_ptr.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/synchronization/lock.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/render_frame_host.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "content/public/browser/resource_request_info.h"
22 #include "content/public/browser/web_contents.h"
23 #include "content/public/browser/web_contents_observer.h"
24 #include "jni/XWalkContentsIoThreadClient_jni.h"
25 #include "net/url_request/url_request.h"
27 #include "xwalk/runtime/browser/android/intercepted_request_data_impl.h"
29 using base::android::AttachCurrentThread;
30 using base::android::ConvertUTF8ToJavaString;
31 using base::android::JavaRef;
32 using base::android::ScopedJavaLocalRef;
33 using base::LazyInstance;
34 using content::BrowserThread;
35 using content::RenderFrameHost;
36 using content::WebContents;
44 struct IoThreadClientData {
45 bool pending_association;
46 JavaObjectWeakGlobalRef io_thread_client;
51 IoThreadClientData::IoThreadClientData() : pending_association(false) {}
53 typedef map<pair<int, int>, IoThreadClientData>
54 RenderFrameHostToIoThreadClientType;
56 static pair<int, int> GetRenderFrameHostIdPair(RenderFrameHost* rfh) {
57 return pair<int, int>(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
60 // RfhToIoThreadClientMap -----------------------------------------------------
61 class RfhToIoThreadClientMap {
63 static RfhToIoThreadClientMap* GetInstance();
64 void Set(pair<int, int> rfh_id, const IoThreadClientData& client);
65 bool Get(pair<int, int> rfh_id, IoThreadClientData* client);
66 void Erase(pair<int, int> rfh_id);
69 static LazyInstance<RfhToIoThreadClientMap> g_instance_;
71 RenderFrameHostToIoThreadClientType rfh_to_io_thread_client_;
75 LazyInstance<RfhToIoThreadClientMap> RfhToIoThreadClientMap::g_instance_ =
76 LAZY_INSTANCE_INITIALIZER;
79 RfhToIoThreadClientMap* RfhToIoThreadClientMap::GetInstance() {
80 return g_instance_.Pointer();
83 void RfhToIoThreadClientMap::Set(pair<int, int> rfh_id,
84 const IoThreadClientData& client) {
85 base::AutoLock lock(map_lock_);
86 rfh_to_io_thread_client_[rfh_id] = client;
89 bool RfhToIoThreadClientMap::Get(
90 pair<int, int> rfh_id, IoThreadClientData* client) {
91 base::AutoLock lock(map_lock_);
92 RenderFrameHostToIoThreadClientType::iterator iterator =
93 rfh_to_io_thread_client_.find(rfh_id);
94 if (iterator == rfh_to_io_thread_client_.end())
97 *client = iterator->second;
101 void RfhToIoThreadClientMap::Erase(pair<int, int> rfh_id) {
102 base::AutoLock lock(map_lock_);
103 rfh_to_io_thread_client_.erase(rfh_id);
106 // ClientMapEntryUpdater ------------------------------------------------------
108 class ClientMapEntryUpdater : public content::WebContentsObserver {
110 ClientMapEntryUpdater(JNIEnv* env, WebContents* web_contents,
113 virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) OVERRIDE;
114 virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) OVERRIDE;
115 virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
118 JavaObjectWeakGlobalRef jdelegate_;
121 ClientMapEntryUpdater::ClientMapEntryUpdater(JNIEnv* env,
122 WebContents* web_contents,
124 : content::WebContentsObserver(web_contents),
125 jdelegate_(env, jdelegate) {
126 DCHECK(web_contents);
129 if (web_contents->GetMainFrame())
130 RenderFrameCreated(web_contents->GetMainFrame());
133 void ClientMapEntryUpdater::RenderFrameCreated(RenderFrameHost* rfh) {
134 IoThreadClientData client_data;
135 client_data.io_thread_client = jdelegate_;
136 client_data.pending_association = false;
137 RfhToIoThreadClientMap::GetInstance()->Set(
138 GetRenderFrameHostIdPair(rfh), client_data);
142 void ClientMapEntryUpdater::RenderFrameDeleted(RenderFrameHost* rfh) {
143 RfhToIoThreadClientMap::GetInstance()->Erase(GetRenderFrameHostIdPair(rfh));
146 void ClientMapEntryUpdater::WebContentsDestroyed(WebContents* web_contents) {
152 // XWalkContentsIoThreadClientImpl -------------------------------------------
155 scoped_ptr<XWalkContentsIoThreadClient>
156 XWalkContentsIoThreadClient::FromID(int render_process_id,
157 int render_frame_id) {
158 pair<int, int> rfh_id(render_process_id, render_frame_id);
159 IoThreadClientData client_data;
160 if (!RfhToIoThreadClientMap::GetInstance()->Get(rfh_id, &client_data))
161 return scoped_ptr<XWalkContentsIoThreadClient>();
163 JNIEnv* env = AttachCurrentThread();
164 ScopedJavaLocalRef<jobject> java_delegate =
165 client_data.io_thread_client.get(env);
166 DCHECK(!client_data.pending_association || java_delegate.is_null());
167 return scoped_ptr<XWalkContentsIoThreadClient>(
168 new XWalkContentsIoThreadClientImpl(
169 client_data.pending_association, java_delegate));
173 void XWalkContentsIoThreadClient::SubFrameCreated(int render_process_id,
174 int parent_render_frame_id,
175 int child_render_frame_id) {
176 pair<int, int> parent_rfh_id(render_process_id, parent_render_frame_id);
177 pair<int, int> child_rfh_id(render_process_id, child_render_frame_id);
178 IoThreadClientData client_data;
179 if (!RfhToIoThreadClientMap::GetInstance()->Get(parent_rfh_id,
185 RfhToIoThreadClientMap::GetInstance()->Set(child_rfh_id, client_data);
190 void XWalkContentsIoThreadClientImpl::RegisterPendingContents(
191 WebContents* web_contents) {
192 IoThreadClientData client_data;
193 client_data.pending_association = true;
194 RfhToIoThreadClientMap::GetInstance()->Set(
195 GetRenderFrameHostIdPair(web_contents->GetMainFrame()), client_data);
199 void XWalkContentsIoThreadClientImpl::Associate(
200 WebContents* web_contents,
201 const JavaRef<jobject>& jclient) {
202 JNIEnv* env = AttachCurrentThread();
203 // The ClientMapEntryUpdater lifespan is tied to the WebContents.
204 new ClientMapEntryUpdater(env, web_contents, jclient.obj());
207 XWalkContentsIoThreadClientImpl::XWalkContentsIoThreadClientImpl(
208 bool pending_association,
209 const JavaRef<jobject>& obj)
210 : pending_association_(pending_association),
214 XWalkContentsIoThreadClientImpl::~XWalkContentsIoThreadClientImpl() {
215 // explict, out-of-line destructor.
218 bool XWalkContentsIoThreadClientImpl::PendingAssociation() const {
219 return pending_association_;
222 XWalkContentsIoThreadClient::CacheMode
223 XWalkContentsIoThreadClientImpl::GetCacheMode() const {
224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
225 if (java_object_.is_null())
226 return XWalkContentsIoThreadClient::LOAD_DEFAULT;
228 JNIEnv* env = AttachCurrentThread();
229 return static_cast<XWalkContentsIoThreadClient::CacheMode>(
230 Java_XWalkContentsIoThreadClient_getCacheMode(
231 env, java_object_.obj()));
234 scoped_ptr<InterceptedRequestData>
235 XWalkContentsIoThreadClientImpl::ShouldInterceptRequest(
236 const GURL& location,
237 const net::URLRequest* request) {
238 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
239 if (java_object_.is_null())
240 return scoped_ptr<InterceptedRequestData>();
241 const content::ResourceRequestInfo* info =
242 content::ResourceRequestInfo::ForRequest(request);
243 bool is_main_frame = info &&
244 info->GetResourceType() == ResourceType::MAIN_FRAME;
246 JNIEnv* env = AttachCurrentThread();
247 ScopedJavaLocalRef<jstring> jstring_url =
248 ConvertUTF8ToJavaString(env, location.spec());
249 ScopedJavaLocalRef<jobject> ret =
250 Java_XWalkContentsIoThreadClient_shouldInterceptRequest(
251 env, java_object_.obj(), jstring_url.obj(), is_main_frame);
253 return scoped_ptr<InterceptedRequestData>();
254 return scoped_ptr<InterceptedRequestData>(
255 new InterceptedRequestDataImpl(ret));
258 bool XWalkContentsIoThreadClientImpl::ShouldBlockContentUrls() const {
259 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
260 if (java_object_.is_null())
263 JNIEnv* env = AttachCurrentThread();
264 return Java_XWalkContentsIoThreadClient_shouldBlockContentUrls(
265 env, java_object_.obj());
268 bool XWalkContentsIoThreadClientImpl::ShouldBlockFileUrls() const {
269 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
270 if (java_object_.is_null())
273 JNIEnv* env = AttachCurrentThread();
274 return Java_XWalkContentsIoThreadClient_shouldBlockFileUrls(
275 env, java_object_.obj());
278 bool XWalkContentsIoThreadClientImpl::ShouldBlockNetworkLoads() const {
279 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
280 if (java_object_.is_null())
283 JNIEnv* env = AttachCurrentThread();
284 return Java_XWalkContentsIoThreadClient_shouldBlockNetworkLoads(
285 env, java_object_.obj());
288 void XWalkContentsIoThreadClientImpl::NewDownload(
290 const std::string& user_agent,
291 const std::string& content_disposition,
292 const std::string& mime_type,
293 int64 content_length) {
294 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
295 if (java_object_.is_null())
298 JNIEnv* env = AttachCurrentThread();
299 ScopedJavaLocalRef<jstring> jstring_url =
300 ConvertUTF8ToJavaString(env, url.spec());
301 ScopedJavaLocalRef<jstring> jstring_user_agent =
302 ConvertUTF8ToJavaString(env, user_agent);
303 ScopedJavaLocalRef<jstring> jstring_content_disposition =
304 ConvertUTF8ToJavaString(env, content_disposition);
305 ScopedJavaLocalRef<jstring> jstring_mime_type =
306 ConvertUTF8ToJavaString(env, mime_type);
308 Java_XWalkContentsIoThreadClient_onDownloadStart(
312 jstring_user_agent.obj(),
313 jstring_content_disposition.obj(),
314 jstring_mime_type.obj(),
318 void XWalkContentsIoThreadClientImpl::NewLoginRequest(
319 const std::string& realm,
320 const std::string& account,
321 const std::string& args) {
322 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
323 if (java_object_.is_null())
326 JNIEnv* env = AttachCurrentThread();
327 ScopedJavaLocalRef<jstring> jrealm = ConvertUTF8ToJavaString(env, realm);
328 ScopedJavaLocalRef<jstring> jargs = ConvertUTF8ToJavaString(env, args);
330 ScopedJavaLocalRef<jstring> jaccount;
331 if (!account.empty())
332 jaccount = ConvertUTF8ToJavaString(env, account);
334 Java_XWalkContentsIoThreadClient_newLoginRequest(
335 env, java_object_.obj(), jrealm.obj(), jaccount.obj(), jargs.obj());
338 bool RegisterXWalkContentsIoThreadClientImpl(JNIEnv* env) {
339 return RegisterNativesImpl(env);