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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/android/download/download_controller.cc
diff --git a/chrome/browser/android/download/download_controller.cc b/chrome/browser/android/download/download_controller.cc
new file mode 100644
index 0000000000000000000000000000000000000000..93981c3a77acceecae8707e243b49cc083b2a60d
--- /dev/null
+++ b/chrome/browser/android/download/download_controller.cc
@@ -0,0 +1,420 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/download/download_controller.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/android/context_utils.h"
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/synchronization/lock.h"
+#include "base/time/time.h"
+#include "chrome/browser/android/download/chrome_download_delegate.h"
+#include "chrome/browser/ui/android/view_android_helper.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/download_manager.h"
+#include "content/public/browser/download_url_parameters.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/common/referrer.h"
+#include "jni/DownloadController_jni.h"
+#include "net/base/filename_util.h"
+#include "ui/android/view_android.h"
+#include "ui/android/window_android.h"
+
+using base::android::ConvertUTF8ToJavaString;
+using base::android::ScopedJavaLocalRef;
+using content::BrowserContext;
+using content::BrowserThread;
+using content::ContextMenuParams;
+using content::DownloadItem;
+using content::DownloadManager;
+using content::WebContents;
+
+namespace {
+// Guards download_controller_
+base::LazyInstance<base::Lock> g_download_controller_lock_;
+
+WebContents* GetWebContents(int render_process_id, int render_view_id) {
+ content::RenderViewHost* render_view_host =
+ content::RenderViewHost::FromID(render_process_id, render_view_id);
+
+ if (!render_view_host)
+ return nullptr;
+
+ return WebContents::FromRenderViewHost(render_view_host);
+}
+
+void CreateContextMenuDownload(int render_process_id,
+ int render_view_id,
+ const content::ContextMenuParams& params,
+ bool is_link,
+ const std::string& extra_headers,
+ bool granted) {
+ if (!granted)
+ return;
+
+ content::WebContents* web_contents =
+ GetWebContents(render_process_id, render_view_id);
+ if (!web_contents)
+ return;
+
+ const GURL& url = is_link ? params.link_url : params.src_url;
+ const GURL& referring_url =
+ params.frame_url.is_empty() ? params.page_url : params.frame_url;
+ content::DownloadManager* dlm =
+ content::BrowserContext::GetDownloadManager(
+ web_contents->GetBrowserContext());
+ std::unique_ptr<content::DownloadUrlParameters> dl_params(
+ content::DownloadUrlParameters::CreateForWebContentsMainFrame(
+ web_contents, url));
+ content::Referrer referrer = content::Referrer::SanitizeForRequest(
+ url,
+ content::Referrer(referring_url.GetAsReferrer(), params.referrer_policy));
+ dl_params->set_referrer(referrer);
+ if (is_link)
+ dl_params->set_referrer_encoding(params.frame_charset);
+ net::HttpRequestHeaders headers;
+ headers.AddHeadersFromString(extra_headers);
+ for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext();)
+ dl_params->add_request_header(it.name(), it.value());
+ if (!is_link && extra_headers.empty())
+ dl_params->set_prefer_cache(true);
+ dl_params->set_prompt(false);
+ dlm->DownloadUrl(std::move(dl_params));
+}
+
+// Check if an interrupted download item can be auto resumed.
+bool IsInterruptedDownloadAutoResumable(content::DownloadItem* download_item) {
+ int interrupt_reason = download_item->GetLastReason();
+ DCHECK_NE(interrupt_reason, content::DOWNLOAD_INTERRUPT_REASON_NONE);
+ return
+ interrupt_reason == content::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT ||
+ interrupt_reason == content::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED ||
+ interrupt_reason ==
+ content::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED;
+}
+
+} // namespace
+
+// JNI methods
+static void Init(JNIEnv* env, const JavaParamRef<jobject>& obj) {
+ DownloadController::GetInstance()->Init(env, obj);
+}
+
+static void OnRequestFileAccessResult(JNIEnv* env,
+ const JavaParamRef<jobject>& obj,
+ jlong callback_id,
+ jboolean granted) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(callback_id);
+
+ // Convert java long long int to c++ pointer, take ownership.
+ std::unique_ptr<
+ DownloadControllerBase::AcquireFileAccessPermissionCallback>
+ cb(reinterpret_cast<
+ DownloadControllerBase::AcquireFileAccessPermissionCallback*>(
+ callback_id));
+ if (!granted) {
+ DownloadController::RecordDownloadCancelReason(
+ DownloadController::CANCEL_REASON_NO_STORAGE_PERMISSION);
+ }
+ cb->Run(granted);
+}
+
+struct DownloadController::JavaObject {
+ ScopedJavaLocalRef<jobject> Controller(JNIEnv* env) {
+ return GetRealObject(env, obj_);
+ }
+ jweak obj_;
+};
+
+// static
+bool DownloadController::RegisterDownloadController(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+// static
+DownloadControllerBase* DownloadControllerBase::Get() {
+ base::AutoLock lock(g_download_controller_lock_.Get());
+ if (!DownloadControllerBase::download_controller_)
+ download_controller_ = DownloadController::GetInstance();
+ return DownloadControllerBase::download_controller_;
+}
+
+// static
+void DownloadControllerBase::SetDownloadControllerBase(
+ DownloadControllerBase* download_controller) {
+ base::AutoLock lock(g_download_controller_lock_.Get());
+ DownloadControllerBase::download_controller_ = download_controller;
+}
+
+// static
+void DownloadController::RecordDownloadCancelReason(
+ DownloadCancelReason reason) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "MobileDownload.CancelReason", reason, CANCEL_REASON_MAX);
+}
+
+// static
+DownloadController* DownloadController::GetInstance() {
+ return base::Singleton<DownloadController>::get();
+}
+
+DownloadController::DownloadController()
+ : java_object_(NULL) {
+}
+
+DownloadController::~DownloadController() {
+ if (java_object_) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ env->DeleteWeakGlobalRef(java_object_->obj_);
+ delete java_object_;
+ base::android::CheckException(env);
+ }
+}
+
+// Initialize references to Java object.
+void DownloadController::Init(JNIEnv* env, jobject obj) {
+ java_object_ = new JavaObject;
+ java_object_->obj_ = env->NewWeakGlobalRef(obj);
+}
+
+void DownloadController::AcquireFileAccessPermission(
+ WebContents* web_contents,
+ const DownloadControllerBase::AcquireFileAccessPermissionCallback& cb) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(web_contents);
+
+ ui::WindowAndroid* window_android =
+ ViewAndroidHelper::FromWebContents(web_contents)->
+ GetViewAndroid()->GetWindowAndroid();
+ if (window_android && HasFileAccessPermission(window_android)) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE, base::Bind(cb, true));
+ return;
+ }
+ // Make copy on the heap so we can pass the pointer through JNI.
+ intptr_t callback_id = reinterpret_cast<intptr_t>(
+ new DownloadControllerBase::AcquireFileAccessPermissionCallback(cb));
+ ChromeDownloadDelegate::FromWebContents(web_contents)->
+ RequestFileAccess(callback_id);
+}
+
+void DownloadController::SetDefaultDownloadFileName(
+ const std::string& file_name) {
+ default_file_name_ = file_name;
+}
+
+bool DownloadController::HasFileAccessPermission(
+ ui::WindowAndroid* window_android) {
+ ScopedJavaLocalRef<jobject> jwindow_android = window_android->GetJavaObject();
+
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ DCHECK(!jwindow_android.is_null());
+
+ JNIEnv* env = base::android::AttachCurrentThread();
+ return Java_DownloadController_hasFileAccess(
+ env, GetJavaObject()->Controller(env).obj(), jwindow_android.obj());
+}
+
+void DownloadController::CreateGETDownload(
+ int render_process_id, int render_view_id, bool must_download,
+ const DownloadInfo& info) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+ // We are yielding the UI thread and render_view_host may go away by
+ // the time we come back. Pass along render_process_id and render_view_id
+ // to retrieve it later (if it still exists).
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&DownloadController::StartAndroidDownload,
+ base::Unretained(this),
+ render_process_id, render_view_id, must_download, info));
+}
+
+void DownloadController::StartAndroidDownload(
+ int render_process_id, int render_view_id, bool must_download,
+ const DownloadInfo& info) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ WebContents* web_contents = GetWebContents(render_process_id, render_view_id);
+ if (!web_contents) {
+ // The view went away. Can't proceed.
+ LOG(ERROR) << "Download failed on URL:" << info.url.spec();
+ return;
+ }
+
+ AcquireFileAccessPermission(
+ web_contents,
+ base::Bind(&DownloadController::StartAndroidDownloadInternal,
+ base::Unretained(this), render_process_id, render_view_id,
+ must_download, info));
+}
+
+void DownloadController::StartAndroidDownloadInternal(
+ int render_process_id, int render_view_id, bool must_download,
+ const DownloadInfo& info, bool allowed) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (!allowed)
+ return;
+
+ WebContents* web_contents = GetWebContents(render_process_id, render_view_id);
+ // The view went away. Can't proceed.
+ if (!web_contents)
+ return;
+
+ base::string16 filename = net::GetSuggestedFilename(
+ info.url, info.content_disposition,
+ std::string(), // referrer_charset
+ std::string(), // suggested_name
+ info.original_mime_type,
+ default_file_name_);
+ ChromeDownloadDelegate::FromWebContents(web_contents)->RequestHTTPGetDownload(
+ info.url.spec(), info.user_agent,
+ info.content_disposition, info.original_mime_type,
+ info.cookie, info.referer, filename,
+ info.total_bytes, info.has_user_gesture,
+ must_download);
+}
+
+void DownloadController::OnDownloadStarted(
+ DownloadItem* download_item) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ WebContents* web_contents = download_item->GetWebContents();
+ if (!web_contents)
+ return;
+
+ // Register for updates to the DownloadItem.
+ download_item->AddObserver(this);
+
+ ChromeDownloadDelegate::FromWebContents(web_contents)->OnDownloadStarted(
+ download_item->GetTargetFilePath().BaseName().value(),
+ download_item->GetMimeType());
+}
+
+void DownloadController::OnDownloadUpdated(DownloadItem* item) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (item->IsDangerous() && (item->GetState() != DownloadItem::CANCELLED))
+ OnDangerousDownload(item);
+
+ JNIEnv* env = base::android::AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> jguid =
+ ConvertUTF8ToJavaString(env, item->GetGuid());
+ ScopedJavaLocalRef<jstring> jurl =
+ ConvertUTF8ToJavaString(env, item->GetURL().spec());
+ ScopedJavaLocalRef<jstring> jmime_type =
+ ConvertUTF8ToJavaString(env, item->GetMimeType());
+ ScopedJavaLocalRef<jstring> jpath =
+ ConvertUTF8ToJavaString(env, item->GetTargetFilePath().value());
+ ScopedJavaLocalRef<jstring> jfilename = ConvertUTF8ToJavaString(
+ env, item->GetTargetFilePath().BaseName().value());
+ ScopedJavaLocalRef<jstring> joriginal_url =
+ ConvertUTF8ToJavaString(env, item->GetOriginalUrl().spec());
+ ScopedJavaLocalRef<jstring> jreferrer_url =
+ ConvertUTF8ToJavaString(env, item->GetReferrerUrl().spec());
+
+ switch (item->GetState()) {
+ case DownloadItem::IN_PROGRESS: {
+ base::TimeDelta time_delta;
+ item->TimeRemaining(&time_delta);
+ Java_DownloadController_onDownloadUpdated(
+ env, GetJavaObject()->Controller(env).obj(), jurl.obj(),
+ jmime_type.obj(), jfilename.obj(), jpath.obj(),
+ item->GetReceivedBytes(), jguid.obj(),
+ item->PercentComplete(), time_delta.InMilliseconds(),
+ item->HasUserGesture(), item->IsPaused(),
+ item->GetBrowserContext()->IsOffTheRecord());
+ break;
+ }
+ case DownloadItem::COMPLETE:
+ // Multiple OnDownloadUpdated() notifications may be issued while the
+ // download is in the COMPLETE state. Only handle one.
+ item->RemoveObserver(this);
+
+ // Call onDownloadCompleted
+ Java_DownloadController_onDownloadCompleted(
+ env, GetJavaObject()->Controller(env).obj(), jurl.obj(),
+ jmime_type.obj(), jfilename.obj(), jpath.obj(),
+ item->GetReceivedBytes(), jguid.obj(),
+ joriginal_url.obj(), jreferrer_url.obj(), item->HasUserGesture());
+ DownloadController::RecordDownloadCancelReason(
+ DownloadController::CANCEL_REASON_NOT_CANCELED);
+ break;
+ case DownloadItem::CANCELLED:
+ Java_DownloadController_onDownloadCancelled(
+ env, GetJavaObject()->Controller(env).obj(), jguid.obj());
+ break;
+ case DownloadItem::INTERRUPTED:
+ // When device loses/changes network, we get a NETWORK_TIMEOUT,
+ // NETWORK_FAILED or NETWORK_DISCONNECTED error. Download should auto
+ // resume in this case.
+ Java_DownloadController_onDownloadInterrupted(
+ env, GetJavaObject()->Controller(env).obj(), jurl.obj(),
+ jmime_type.obj(), jfilename.obj(), jpath.obj(),
+ item->GetReceivedBytes(), jguid.obj(),
+ item->CanResume(), IsInterruptedDownloadAutoResumable(item),
+ item->GetBrowserContext()->IsOffTheRecord());
+ item->RemoveObserver(this);
+ break;
+ case DownloadItem::MAX_DOWNLOAD_STATE:
+ NOTREACHED();
+ }
+}
+
+void DownloadController::OnDangerousDownload(DownloadItem* item) {
+ WebContents* web_contents = item->GetWebContents();
+ if (!web_contents)
+ return;
+ ChromeDownloadDelegate::FromWebContents(web_contents)->OnDangerousDownload(
+ item->GetTargetFilePath().BaseName().value(), item->GetGuid());
+}
+
+DownloadController::JavaObject*
+ DownloadController::GetJavaObject() {
+ if (!java_object_) {
+ // Initialize Java DownloadController by calling
+ // DownloadController.getInstance(), which will call Init()
+ // if Java DownloadController is not instantiated already.
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_DownloadController_getInstance(env);
+ }
+
+ DCHECK(java_object_);
+ return java_object_;
+}
+
+void DownloadController::StartContextMenuDownload(
+ const ContextMenuParams& params, WebContents* web_contents, bool is_link,
+ const std::string& extra_headers) {
+ int process_id = web_contents->GetRenderProcessHost()->GetID();
+ int routing_id = web_contents->GetRoutingID();
+ AcquireFileAccessPermission(
+ web_contents, base::Bind(&CreateContextMenuDownload, process_id,
+ routing_id, params, is_link, extra_headers));
+}
+
+void DownloadController::DangerousDownloadValidated(
+ WebContents* web_contents,
+ const std::string& download_guid,
+ bool accept) {
+ if (!web_contents)
+ return;
+ DownloadManager* dlm =
+ BrowserContext::GetDownloadManager(web_contents->GetBrowserContext());
+ DownloadItem* item = dlm->GetDownloadByGuid(download_guid);
+ if (!item)
+ return;
+ if (accept)
+ item->ValidateDangerousDownload();
+ else
+ item->Remove();
+}
« 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