blob: 2296225d63ffb4003d2c2e20305cdf11e5891c23 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/shortcuts/document_icon_fetcher_task.h"
#include <iterator>
#include <memory>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_forward.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/types/expected.h"
#include "chrome/browser/shortcuts/shortcut_icon_generator.h"
#include "components/webapps/common/web_page_metadata.mojom.h"
#include "components/webapps/common/web_page_metadata_agent.mojom.h"
#include "content/public/browser/web_contents.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/geometry/size.h"
namespace shortcuts {
DocumentIconFetcherTask::DocumentIconFetcherTask(
content::WebContents& web_contents,
FetchIconsFromDocumentCallback callback)
: web_contents_(web_contents.GetWeakPtr()),
fallback_letter_(GenerateIconLetterFromName(web_contents.GetTitle())),
final_callback_(std::move(callback)) {}
DocumentIconFetcherTask::~DocumentIconFetcherTask() {
// If the final_callback_ has not been run yet, prevent that from hanging by
// returning an error message. Although this function calls
// `OnIconFetchingCompleteSelfDestruct()`, for this use-case, self-destruction
// will not occur since this is being triggered as part of destruction itself.
if (!final_callback_.is_null()) {
OnIconFetchingCompleteSelfDestruct(
base::unexpected(FetchIconsForDocumentError::kTaskDestroyed));
}
}
void DocumentIconFetcherTask::StartIconFetching() {
mojo::AssociatedRemote<webapps::mojom::WebPageMetadataAgent> metadata_agent;
web_contents_->GetPrimaryMainFrame()
->GetRemoteAssociatedInterfaces()
->GetInterface(&metadata_agent);
// Set the error handler so that we can run abort this task if the WebContents
// or the RenderFrameHost are destroyed and the connection to
// ChromeRenderFrame is lost.
metadata_agent.set_disconnect_handler(
base::BindOnce(&DocumentIconFetcherTask::OnMetadataFetchError,
weak_factory_.GetWeakPtr()));
// Bind the InterfacePtr into the callback so that it's kept alive
// until there's either a connection error or a response.
auto* web_page_metadata_proxy = metadata_agent.get();
web_page_metadata_proxy->GetWebPageMetadata(
base::BindOnce(&DocumentIconFetcherTask::OnWebPageMetadataObtained,
weak_factory_.GetWeakPtr(), std::move(metadata_agent)));
}
void DocumentIconFetcherTask::OnMetadataFetchError() {
OnIconFetchingCompleteSelfDestruct(
base::unexpected(FetchIconsForDocumentError::kMetadataFetchFailed));
}
void DocumentIconFetcherTask::OnWebPageMetadataObtained(
mojo::AssociatedRemote<webapps::mojom::WebPageMetadataAgent> metadata_agent,
webapps::mojom::WebPageMetadataPtr web_page_metadata) {
metadata_fetch_complete_ = true;
std::vector<GURL> icons;
for (const auto& icon_info : web_page_metadata->icons) {
icons.push_back(icon_info->url);
}
for (const auto& favicon_url : web_contents_->GetFaviconURLs()) {
icons.push_back(favicon_url->icon_url);
}
// Eliminate duplicates.
base::flat_set<GURL> icon_set(std::move(icons));
num_pending_image_requests_ = icon_set.size();
for (const GURL& url : icon_set) {
web_contents_->DownloadImage(
url, /*is_favicon=*/true,
/*preferred_size=*/gfx::Size(),
/*max_bitmap_size=*/0, /*bypass_cache=*/false,
base::BindOnce(&DocumentIconFetcherTask::DidDownloadFavicon,
weak_factory_.GetWeakPtr()));
}
MaybeCompleteImageDownloadAndSelfDestruct();
}
void DocumentIconFetcherTask::DidDownloadFavicon(
int id,
int http_status_code,
const GURL& image_url,
const std::vector<SkBitmap>& bitmaps,
const std::vector<gfx::Size>& sizes) {
icons_.reserve(icons_.size() + bitmaps.size());
for (const SkBitmap& bitmap : bitmaps) {
if (bitmap.drawsNothing()) {
continue;
}
icons_.push_back(bitmap);
}
--num_pending_image_requests_;
MaybeCompleteImageDownloadAndSelfDestruct();
}
void DocumentIconFetcherTask::MaybeCompleteImageDownloadAndSelfDestruct() {
if (!metadata_fetch_complete_ || num_pending_image_requests_ > 0) {
return;
}
OnIconFetchingCompleteSelfDestruct(base::ok(std::move(icons_)));
}
void DocumentIconFetcherTask::OnIconFetchingCompleteSelfDestruct(
FetchIconsFromDocumentResult result) {
if (result.has_value() && result->empty()) {
result->push_back(GenerateBitmap(128, fallback_letter_));
}
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(final_callback_), result));
}
} // namespace shortcuts