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