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