blob: 2f102cbd899cfbe1b6f61c974e62b88b691f82c5 [file] [log] [blame]
// Copyright 2020 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 "chrome/browser/apps/intent_helper/intent_picker_helpers.h"
#include <string>
#include <utility>
#include "base/feature_list.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/apps/intent_helper/intent_picker_auto_display_service.h"
#include "chrome/browser/apps/intent_helper/intent_picker_internal.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/intent_picker_tab_helper.h"
#include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
#include "chrome/common/chrome_features.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/apps/intent_helper/chromeos_intent_picker_helpers.h"
#include "chrome/browser/apps/intent_helper/metrics/intent_handling_metrics.h"
#elif BUILDFLAG(IS_MAC)
#include "chrome/browser/apps/intent_helper/mac_intent_picker_helpers.h"
#endif // BUILDFLAG(IS_CHROMEOS)
namespace apps {
namespace {
std::vector<IntentPickerAppInfo> FindAppsForUrl(
content::WebContents* web_contents,
const GURL& url,
std::vector<IntentPickerAppInfo> apps) {
#if BUILDFLAG(IS_MAC)
// On the Mac, if there is a Universal Link, it goes first.
if (absl::optional<IntentPickerAppInfo> mac_app = FindMacAppForUrl(url))
apps.push_back(std::move(mac_app.value()));
#endif // BUILDFLAG(IS_MAC)
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
auto* proxy = AppServiceProxyFactory::GetForProfile(profile);
std::vector<std::string> app_ids =
proxy->GetAppIdsForUrl(url, /*exclude_browsers=*/true);
for (const std::string& app_id : app_ids) {
proxy->AppRegistryCache().ForOneApp(
app_id, [&apps](const AppUpdate& update) {
apps.emplace(apps.begin(), GetPickerEntryType(update.AppType()),
ui::ImageModel(), update.AppId(), update.Name());
});
}
return apps;
}
void LaunchAppFromIntentPicker(content::WebContents* web_contents,
const GURL& url,
const std::string& launch_name,
PickerEntryType app_type) {
#if BUILDFLAG(IS_CHROMEOS)
LaunchAppFromIntentPickerChromeOs(web_contents, url, launch_name, app_type);
#else
switch (app_type) {
case PickerEntryType::kWeb:
web_app::ReparentWebContentsIntoAppBrowser(web_contents, launch_name);
break;
case PickerEntryType::kMacOs:
#if BUILDFLAG(IS_MAC)
LaunchMacApp(url, launch_name);
break;
#endif // BUILDFLAG(IS_MAC)
case PickerEntryType::kArc:
case PickerEntryType::kDevice:
case PickerEntryType::kUnknown:
NOTREACHED();
}
#endif // BUILDFLAG(IS_CHROMEOS)
}
void OnIntentPickerClosed(
content::WebContents* web_contents,
IntentPickerAutoDisplayService* ui_auto_display_service,
const GURL& url,
const std::string& launch_name,
PickerEntryType entry_type,
IntentPickerCloseReason close_reason,
bool should_persist) {
#if BUILDFLAG(IS_CHROMEOS)
OnIntentPickerClosedChromeOs(web_contents, ui_auto_display_service,
PickerShowState::kOmnibox, url, launch_name,
entry_type, close_reason, should_persist);
#else
const bool should_launch_app =
close_reason == apps::IntentPickerCloseReason::OPEN_APP;
if (should_launch_app) {
LaunchAppFromIntentPicker(web_contents, url, launch_name, entry_type);
}
if (entry_type == PickerEntryType::kUnknown &&
close_reason == IntentPickerCloseReason::DIALOG_DEACTIVATED &&
ui_auto_display_service) {
// We reach here if the picker was closed without an app being chosen, e.g.
// due to the tab being closed. Keep count of this scenario so we can stop
// the UI from showing after 2+ dismissals.
ui_auto_display_service->IncrementCounter(url);
}
#endif // BUILDFLAG(IS_CHROMEOS)
}
void OnAppIconsLoaded(content::WebContents* web_contents,
IntentPickerAutoDisplayService* ui_auto_display_service,
const GURL& url,
std::vector<IntentPickerAppInfo> apps) {
ShowIntentPickerBubbleForApps(
web_contents, std::move(apps),
#if BUILDFLAG(IS_CHROMEOS)
/*show_stay_in_chrome=*/true,
/*show_remember_selection=*/true,
#else
/*show_stay_in_chrome=*/false,
/*show_remember_selection=*/false,
#endif // BUILDFLAG(IS_CHROMEOS)
base::BindOnce(&OnIntentPickerClosed, web_contents,
ui_auto_display_service, url));
}
std::vector<IntentPickerAppInfo> GetAppsForIntentPicker(
content::WebContents* web_contents) {
std::vector<IntentPickerAppInfo> apps = {};
if (!ShouldCheckAppsForUrl(web_contents))
return apps;
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
if (!AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile))
return apps;
const GURL& url = web_contents->GetLastCommittedURL();
apps = FindAppsForUrl(web_contents, url, std::move(apps));
return apps;
}
} // namespace
// for chromeos, this should apply when navigation is not deferred for pwa only
// case also when navigation deferred and then resumed
bool MaybeShowIntentPicker(content::NavigationHandle* navigation_handle) {
content::WebContents* web_contents = navigation_handle->GetWebContents();
std::vector<IntentPickerAppInfo> apps = GetAppsForIntentPicker(web_contents);
bool show_intent_icon = !apps.empty();
#if BUILDFLAG(IS_CHROMEOS)
MaybeShowIntentPickerBubble(navigation_handle, std::move(apps));
#endif // BUILDFLAG(IS_CHROMEOS)
return show_intent_icon;
}
void MaybeShowIntentPicker(content::WebContents* web_contents) {
std::vector<IntentPickerAppInfo> apps = GetAppsForIntentPicker(web_contents);
IntentPickerTabHelper::SetShouldShowIcon(web_contents, !apps.empty());
}
void ShowIntentPickerBubble(content::WebContents* web_contents,
const GURL& url) {
std::vector<IntentPickerAppInfo> apps = FindAppsForUrl(web_contents, url, {});
if (apps.empty())
return;
#if BUILDFLAG(IS_CHROMEOS)
apps::IntentHandlingMetrics::RecordIntentPickerIconEvent(
apps::IntentHandlingMetrics::IntentPickerIconEvent::kIconClicked);
#endif
if (apps.size() == 1 &&
base::FeatureList::IsEnabled(features::kLinkCapturingUiUpdate)) {
LaunchAppFromIntentPicker(web_contents, url, apps[0].launch_name,
apps[0].type);
return;
}
IntentPickerTabHelper::LoadAppIcons(
web_contents, std::move(apps),
base::BindOnce(&OnAppIconsLoaded, web_contents,
/*ui_auto_display_service=*/nullptr, url));
}
} // namespace apps