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