| // Copyright 2020 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/ash/remote_apps/remote_apps_impl.h" |
| |
| #include <algorithm> |
| #include <optional> |
| #include <utility> |
| |
| #include "base/containers/contains.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "chrome/browser/ash/remote_apps/remote_apps_manager.h" |
| #include "chrome/browser/ash/remote_apps/remote_apps_manager_factory.h" |
| #include "chrome/browser/ash/remote_apps/remote_apps_types.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/features/behavior_feature.h" |
| #include "extensions/common/features/feature.h" |
| #include "extensions/common/features/feature_provider.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| constexpr char kErrNotReady[] = "Manager for remote apps is not ready"; |
| constexpr char kErrFolderIdDoesNotExist[] = "Folder ID provided does not exist"; |
| constexpr char kErrAppIdDoesNotExist[] = "App ID provided does not exist"; |
| constexpr char kErrFailedToPinAnApp[] = |
| "Invalid app ID or corresponding app is already pinned"; |
| constexpr char kErrPinningMultipleAppsNotSupported[] = |
| "Pinning multiple apps is not yet supported"; |
| |
| static bool g_bypass_checks_for_testing_ = false; |
| |
| } // namespace |
| |
| // static |
| bool RemoteAppsImpl::IsMojoPrivateApiAllowed( |
| content::RenderFrameHost* render_frame_host, |
| const extensions::Extension* extension) { |
| if (!render_frame_host || !extension) |
| return false; |
| |
| if (g_bypass_checks_for_testing_) |
| return true; |
| |
| const extensions::Feature* feature = |
| extensions::FeatureProvider::GetBehaviorFeature( |
| extensions::behavior_feature::kImprivataInSessionExtension); |
| DCHECK(feature); |
| if (!feature->IsAvailableToExtension(extension).is_available()) |
| return false; |
| |
| Profile* profile = |
| Profile::FromBrowserContext(render_frame_host->GetBrowserContext()); |
| DCHECK(profile); |
| // RemoteApps are available for managed guest sessions and regular user |
| // sessions. |
| if (!RemoteAppsManagerFactory::GetForProfile(profile)) |
| return false; |
| |
| return true; |
| } |
| |
| // static |
| void RemoteAppsImpl::SetBypassChecksForTesting(bool bypass_checks_for_testing) { |
| g_bypass_checks_for_testing_ = bypass_checks_for_testing; |
| } |
| |
| RemoteAppsImpl::RemoteAppsImpl(RemoteAppsManager* manager) : manager_(manager) { |
| DCHECK(manager); |
| app_launch_observers_with_source_id_.set_disconnect_handler( |
| base::BindRepeating(&RemoteAppsImpl::DisconnectHandler, |
| base::Unretained(this))); |
| } |
| |
| RemoteAppsImpl::~RemoteAppsImpl() = default; |
| |
| void RemoteAppsImpl::BindRemoteAppsAndAppLaunchObserver( |
| const std::optional<std::string>& source_id, |
| mojo::PendingReceiver<chromeos::remote_apps::mojom::RemoteApps> |
| pending_remote_apps, |
| mojo::PendingRemote<chromeos::remote_apps::mojom::RemoteAppLaunchObserver> |
| pending_observer) { |
| receivers_.Add(this, std::move(pending_remote_apps)); |
| if (!source_id) { |
| app_launch_broadcast_observers_.Add(std::move(pending_observer)); |
| return; |
| } |
| |
| const mojo::RemoteSetElementId& remote_id = |
| app_launch_observers_with_source_id_.Add( |
| mojo::Remote<chromeos::remote_apps::mojom::RemoteAppLaunchObserver>( |
| std::move(pending_observer))); |
| source_id_to_remote_id_map_.insert( |
| std::pair<std::string, mojo::RemoteSetElementId>(*source_id, remote_id)); |
| } |
| |
| void RemoteAppsImpl::AddFolder(const std::string& name, |
| bool add_to_front, |
| AddFolderCallback callback) { |
| const std::string& folder_id = manager_->AddFolder(name, add_to_front); |
| std::move(callback).Run( |
| chromeos::remote_apps::mojom::AddFolderResult::NewFolderId(folder_id)); |
| } |
| |
| void RemoteAppsImpl::AddApp(const std::string& source_id, |
| const std::string& name, |
| const std::string& folder_id, |
| const GURL& icon_url, |
| bool add_to_front, |
| AddAppCallback callback) { |
| manager_->AddApp( |
| source_id, name, folder_id, icon_url, add_to_front, |
| base::BindOnce(&RemoteAppsImpl::OnAppAdded, weak_factory_.GetWeakPtr(), |
| std::move(callback))); |
| } |
| |
| void RemoteAppsImpl::DeleteApp(const std::string& app_id, |
| DeleteAppCallback callback) { |
| ash::RemoteAppsError error = manager_->DeleteApp(app_id); |
| |
| switch (error) { |
| case RemoteAppsError::kNotReady: |
| std::move(callback).Run(kErrNotReady); |
| return; |
| case RemoteAppsError::kNone: |
| std::move(callback).Run(std::nullopt); |
| return; |
| case RemoteAppsError::kAppIdDoesNotExist: |
| std::move(callback).Run(kErrAppIdDoesNotExist); |
| return; |
| case RemoteAppsError::kFolderIdDoesNotExist: |
| case RemoteAppsError::kFailedToPinAnApp: |
| case RemoteAppsError::kPinningMultipleAppsNotSupported: |
| // Errors specific to other methods. |
| NOTREACHED(); |
| } |
| } |
| |
| void RemoteAppsImpl::SortLauncherWithRemoteAppsFirst( |
| SortLauncherWithRemoteAppsFirstCallback callback) { |
| manager_->SortLauncherWithRemoteAppsFirst(); |
| std::move(callback).Run(std::nullopt); |
| } |
| |
| void RemoteAppsImpl::SetPinnedApps(const std::vector<std::string>& app_ids, |
| SetPinnedAppsCallback callback) { |
| ash::RemoteAppsError error = manager_->SetPinnedApps(app_ids); |
| switch (error) { |
| case RemoteAppsError::kNone: |
| std::move(callback).Run(std::nullopt); |
| return; |
| case RemoteAppsError::kFailedToPinAnApp: |
| std::move(callback).Run(kErrFailedToPinAnApp); |
| return; |
| case RemoteAppsError::kPinningMultipleAppsNotSupported: |
| std::move(callback).Run(kErrPinningMultipleAppsNotSupported); |
| return; |
| case RemoteAppsError::kAppIdDoesNotExist: |
| case RemoteAppsError::kFolderIdDoesNotExist: |
| case RemoteAppsError::kNotReady: |
| // Errors specific to other methods. |
| NOTREACHED(); |
| } |
| } |
| |
| void RemoteAppsImpl::OnAppLaunched(const std::string& source_id, |
| const std::string& app_id) { |
| // Dispatch events to broadcast observers. |
| for (auto& observer : app_launch_broadcast_observers_) |
| observer->OnRemoteAppLaunched(app_id, source_id); |
| |
| // Find remote associated with `source_id` and dispatch event to it. |
| auto it = source_id_to_remote_id_map_.find(source_id); |
| if (it == source_id_to_remote_id_map_.end()) |
| return; |
| |
| chromeos::remote_apps::mojom::RemoteAppLaunchObserver* observer = |
| app_launch_observers_with_source_id_.Get(it->second); |
| if (!observer) |
| return; |
| |
| observer->OnRemoteAppLaunched(app_id, source_id); |
| } |
| |
| void RemoteAppsImpl::OnAppAdded(AddAppCallback callback, |
| const std::string& app_id, |
| RemoteAppsError error) { |
| switch (error) { |
| case RemoteAppsError::kNotReady: |
| std::move(callback).Run( |
| chromeos::remote_apps::mojom::AddAppResult::NewError(kErrNotReady)); |
| return; |
| case RemoteAppsError::kFolderIdDoesNotExist: |
| std::move(callback).Run( |
| chromeos::remote_apps::mojom::AddAppResult::NewError( |
| kErrFolderIdDoesNotExist)); |
| return; |
| case RemoteAppsError::kNone: |
| std::move(callback).Run( |
| chromeos::remote_apps::mojom::AddAppResult::NewAppId(app_id)); |
| return; |
| case RemoteAppsError::kAppIdDoesNotExist: |
| case RemoteAppsError::kFailedToPinAnApp: |
| case RemoteAppsError::kPinningMultipleAppsNotSupported: |
| // Errors specific to other methods. |
| NOTREACHED(); |
| } |
| } |
| |
| void RemoteAppsImpl::DisconnectHandler(mojo::RemoteSetElementId id) { |
| const auto& it = std::ranges::find(source_id_to_remote_id_map_, id, |
| &SourceToRemoteIds::value_type::second); |
| |
| if (it == source_id_to_remote_id_map_.end()) |
| return; |
| |
| source_id_to_remote_id_map_.erase(it); |
| } |
| |
| } // namespace ash |