// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ios/chrome/browser/webui/ui_bundled/download_internals_ui.h"
#include <vector>
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/uuid.h"
#include "components/download/public/background_service/background_download_service.h"
#include "components/download/public/background_service/download_params.h"
#include "components/download/public/background_service/logger.h"
#include "components/grit/download_internals_resources.h"
#include "components/grit/download_internals_resources_map.h"
#include "ios/chrome/browser/download/model/background_service/background_download_service_factory.h"
#include "ios/chrome/browser/shared/model/profile/profile_ios.h"
#include "ios/chrome/browser/shared/model/url/chrome_url_constants.h"
#include "ios/web/public/webui/web_ui_ios.h"
#include "ios/web/public/webui/web_ui_ios_data_source.h"
#include "ios/web/public/webui/web_ui_ios_message_handler.h"
// DownloadInternalsUIMessageHandler.
// Glue code between web UI frontend and background download service native
// code.
class DownloadInternalsUIMessageHandler : public web::WebUIIOSMessageHandler,
public download::Logger::Observer {
public:
DownloadInternalsUIMessageHandler() = default;
DownloadInternalsUIMessageHandler(const DownloadInternalsUIMessageHandler&) =
delete;
void operator=(const DownloadInternalsUIMessageHandler&) = delete;
~DownloadInternalsUIMessageHandler() override {
if (download_service_) {
download_service_->GetLogger()->RemoveObserver(this);
}
}
private:
// WebUIIOSMessageHandler implementation.
void RegisterMessages() override {
web_ui()->RegisterMessageCallback(
"getServiceStatus",
base::BindRepeating(
&DownloadInternalsUIMessageHandler::HandleGetServiceStatus,
weak_ptr_factory_.GetWeakPtr()));
web_ui()->RegisterMessageCallback(
"getServiceDownloads",
base::BindRepeating(
&DownloadInternalsUIMessageHandler::HandleGetServiceDownloads,
weak_ptr_factory_.GetWeakPtr()));
web_ui()->RegisterMessageCallback(
"startDownload",
base::BindRepeating(
&DownloadInternalsUIMessageHandler::HandleStartDownload,
weak_ptr_factory_.GetWeakPtr()));
ChromeBrowserState* browser_state =
ChromeBrowserState::FromWebUIIOS(web_ui());
download_service_ =
BackgroundDownloadServiceFactory::GetForBrowserState(browser_state);
// download_service_ will be null in incognito mode on iOS.
if (download_service_)
download_service_->GetLogger()->AddObserver(this);
}
// download::Logger::Observer implementation.
void OnServiceStatusChanged(
const base::Value::Dict& service_status) override {
web_ui()->FireWebUIListener("service-status-changed", service_status);
}
void OnServiceDownloadsAvailable(
const base::Value::List& service_downloads) override {
web_ui()->FireWebUIListener("service-downloads-available",
service_downloads);
}
void OnServiceDownloadChanged(
const base::Value::Dict& service_download) override {
web_ui()->FireWebUIListener("service-download-changed", service_download);
}
void OnServiceDownloadFailed(
const base::Value::Dict& service_download) override {
web_ui()->FireWebUIListener("service-download-failed", service_download);
}
void OnServiceRequestMade(const base::Value::Dict& service_request) override {
web_ui()->FireWebUIListener("service-request-made", service_request);
}
void HandleGetServiceStatus(const base::Value::List& args) {
if (!download_service_)
return;
web_ui()->ResolveJavascriptCallback(
args[0], download_service_->GetLogger()->GetServiceStatus());
}
void HandleGetServiceDownloads(const base::Value::List& args) {
if (!download_service_)
return;
web_ui()->ResolveJavascriptCallback(
args[0], download_service_->GetLogger()->GetServiceDownloads());
}
void HandleStartDownload(const base::Value::List& args) {
if (!download_service_)
return;
CHECK_GT(args.size(), 1u) << "Missing argument download URL.";
GURL url = GURL(args[1].GetString());
if (!url.is_valid()) {
LOG(WARNING) << "Can't parse download URL, try to enter a valid URL.";
return;
}
download::DownloadParams params;
params.guid = base::Uuid::GenerateRandomV4().AsLowercaseString();
params.client = download::DownloadClient::DEBUGGING;
params.request_params.method = "GET";
params.request_params.url = url;
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("download_internals_webui_source",
R"(
semantics {
sender: "Download Internals Page"
description:
"Starts a download with background download service in WebUI."
trigger:
"User clicks on the download button in "
"chrome://download-internals."
data: "None"
destination: WEBSITE
}
policy {
cookies_allowed: YES
cookies_store: "user"
setting: "This feature cannot be disabled by settings."
policy_exception_justification: "Not implemented."
})");
params.traffic_annotation =
net::MutableNetworkTrafficAnnotationTag(traffic_annotation);
download_service_->StartDownload(std::move(params));
}
raw_ptr<download::BackgroundDownloadService> download_service_ = nullptr;
base::WeakPtrFactory<DownloadInternalsUIMessageHandler> weak_ptr_factory_{
this};
};
DownloadInternalsUI::DownloadInternalsUI(web::WebUIIOS* web_ui,
const std::string& host)
: web::WebUIIOSController(web_ui, host) {
web_ui->AddMessageHandler(
std::make_unique<DownloadInternalsUIMessageHandler>());
web::WebUIIOSDataSource* html_source =
web::WebUIIOSDataSource::Create(kChromeUIDownloadInternalsHost);
html_source->UseStringsJs();
html_source->AddResourcePaths(base::make_span(
kDownloadInternalsResources, kDownloadInternalsResourcesSize));
html_source->SetDefaultResource(
IDR_DOWNLOAD_INTERNALS_DOWNLOAD_INTERNALS_HTML);
web::WebUIIOSDataSource::Add(ChromeBrowserState::FromWebUIIOS(web_ui),
html_source);
}
DownloadInternalsUI::~DownloadInternalsUI() = default;