Commit 55bacf13 authored by Jinho Bang's avatar Jinho Bang Committed by Commit Bot

PaymentHandler: Refactor PaymentInstrumentIconFetcher class.

The current PaymentInstrumentIconFetcher class is too complicated and
can not ensure thread-safety due to misuse some APIs. So, this patch
refactors the class to use ManifestIconDownloader internally. After
this patch, we can remove much duplicated codes and resolve some
crashes in debug mode as well.

Bug: 752836
Change-Id: I04c01d433d093ae138148e6aa2849bc1713c1c85
Reviewed-on: https://chromium-review.googlesource.com/658201
Commit-Queue: Jinho Bang <jinho.bang@samsung.com>
Reviewed-by: default avatarGanggui Tang <gogerald@chromium.org>
Reviewed-by: default avatarRouslan Solomakhin <rouslan@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#501589}
parent 356b0964
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/service_worker/service_worker_registration.h" #include "content/browser/service_worker/service_worker_registration.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/common/manifest.h"
#include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/image/image.h" #include "ui/gfx/image/image.h"
#include "url/gurl.h" #include "url/gurl.h"
...@@ -194,15 +195,26 @@ void PaymentAppDatabase::WritePaymentInstrument( ...@@ -194,15 +195,26 @@ void PaymentAppDatabase::WritePaymentInstrument(
DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (instrument->icons.size() > 0) { if (instrument->icons.size() > 0) {
std::unique_ptr<PaymentInstrumentIconFetcher> icon_fetcher = std::vector<Manifest::Icon> icons;
base::MakeUnique<PaymentInstrumentIconFetcher>(); for (const auto& image_object : instrument->icons) {
icon_fetcher->Start( Manifest::Icon icon;
instrument->icons, service_worker_context_, icon.src = image_object->src;
// TODO(zino): We should pass an actual image size comes from the
// renderer, but we just use gfx::Size(0, 0) for now until the feature
// is implemented. It means "any" size.
// Please see https://crbug.com/763886
icon.sizes.emplace_back(gfx::Size());
icon.purpose.emplace_back(Manifest::Icon::IconPurpose::ANY);
icons.emplace_back(icon);
}
PaymentInstrumentIconFetcher::Start(
scope, service_worker_context_->GetProviderHostIds(scope.GetOrigin()),
std::move(icons),
base::BindOnce(&PaymentAppDatabase::DidFetchedPaymentInstrumentIcon, base::BindOnce(&PaymentAppDatabase::DidFetchedPaymentInstrumentIcon,
weak_ptr_factory_.GetWeakPtr(), scope, instrument_key, weak_ptr_factory_.GetWeakPtr(), scope, instrument_key,
base::Passed(std::move(instrument)), base::Passed(std::move(instrument)),
base::Passed(std::move(callback)), base::Passed(std::move(callback))));
base::Passed(std::move(icon_fetcher))));
} else { } else {
service_worker_context_->FindReadyRegistrationForPattern( service_worker_context_->FindReadyRegistrationForPattern(
scope, scope,
...@@ -219,11 +231,9 @@ void PaymentAppDatabase::DidFetchedPaymentInstrumentIcon( ...@@ -219,11 +231,9 @@ void PaymentAppDatabase::DidFetchedPaymentInstrumentIcon(
const std::string& instrument_key, const std::string& instrument_key,
payments::mojom::PaymentInstrumentPtr instrument, payments::mojom::PaymentInstrumentPtr instrument,
WritePaymentInstrumentCallback callback, WritePaymentInstrumentCallback callback,
std::unique_ptr<PaymentInstrumentIconFetcher> icon_fetcher,
const std::string& icon) { const std::string& icon) {
DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK_CURRENTLY_ON(BrowserThread::IO);
icon_fetcher.reset();
if (icon.empty()) { if (icon.empty()) {
std::move(callback).Run(PaymentHandlerStatus::FETCH_INSTRUMENT_ICON_FAILED); std::move(callback).Run(PaymentHandlerStatus::FETCH_INSTRUMENT_ICON_FAILED);
return; return;
......
...@@ -165,7 +165,6 @@ class CONTENT_EXPORT PaymentAppDatabase { ...@@ -165,7 +165,6 @@ class CONTENT_EXPORT PaymentAppDatabase {
const std::string& instrument_key, const std::string& instrument_key,
payments::mojom::PaymentInstrumentPtr instrument, payments::mojom::PaymentInstrumentPtr instrument,
WritePaymentInstrumentCallback callback, WritePaymentInstrumentCallback callback,
std::unique_ptr<PaymentInstrumentIconFetcher> fetcher,
const std::string& icon); const std::string& icon);
// ClearPaymentInstruments callbacks // ClearPaymentInstruments callbacks
......
...@@ -6,160 +6,145 @@ ...@@ -6,160 +6,145 @@
#include "base/base64.h" #include "base/base64.h"
#include "base/bind_helpers.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/storage_partition_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/common/service_manager_connection.h" #include "content/public/browser/manifest_icon_downloader.h"
#include "net/base/load_flags.h" #include "content/public/browser/manifest_icon_selector.h"
#include "net/http/http_status_code.h" #include "third_party/skia/include/core/SkBitmap.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/data_decoder/public/cpp/decode_image.h"
#include "services/service_manager/public/cpp/connector.h"
#include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image.h" #include "ui/gfx/image/image.h"
namespace content { namespace content {
namespace { namespace {
net::NetworkTrafficAnnotationTag g_traffic_annotation = // TODO(zino): Choose appropriate icon size dynamically on different platforms.
net::DefineNetworkTrafficAnnotation("payment_instrument_icon_fetcher", R"( // Here we choose a large ideal icon size to be big enough for all platforms.
semantics { // Note that we only scale down for this icon size but not scale up.
sender: "Web Payments" // Please see: https://crbug.com/763886
description: const int kPaymentAppIdealIconSize = 0xFFFF;
"Chromium downloads payment instrument icons when registering " const int kPaymentAppMinimumIconSize = 0;
"payment instruments."
trigger: void DownloadBestMatchingIcon(
"When user navigates to a website to register web payment apps." WebContents* web_contents,
data: const std::vector<Manifest::Icon>& icons,
"URL of the required icon to fetch. No user information is sent." PaymentInstrumentIconFetcher::PaymentInstrumentIconFetcherCallback
destination: WEBSITE callback);
}
policy { void OnIconFetched(
cookies_allowed: NO WebContents* web_contents,
setting: const std::vector<Manifest::Icon>& icons,
"This feature cannot be disabled in settings. Users can refuse to " PaymentInstrumentIconFetcher::PaymentInstrumentIconFetcherCallback callback,
"install web payment apps." const SkBitmap& bitmap) {
policy_exception_justification: "Not implemented."
})");
} // namespace
PaymentInstrumentIconFetcher::PaymentInstrumentIconFetcher()
: checking_image_object_index_(0), weak_ptr_factory_(this) {}
PaymentInstrumentIconFetcher::~PaymentInstrumentIconFetcher() {}
void PaymentInstrumentIconFetcher::Start(
const std::vector<payments::mojom::ImageObjectPtr>& image_objects,
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
PaymentInstrumentIconFetcherCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
for (const auto& obj : image_objects) {
image_objects_.emplace_back(payments::mojom::ImageObject::New(obj->src));
}
DCHECK_GT(image_objects_.size(), 0U);
callback_ = std::move(callback);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::BindOnce(&PaymentInstrumentIconFetcher::StartFromUIThread,
weak_ptr_factory_.GetWeakPtr(),
std::move(service_worker_context)));
}
void PaymentInstrumentIconFetcher::StartFromUIThread(
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
url_request_context_getter_ = if (bitmap.drawsNothing()) {
service_worker_context->storage_partition()->GetURLRequestContext(); if (icons.empty()) {
if (!url_request_context_getter_) { BrowserThread::PostTask(
PostCallbackToIOThread(std::string()); 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; return;
} }
FetchIcon(); 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 PaymentInstrumentIconFetcher::FetchIcon() { void DownloadBestMatchingIcon(
WebContents* web_contents,
const std::vector<Manifest::Icon>& icons,
PaymentInstrumentIconFetcher::PaymentInstrumentIconFetcherCallback
callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (checking_image_object_index_ >= image_objects_.size()) { GURL icon_url = ManifestIconSelector::FindBestMatchingIcon(
PostCallbackToIOThread(std::string()); icons, kPaymentAppIdealIconSize, kPaymentAppMinimumIconSize,
return; Manifest::Icon::IconPurpose::ANY);
}
GURL* icon_src_url = &(image_objects_[checking_image_object_index_]->src); std::vector<Manifest::Icon> copy_icons;
if (!icon_src_url->is_valid()) { for (const auto& icon : icons) {
checking_image_object_index_++; if (icon.src != icon_url) {
FetchIcon(); copy_icons.emplace_back(icon);
return; }
} }
fetcher_ = net::URLFetcher::Create(*icon_src_url, net::URLFetcher::GET, this, bool can_download_icon = ManifestIconDownloader::Download(
g_traffic_annotation); web_contents, icon_url, kPaymentAppIdealIconSize,
fetcher_->SetRequestContext(url_request_context_getter_.get()); kPaymentAppMinimumIconSize,
fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | base::Bind(&OnIconFetched, web_contents, copy_icons,
net::LOAD_DO_NOT_SAVE_COOKIES); base::Passed(std::move(callback))));
fetcher_->SetStopOnRedirect(true); // If the icon url is invalid, it's better to give the information to
fetcher_->Start(); // 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()));
}
} }
void PaymentInstrumentIconFetcher::OnURLFetchComplete( WebContents* GetWebContentsFromProviderHostIds(
const net::URLFetcher* source) { const GURL& scope,
std::unique_ptr<std::vector<std::pair<int, int>>> provider_hosts) {
DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK_EQ(fetcher_.get(), source); for (const auto& host : *provider_hosts) {
std::unique_ptr<net::URLFetcher> free_fetcher = std::move(fetcher_); RenderFrameHostImpl* render_frame_host =
RenderFrameHostImpl::FromID(host.first, host.second);
std::string data; if (!render_frame_host)
if (!(source->GetStatus().is_success() && continue;
source->GetResponseCode() == net::HTTP_OK &&
source->GetResponseAsString(&data))) { WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
checking_image_object_index_++; WebContents::FromRenderFrameHost(render_frame_host));
FetchIcon(); if (!web_contents || web_contents->IsHidden() ||
return; scope.GetOrigin().spec().compare(
web_contents->GetLastCommittedURL().GetOrigin().spec()) != 0) {
continue;
}
return web_contents;
} }
return nullptr;
service_manager::mojom::ConnectorRequest connector_request;
std::unique_ptr<service_manager::Connector> connector =
service_manager::Connector::Create(&connector_request);
content::ServiceManagerConnection::GetForProcess()
->GetConnector()
->BindConnectorRequest(std::move(connector_request));
std::vector<uint8_t> image_data(data.begin(), data.end());
data_decoder::DecodeImage(
connector.get(), image_data, data_decoder::mojom::ImageCodec::DEFAULT,
false, data_decoder::kDefaultMaxSizeInBytes, gfx::Size(),
base::BindOnce(&PaymentInstrumentIconFetcher::DecodeImageCallback,
weak_ptr_factory_.GetWeakPtr()));
} }
void PaymentInstrumentIconFetcher::DecodeImageCallback(const SkBitmap& bitmap) { 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); DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (bitmap.drawsNothing()) { WebContents* web_contents =
checking_image_object_index_++; GetWebContentsFromProviderHostIds(scope, std::move(provider_hosts));
FetchIcon(); DownloadBestMatchingIcon(web_contents, icons, std::move(callback));
return; }
}
gfx::Image decoded_image = gfx::Image::CreateFrom1xBitmap(bitmap); } // namespace
scoped_refptr<base::RefCountedMemory> raw_data = decoded_image.As1xPNGBytes();
std::string base_64;
base::Base64Encode(
base::StringPiece(raw_data->front_as<char>(), raw_data->size()),
&base_64);
PostCallbackToIOThread(base_64); // 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);
void PaymentInstrumentIconFetcher::PostCallbackToIOThread( BrowserThread::PostTask(
const std::string& decoded_data) { BrowserThread::UI, FROM_HERE,
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::BindOnce(&StartOnUI, scope, std::move(provider_hosts), icons,
base::BindOnce(std::move(callback_), decoded_data)); std::move(callback)));
} }
} // namespace content } // namespace content
...@@ -8,65 +8,28 @@ ...@@ -8,65 +8,28 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "base/callback_forward.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/public/common/manifest.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "net/url_request/url_request_context_getter.h"
#include "third_party/WebKit/public/platform/modules/payments/payment_app.mojom.h" #include "third_party/WebKit/public/platform/modules/payments/payment_app.mojom.h"
#include "third_party/skia/include/core/SkBitmap.h"
namespace content { namespace content {
class PaymentInstrumentIconFetcher final : private net::URLFetcherDelegate { class PaymentInstrumentIconFetcher {
public: public:
using PaymentInstrumentIconFetcherCallback = using PaymentInstrumentIconFetcherCallback =
base::OnceCallback<void(const std::string&)>; base::OnceCallback<void(const std::string&)>;
PaymentInstrumentIconFetcher(); // Should be called on IO thread.
~PaymentInstrumentIconFetcher() override; static void Start(
const GURL& scope,
// Starts fetching and decoding payment instrument icon from online. The std::unique_ptr<std::vector<std::pair<int, int>>> provider_hosts,
// result will be send back through |callback|. const std::vector<Manifest::Icon>& icons,
// TODO(gogerald): Right now, we return the first fetchable and decodable icon PaymentInstrumentIconFetcherCallback callback);
// with smallest available size. We may add more logic to choose appropriate
// icon according to icon size and our usage.
void Start(const std::vector<payments::mojom::ImageObjectPtr>& image_objects,
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context,
PaymentInstrumentIconFetcherCallback callback);
private: private:
void StartFromUIThread( DISALLOW_IMPLICIT_CONSTRUCTORS(PaymentInstrumentIconFetcher);
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context);
void PostCallbackToIOThread(const std::string& decoded_data);
// data_decoder::mojom::ImageDecoder::DecodeImageCallback.
void DecodeImageCallback(const SkBitmap& bitmap);
// Override net::URLFetcherDelegate.
void OnURLFetchComplete(const net::URLFetcher* source) override;
void FetchIcon();
// Declared set of image objects of the payment instrument.
std::vector<payments::mojom::ImageObjectPtr> image_objects_;
// The index of the currently checking image object in image_objects_.
size_t checking_image_object_index_;
// The callback to notify after complete.
PaymentInstrumentIconFetcherCallback callback_;
// The url request context getter for fetching icon from online.
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
// The url fetcher to fetch raw icon from online.
std::unique_ptr<net::URLFetcher> fetcher_;
base::WeakPtrFactory<PaymentInstrumentIconFetcher> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(PaymentInstrumentIconFetcher);
}; };
} // namespace content } // namespace content
......
...@@ -17,14 +17,54 @@ function registerPaymentApp() { ...@@ -17,14 +17,54 @@ function registerPaymentApp() {
'basic-card-payment-app-id', 'basic-card-payment-app-id',
{ {
name: 'Visa ****', name: 'Visa ****',
enabledMethods: ['basic-card'] enabledMethods: ['basic-card'],
icons: [
{
'src': 'icon-1x.png',
'sizes': '48x48',
'type': 'image/png'
},
{
'src': 'icon-1-5x.png',
'sizes': '72x72',
'type': 'image/png'
},
{
'src': 'icon-2x.png',
'sizes': '96x96',
'type': 'image/png',
'purpose': 'any badge'
}
]
}), }),
registration.paymentManager.instruments.set( registration.paymentManager.instruments.set(
'bobpay-payment-app-id', 'bobpay-payment-app-id',
{ {
name: "Bob Pay", name: 'Bob Pay',
enabledMethods: ['https://bobpay.com'] enabledMethods: ['https://bobpay.com'],
icons: [
{
'src': 'icon-1x.png',
'sizes': '72x72',
'type': 'image/png'
},
{
'src': 'icon-3x.png',
'sizes': '144x144',
'type': 'image/png'
},
{
'src': 'icon-4x.png',
'sizes': '192x192',
'type': 'image/png'
},
{
'src': 'invalid_icon.png',
'sizes': '192x192',
'type': 'image/png'
}
]
}), }),
]; ];
...@@ -34,7 +74,7 @@ function registerPaymentApp() { ...@@ -34,7 +74,7 @@ function registerPaymentApp() {
sendResultToTest('registered'); sendResultToTest('registered');
}) })
.catch(result => { .catch(result => {
sendResultToTest('error'); sendResultToTest(result);
}); });
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment