blob: 4b28e0318b5e9e6a8b2d6f0063290ff58fcd2825 [file] [log] [blame]
// Copyright (c) 2012 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/ui/ash/launcher/launcher_controller_helper.h"
#include <vector>
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/arc/arc_session_manager.h"
#include "chrome/browser/chromeos/arc/arc_util.h"
#include "chrome/browser/chromeos/crostini/crostini_registry_service.h"
#include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h"
#include "chrome/browser/chromeos/crostini/crostini_util.h"
#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/extensions/launch_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
#include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
#include "chrome/browser/ui/app_list/internal_app/internal_app_metadata.h"
#include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h"
#include "chrome/browser/ui/ash/launcher/arc_app_window_launcher_controller.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/extensions/app_launch_params.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/browser/ui/extensions/extension_enable_flow.h"
#include "chrome/browser/web_applications/components/web_app_helpers.h"
#include "chrome/browser/web_applications/extensions/bookmark_app_util.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "components/arc/arc_util.h"
#include "components/arc/metrics/arc_metrics_constants.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_set.h"
#include "net/base/url_util.h"
#include "ui/display/types/display_constants.h"
#include "ui/events/event_constants.h"
namespace {
const extensions::Extension* GetExtensionForTab(Profile* profile,
content::WebContents* tab) {
extensions::ExtensionService* extension_service =
extensions::ExtensionSystem::Get(profile)->extension_service();
if (!extension_service || !extension_service->extensions_enabled())
return nullptr;
// Note: It is possible to come here after a tab got removed form the browser
// before it gets destroyed, in which case there is no browser.
Browser* browser = chrome::FindBrowserWithWebContents(tab);
extensions::ExtensionRegistry* registry =
extensions::ExtensionRegistry::Get(profile);
// Use the Browser's app name to determine the extension for app windows and
// use the tab's url for app tabs.
if (browser && browser->is_app()) {
return registry->GetExtensionById(
web_app::GetAppIdFromApplicationName(browser->app_name()),
extensions::ExtensionRegistry::EVERYTHING);
}
const GURL url = tab->GetURL();
const extensions::ExtensionSet& extensions = registry->enabled_extensions();
const extensions::Extension* extension = extensions.GetAppByURL(url);
if (extension && !extensions::LaunchesInWindow(profile, extension))
return extension;
// Bookmark app windows should match their launch url extension despite
// their web extents.
if (extensions::util::IsNewBookmarkAppsEnabled()) {
for (const auto& i : extensions) {
if (i.get()->from_bookmark() &&
extensions::IsInNavigationScopeForLaunchUrl(
extensions::AppLaunchInfo::GetLaunchWebURL(i.get()), url) &&
!extensions::LaunchesInWindow(profile, i.get())) {
return i.get();
}
}
}
return nullptr;
}
const extensions::Extension* GetExtensionByID(Profile* profile,
const std::string& id) {
return extensions::ExtensionRegistry::Get(profile)->GetExtensionById(
id, extensions::ExtensionRegistry::EVERYTHING);
}
std::string GetSourceFromAppListSource(ash::ShelfLaunchSource source) {
switch (source) {
case ash::LAUNCH_FROM_APP_LIST:
return std::string(extension_urls::kLaunchSourceAppList);
case ash::LAUNCH_FROM_APP_LIST_SEARCH:
return std::string(extension_urls::kLaunchSourceAppListSearch);
default:
return std::string();
}
}
} // namespace
LauncherControllerHelper::LauncherControllerHelper(Profile* profile)
: profile_(profile) {}
LauncherControllerHelper::~LauncherControllerHelper() {}
// static
base::string16 LauncherControllerHelper::GetAppTitle(
Profile* profile,
const std::string& app_id) {
if (app_id.empty())
return base::string16();
// Get the title if the app is an ARC app.
if (arc::IsArcItem(profile, app_id)) {
std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
ArcAppListPrefs::Get(profile)->GetApp(
arc::ArcAppShelfId::FromString(app_id).app_id());
DCHECK(app_info.get());
if (app_info)
return base::UTF8ToUTF16(app_info->name);
}
crostini::CrostiniRegistryService* registry_service =
crostini::CrostiniRegistryServiceFactory::GetForProfile(profile);
if (registry_service && registry_service->IsCrostiniShelfAppId(app_id)) {
base::Optional<crostini::CrostiniRegistryService::Registration>
registration = registry_service->GetRegistration(app_id);
if (!registration)
return base::string16();
return base::UTF8ToUTF16(registration->Name());
}
const extensions::Extension* extension = GetExtensionByID(profile, app_id);
if (extension)
return base::UTF8ToUTF16(extension->name());
if (app_list::IsInternalApp(app_id))
return app_list::GetInternalAppNameById(app_id);
return base::string16();
}
std::string LauncherControllerHelper::GetAppID(content::WebContents* tab) {
ProfileManager* profile_manager = g_browser_process->profile_manager();
if (profile_manager) {
const std::vector<Profile*> profile_list =
profile_manager->GetLoadedProfiles();
if (!profile_list.empty()) {
for (auto* i : profile_list) {
const extensions::Extension* extension = GetExtensionForTab(i, tab);
if (extension)
return extension->id();
}
return std::string();
}
}
// If there is no profile manager we only use the known profile.
const extensions::Extension* extension = GetExtensionForTab(profile_, tab);
return extension ? extension->id() : std::string();
}
bool LauncherControllerHelper::IsValidIDForCurrentUser(
const std::string& id) const {
const ArcAppListPrefs* arc_prefs = GetArcAppListPrefs();
if (arc_prefs && arc_prefs->IsRegistered(id))
return true;
crostini::CrostiniRegistryService* registry_service =
crostini::CrostiniRegistryServiceFactory::GetForProfile(profile_);
if (registry_service && registry_service->IsCrostiniShelfAppId(id)) {
return crostini::IsCrostiniUIAllowedForProfile(profile_) &&
registry_service->GetRegistration(id).has_value();
}
if (app_list::IsInternalApp(id))
return true;
if (!GetExtensionByID(profile_, id))
return false;
if (id == arc::kPlayStoreAppId) {
if (!arc::IsArcAllowedForProfile(profile()) || !arc::IsPlayStoreAvailable())
return false;
const arc::ArcSessionManager* arc_session_manager =
arc::ArcSessionManager::Get();
DCHECK(arc_session_manager);
if (!arc_session_manager->IsAllowed())
return false;
if (!arc::IsArcPlayStoreEnabledForProfile(profile()) &&
arc::IsArcPlayStoreEnabledPreferenceManagedForProfile(profile()))
return false;
}
return true;
}
void LauncherControllerHelper::LaunchApp(const ash::ShelfID& id,
ash::ShelfLaunchSource source,
int event_flags,
int64_t display_id) {
// Handle recording app launch source from the Shelf in Demo Mode.
if (source == ash::ShelfLaunchSource::LAUNCH_FROM_SHELF) {
chromeos::DemoSession::RecordAppLaunchSourceIfInDemoMode(
chromeos::DemoSession::AppLaunchSource::kShelf);
}
const std::string& app_id = id.app_id;
const ArcAppListPrefs* arc_prefs = GetArcAppListPrefs();
if (arc_prefs && arc_prefs->IsRegistered(app_id)) {
arc::LaunchApp(profile_, app_id, event_flags,
arc::UserInteractionType::APP_STARTED_FROM_SHELF,
display_id);
return;
}
crostini::CrostiniRegistryService* registry_service =
crostini::CrostiniRegistryServiceFactory::GetForProfile(profile_);
if (registry_service && registry_service->IsCrostiniShelfAppId(app_id)) {
// This expects a valid app list id, which is fine as we only get here for
// shelf entries associated with an actual app and not arbitrary Crostini
// windows.
crostini::LaunchCrostiniApp(profile_, app_id, display_id);
return;
}
if (app_list::IsInternalApp(app_id)) {
app_list::OpenInternalApp(app_id, profile_, event_flags);
return;
}
// |extension| could be null when it is being unloaded for updating.
const extensions::Extension* extension = GetExtensionByID(profile_, app_id);
if (!extension)
return;
if (!extensions::util::IsAppLaunchableWithoutEnabling(app_id, profile_)) {
// Do nothing if there is already a running enable flow.
if (extension_enable_flow_)
return;
extension_enable_flow_.reset(
new ExtensionEnableFlow(profile_, app_id, this));
extension_enable_flow_->StartForNativeWindow(nullptr);
return;
}
// The app will be created for the currently active profile.
AppLaunchParams params = CreateAppLaunchParamsWithEventFlags(
profile_, extension, event_flags, extensions::SOURCE_APP_LAUNCHER,
display_id);
if ((source == ash::LAUNCH_FROM_APP_LIST ||
source == ash::LAUNCH_FROM_APP_LIST_SEARCH) &&
app_id == extensions::kWebStoreAppId) {
// Get the corresponding source string.
std::string source_value = GetSourceFromAppListSource(source);
// Set an override URL to include the source.
GURL extension_url = extensions::AppLaunchInfo::GetFullLaunchURL(extension);
params.override_url = net::AppendQueryParameter(
extension_url, extension_urls::kWebstoreSourceField, source_value);
}
params.launch_id = id.launch_id;
OpenApplication(params);
}
ArcAppListPrefs* LauncherControllerHelper::GetArcAppListPrefs() const {
return ArcAppListPrefs::Get(profile_);
}
void LauncherControllerHelper::ExtensionEnableFlowFinished() {
LaunchApp(ash::ShelfID(extension_enable_flow_->extension_id()),
ash::LAUNCH_FROM_UNKNOWN, ui::EF_NONE, display::kInvalidDisplayId);
extension_enable_flow_.reset();
}
void LauncherControllerHelper::ExtensionEnableFlowAborted(bool user_initiated) {
extension_enable_flow_.reset();
}