| // 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 |