blob: a6b8a0c102d984c1fac81787c4fac6191eed87e4 [file] [log] [blame]
// Copyright 2018 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.
#import "ios/chrome/browser/download/ar_quick_look_tab_helper.h"
#include <memory>
#include <string>
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#import "ios/chrome/browser/download/ar_quick_look_tab_helper_delegate.h"
#include "ios/chrome/browser/download/download_directory_util.h"
#include "ios/chrome/browser/download/usdz_mime_type.h"
#import "ios/web/public/download/download_task.h"
#include "net/base/net_errors.h"
#include "net/url_request/url_fetcher_response_writer.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
const char kIOSDownloadARModelStateHistogram[] =
"Download.IOSDownloadARModelState";
const char kUsdzMimeTypeHistogramSuffix[] = ".USDZ";
namespace {
// Returns a suffix for Download.IOSDownloadARModelState histogram for the
// |download_task|.
std::string GetMimeTypeSuffix(web::DownloadTask* download_task) {
DCHECK(download_task->GetOriginalMimeType() == kUsdzMimeType);
return kUsdzMimeTypeHistogramSuffix;
}
// Returns an enum for Download.IOSDownloadARModelState histogram for the
// terminated |download_task|.
IOSDownloadARModelState GetHistogramEnum(web::DownloadTask* download_task) {
DCHECK(download_task);
if (download_task->GetState() == web::DownloadTask::State::kNotStarted) {
return IOSDownloadARModelState::kCreated;
}
if (download_task->GetState() == web::DownloadTask::State::kInProgress) {
return IOSDownloadARModelState::kStarted;
}
DCHECK(download_task->IsDone());
if (download_task->GetMimeType() != kUsdzMimeType) {
return IOSDownloadARModelState::kWrongMimeTypeFailure;
}
if (download_task->GetHttpCode() == 401 ||
download_task->GetHttpCode() == 403) {
return IOSDownloadARModelState::kUnauthorizedFailure;
}
if (download_task->GetErrorCode()) {
return IOSDownloadARModelState::kOtherFailure;
}
return IOSDownloadARModelState::kSuccessful;
}
// Logs Download.IOSDownloadARModelState* histogram for the |download_task|.
void LogHistogram(web::DownloadTask* download_task) {
DCHECK(download_task);
base::UmaHistogramEnumeration(
kIOSDownloadARModelStateHistogram + GetMimeTypeSuffix(download_task),
GetHistogramEnum(download_task));
}
} // namespace
ARQuickLookTabHelper::ARQuickLookTabHelper(web::WebState* web_state)
: web_state_(web_state) {
DCHECK(web_state_);
}
ARQuickLookTabHelper::~ARQuickLookTabHelper() {
if (download_task_) {
RemoveCurrentDownload();
}
}
void ARQuickLookTabHelper::CreateForWebState(web::WebState* web_state) {
DCHECK(web_state);
if (!FromWebState(web_state)) {
web_state->SetUserData(UserDataKey(),
std::make_unique<ARQuickLookTabHelper>(web_state));
}
}
void ARQuickLookTabHelper::Download(
std::unique_ptr<web::DownloadTask> download_task) {
DCHECK(download_task);
LogHistogram(download_task.get());
base::FilePath download_dir;
if (!GetDownloadsDirectory(&download_dir)) {
return;
}
if (download_task_) {
RemoveCurrentDownload();
}
// Take ownership of |download_task| and start the download.
download_task_ = std::move(download_task);
download_task_->AddObserver(this);
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
base::BindOnce(&base::CreateDirectory, download_dir),
base::BindOnce(&ARQuickLookTabHelper::DownloadWithDestinationDir,
AsWeakPtr(), download_dir, download_task_.get()));
}
void ARQuickLookTabHelper::DidFinishDownload() {
DCHECK_EQ(download_task_->GetState(), web::DownloadTask::State::kComplete);
// Inform the delegate only if the download has been successful.
if (download_task_->GetHttpCode() == 401 ||
download_task_->GetHttpCode() == 403 || download_task_->GetErrorCode() ||
download_task_->GetMimeType() != kUsdzMimeType) {
return;
}
net::URLFetcherFileWriter* file_writer =
download_task_->GetResponseWriter()->AsFileWriter();
base::FilePath path = file_writer->file_path();
NSURL* fileURL =
[NSURL fileURLWithPath:base::SysUTF8ToNSString(path.value())];
[delegate_ ARQuickLookTabHelper:this didFinishDowloadingFileWithURL:fileURL];
}
void ARQuickLookTabHelper::RemoveCurrentDownload() {
download_task_->RemoveObserver(this);
download_task_.reset();
}
void ARQuickLookTabHelper::DownloadWithDestinationDir(
const base::FilePath& destination_dir,
web::DownloadTask* download_task,
bool directory_created) {
// Return early if |download_task_| has changed.
if (download_task != download_task_.get()) {
return;
}
if (!directory_created) {
RemoveCurrentDownload();
return;
}
auto task_runner = base::CreateSequencedTaskRunnerWithTraits(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE});
base::string16 file_name = download_task_->GetSuggestedFilename();
base::FilePath path = destination_dir.Append(base::UTF16ToUTF8(file_name));
auto writer = std::make_unique<net::URLFetcherFileWriter>(task_runner, path);
writer->Initialize(base::BindRepeating(
&ARQuickLookTabHelper::DownloadWithWriter, AsWeakPtr(),
base::Passed(std::move(writer)), download_task_.get()));
}
void ARQuickLookTabHelper::DownloadWithWriter(
std::unique_ptr<net::URLFetcherFileWriter> writer,
web::DownloadTask* download_task,
int writer_initialization_status) {
// Return early if |download_task_| has changed.
if (download_task != download_task_.get()) {
return;
}
if (writer_initialization_status == net::OK) {
download_task_->Start(std::move(writer));
LogHistogram(download_task_.get());
} else {
RemoveCurrentDownload();
}
}
void ARQuickLookTabHelper::OnDownloadUpdated(web::DownloadTask* download_task) {
DCHECK_EQ(download_task, download_task_.get());
switch (download_task_->GetState()) {
case web::DownloadTask::State::kCancelled:
LogHistogram(download_task_.get());
RemoveCurrentDownload();
break;
case web::DownloadTask::State::kInProgress:
// Do nothing. Histogram is already logged after the task was started.
break;
case web::DownloadTask::State::kComplete:
LogHistogram(download_task_.get());
DidFinishDownload();
break;
case web::DownloadTask::State::kNotStarted:
NOTREACHED() << "Invalid state.";
}
}