blob: 4c0e69dacad5d26017367a518ef1c2651ba17bc3 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/installedapp/installed_app_provider_impl.h"
#include <algorithm>
#include "base/check_is_test.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/functional/concurrent_callbacks.h"
#include "base/task/task_traits.h"
#include "build/build_config.h"
#include "content/browser/browser_thread_impl.h"
#if !BUILDFLAG(IS_ANDROID)
#include "content/browser/installedapp/fetch_related_web_apps_task.h"
#endif // !BUILDFLAG(IS_ANDROID)
#include "content/common/features.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/common/content_features.h"
#include "mojo/public/cpp/bindings/clone_traits.h"
#include "third_party/blink/public/mojom/installedapp/related_application.mojom.h"
#if BUILDFLAG(IS_WIN)
#include "content/browser/installedapp/fetch_related_win_apps_task.h"
#include "content/browser/installedapp/native_win_app_fetcher.h"
#include "content/browser/installedapp/native_win_app_fetcher_impl.h"
#endif
namespace content {
namespace {
constexpr int kMaxNumberOfQueriedApps = 10;
#if BUILDFLAG(IS_WIN)
std::unique_ptr<NativeWinAppFetcher> CreateNativeWinAppFetcher() {
return std::make_unique<NativeWinAppFetcherImpl>();
}
#endif // BUILDFLAG(IS_WIN)
}
InstalledAppProviderImpl::InstalledAppProviderImpl(
RenderFrameHost& render_frame_host,
mojo::PendingReceiver<blink::mojom::InstalledAppProvider> pending_receiver)
: DocumentService(render_frame_host, std::move(pending_receiver)) {
#if BUILDFLAG(IS_WIN)
native_win_app_fetcher_factory_ =
base::BindRepeating(&CreateNativeWinAppFetcher);
#endif // BUILDFLAG(IS_WIN)
}
InstalledAppProviderImpl::~InstalledAppProviderImpl() = default;
void InstalledAppProviderImpl::FilterInstalledApps(
std::vector<blink::mojom::RelatedApplicationPtr> related_apps,
const GURL& manifest_url,
bool add_saved_related_applications,
FilterInstalledAppsCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!base::FeatureList::IsEnabled(features::kInstalledAppProvider)) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback),
std::vector<blink::mojom::RelatedApplicationPtr>()));
return;
}
// If we failed to retrieve the manifest, fall back to the saved
// related_apps.
if (add_saved_related_applications) {
WebContents* web_contents =
WebContents::FromRenderFrameHost(&render_frame_host());
WebContentsDelegate* delegate = web_contents->GetDelegate();
if (delegate) {
std::vector<blink::mojom::RelatedApplicationPtr> saved_related_apps =
delegate->GetSavedRelatedApplications(web_contents);
related_apps.insert(related_apps.end(),
std::make_move_iterator(saved_related_apps.begin()),
std::make_move_iterator(saved_related_apps.end()));
}
}
if (related_apps.size() > kMaxNumberOfQueriedApps) {
related_apps.resize(kMaxNumberOfQueriedApps);
}
base::ConcurrentCallbacks<FetchRelatedAppsTaskResult> concurrent;
#if BUILDFLAG(IS_WIN)
if (base::FeatureList::IsEnabled(features::kFilterInstalledAppsWinMatching)) {
StartTask(std::make_unique<FetchRelatedWinAppsTask>(
native_win_app_fetcher_factory_.Run()),
related_apps, concurrent.CreateCallback());
}
#endif
#if !BUILDFLAG(IS_ANDROID)
if (base::FeatureList::IsEnabled(
features::kFilterInstalledAppsWebAppMatching)) {
StartTask(std::make_unique<FetchRelatedWebAppsTask>(
render_frame_host().GetBrowserContext()),
related_apps, concurrent.CreateCallback());
}
#endif // !BUILDFLAG(IS_ANDROID)
std::move(concurrent)
.Done(base::BindOnce(&InstalledAppProviderImpl::AggregateTaskResults,
weak_ptr_factory_.GetWeakPtr(),
std::move(callback)));
}
void InstalledAppProviderImpl::StartTask(
std::unique_ptr<FetchRelatedAppsTask> task,
std::vector<blink::mojom::RelatedApplicationPtr>& related_apps,
FetchRelatedAppsTaskCallback callback) {
auto* task_ptr = task.get();
tasks_.insert({task_ptr, std::move(task)});
auto erase_task_cb = base::BindOnce(&InstalledAppProviderImpl::EraseTask,
weak_ptr_factory_.GetWeakPtr(), task_ptr);
task_ptr->Start(render_frame_host().GetLastCommittedURL(),
mojo::Clone(related_apps),
std::move(callback).Then(std::move(erase_task_cb)));
}
void InstalledAppProviderImpl::EraseTask(FetchRelatedAppsTask* task) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
int num_removed = tasks_.erase(task);
CHECK(num_removed != 0);
}
void InstalledAppProviderImpl::AggregateTaskResults(
FilterInstalledAppsCallback callback,
std::vector<FetchRelatedAppsTaskResult> task_result_list) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::vector<blink::mojom::RelatedApplicationPtr> matched_apps;
for (FetchRelatedAppsTaskResult& result : task_result_list) {
for (blink::mojom::RelatedApplicationPtr& app : result) {
matched_apps.push_back(std::move(app));
}
}
// |is_off_the_record| should be checked at the end to prevent clients from
// using timing functions to test if the user is in private.
bool is_off_the_record =
render_frame_host().GetProcess()->GetBrowserContext()->IsOffTheRecord();
if (is_off_the_record) {
return std::move(callback).Run(
std::vector<blink::mojom::RelatedApplicationPtr>());
}
return std::move(callback).Run(mojo::Clone(matched_apps));
}
#if BUILDFLAG(IS_WIN)
void InstalledAppProviderImpl::SetNativeWinAppFetcherFactoryForTesting(
base::RepeatingCallback<std::unique_ptr<NativeWinAppFetcher>()> factory) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK_IS_TEST();
native_win_app_fetcher_factory_ = std::move(factory);
}
#endif // BUILDFLAG(IS_WIN)
// static
void InstalledAppProviderImpl::Create(
RenderFrameHost& host,
mojo::PendingReceiver<blink::mojom::InstalledAppProvider> receiver) {
if (host.GetParentOrOuterDocument()) {
// The renderer is supposed to disallow this and we shouldn't end up here.
mojo::ReportBadMessage(
"InstalledAppProvider only allowed for outermost main frame.");
return;
}
// The object is bound to the lifetime of |host|'s current document and the
// mojo connection. See DocumentService for details.
new InstalledAppProviderImpl(host, std::move(receiver));
}
// static
InstalledAppProviderImpl* InstalledAppProviderImpl::CreateForTesting(
RenderFrameHost& render_frame_host,
mojo::PendingReceiver<blink::mojom::InstalledAppProvider> receiver) {
CHECK_IS_TEST();
return new InstalledAppProviderImpl(render_frame_host, std::move(receiver));
}
} // namespace content