Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / android_webview / native / aw_contents_io_thread_client_impl.cc
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 "android_webview/native/aw_contents_io_thread_client_impl.h"
6
7 #include <map>
8 #include <utility>
9
10 #include "android_webview/common/devtools_instrumentation.h"
11 #include "android_webview/native/aw_web_resource_response_impl.h"
12 #include "base/android/jni_array.h"
13 #include "base/android/jni_string.h"
14 #include "base/android/jni_weak_ref.h"
15 #include "base/lazy_instance.h"
16 #include "base/memory/linked_ptr.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/synchronization/lock.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/render_frame_host.h"
21 #include "content/public/browser/render_process_host.h"
22 #include "content/public/browser/render_view_host.h"
23 #include "content/public/browser/resource_request_info.h"
24 #include "content/public/browser/web_contents.h"
25 #include "content/public/browser/web_contents_observer.h"
26 #include "jni/AwContentsIoThreadClient_jni.h"
27 #include "net/http/http_request_headers.h"
28 #include "net/url_request/url_request.h"
29 #include "url/gurl.h"
30
31 using base::android::AttachCurrentThread;
32 using base::android::ConvertUTF8ToJavaString;
33 using base::android::JavaRef;
34 using base::android::ScopedJavaLocalRef;
35 using base::android::ToJavaArrayOfStrings;
36 using base::LazyInstance;
37 using content::BrowserThread;
38 using content::RenderFrameHost;
39 using content::ResourceType;
40 using content::WebContents;
41 using std::map;
42 using std::pair;
43 using std::string;
44 using std::vector;
45
46 namespace android_webview {
47
48 namespace {
49
50 struct IoThreadClientData {
51   bool pending_association;
52   JavaObjectWeakGlobalRef io_thread_client;
53
54   IoThreadClientData();
55 };
56
57 IoThreadClientData::IoThreadClientData() : pending_association(false) {}
58
59 typedef map<pair<int, int>, IoThreadClientData>
60     RenderFrameHostToIoThreadClientType;
61
62 static pair<int, int> GetRenderFrameHostIdPair(RenderFrameHost* rfh) {
63   return pair<int, int>(rfh->GetProcess()->GetID(), rfh->GetRoutingID());
64 }
65
66 // RfhToIoThreadClientMap -----------------------------------------------------
67 class RfhToIoThreadClientMap {
68  public:
69   static RfhToIoThreadClientMap* GetInstance();
70   void Set(pair<int, int> rfh_id, const IoThreadClientData& client);
71   bool Get(pair<int, int> rfh_id, IoThreadClientData* client);
72   void Erase(pair<int, int> rfh_id);
73
74  private:
75   static LazyInstance<RfhToIoThreadClientMap> g_instance_;
76   base::Lock map_lock_;
77   RenderFrameHostToIoThreadClientType rfh_to_io_thread_client_;
78 };
79
80 // static
81 LazyInstance<RfhToIoThreadClientMap> RfhToIoThreadClientMap::g_instance_ =
82     LAZY_INSTANCE_INITIALIZER;
83
84 // static
85 RfhToIoThreadClientMap* RfhToIoThreadClientMap::GetInstance() {
86   return g_instance_.Pointer();
87 }
88
89 void RfhToIoThreadClientMap::Set(pair<int, int> rfh_id,
90                                  const IoThreadClientData& client) {
91   base::AutoLock lock(map_lock_);
92   rfh_to_io_thread_client_[rfh_id] = client;
93 }
94
95 bool RfhToIoThreadClientMap::Get(
96     pair<int, int> rfh_id, IoThreadClientData* client) {
97   base::AutoLock lock(map_lock_);
98   RenderFrameHostToIoThreadClientType::iterator iterator =
99       rfh_to_io_thread_client_.find(rfh_id);
100   if (iterator == rfh_to_io_thread_client_.end())
101     return false;
102
103   *client = iterator->second;
104   return true;
105 }
106
107 void RfhToIoThreadClientMap::Erase(pair<int, int> rfh_id) {
108   base::AutoLock lock(map_lock_);
109   rfh_to_io_thread_client_.erase(rfh_id);
110 }
111
112 // ClientMapEntryUpdater ------------------------------------------------------
113
114 class ClientMapEntryUpdater : public content::WebContentsObserver {
115  public:
116   ClientMapEntryUpdater(JNIEnv* env, WebContents* web_contents,
117                         jobject jdelegate);
118
119   virtual void RenderFrameCreated(RenderFrameHost* render_frame_host) override;
120   virtual void RenderFrameDeleted(RenderFrameHost* render_frame_host) override;
121   virtual void WebContentsDestroyed() override;
122
123  private:
124   JavaObjectWeakGlobalRef jdelegate_;
125 };
126
127 ClientMapEntryUpdater::ClientMapEntryUpdater(JNIEnv* env,
128                                              WebContents* web_contents,
129                                              jobject jdelegate)
130     : content::WebContentsObserver(web_contents),
131       jdelegate_(env, jdelegate) {
132   DCHECK(web_contents);
133   DCHECK(jdelegate);
134
135   if (web_contents->GetMainFrame())
136     RenderFrameCreated(web_contents->GetMainFrame());
137 }
138
139 void ClientMapEntryUpdater::RenderFrameCreated(RenderFrameHost* rfh) {
140   IoThreadClientData client_data;
141   client_data.io_thread_client = jdelegate_;
142   client_data.pending_association = false;
143   RfhToIoThreadClientMap::GetInstance()->Set(
144       GetRenderFrameHostIdPair(rfh), client_data);
145 }
146
147 void ClientMapEntryUpdater::RenderFrameDeleted(RenderFrameHost* rfh) {
148   RfhToIoThreadClientMap::GetInstance()->Erase(GetRenderFrameHostIdPair(rfh));
149 }
150
151 void ClientMapEntryUpdater::WebContentsDestroyed() {
152   delete this;
153 }
154
155 } // namespace
156
157 // AwContentsIoThreadClientImpl -----------------------------------------------
158
159 // static
160 scoped_ptr<AwContentsIoThreadClient>
161 AwContentsIoThreadClient::FromID(int render_process_id, int render_frame_id) {
162   pair<int, int> rfh_id(render_process_id, render_frame_id);
163   IoThreadClientData client_data;
164   if (!RfhToIoThreadClientMap::GetInstance()->Get(rfh_id, &client_data))
165     return scoped_ptr<AwContentsIoThreadClient>();
166
167   JNIEnv* env = AttachCurrentThread();
168   ScopedJavaLocalRef<jobject> java_delegate =
169       client_data.io_thread_client.get(env);
170   DCHECK(!client_data.pending_association || java_delegate.is_null());
171   return scoped_ptr<AwContentsIoThreadClient>(new AwContentsIoThreadClientImpl(
172       client_data.pending_association, java_delegate));
173 }
174
175 // static
176 void AwContentsIoThreadClient::SubFrameCreated(int render_process_id,
177                                                int parent_render_frame_id,
178                                                int child_render_frame_id) {
179   pair<int, int> parent_rfh_id(render_process_id, parent_render_frame_id);
180   pair<int, int> child_rfh_id(render_process_id, child_render_frame_id);
181   IoThreadClientData client_data;
182   if (!RfhToIoThreadClientMap::GetInstance()->Get(parent_rfh_id,
183                                                   &client_data)) {
184     NOTREACHED();
185     return;
186   }
187
188   RfhToIoThreadClientMap::GetInstance()->Set(child_rfh_id, client_data);
189 }
190
191 // static
192 void AwContentsIoThreadClientImpl::RegisterPendingContents(
193     WebContents* web_contents) {
194   IoThreadClientData client_data;
195   client_data.pending_association = true;
196   RfhToIoThreadClientMap::GetInstance()->Set(
197       GetRenderFrameHostIdPair(web_contents->GetMainFrame()), client_data);
198 }
199
200 // static
201 void AwContentsIoThreadClientImpl::Associate(
202     WebContents* web_contents,
203     const JavaRef<jobject>& jclient) {
204   JNIEnv* env = AttachCurrentThread();
205   // The ClientMapEntryUpdater lifespan is tied to the WebContents.
206   new ClientMapEntryUpdater(env, web_contents, jclient.obj());
207 }
208
209 AwContentsIoThreadClientImpl::AwContentsIoThreadClientImpl(
210     bool pending_association,
211     const JavaRef<jobject>& obj)
212   : pending_association_(pending_association),
213     java_object_(obj) {
214 }
215
216 AwContentsIoThreadClientImpl::~AwContentsIoThreadClientImpl() {
217   // explict, out-of-line destructor.
218 }
219
220 bool AwContentsIoThreadClientImpl::PendingAssociation() const {
221   return pending_association_;
222 }
223
224 AwContentsIoThreadClient::CacheMode
225 AwContentsIoThreadClientImpl::GetCacheMode() const {
226   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
227   if (java_object_.is_null())
228     return AwContentsIoThreadClient::LOAD_DEFAULT;
229
230   JNIEnv* env = AttachCurrentThread();
231   return static_cast<AwContentsIoThreadClient::CacheMode>(
232       Java_AwContentsIoThreadClient_getCacheMode(
233           env, java_object_.obj()));
234 }
235
236 scoped_ptr<AwWebResourceResponse>
237 AwContentsIoThreadClientImpl::ShouldInterceptRequest(
238     const GURL& location,
239     const net::URLRequest* request) {
240   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
241   if (java_object_.is_null())
242     return scoped_ptr<AwWebResourceResponse>();
243   const content::ResourceRequestInfo* info =
244       content::ResourceRequestInfo::ForRequest(request);
245   bool is_main_frame = info &&
246       info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME;
247   bool has_user_gesture = info && info->HasUserGesture();
248
249   vector<string> headers_names;
250   vector<string> headers_values;
251   {
252     net::HttpRequestHeaders headers;
253     if (!request->GetFullRequestHeaders(&headers))
254       headers = request->extra_request_headers();
255     net::HttpRequestHeaders::Iterator headers_iterator(headers);
256     while (headers_iterator.GetNext()) {
257       headers_names.push_back(headers_iterator.name());
258       headers_values.push_back(headers_iterator.value());
259     }
260   }
261
262   JNIEnv* env = AttachCurrentThread();
263   ScopedJavaLocalRef<jstring> jstring_url =
264       ConvertUTF8ToJavaString(env, location.spec());
265   ScopedJavaLocalRef<jstring> jstring_method =
266       ConvertUTF8ToJavaString(env, request->method());
267   ScopedJavaLocalRef<jobjectArray> jstringArray_headers_names =
268       ToJavaArrayOfStrings(env, headers_names);
269   ScopedJavaLocalRef<jobjectArray> jstringArray_headers_values =
270       ToJavaArrayOfStrings(env, headers_values);
271   devtools_instrumentation::ScopedEmbedderCallbackTask embedder_callback(
272       "shouldInterceptRequest");
273   ScopedJavaLocalRef<jobject> ret =
274       Java_AwContentsIoThreadClient_shouldInterceptRequest(
275           env,
276           java_object_.obj(),
277           jstring_url.obj(),
278           is_main_frame,
279           has_user_gesture,
280           jstring_method.obj(),
281           jstringArray_headers_names.obj(),
282           jstringArray_headers_values.obj());
283   if (ret.is_null())
284     return scoped_ptr<AwWebResourceResponse>();
285   return scoped_ptr<AwWebResourceResponse>(
286       new AwWebResourceResponseImpl(ret));
287 }
288
289 bool AwContentsIoThreadClientImpl::ShouldBlockContentUrls() const {
290   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
291   if (java_object_.is_null())
292     return false;
293
294   JNIEnv* env = AttachCurrentThread();
295   return Java_AwContentsIoThreadClient_shouldBlockContentUrls(
296       env, java_object_.obj());
297 }
298
299 bool AwContentsIoThreadClientImpl::ShouldBlockFileUrls() const {
300   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
301   if (java_object_.is_null())
302     return false;
303
304   JNIEnv* env = AttachCurrentThread();
305   return Java_AwContentsIoThreadClient_shouldBlockFileUrls(
306       env, java_object_.obj());
307 }
308
309 bool AwContentsIoThreadClientImpl::ShouldAcceptThirdPartyCookies() const {
310   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
311   if (java_object_.is_null())
312     return false;
313
314   JNIEnv* env = AttachCurrentThread();
315   return Java_AwContentsIoThreadClient_shouldAcceptThirdPartyCookies(
316       env, java_object_.obj());
317 }
318
319 bool AwContentsIoThreadClientImpl::ShouldBlockNetworkLoads() const {
320   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
321   if (java_object_.is_null())
322     return false;
323
324   JNIEnv* env = AttachCurrentThread();
325   return Java_AwContentsIoThreadClient_shouldBlockNetworkLoads(
326       env, java_object_.obj());
327 }
328
329 void AwContentsIoThreadClientImpl::NewDownload(
330     const GURL& url,
331     const string& user_agent,
332     const string& content_disposition,
333     const string& mime_type,
334     int64 content_length) {
335   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
336   if (java_object_.is_null())
337     return;
338
339   JNIEnv* env = AttachCurrentThread();
340   ScopedJavaLocalRef<jstring> jstring_url =
341       ConvertUTF8ToJavaString(env, url.spec());
342   ScopedJavaLocalRef<jstring> jstring_user_agent =
343       ConvertUTF8ToJavaString(env, user_agent);
344   ScopedJavaLocalRef<jstring> jstring_content_disposition =
345       ConvertUTF8ToJavaString(env, content_disposition);
346   ScopedJavaLocalRef<jstring> jstring_mime_type =
347       ConvertUTF8ToJavaString(env, mime_type);
348
349   Java_AwContentsIoThreadClient_onDownloadStart(
350       env,
351       java_object_.obj(),
352       jstring_url.obj(),
353       jstring_user_agent.obj(),
354       jstring_content_disposition.obj(),
355       jstring_mime_type.obj(),
356       content_length);
357 }
358
359 void AwContentsIoThreadClientImpl::NewLoginRequest(const string& realm,
360                                                    const string& account,
361                                                    const string& args) {
362   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
363   if (java_object_.is_null())
364     return;
365
366   JNIEnv* env = AttachCurrentThread();
367   ScopedJavaLocalRef<jstring> jrealm = ConvertUTF8ToJavaString(env, realm);
368   ScopedJavaLocalRef<jstring> jargs = ConvertUTF8ToJavaString(env, args);
369
370   ScopedJavaLocalRef<jstring> jaccount;
371   if (!account.empty())
372     jaccount = ConvertUTF8ToJavaString(env, account);
373
374   Java_AwContentsIoThreadClient_newLoginRequest(
375       env, java_object_.obj(), jrealm.obj(), jaccount.obj(), jargs.obj());
376 }
377
378 bool RegisterAwContentsIoThreadClientImpl(JNIEnv* env) {
379   return RegisterNativesImpl(env);
380 }
381
382 } // namespace android_webview