Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(420)

Side by Side Diff: chrome/browser/android/download/download_controller.cc

Issue 2014803002: Move DownloadControllerAndroid from content/ to chrome/ (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix failing tests/bugs Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 "chrome/browser/android/download/download_controller.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/android/context_utils.h"
11 #include "base/android/jni_android.h"
12 #include "base/android/jni_string.h"
13 #include "base/bind.h"
14 #include "base/lazy_instance.h"
15 #include "base/logging.h"
16 #include "base/metrics/histogram_macros.h"
17 #include "base/synchronization/lock.h"
18 #include "base/time/time.h"
19 #include "chrome/browser/android/download/chrome_download_delegate.h"
20 #include "chrome/browser/ui/android/view_android_helper.h"
21 #include "content/public/browser/browser_context.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/download_manager.h"
24 #include "content/public/browser/download_url_parameters.h"
25 #include "content/public/browser/render_process_host.h"
26 #include "content/public/browser/render_view_host.h"
27 #include "content/public/common/referrer.h"
28 #include "jni/DownloadController_jni.h"
29 #include "net/base/filename_util.h"
30 #include "ui/android/view_android.h"
31 #include "ui/android/window_android.h"
32
33 using base::android::ConvertUTF8ToJavaString;
34 using base::android::ScopedJavaLocalRef;
35 using content::BrowserContext;
36 using content::BrowserThread;
37 using content::ContextMenuParams;
38 using content::DownloadItem;
39 using content::DownloadManager;
40 using content::WebContents;
41
42 namespace {
43 // Guards download_controller_
44 base::LazyInstance<base::Lock> g_download_controller_lock_;
45
46 WebContents* GetWebContents(int render_process_id, int render_view_id) {
47 content::RenderViewHost* render_view_host =
48 content::RenderViewHost::FromID(render_process_id, render_view_id);
49
50 if (!render_view_host)
51 return nullptr;
52
53 return WebContents::FromRenderViewHost(render_view_host);
54 }
55
56 void CreateContextMenuDownload(int render_process_id,
57 int render_view_id,
58 const content::ContextMenuParams& params,
59 bool is_link,
60 const std::string& extra_headers,
61 bool granted) {
62 if (!granted)
63 return;
64
65 content::WebContents* web_contents =
66 GetWebContents(render_process_id, render_view_id);
67 if (!web_contents)
68 return;
69
70 const GURL& url = is_link ? params.link_url : params.src_url;
71 const GURL& referring_url =
72 params.frame_url.is_empty() ? params.page_url : params.frame_url;
73 content::DownloadManager* dlm =
74 content::BrowserContext::GetDownloadManager(
75 web_contents->GetBrowserContext());
76 std::unique_ptr<content::DownloadUrlParameters> dl_params(
77 content::DownloadUrlParameters::CreateForWebContentsMainFrame(
78 web_contents, url));
79 content::Referrer referrer = content::Referrer::SanitizeForRequest(
80 url,
81 content::Referrer(referring_url.GetAsReferrer(), params.referrer_policy));
82 dl_params->set_referrer(referrer);
83 if (is_link)
84 dl_params->set_referrer_encoding(params.frame_charset);
85 net::HttpRequestHeaders headers;
86 headers.AddHeadersFromString(extra_headers);
87 for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext();)
88 dl_params->add_request_header(it.name(), it.value());
89 if (!is_link && extra_headers.empty())
90 dl_params->set_prefer_cache(true);
91 dl_params->set_prompt(false);
92 dlm->DownloadUrl(std::move(dl_params));
93 }
94
95 // Check if an interrupted download item can be auto resumed.
96 bool IsInterruptedDownloadAutoResumable(content::DownloadItem* download_item) {
97 int interrupt_reason = download_item->GetLastReason();
98 DCHECK_NE(interrupt_reason, content::DOWNLOAD_INTERRUPT_REASON_NONE);
99 return
100 interrupt_reason == content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT ||
101 interrupt_reason == content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED ||
102 interrupt_reason ==
103 content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED;
104 }
105
106 } // namespace
107
108 // JNI methods
109 static void Init(JNIEnv* env, const JavaParamRef<jobject>& obj) {
110 DownloadController::GetInstance()->Init(env, obj);
111 }
112
113 static void OnRequestFileAccessResult(JNIEnv* env,
114 const JavaParamRef<jobject>& obj,
115 jlong callback_id,
116 jboolean granted) {
117 DCHECK_CURRENTLY_ON(BrowserThread::UI);
118 DCHECK(callback_id);
119
120 // Convert java long long int to c++ pointer, take ownership.
121 std::unique_ptr<
122 DownloadControllerBase::AcquireFileAccessPermissionCallback>
123 cb(reinterpret_cast<
124 DownloadControllerBase::AcquireFileAccessPermissionCallback*>(
125 callback_id));
126 if (!granted) {
127 DownloadController::RecordDownloadCancelReason(
128 DownloadController::CANCEL_REASON_NO_STORAGE_PERMISSION);
129 }
130 cb->Run(granted);
131 }
132
133 struct DownloadController::JavaObject {
134 ScopedJavaLocalRef<jobject> Controller(JNIEnv* env) {
135 return GetRealObject(env, obj_);
136 }
137 jweak obj_;
138 };
139
140 // static
141 bool DownloadController::RegisterDownloadController(JNIEnv* env) {
142 return RegisterNativesImpl(env);
143 }
144
145 // static
146 DownloadControllerBase* DownloadControllerBase::Get() {
147 base::AutoLock lock(g_download_controller_lock_.Get());
148 if (!DownloadControllerBase::download_controller_)
149 download_controller_ = DownloadController::GetInstance();
150 return DownloadControllerBase::download_controller_;
151 }
152
153 // static
154 void DownloadControllerBase::SetDownloadControllerBase(
155 DownloadControllerBase* download_controller) {
156 base::AutoLock lock(g_download_controller_lock_.Get());
157 DownloadControllerBase::download_controller_ = download_controller;
158 }
159
160 // static
161 void DownloadController::RecordDownloadCancelReason(
162 DownloadCancelReason reason) {
163 UMA_HISTOGRAM_ENUMERATION(
164 "MobileDownload.CancelReason", reason, CANCEL_REASON_MAX);
165 }
166
167 // static
168 DownloadController* DownloadController::GetInstance() {
169 return base::Singleton<DownloadController>::get();
170 }
171
172 DownloadController::DownloadController()
173 : java_object_(NULL) {
174 }
175
176 DownloadController::~DownloadController() {
177 if (java_object_) {
178 JNIEnv* env = base::android::AttachCurrentThread();
179 env->DeleteWeakGlobalRef(java_object_->obj_);
180 delete java_object_;
181 base::android::CheckException(env);
182 }
183 }
184
185 // Initialize references to Java object.
186 void DownloadController::Init(JNIEnv* env, jobject obj) {
187 java_object_ = new JavaObject;
188 java_object_->obj_ = env->NewWeakGlobalRef(obj);
189 }
190
191 void DownloadController::AcquireFileAccessPermission(
192 WebContents* web_contents,
193 const DownloadControllerBase::AcquireFileAccessPermissionCallback& cb) {
194 DCHECK_CURRENTLY_ON(BrowserThread::UI);
195 DCHECK(web_contents);
196
197 ui::WindowAndroid* window_android =
198 ViewAndroidHelper::FromWebContents(web_contents)->
199 GetViewAndroid()->GetWindowAndroid();
200 if (window_android && HasFileAccessPermission(window_android)) {
201 BrowserThread::PostTask(
202 BrowserThread::UI, FROM_HERE, base::Bind(cb, true));
203 return;
204 }
205 // Make copy on the heap so we can pass the pointer through JNI.
206 intptr_t callback_id = reinterpret_cast<intptr_t>(
207 new DownloadControllerBase::AcquireFileAccessPermissionCallback(cb));
208 ChromeDownloadDelegate::FromWebContents(web_contents)->
209 RequestFileAccess(callback_id);
210 }
211
212 void DownloadController::SetDefaultDownloadFileName(
213 const std::string& file_name) {
214 default_file_name_ = file_name;
215 }
216
217 bool DownloadController::HasFileAccessPermission(
218 ui::WindowAndroid* window_android) {
219 ScopedJavaLocalRef<jobject> jwindow_android = window_android->GetJavaObject();
220
221 DCHECK_CURRENTLY_ON(BrowserThread::UI);
222 DCHECK(!jwindow_android.is_null());
223
224 JNIEnv* env = base::android::AttachCurrentThread();
225 return Java_DownloadController_hasFileAccess(
226 env, GetJavaObject()->Controller(env).obj(), jwindow_android.obj());
227 }
228
229 void DownloadController::CreateGETDownload(
230 int render_process_id, int render_view_id, bool must_download,
231 const DownloadInfo& info) {
232 DCHECK_CURRENTLY_ON(BrowserThread::IO);
233
234 // We are yielding the UI thread and render_view_host may go away by
235 // the time we come back. Pass along render_process_id and render_view_id
236 // to retrieve it later (if it still exists).
237 BrowserThread::PostTask(
238 BrowserThread::UI, FROM_HERE,
239 base::Bind(&DownloadController::StartAndroidDownload,
240 base::Unretained(this),
241 render_process_id, render_view_id, must_download, info));
242 }
243
244 void DownloadController::StartAndroidDownload(
245 int render_process_id, int render_view_id, bool must_download,
246 const DownloadInfo& info) {
247 DCHECK_CURRENTLY_ON(BrowserThread::UI);
248
249 WebContents* web_contents = GetWebContents(render_process_id, render_view_id);
250 if (!web_contents) {
251 // The view went away. Can't proceed.
252 LOG(ERROR) << "Download failed on URL:" << info.url.spec();
253 return;
254 }
255
256 AcquireFileAccessPermission(
257 web_contents,
258 base::Bind(&DownloadController::StartAndroidDownloadInternal,
259 base::Unretained(this), render_process_id, render_view_id,
260 must_download, info));
261 }
262
263 void DownloadController::StartAndroidDownloadInternal(
264 int render_process_id, int render_view_id, bool must_download,
265 const DownloadInfo& info, bool allowed) {
266 DCHECK_CURRENTLY_ON(BrowserThread::UI);
267 if (!allowed)
268 return;
269
270 WebContents* web_contents = GetWebContents(render_process_id, render_view_id);
271 // The view went away. Can't proceed.
272 if (!web_contents)
273 return;
274
275 base::string16 filename = net::GetSuggestedFilename(
276 info.url, info.content_disposition,
277 std::string(), // referrer_charset
278 std::string(), // suggested_name
279 info.original_mime_type,
280 default_file_name_);
281 ChromeDownloadDelegate::FromWebContents(web_contents)->RequestHTTPGetDownload(
282 info.url.spec(), info.user_agent,
283 info.content_disposition, info.original_mime_type,
284 info.cookie, info.referer, filename,
285 info.total_bytes, info.has_user_gesture,
286 must_download);
287 }
288
289 void DownloadController::OnDownloadStarted(
290 DownloadItem* download_item) {
291 DCHECK_CURRENTLY_ON(BrowserThread::UI);
292 WebContents* web_contents = download_item->GetWebContents();
293 if (!web_contents)
294 return;
295
296 // Register for updates to the DownloadItem.
297 download_item->AddObserver(this);
298
299 ChromeDownloadDelegate::FromWebContents(web_contents)->OnDownloadStarted(
300 download_item->GetTargetFilePath().BaseName().value(),
301 download_item->GetMimeType());
302 }
303
304 void DownloadController::OnDownloadUpdated(DownloadItem* item) {
305 DCHECK_CURRENTLY_ON(BrowserThread::UI);
306 if (item->IsDangerous() && (item->GetState() != DownloadItem::CANCELLED))
307 OnDangerousDownload(item);
308
309 JNIEnv* env = base::android::AttachCurrentThread();
310 ScopedJavaLocalRef<jstring> jguid =
311 ConvertUTF8ToJavaString(env, item->GetGuid());
312 ScopedJavaLocalRef<jstring> jurl =
313 ConvertUTF8ToJavaString(env, item->GetURL().spec());
314 ScopedJavaLocalRef<jstring> jmime_type =
315 ConvertUTF8ToJavaString(env, item->GetMimeType());
316 ScopedJavaLocalRef<jstring> jpath =
317 ConvertUTF8ToJavaString(env, item->GetTargetFilePath().value());
318 ScopedJavaLocalRef<jstring> jfilename = ConvertUTF8ToJavaString(
319 env, item->GetTargetFilePath().BaseName().value());
320 ScopedJavaLocalRef<jstring> joriginal_url =
321 ConvertUTF8ToJavaString(env, item->GetOriginalUrl().spec());
322 ScopedJavaLocalRef<jstring> jreferrer_url =
323 ConvertUTF8ToJavaString(env, item->GetReferrerUrl().spec());
324
325 switch (item->GetState()) {
326 case DownloadItem::IN_PROGRESS: {
327 base::TimeDelta time_delta;
328 item->TimeRemaining(&time_delta);
329 Java_DownloadController_onDownloadUpdated(
330 env, GetJavaObject()->Controller(env).obj(), jurl.obj(),
331 jmime_type.obj(), jfilename.obj(), jpath.obj(),
332 item->GetReceivedBytes(), jguid.obj(),
333 item->PercentComplete(), time_delta.InMilliseconds(),
334 item->HasUserGesture(), item->IsPaused(),
335 item->GetBrowserContext()->IsOffTheRecord());
336 break;
337 }
338 case DownloadItem::COMPLETE:
339 // Multiple OnDownloadUpdated() notifications may be issued while the
340 // download is in the COMPLETE state. Only handle one.
341 item->RemoveObserver(this);
342
343 // Call onDownloadCompleted
344 Java_DownloadController_onDownloadCompleted(
345 env, GetJavaObject()->Controller(env).obj(), jurl.obj(),
346 jmime_type.obj(), jfilename.obj(), jpath.obj(),
347 item->GetReceivedBytes(), jguid.obj(),
348 joriginal_url.obj(), jreferrer_url.obj(), item->HasUserGesture());
349 DownloadController::RecordDownloadCancelReason(
350 DownloadController::CANCEL_REASON_NOT_CANCELED);
351 break;
352 case DownloadItem::CANCELLED:
353 Java_DownloadController_onDownloadCancelled(
354 env, GetJavaObject()->Controller(env).obj(), jguid.obj());
355 break;
356 case DownloadItem::INTERRUPTED:
357 // When device loses/changes network, we get a NETWORK_TIMEOUT,
358 // NETWORK_FAILED or NETWORK_DISCONNECTED error. Download should auto
359 // resume in this case.
360 Java_DownloadController_onDownloadInterrupted(
361 env, GetJavaObject()->Controller(env).obj(), jurl.obj(),
362 jmime_type.obj(), jfilename.obj(), jpath.obj(),
363 item->GetReceivedBytes(), jguid.obj(),
364 item->CanResume(), IsInterruptedDownloadAutoResumable(item),
365 item->GetBrowserContext()->IsOffTheRecord());
366 item->RemoveObserver(this);
367 break;
368 case DownloadItem::MAX_DOWNLOAD_STATE:
369 NOTREACHED();
370 }
371 }
372
373 void DownloadController::OnDangerousDownload(DownloadItem* item) {
374 WebContents* web_contents = item->GetWebContents();
375 if (!web_contents)
376 return;
377 ChromeDownloadDelegate::FromWebContents(web_contents)->OnDangerousDownload(
378 item->GetTargetFilePath().BaseName().value(), item->GetGuid());
379 }
380
381 DownloadController::JavaObject*
382 DownloadController::GetJavaObject() {
383 if (!java_object_) {
384 // Initialize Java DownloadController by calling
385 // DownloadController.getInstance(), which will call Init()
386 // if Java DownloadController is not instantiated already.
387 JNIEnv* env = base::android::AttachCurrentThread();
388 Java_DownloadController_getInstance(env);
389 }
390
391 DCHECK(java_object_);
392 return java_object_;
393 }
394
395 void DownloadController::StartContextMenuDownload(
396 const ContextMenuParams& params, WebContents* web_contents, bool is_link,
397 const std::string& extra_headers) {
398 int process_id = web_contents->GetRenderProcessHost()->GetID();
399 int routing_id = web_contents->GetRoutingID();
400 AcquireFileAccessPermission(
401 web_contents, base::Bind(&CreateContextMenuDownload, process_id,
402 routing_id, params, is_link, extra_headers));
403 }
404
405 void DownloadController::DangerousDownloadValidated(
406 WebContents* web_contents,
407 const std::string& download_guid,
408 bool accept) {
409 if (!web_contents)
410 return;
411 DownloadManager* dlm =
412 BrowserContext::GetDownloadManager(web_contents->GetBrowserContext());
413 DownloadItem* item = dlm->GetDownloadByGuid(download_guid);
414 if (!item)
415 return;
416 if (accept)
417 item->ValidateDangerousDownload();
418 else
419 item->Remove();
420 }
OLDNEW
« no previous file with comments | « chrome/browser/android/download/download_controller.h ('k') | chrome/browser/android/download/download_controller_base.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698