Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / browser / android / download_controller_android_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 "content/browser/android/download_controller_android_impl.h"
6
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_string.h"
9 #include "base/bind.h"
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/time/time.h"
13 #include "content/browser/android/content_view_core_impl.h"
14 #include "content/browser/download/download_item_impl.h"
15 #include "content/browser/download/download_manager_impl.h"
16 #include "content/browser/loader/resource_dispatcher_host_impl.h"
17 #include "content/browser/renderer_host/render_process_host_impl.h"
18 #include "content/browser/renderer_host/render_view_host_delegate.h"
19 #include "content/browser/renderer_host/render_view_host_impl.h"
20 #include "content/browser/web_contents/web_contents_impl.h"
21 #include "content/public/browser/browser_context.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/download_url_parameters.h"
24 #include "content/public/browser/global_request_id.h"
25 #include "content/public/common/referrer.h"
26 #include "jni/DownloadController_jni.h"
27 #include "net/cookies/cookie_options.h"
28 #include "net/cookies/cookie_store.h"
29 #include "net/http/http_content_disposition.h"
30 #include "net/http/http_request_headers.h"
31 #include "net/http/http_response_headers.h"
32 #include "net/url_request/url_request.h"
33 #include "net/url_request/url_request_context.h"
34
35 using base::android::ConvertUTF8ToJavaString;
36 using base::android::ScopedJavaLocalRef;
37
38 namespace content {
39
40 // JNI methods
41 static void Init(JNIEnv* env, jobject obj) {
42   DownloadControllerAndroidImpl::GetInstance()->Init(env, obj);
43 }
44
45 struct DownloadControllerAndroidImpl::JavaObject {
46   ScopedJavaLocalRef<jobject> Controller(JNIEnv* env) {
47     return GetRealObject(env, obj);
48   }
49   jweak obj;
50 };
51
52 // static
53 bool DownloadControllerAndroidImpl::RegisterDownloadController(JNIEnv* env) {
54   return RegisterNativesImpl(env);
55 }
56
57 // static
58 DownloadControllerAndroid* DownloadControllerAndroid::Get() {
59   return DownloadControllerAndroidImpl::GetInstance();
60 }
61
62 // static
63 DownloadControllerAndroidImpl* DownloadControllerAndroidImpl::GetInstance() {
64   return Singleton<DownloadControllerAndroidImpl>::get();
65 }
66
67 DownloadControllerAndroidImpl::DownloadControllerAndroidImpl()
68     : java_object_(NULL) {
69 }
70
71 DownloadControllerAndroidImpl::~DownloadControllerAndroidImpl() {
72   if (java_object_) {
73     JNIEnv* env = base::android::AttachCurrentThread();
74     env->DeleteWeakGlobalRef(java_object_->obj);
75     delete java_object_;
76     base::android::CheckException(env);
77   }
78 }
79
80 // Initialize references to Java object.
81 void DownloadControllerAndroidImpl::Init(JNIEnv* env, jobject obj) {
82   java_object_ = new JavaObject;
83   java_object_->obj = env->NewWeakGlobalRef(obj);
84 }
85
86 void DownloadControllerAndroidImpl::CreateGETDownload(
87     int render_process_id, int render_view_id, int request_id) {
88   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
89   GlobalRequestID global_id(render_process_id, request_id);
90
91   // We are yielding the UI thread and render_view_host may go away by
92   // the time we come back. Pass along render_process_id and render_view_id
93   // to retrieve it later (if it still exists).
94   GetDownloadInfoCB cb = base::Bind(
95         &DownloadControllerAndroidImpl::StartAndroidDownload,
96         base::Unretained(this), render_process_id,
97         render_view_id);
98
99   PrepareDownloadInfo(
100       global_id,
101       base::Bind(&DownloadControllerAndroidImpl::StartDownloadOnUIThread,
102                  base::Unretained(this), cb));
103 }
104
105 void DownloadControllerAndroidImpl::PrepareDownloadInfo(
106     const GlobalRequestID& global_id,
107     const GetDownloadInfoCB& callback) {
108   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
109
110   net::URLRequest* request =
111       ResourceDispatcherHostImpl::Get()->GetURLRequest(global_id);
112   if (!request) {
113     LOG(ERROR) << "Request to download not found.";
114     return;
115   }
116
117   DownloadInfoAndroid info_android(request);
118
119   net::CookieStore* cookie_store = request->context()->cookie_store();
120   if (cookie_store) {
121     net::CookieMonster* cookie_monster = cookie_store->GetCookieMonster();
122     if (cookie_monster) {
123       cookie_monster->GetAllCookiesForURLAsync(
124           request->url(),
125           base::Bind(&DownloadControllerAndroidImpl::CheckPolicyAndLoadCookies,
126                      base::Unretained(this), info_android, callback,
127                      global_id));
128     } else {
129       DoLoadCookies(info_android, callback, global_id);
130     }
131   } else {
132     // Can't get any cookies, start android download.
133     callback.Run(info_android);
134   }
135 }
136
137 void DownloadControllerAndroidImpl::CheckPolicyAndLoadCookies(
138     const DownloadInfoAndroid& info, const GetDownloadInfoCB& callback,
139     const GlobalRequestID& global_id, const net::CookieList& cookie_list) {
140   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
141
142   net::URLRequest* request =
143       ResourceDispatcherHostImpl::Get()->GetURLRequest(global_id);
144   if (!request) {
145     LOG(ERROR) << "Request to download not found.";
146     return;
147   }
148
149   if (request->context()->network_delegate()->CanGetCookies(
150       *request, cookie_list)) {
151     DoLoadCookies(info, callback, global_id);
152   } else {
153     callback.Run(info);
154   }
155 }
156
157 void DownloadControllerAndroidImpl::DoLoadCookies(
158     const DownloadInfoAndroid& info, const GetDownloadInfoCB& callback,
159     const GlobalRequestID& global_id) {
160   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
161
162   net::CookieOptions options;
163   options.set_include_httponly();
164
165   net::URLRequest* request =
166       ResourceDispatcherHostImpl::Get()->GetURLRequest(global_id);
167   if (!request) {
168     LOG(ERROR) << "Request to download not found.";
169     return;
170   }
171
172   request->context()->cookie_store()->GetCookiesWithOptionsAsync(
173       info.url, options,
174       base::Bind(&DownloadControllerAndroidImpl::OnCookieResponse,
175                  base::Unretained(this), info, callback));
176 }
177
178 void DownloadControllerAndroidImpl::OnCookieResponse(
179     DownloadInfoAndroid download_info,
180     const GetDownloadInfoCB& callback,
181     const std::string& cookie) {
182   download_info.cookie = cookie;
183
184   // We have everything we need, start Android download.
185   callback.Run(download_info);
186 }
187
188 void DownloadControllerAndroidImpl::StartDownloadOnUIThread(
189     const GetDownloadInfoCB& callback,
190     const DownloadInfoAndroid& info) {
191   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
192   BrowserThread::PostTask(
193       BrowserThread::UI, FROM_HERE, base::Bind(callback, info));
194 }
195
196 void DownloadControllerAndroidImpl::StartAndroidDownload(
197     int render_process_id, int render_view_id,
198     const DownloadInfoAndroid& info) {
199   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
200   JNIEnv* env = base::android::AttachCurrentThread();
201
202   // Call newHttpGetDownload
203   ScopedJavaLocalRef<jobject> view = GetContentView(render_process_id,
204                                                     render_view_id);
205   if (view.is_null()) {
206     // The view went away. Can't proceed.
207     LOG(ERROR) << "Download failed on URL:" << info.url.spec();
208     return;
209   }
210
211   ScopedJavaLocalRef<jstring> jurl =
212       ConvertUTF8ToJavaString(env, info.url.spec());
213   ScopedJavaLocalRef<jstring> juser_agent =
214       ConvertUTF8ToJavaString(env, info.user_agent);
215   ScopedJavaLocalRef<jstring> jcontent_disposition =
216       ConvertUTF8ToJavaString(env, info.content_disposition);
217   ScopedJavaLocalRef<jstring> jmime_type =
218       ConvertUTF8ToJavaString(env, info.original_mime_type);
219   ScopedJavaLocalRef<jstring> jcookie =
220       ConvertUTF8ToJavaString(env, info.cookie);
221   ScopedJavaLocalRef<jstring> jreferer =
222       ConvertUTF8ToJavaString(env, info.referer);
223
224   // Try parsing the content disposition header to get a
225   // explicitly specified filename if available.
226   net::HttpContentDisposition header(info.content_disposition, "");
227   ScopedJavaLocalRef<jstring> jfilename =
228       ConvertUTF8ToJavaString(env, header.filename());
229
230   Java_DownloadController_newHttpGetDownload(
231       env, GetJavaObject()->Controller(env).obj(), view.obj(), jurl.obj(),
232       juser_agent.obj(), jcontent_disposition.obj(), jmime_type.obj(),
233       jcookie.obj(), jreferer.obj(), jfilename.obj(), info.total_bytes);
234 }
235
236 void DownloadControllerAndroidImpl::OnDownloadStarted(
237     DownloadItem* download_item) {
238   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
239   if (!download_item->GetWebContents())
240     return;
241
242   JNIEnv* env = base::android::AttachCurrentThread();
243
244   // Register for updates to the DownloadItem.
245   download_item->AddObserver(this);
246
247   ScopedJavaLocalRef<jobject> view =
248       GetContentViewCoreFromWebContents(download_item->GetWebContents());
249   // The view went away. Can't proceed.
250   if (view.is_null())
251     return;
252
253   ScopedJavaLocalRef<jstring> jmime_type =
254       ConvertUTF8ToJavaString(env, download_item->GetMimeType());
255   ScopedJavaLocalRef<jstring> jfilename = ConvertUTF8ToJavaString(
256       env, download_item->GetTargetFilePath().BaseName().value());
257   Java_DownloadController_onDownloadStarted(
258       env, GetJavaObject()->Controller(env).obj(), view.obj(), jfilename.obj(),
259       jmime_type.obj());
260 }
261
262 void DownloadControllerAndroidImpl::OnDownloadUpdated(DownloadItem* item) {
263   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
264   if (item->IsDangerous() && (item->GetState() != DownloadItem::CANCELLED))
265     OnDangerousDownload(item);
266
267   JNIEnv* env = base::android::AttachCurrentThread();
268   ScopedJavaLocalRef<jstring> jurl =
269       ConvertUTF8ToJavaString(env, item->GetURL().spec());
270   ScopedJavaLocalRef<jstring> jmime_type =
271       ConvertUTF8ToJavaString(env, item->GetMimeType());
272   ScopedJavaLocalRef<jstring> jpath =
273       ConvertUTF8ToJavaString(env, item->GetTargetFilePath().value());
274   ScopedJavaLocalRef<jstring> jfilename = ConvertUTF8ToJavaString(
275       env, item->GetTargetFilePath().BaseName().value());
276
277   switch (item->GetState()) {
278     case DownloadItem::IN_PROGRESS: {
279       base::TimeDelta time_delta;
280       item->TimeRemaining(&time_delta);
281       Java_DownloadController_onDownloadUpdated(
282           env, GetJavaObject()->Controller(env).obj(),
283           base::android::GetApplicationContext(), jurl.obj(), jmime_type.obj(),
284           jfilename.obj(), jpath.obj(), item->GetReceivedBytes(), true,
285           item->GetId(), item->PercentComplete(), time_delta.InMilliseconds());
286       break;
287     }
288     case DownloadItem::COMPLETE:
289       // Multiple OnDownloadUpdated() notifications may be issued while the
290       // download is in the COMPLETE state. Only handle one.
291       item->RemoveObserver(this);
292
293       // Call onDownloadCompleted
294       Java_DownloadController_onDownloadCompleted(
295           env, GetJavaObject()->Controller(env).obj(),
296           base::android::GetApplicationContext(), jurl.obj(), jmime_type.obj(),
297           jfilename.obj(), jpath.obj(), item->GetReceivedBytes(), true,
298           item->GetId());
299       break;
300     case DownloadItem::CANCELLED:
301     // TODO(shashishekhar): An interrupted download can be resumed. Android
302     // currently does not support resumable downloads. Add handling for
303     // interrupted case based on item->CanResume().
304     case DownloadItem::INTERRUPTED:
305       // Call onDownloadCompleted with success = false.
306       Java_DownloadController_onDownloadCompleted(
307           env, GetJavaObject()->Controller(env).obj(),
308           base::android::GetApplicationContext(), jurl.obj(), jmime_type.obj(),
309           jfilename.obj(), jpath.obj(), item->GetReceivedBytes(), false,
310           item->GetId());
311       break;
312     case DownloadItem::MAX_DOWNLOAD_STATE:
313       NOTREACHED();
314   }
315 }
316
317 void DownloadControllerAndroidImpl::OnDangerousDownload(DownloadItem* item) {
318   JNIEnv* env = base::android::AttachCurrentThread();
319   ScopedJavaLocalRef<jstring> jfilename = ConvertUTF8ToJavaString(
320       env, item->GetTargetFilePath().BaseName().value());
321   ScopedJavaLocalRef<jobject> view_core = GetContentViewCoreFromWebContents(
322       item->GetWebContents());
323   if (!view_core.is_null()) {
324     Java_DownloadController_onDangerousDownload(
325         env, GetJavaObject()->Controller(env).obj(), view_core.obj(),
326         jfilename.obj(), item->GetId());
327   }
328 }
329
330 ScopedJavaLocalRef<jobject> DownloadControllerAndroidImpl::GetContentView(
331     int render_process_id, int render_view_id) {
332   RenderViewHost* render_view_host =
333       RenderViewHost::FromID(render_process_id, render_view_id);
334
335   if (!render_view_host)
336     return ScopedJavaLocalRef<jobject>();
337
338   WebContents* web_contents =
339       render_view_host->GetDelegate()->GetAsWebContents();
340
341   return GetContentViewCoreFromWebContents(web_contents);
342 }
343
344 ScopedJavaLocalRef<jobject>
345     DownloadControllerAndroidImpl::GetContentViewCoreFromWebContents(
346     WebContents* web_contents) {
347   if (!web_contents)
348     return ScopedJavaLocalRef<jobject>();
349
350   ContentViewCore* view_core = ContentViewCore::FromWebContents(web_contents);
351   return view_core ? view_core->GetJavaObject() :
352       ScopedJavaLocalRef<jobject>();
353 }
354
355 DownloadControllerAndroidImpl::JavaObject*
356     DownloadControllerAndroidImpl::GetJavaObject() {
357   if (!java_object_) {
358     // Initialize Java DownloadController by calling
359     // DownloadController.getInstance(), which will call Init()
360     // if Java DownloadController is not instantiated already.
361     JNIEnv* env = base::android::AttachCurrentThread();
362     Java_DownloadController_getInstance(env);
363   }
364
365   DCHECK(java_object_);
366   return java_object_;
367 }
368
369 void DownloadControllerAndroidImpl::StartContextMenuDownload(
370     const ContextMenuParams& params, WebContents* web_contents, bool is_link) {
371   const GURL& url = is_link ? params.link_url : params.src_url;
372   const GURL& referrer = params.frame_url.is_empty() ?
373       params.page_url : params.frame_url;
374   DownloadManagerImpl* dlm = static_cast<DownloadManagerImpl*>(
375       BrowserContext::GetDownloadManager(web_contents->GetBrowserContext()));
376   scoped_ptr<DownloadUrlParameters> dl_params(
377       DownloadUrlParameters::FromWebContents(web_contents, url));
378   dl_params->set_referrer(
379       Referrer(referrer, params.referrer_policy));
380   if (is_link)
381     dl_params->set_referrer_encoding(params.frame_charset);
382   else
383     dl_params->set_prefer_cache(true);
384   dl_params->set_prompt(false);
385   dlm->DownloadUrl(dl_params.Pass());
386 }
387
388 void DownloadControllerAndroidImpl::DangerousDownloadValidated(
389     WebContents* web_contents, int download_id, bool accept) {
390   if (!web_contents)
391     return;
392   DownloadManagerImpl* dlm = static_cast<DownloadManagerImpl*>(
393       BrowserContext::GetDownloadManager(web_contents->GetBrowserContext()));
394   DownloadItem* item = dlm->GetDownload(download_id);
395   if (!item)
396     return;
397   if (accept)
398     item->ValidateDangerousDownload();
399   else
400     item->Remove();
401 }
402
403 DownloadControllerAndroidImpl::DownloadInfoAndroid::DownloadInfoAndroid(
404     net::URLRequest* request) {
405   request->GetResponseHeaderByName("content-disposition", &content_disposition);
406
407   if (request->response_headers())
408     request->response_headers()->GetMimeType(&original_mime_type);
409
410   request->extra_request_headers().GetHeader(
411       net::HttpRequestHeaders::kUserAgent, &user_agent);
412   GURL referer_url(request->referrer());
413   if (referer_url.is_valid())
414     referer = referer_url.spec();
415   if (!request->url_chain().empty()) {
416     original_url = request->url_chain().front();
417     url = request->url_chain().back();
418   }
419 }
420
421 DownloadControllerAndroidImpl::DownloadInfoAndroid::~DownloadInfoAndroid() {}
422
423 }  // namespace content