blob: bb594bbd110c6fefb2f14fbfba2faacff47f1df5 [file]
// 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/webapk/webapk_icon_hasher.h"
#include <utility>
#include "base/barrier_closure.h"
#include "base/bind.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "net/base/data_url.h"
#include "net/base/network_isolation_key.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "third_party/smhasher/src/MurmurHash2.h"
namespace {
// The seed to use when taking the murmur2 hash of the icon.
const uint64_t kMurmur2HashSeed = 0;
// The default number of milliseconds to wait for the icon download to complete.
const int kDownloadTimeoutInMilliseconds = 60000;
// Computes Murmur2 hash of |raw_image_data|.
std::string ComputeMurmur2Hash(const std::string& raw_image_data) {
// WARNING: We are running in the browser process. |raw_image_data| is the
// image's raw, unsanitized bytes from the web. |raw_image_data| may contain
// malicious data. Decoding unsanitized bitmap data to an SkBitmap in the
// browser process is a security bug.
uint64_t hash = MurmurHash64A(raw_image_data.data(), raw_image_data.size(),
kMurmur2HashSeed);
return base::NumberToString(hash);
}
void OnMurmur2Hash(WebApkIconHasher::Icon* result,
base::OnceClosure done_closure,
WebApkIconHasher::Icon icon) {
*result = std::move(icon);
std::move(done_closure).Run();
}
void OnAllMurmur2Hashes(
std::unique_ptr<std::map<std::string, WebApkIconHasher::Icon>> icons,
WebApkIconHasher::Murmur2HashMultipleCallback callback) {
for (const auto& icon_pair : *icons) {
if (icon_pair.second.hash.empty()) {
std::move(callback).Run(base::nullopt);
return;
}
}
std::move(callback).Run(std::move(*icons));
}
} // anonymous namespace
// static
void WebApkIconHasher::DownloadAndComputeMurmur2Hash(
network::mojom::URLLoaderFactory* url_loader_factory,
const url::Origin& request_initiator,
const std::set<GURL>& icon_urls,
Murmur2HashMultipleCallback callback) {
auto icons_ptr = std::make_unique<std::map<std::string, Icon>>();
auto& icons = *icons_ptr;
auto barrier_closure = base::BarrierClosure(
icon_urls.size(),
base::BindOnce(&OnAllMurmur2Hashes, std::move(icons_ptr),
std::move(callback)));
for (const auto& icon_url : icon_urls) {
// |hashes| is owned by |barrier_closure|.
DownloadAndComputeMurmur2HashWithTimeout(
url_loader_factory, request_initiator, icon_url,
kDownloadTimeoutInMilliseconds,
base::BindOnce(&OnMurmur2Hash, &icons[icon_url.spec()],
barrier_closure));
}
}
// static
void WebApkIconHasher::DownloadAndComputeMurmur2HashWithTimeout(
network::mojom::URLLoaderFactory* url_loader_factory,
const url::Origin& request_initiator,
const GURL& icon_url,
int timeout_ms,
Murmur2HashCallback callback) {
if (!icon_url.is_valid()) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), Icon{}));
return;
}
if (icon_url.SchemeIs(url::kDataScheme)) {
std::string mime_type, char_set, data;
Icon icon;
if (net::DataURL::Parse(icon_url, &mime_type, &char_set, &data) &&
!data.empty()) {
icon.hash = ComputeMurmur2Hash(data);
icon.unsafe_data = std::move(data);
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), std::move(icon)));
return;
}
// The icon hasher will delete itself when it is done.
new WebApkIconHasher(url_loader_factory, request_initiator, icon_url,
timeout_ms, std::move(callback));
}
WebApkIconHasher::WebApkIconHasher(
network::mojom::URLLoaderFactory* url_loader_factory,
const url::Origin& request_initiator,
const GURL& icon_url,
int timeout_ms,
Murmur2HashCallback callback)
: callback_(std::move(callback)) {
DCHECK(url_loader_factory);
download_timeout_timer_.Start(
FROM_HERE, base::TimeDelta::FromMilliseconds(timeout_ms),
base::BindOnce(&WebApkIconHasher::OnDownloadTimedOut,
base::Unretained(this)));
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->trusted_params = network::ResourceRequest::TrustedParams();
resource_request->trusted_params->isolation_info = net::IsolationInfo::Create(
net::IsolationInfo::RequestType::kOther, request_initiator,
request_initiator, net::SiteForCookies());
resource_request->request_initiator = request_initiator;
resource_request->url = icon_url;
simple_url_loader_ = network::SimpleURLLoader::Create(
std::move(resource_request),
TRAFFIC_ANNOTATION_WITHOUT_PROTO("webapk icon hasher"));
simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
url_loader_factory,
base::BindOnce(&WebApkIconHasher::OnSimpleLoaderComplete,
base::Unretained(this)));
}
WebApkIconHasher::~WebApkIconHasher() {}
void WebApkIconHasher::OnSimpleLoaderComplete(
std::unique_ptr<std::string> response_body) {
download_timeout_timer_.Stop();
// Check for non-empty body in case of HTTP 204 (no content) response.
if (!response_body || response_body->empty()) {
RunCallback({});
return;
}
// WARNING: We are running in the browser process. |*response_body| is the
// image's raw, unsanitized bytes from the web. |*response_body| may contain
// malicious data. Decoding unsanitized bitmap data to an SkBitmap in the
// browser process is a security bug.
Icon icon;
icon.unsafe_data = std::move(*response_body);
icon.hash = ComputeMurmur2Hash(icon.unsafe_data);
RunCallback(std::move(icon));
}
void WebApkIconHasher::OnDownloadTimedOut() {
simple_url_loader_.reset();
RunCallback({});
}
void WebApkIconHasher::RunCallback(Icon icon) {
std::move(callback_).Run(std::move(icon));
delete this;
}