blob: de37dbfd774c5c5bdd82ba77d30aaf708ef1dbb4 [file] [log] [blame]
// Copyright 2017 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 "content/browser/payments/payment_instrument_icon_fetcher.h"
#include "base/base64.h"
#include "base/bind_helpers.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/storage_partition_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/manifest_icon_downloader.h"
#include "content/public/browser/manifest_icon_selector.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image.h"
namespace content {
namespace {
// TODO(zino): Choose appropriate icon size dynamically on different platforms.
// Here we choose a large ideal icon size to be big enough for all platforms.
// Note that we only scale down for this icon size but not scale up.
// Please see: https://crbug.com/763886
const int kPaymentAppIdealIconSize = 0xFFFF;
const int kPaymentAppMinimumIconSize = 0;
void DownloadBestMatchingIcon(
WebContents* web_contents,
const std::vector<Manifest::Icon>& icons,
PaymentInstrumentIconFetcher::PaymentInstrumentIconFetcherCallback
callback);
void OnIconFetched(
WebContents* web_contents,
const std::vector<Manifest::Icon>& icons,
PaymentInstrumentIconFetcher::PaymentInstrumentIconFetcherCallback callback,
const SkBitmap& bitmap) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (bitmap.drawsNothing()) {
if (icons.empty()) {
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::BindOnce(std::move(callback), std::string()));
} else {
// If could not download or decode the chosen image(e.g. not supported,
// invalid), try it again with remaining icons.
DownloadBestMatchingIcon(web_contents, icons, std::move(callback));
}
return;
}
gfx::Image decoded_image = gfx::Image::CreateFrom1xBitmap(bitmap);
scoped_refptr<base::RefCountedMemory> raw_data = decoded_image.As1xPNGBytes();
std::string encoded_data;
base::Base64Encode(
base::StringPiece(raw_data->front_as<char>(), raw_data->size()),
&encoded_data);
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::BindOnce(std::move(callback), encoded_data));
}
void DownloadBestMatchingIcon(
WebContents* web_contents,
const std::vector<Manifest::Icon>& icons,
PaymentInstrumentIconFetcher::PaymentInstrumentIconFetcherCallback
callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
GURL icon_url = ManifestIconSelector::FindBestMatchingIcon(
icons, kPaymentAppIdealIconSize, kPaymentAppMinimumIconSize,
Manifest::Icon::IconPurpose::ANY);
std::vector<Manifest::Icon> copy_icons;
for (const auto& icon : icons) {
if (icon.src != icon_url) {
copy_icons.emplace_back(icon);
}
}
bool can_download_icon = ManifestIconDownloader::Download(
web_contents, icon_url, kPaymentAppIdealIconSize,
kPaymentAppMinimumIconSize,
base::Bind(&OnIconFetched, web_contents, copy_icons,
base::Passed(std::move(callback))));
// If the icon url is invalid, it's better to give the information to
// developers in advance unlike when fetching or decoding fails.
// We already checked whether they are valid in renderer side. So, if the
// icon url is invalid, it's something wrong.
if (!can_download_icon) {
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::BindOnce(std::move(callback), std::string()));
}
}
WebContents* GetWebContentsFromProviderHostIds(
const GURL& scope,
std::unique_ptr<std::vector<std::pair<int, int>>> provider_hosts) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (const auto& host : *provider_hosts) {
RenderFrameHostImpl* render_frame_host =
RenderFrameHostImpl::FromID(host.first, host.second);
if (!render_frame_host)
continue;
WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
WebContents::FromRenderFrameHost(render_frame_host));
if (!web_contents || web_contents->IsHidden() ||
scope.GetOrigin().spec().compare(
web_contents->GetLastCommittedURL().GetOrigin().spec()) != 0) {
continue;
}
return web_contents;
}
return nullptr;
}
void StartOnUI(
const GURL& scope,
std::unique_ptr<std::vector<std::pair<int, int>>> provider_hosts,
const std::vector<Manifest::Icon>& icons,
PaymentInstrumentIconFetcher::PaymentInstrumentIconFetcherCallback
callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
WebContents* web_contents =
GetWebContentsFromProviderHostIds(scope, std::move(provider_hosts));
DownloadBestMatchingIcon(web_contents, icons, std::move(callback));
}
} // namespace
// static
void PaymentInstrumentIconFetcher::Start(
const GURL& scope,
std::unique_ptr<std::vector<std::pair<int, int>>> provider_hosts,
const std::vector<Manifest::Icon>& icons,
PaymentInstrumentIconFetcherCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(&StartOnUI, scope, std::move(provider_hosts), icons,
std::move(callback)));
}
} // namespace content